From bde01e402c7368797784239dd69034ef28ce2331 Mon Sep 17 00:00:00 2001 From: slederer Date: Sat, 31 May 2025 22:19:10 +0200 Subject: [PATCH] add program to recover deleted files --- pcomp/Makefile | 1 + pcomp/make.bat | 1 + progs/recover.pas | 167 ++++++++++++++++++++++++++++++++++++++++++++++ utils/tdrimg.py | 1 + 4 files changed, 170 insertions(+) create mode 100644 progs/recover.pas diff --git a/pcomp/Makefile b/pcomp/Makefile index beb04c4..4200997 100644 --- a/pcomp/Makefile +++ b/pcomp/Makefile @@ -30,6 +30,7 @@ nativeprogs: nativecomp $(PCOMP) ../progs/dumpdir.pas $(PCOMP) ../progs/partmgr.pas $(PCOMP) ../progs/xfer.pas + $(PCOMP) ../progs/recover.pas $(SASM) ../lib/rommon.s $(SASM) -A ../lib/rommon.s ../lib/rom.mem diff --git a/pcomp/make.bat b/pcomp/make.bat index 7f9bc0a..0db1f59 100644 --- a/pcomp/make.bat +++ b/pcomp/make.bat @@ -27,6 +27,7 @@ py pcomp.py ..\progs\reclaim.pas py pcomp.py ..\progs\dumpdir.pas py pcomp.py ..\progs\partmgr.pas py pcomp.py ..\progs\xfer.pas +py pcomp.py ..\progs\recover.pas sasm ..\lib\rommon.s sasm -A ..\lib\rommon.s ..\lib\rom.mem diff --git a/progs/recover.pas b/progs/recover.pas new file mode 100644 index 0000000..cca6204 --- /dev/null +++ b/progs/recover.pas @@ -0,0 +1,167 @@ +(* Copyright 2025 Sebastian Lederer. See the file LICENSE.md for details *) +program recover; +const PageMargin = 4; +var filename:string; + volid:integer; + +(* we use some stuff internal to stdlib.pas *) +procedure getdirslot(volumeid:integer;slotNo:integer;var result:DirectorySlot;var error:integer); + external; +procedure openfile(volid:integer; slotno:integer; var dirslot:DirectorySlot; var aFile:File; mode:filemode); + external; + +function openvolume:integer; +var volid:integer; +begin + openvolume := -1; + + volid := findvolume(DefaultVolume); + if volid < 1 then + writeln('Volume ', DefaultVolume, ' not found.') + else + openvolume := volid; +end; + +procedure waitForKey; +var c:char; +begin + writeln; + writeln('-- press any key to continue --'); + c := conin; +end; + +procedure copyfile(volid:integer;slotno:integer;oldname,newname:string); +var srcfile,destfile:file; + dirslot:DirectorySlot; + error:integer; + ch:char; + count:integer; +begin + (* to open the deleted source file, we emulate parts + of the open procedure from stdlib *) + getdirslot(volid, slotno, dirslot, error); + if not (SlotDeleted in dirslot.flags) or (dirslot.name <> oldname) then + writeln('Invalid slot ', slotno) + else + begin + openfile(volid, slotno, dirslot, srcfile, ModeReadOnly); + if error <> 0 then + writeln('Error opening file from slot ', slotno, + ': ', ErrorStr(error)) + else + begin + open(destfile, newname, ModeCreate); + if IOResult(destfile) = IOFileExists then + begin + write('File ', newname, ' already exists, overwrite? [y/n] '); + readln(ch); + if ch in ['Y', 'y'] then + open(destfile, newname, ModeOverwrite); + end; + if IOResult(destfile) <> 0 then + writeln('Error opening ', newname, ': ', + ErrorStr(IOResult(destfile))) + else + begin + (* taken from shell.pas copyFile *) + write('Recovering from slot ', slotno, ' to ', newname, '...'); + count := 0; + while not eof(srcfile) do + begin + read(srcfile,ch); + write(destfile,ch); + count := count + 1; + if (count and 8191) = 0 then write('.'); + end; + writeln; + close(destfile); + end; + close(srcfile); + end; + end; +end; + +procedure recoverfile(volid:integer;wantedname:string); +var dirs:DirectorySlot; + i:integer; + lastSlot:integer; + error:integer; + screenW,screenH:integer; + count:integer; + datestr:string; + ftime:DateTime; + wantedslot:integer; + newname:string; + foundsomething:boolean; +begin + writeln('Files available for recovery:'); + foundsomething := false; + newname := ''; + + GetTermSize(screenW, screenH); + count := PageMargin; + + lastSlot := volumeTable[volid].part.dirSize - 1; + openvolumeid(volid); + + for i := 0 to lastSlot do + begin + getdirslot(volid, i, dirs, error); + with dirs do + begin + if (SlotFirst in flags) or (SlotDeleted in flags) then + if name = wantedname then + begin + ftime := GetDateTime(dirs.modTime); + datestr := DateStr(ftime) + ' ' + TimeStr(ftime, true); + write('slot ', i:4, name:34, sizeBytes:8, datestr:21, ' '); + if SlotFirst in flags then write('Cur'); + if SlotExtent in flags then write('Extent'); + if SlotReserved in flags then write('Resvd'); + if SlotDeleted in flags then write('Del'); + if SlotFree in flags then write('Free'); + if SlotEndScan in flags then write('End'); + writeln; + foundsomething := true; + count := count + 1; + if count >= screenH then + begin + count := PageMargin; + waitForKey; + end; + if SlotEndScan in flags then break; + end; + end; + end; + + if foundsomething then + begin + write('Slot no to recover> '); + readln(wantedslot); + if (wantedslot < 1) or (wantedslot >= volumeTable[volid].part.dirSize) then + writeln('Invalid slot number.') + else + begin + write('New filename> '); + readln(newname); + end; + + if length(newname) > 0 then + copyfile(volid, wantedslot, wantedname, newname); + end; + + closevolumeid(volid); +end; + +begin + if ParamCount > 0 then + filename := ParamStr(1) + else + begin + write('Filename to recover> '); + readln(filename); + end; + volid := openvolume; + if volid > 0 then + recoverfile(volid, filename); +end. diff --git a/utils/tdrimg.py b/utils/tdrimg.py index 1000bb8..a120250 100644 --- a/utils/tdrimg.py +++ b/utils/tdrimg.py @@ -535,6 +535,7 @@ def create_image_with_stuff(imgfile): 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) + slotnr = putfile("../progs/recover.prog", None , f, part, partstart, slotnr) listdir(f, part)