742 lines
18 KiB
ObjectPascal
742 lines
18 KiB
ObjectPascal
(* Copyright 2021-2024 Sebastian Lederer. See the file LICENSE.md for details *)
|
|
program partmgr;
|
|
const MaxPartitions = 32;
|
|
LastPartBlock = 7;
|
|
PartsPerBlock = 8;
|
|
|
|
var partTable:array[0..LastPartBlock] of PartitionTableBlock;
|
|
changed:array[0..LastPartBlock] of boolean;
|
|
detectedCardSize:integer;
|
|
cmd:char;
|
|
done:boolean;
|
|
lastPartNo:integer;
|
|
|
|
function flags2str(flags:PartFlags):string;
|
|
begin
|
|
flags2str := '';
|
|
|
|
if PartEnabled in flags then flags2str := flags2str + 'E ';
|
|
if PartBoot in flags then flags2str := flags2str + 'B ';
|
|
if PartLast in flags then flags2str := flags2str + 'L ';
|
|
if PartPhysical in flags then flags2str := flags2str + 'P ';
|
|
if PartDefault in flags then flags2str := flags2str + 'D ';
|
|
end;
|
|
|
|
function str2flags(var s:string):PartFlags;
|
|
begin
|
|
str2flags := [];
|
|
|
|
if 'E' in s then str2flags := str2flags + [PartEnabled];
|
|
if 'B' in s then str2flags := str2flags + [PartBoot];
|
|
if 'L' in s then str2flags := str2flags + [PartLast];
|
|
if 'P' in s then str2flags := str2flags + [PartPhysical];
|
|
if 'D' in s then str2flags := str2flags + [PartDefault];
|
|
end;
|
|
|
|
function sanitizeName(var name:string):string;
|
|
begin
|
|
if (length(name) <= 32) and (maxlength(name) = 32) then
|
|
sanitizeName := name
|
|
else
|
|
sanitizeName := '<invalid>';
|
|
end;
|
|
|
|
procedure changeNumber(prompt:string; var num:integer);
|
|
var buf:string;
|
|
err:integer;
|
|
digits:string;
|
|
begin
|
|
str(num, digits);
|
|
buf := prompt + ' [' + digits + ']> ';
|
|
write(buf:30);
|
|
readln(buf);
|
|
val(buf,num,err);
|
|
end;
|
|
|
|
|
|
procedure readPartTable;
|
|
var done:boolean;
|
|
curblk:integer;
|
|
error:integer;
|
|
devid:integer;
|
|
begin
|
|
done := false;
|
|
curblk := 0;
|
|
devid := 0; (* we only support one device *)
|
|
|
|
while not done do
|
|
begin
|
|
changed[curBlk] := false;
|
|
|
|
readPartBlk(curblk, partTable[curblk], error, devid);
|
|
if error <> 0 then
|
|
begin
|
|
done := true;
|
|
writeln('Error ', error,
|
|
' reading partition block ', curblk);
|
|
end
|
|
else
|
|
curblk := curblk + 1;
|
|
|
|
|
|
if curBlk > LastPartBlock then
|
|
done := true;
|
|
end;
|
|
end;
|
|
|
|
procedure writePartBlock(no:integer);
|
|
var error:integer;
|
|
devid:integer;
|
|
begin
|
|
devid := 0;
|
|
writePartBlk(no, partTable[no], error, devid);
|
|
if error <> 0 then
|
|
writeln('Error ', error,
|
|
' reading partition block ', no);
|
|
end;
|
|
|
|
procedure writePartitions;
|
|
var blkNo:integer;
|
|
begin
|
|
for blkNo := 0 to LastPartBlock do
|
|
begin
|
|
if changed[blkNo] then
|
|
begin
|
|
writeln('Writing back partition block ',blkNo);
|
|
writePartBlock(blkNo);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function getPartition(partNo:integer):Partition;
|
|
var blkNo:integer;
|
|
begin
|
|
blkNo := partNo div PartsPerBlock;
|
|
if (blkNo < 0) or (blkNo > LastPartBlock) then
|
|
writeln('internal error: invalid part no in getPartition')
|
|
else
|
|
getPartition := partTable[blkNo][partNo mod PartsPerBlock];
|
|
{ writeln('** getPartition: ', blkNo, ' ', partNo mod PartsPerBlock); }
|
|
end;
|
|
|
|
procedure putPartition(var part:Partition; partNo:integer);
|
|
var blkNo:integer;
|
|
begin
|
|
blkNo := partNo div PartsPerBlock;
|
|
{ writeln('** putPartition: ', blkNo, ' ', partNo mod PartsPerBlock); }
|
|
if (blkNo < 0) or (blkNo > LastPartBlock) then
|
|
writeln('internal error: invalid part no in getPartition')
|
|
else
|
|
begin
|
|
partTable[blkNo][partNo mod PartsPerBlock] := part;
|
|
changed[blkNo] := true;
|
|
end;
|
|
end;
|
|
|
|
function isEmptyPart(var part:Partition):boolean;
|
|
begin
|
|
isEmptyPart := (part.startBlock = 0) and (part.blocks = 0);
|
|
end;
|
|
|
|
procedure printPartTable;
|
|
var blkNo, partNo:integer;
|
|
partBlk:PartitionTableBlock;
|
|
part:Partition;
|
|
totalPartNo:integer;
|
|
begin
|
|
totalPartNo := 0;
|
|
|
|
writeln('Partition Table:');
|
|
writeln('No. ', 'Flags':11, 'Name':32, 'Start':10, 'Size':10);
|
|
|
|
for blkNo := 0 to LastPartBlock do
|
|
begin
|
|
partBlk := partTable[blkNo];
|
|
for partNo := 0 to PartsPerBlock - 1 do
|
|
begin
|
|
part := partBlk[partNo];
|
|
|
|
if not isEmptyPart(part) then
|
|
begin
|
|
write(totalPartNo:3, ': ', flags2Str(part.flags):11,
|
|
sanitizeName(part.name):32,
|
|
part.startBlock:10,
|
|
part.blocks:10);
|
|
if PartBoot in part.flags then write(' ', part.bootBlocks);
|
|
writeln;
|
|
lastPartNo := totalPartNo;
|
|
end;
|
|
totalPartNo := totalPartNo + 1;
|
|
end;
|
|
end;
|
|
writeln('Flags: P=Physical B=Boot E=Enabled L=Last D=Default');
|
|
end;
|
|
|
|
function askPartNo:integer;
|
|
var i:integer;
|
|
s:string;
|
|
begin
|
|
askPartNo := -1;
|
|
write('Enter partition number (0-', lastPartNo, ')> ');
|
|
readln(s);
|
|
if length(s) > 0 then
|
|
begin
|
|
val(s,askPartNo,i);
|
|
if i > 0 then
|
|
writeln('Invalid partition number');
|
|
end;
|
|
end;
|
|
|
|
function askConfirmPartNo:integer;
|
|
var partNo:integer;
|
|
part:Partition;
|
|
answer:char;
|
|
begin
|
|
askConfirmPartNo := -1;
|
|
|
|
partNo := askPartNo;
|
|
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
write('Any data on partition ', partNo,
|
|
' (', sanitizeName(part.name), ') ',
|
|
'will be destroyed. Sure [y/n]? ');
|
|
readln(answer);
|
|
if upcase(answer) = 'Y' then
|
|
askConfirmPartNo := partNo;
|
|
end;
|
|
end;
|
|
|
|
function guessExtentSize(blocks:integer):integer;
|
|
begin
|
|
if blocks >= 4194304 then (* 2 GB *)
|
|
guessExtentSize := 1048576 (* use 1MB extents *)
|
|
else
|
|
if blocks >= 1048576 then (* 512 MB *)
|
|
guessExtentSize := 524288 (* use 512K extents *)
|
|
else
|
|
if blocks >= 524288 then (* 256 MB *)
|
|
guessExtentSize := 131072
|
|
else
|
|
if blocks >= 262144 then (* 128 MB *)
|
|
guessExtentSize := 65536
|
|
else
|
|
if blocks >= 32768 then (* 16 MB *)
|
|
guessExtentSize := 16384
|
|
else
|
|
guessExtentSize := 8192;
|
|
end;
|
|
|
|
function getDirSize(extentSize,blocks:integer):integer;
|
|
begin
|
|
getDirSize := blocks div (extentSize div 512);
|
|
end;
|
|
|
|
procedure createFilesystem(partNo:integer); forward;
|
|
|
|
procedure addVolume;
|
|
var nextFreeBlock:integer;
|
|
nextBlock:integer;
|
|
extentSize:integer;
|
|
size:integer;
|
|
i:integer;
|
|
part:Partition;
|
|
maxBlocks:integer;
|
|
freeBlocks:integer;
|
|
newPartNo:integer;
|
|
newPart:Partition;
|
|
begin
|
|
part := getPartition(0);
|
|
maxBlocks := part.blocks;
|
|
nextFreeBlock := 0;
|
|
(* read all partitions *)
|
|
for i := 1 to lastPartNo do
|
|
begin
|
|
part := getPartition(i);
|
|
nextBlock := part.startBlock + part.blocks;
|
|
if nextBlock > nextFreeBlock then
|
|
nextFreeBlock := nextBlock;
|
|
end;
|
|
(* remember last used block *)
|
|
writeln('next free partition: ', lastPartNo + 1,
|
|
' next free block: ', nextFreeBlock);
|
|
|
|
freeBlocks := maxBlocks - nextFreeBlock;
|
|
|
|
if freeBlocks < 1 then
|
|
writeln('Cannot add partition - no free blocks after last partition.')
|
|
else
|
|
begin
|
|
newPartNo := lastPartNo + 1;
|
|
(* remove last partition flag on previous last partition *)
|
|
part.flags := part.flags - [PartLast];
|
|
putPartition(part, lastPartNo);
|
|
|
|
(* create new partition *)
|
|
size := freeBlocks;
|
|
changeNumber('Size (blocks)', size);
|
|
write('Name> ':30);
|
|
readln(newPart.name);
|
|
|
|
newPart.startBlock := nextFreeBlock;
|
|
newPart.blocks := size;
|
|
newPart.extentSize := guessExtentSize(size);
|
|
newPart.dirSize := getDirSize(newPart.extentSize, size);
|
|
newPart.bootBlocks := 0;
|
|
(* mark new partition as last partition *)
|
|
newPart.flags := [ PartEnabled, PartLast ];
|
|
putPartition(newPart, newPartNo);
|
|
|
|
writeln('Partition ', newPartNo, ' created, extent size:', newPart.extentSize,
|
|
' directory size: ', newPart.dirSize);
|
|
createFilesystem(newPartNo);
|
|
|
|
lastPartNo := lastPartNo + 1;
|
|
end;
|
|
end;
|
|
|
|
procedure renameVolume;
|
|
var partNo:integer;
|
|
newName:string;
|
|
part:Partition;
|
|
|
|
begin
|
|
partNo := askPartNo;
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
writeln('Old partition/volume name: ', sanitizeName(part.name));
|
|
write('New partion/volume name: ');
|
|
readln(newName);
|
|
if length(newName) > 0 then
|
|
begin
|
|
part.name := newName;
|
|
putPartition(part, partNo);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure toggleDefaultFlag;
|
|
var partNo:integer;
|
|
part:Partition;
|
|
|
|
begin
|
|
partNo := askPartNo;
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
write('Default flag ');
|
|
if PartDefault in part.flags then
|
|
begin
|
|
part.flags := part.flags - [PartDefault];
|
|
write('cleared');
|
|
end
|
|
else
|
|
begin
|
|
part.flags := part.flags + [PartDefault];
|
|
write('set');
|
|
end;
|
|
writeln(' on partition ', partNo, ' (', sanitizeName(part.name), ').');
|
|
putPartition(part, partNo);
|
|
end;
|
|
end;
|
|
|
|
procedure deleteVolume;
|
|
var partNo:integer;
|
|
part:Partition;
|
|
begin
|
|
partNo := askConfirmPartNo;
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
part.flags := [];
|
|
part.name := '';
|
|
part.startBlock := 0;
|
|
part.blocks := 0;
|
|
part.extentSize := 0;
|
|
part.dirSize := 0;
|
|
part.bootBlocks := 0;
|
|
putPartition(part, partNo);
|
|
|
|
writeln('Partition ', partNo, ' deleted.');
|
|
|
|
(* try to fix last partition flag *)
|
|
(* only works if the previous entry has
|
|
a valid partition *)
|
|
if partNo = lastPartNo then
|
|
begin
|
|
lastPartNo := lastPartNo - 1;
|
|
part := getPartition(lastPartNo);
|
|
part.flags := part.flags + [PartLast];
|
|
putPartition(part, lastPartNo);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure validatePartTable;
|
|
var partNo:integer;
|
|
phys:Partition;
|
|
part,part2:Partition;
|
|
answer:char;
|
|
p,p2:integer;
|
|
valid:boolean;
|
|
begin
|
|
valid := true;
|
|
|
|
phys := getPartition(0);
|
|
if not (PartPhysical in phys.flags) then
|
|
begin
|
|
writeln('PHYS partition missing, initialize card first!');
|
|
exit;
|
|
end;
|
|
|
|
if phys.blocks <> detectedCardSize then
|
|
begin
|
|
write('PHYS partition size does not match detected card size, fix? [y/n]');
|
|
readln(answer);
|
|
if upcase(answer) = 'Y' then
|
|
begin
|
|
phys.blocks := detectedCardSize;
|
|
putPartition(phys,0);
|
|
end
|
|
else
|
|
valid := false;
|
|
end;
|
|
|
|
for p := 1 to lastPartNo do
|
|
begin
|
|
part := getPartition(p);
|
|
if (part.startBlock < 0) or (part.startBlock + part.blocks > phys.blocks) then
|
|
begin
|
|
writeln('Partition ', p, ' outside of physical bounds.');
|
|
valid := false;
|
|
end;
|
|
|
|
if PartEnabled in part.flags then
|
|
if part.dirSize <> getDirSize(part.extentSize, part.blocks) then
|
|
begin
|
|
write('Partition ', p, ' has an invalid directory size (is ');
|
|
writeln(part.dirSize, ', should be ',
|
|
getDirSize(part.extentSize, part.blocks), ').');
|
|
valid := false;
|
|
end;
|
|
|
|
for p2 := 1 to lastPartNo do
|
|
begin
|
|
part2 := getPartition(p2);
|
|
if (p <> p2) then
|
|
begin
|
|
if ((part.startBlock >= part2.startBlock) and
|
|
(part.startBlock < part2.startBlock + part2.blocks)) or
|
|
((part2.startBlock > part.startBlock) and
|
|
(part2.startBlock < part.startBlock + part.blocks))
|
|
then
|
|
begin
|
|
writeln('Partition ',p ,' overlaps with partition ', p2);
|
|
valid := false;
|
|
end;
|
|
|
|
if (part.name = part2.name) and (p > p2) then
|
|
begin
|
|
writeln('Duplicate volume name ', part.name);
|
|
valid := false;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
write('Partition table is ');
|
|
if not valid then write('in');
|
|
writeln('valid.');
|
|
end;
|
|
|
|
procedure checkNewCard; forward;
|
|
|
|
procedure initializeCard;
|
|
var part:Partition;
|
|
answer:char;
|
|
p:integer;
|
|
begin
|
|
writeln('Initializing a card will create an empty partition table with');
|
|
writeln('the standard PHYS and BOOT partitions.');
|
|
write('This will likely destroy any data on the card - sure? [y/n] ');
|
|
readln(answer);
|
|
if upcase(answer) <> 'Y' then exit;
|
|
|
|
(* create PHYS partition using detectedcardblocks *)
|
|
part.name := 'PHYS';
|
|
part.startBlock := 0;
|
|
part.blocks := detectedCardSize;
|
|
part.flags := [PartPhysical];
|
|
part.extentSize := 0;
|
|
part.dirSize := 0;
|
|
part.bootBlocks := 0;
|
|
putPartition(part,0);
|
|
|
|
(* create BOOT partition without PartBoot flag *)
|
|
part.name := 'BOOT';
|
|
part.startBlock := 16; (* 16 possible partition blocks with 8 partitions each *)
|
|
part.blocks := 8192 - 16; (* align first volume to 4MB *)
|
|
part.flags := [PartBoot, PartLast];
|
|
putPartition(part,1);
|
|
|
|
part.name := '';
|
|
part.startBlock := 0;
|
|
part.blocks := 0;
|
|
part.flags := [];
|
|
|
|
for p := 2 to 7 do
|
|
putPartition(part,p);
|
|
|
|
writeln('Empty partition table created.');
|
|
end;
|
|
|
|
procedure createFilesystem(partNo:integer);
|
|
var firstDirBlock:integer;
|
|
slot:DirectorySlot;
|
|
dirblk:DirBlock;
|
|
dirblockCount:integer;
|
|
metaSlotsCount:integer;
|
|
dirSlotsPerBlock:integer;
|
|
dirSlotsPerExtent:integer;
|
|
part:Partition;
|
|
ts:Timestamp;
|
|
i,b:integer;
|
|
error,devid:integer;
|
|
begin
|
|
devid := 0;
|
|
ts := 0;
|
|
|
|
part := getPartition(partNo);
|
|
firstDirBlock := part.startBlock;
|
|
dirSlotsPerBlock := 512 div 64;
|
|
dirSlotsPerExtent := part.extentSize div 64;
|
|
dirblockCount := (part.dirSize - 1) div dirSlotsPerBlock + 1;
|
|
metaSlotsCount := (part.dirSize - 1) div dirSlotsPerExtent + 1;
|
|
|
|
writeln('partition size: ', part.blocks);
|
|
writeln('extent size: ', part.extentSize);
|
|
writeln('directory size: ', part.dirSize);
|
|
{ writeln('dirslots per extent:', dirSlotsPerExtent);
|
|
writeln('dirblocks: ', dirblockCount);
|
|
writeln('metaslots: ', metaSlotsCount);
|
|
writeln('first dir block: ', firstDirBlock);
|
|
}
|
|
for b := firstDirBlock to firstDirBlock + dirblockCount - 1 do
|
|
begin
|
|
for i := 0 to dirSlotsPerBlock - 1 do
|
|
begin
|
|
if metaSlotsCount > 0 then
|
|
begin
|
|
(* write DIR/Reserved directory slots *)
|
|
slot.name := 'DIR';
|
|
slot.flags := [ SlotReserved ];
|
|
metaSlotsCount := metaSlotsCount - 1;
|
|
end
|
|
else
|
|
begin
|
|
(* write Free + EndScan directory slots *)
|
|
slot.name := '';
|
|
slot.flags := [ SlotFree , SlotEndScan ];
|
|
end;
|
|
slot.sizeBytes := 0;
|
|
slot.createTime := ts;
|
|
slot.modTime := ts;
|
|
slot.generation := 0;
|
|
slot.owner := 0;
|
|
|
|
dirBlk[i] := slot;
|
|
end;
|
|
writedirblk(b, dirBlk, error, devid);
|
|
if error > 0 then
|
|
writeln('error writing block ', b, ': ', error);
|
|
end;
|
|
writeln('Volume ', part.name, ' initialized.');
|
|
end;
|
|
|
|
procedure initializeVolume;
|
|
var partNo:integer;
|
|
part:Partition;
|
|
begin
|
|
partNo := askConfirmPartNo;
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
if not (PartEnabled in part.flags) then
|
|
writeln('Wrong partition flags (must be Enabled)')
|
|
else
|
|
createFilesystem(partNo);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure rawEdit;
|
|
var partNo:integer;
|
|
newName:string;
|
|
part:Partition;
|
|
buf:string;
|
|
begin
|
|
writeln('Raw editing partition entry - use caution!');
|
|
|
|
partNo := askPartNo;
|
|
if partNo >= 0 then
|
|
begin
|
|
part := getPartition(partNo);
|
|
writeln('Volume name: ', sanitizeName(part.name));
|
|
write('Flags> ':30);
|
|
readln(buf);
|
|
if length(buf) > 0 then
|
|
part.flags := str2flags(buf);
|
|
changeNumber('Start block', part.startBlock);
|
|
changeNumber('Size (blocks)', part.blocks);
|
|
changeNumber('Extent size (blocks)', part.extentSize);
|
|
changeNumber('Dir size (slots)', part.dirSize);
|
|
changeNumber('Boot blocks', part.bootBlocks);
|
|
|
|
putPartition(part, partNo);
|
|
end;
|
|
end;
|
|
|
|
procedure installBoot;
|
|
var bootfile:file;
|
|
name:string;
|
|
part:Partition;
|
|
partNo:integer;
|
|
buf:IOBlock;
|
|
b,blkCount:integer;
|
|
devId:integer;
|
|
error:integer;
|
|
|
|
procedure readWordsIntoBuf;
|
|
var i:integer;
|
|
w:integer;
|
|
c1,c2,c3,c4:char;
|
|
begin
|
|
for i := 0 to 127 do
|
|
begin
|
|
if not eof(bootfile) then
|
|
begin
|
|
read(bootfile, c1, c2, c3, c4);
|
|
w := (ord(c1) shl 24) or
|
|
(ord(c2) shl 16) or
|
|
(ord(c3) shl 8) or
|
|
ord(c4);
|
|
end
|
|
else
|
|
w := 0;
|
|
buf[i] := w;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
devId := 0; (* only one device supported *)
|
|
partNo := 1; (* BOOT partition is always at position 1 *)
|
|
|
|
part := getPartition(partNo);
|
|
if part.name <> 'BOOT' then
|
|
begin
|
|
writeln('No BOOT partition at position 1.');
|
|
exit;
|
|
end;
|
|
|
|
write('Boot file name> ');
|
|
readln(name);
|
|
if length(name) > 0 then
|
|
begin
|
|
open(bootfile, name, ModeReadonly);
|
|
if IOResult(bootfile) <> 0 then
|
|
writeln('Error opening file: ', ErrorStr(IOResult(bootfile)))
|
|
else
|
|
begin
|
|
blkCount := filesize(bootfile) div 512 + 1;
|
|
if blkCount > part.blocks then
|
|
writeln('Boot partition too small, need ', blkCount)
|
|
else
|
|
begin
|
|
part.bootBlocks := blkCount;
|
|
if not (PartBoot in part.flags) then
|
|
begin
|
|
write('Boot flag set');
|
|
writeln(' on partition ', partNo,
|
|
' (', sanitizeName(part.name), ').');
|
|
part.flags := part.flags + [PartBoot];
|
|
end;
|
|
putPartition(part, partNo);
|
|
for b := 0 to blkCount - 1 do
|
|
begin
|
|
readWordsIntoBuf;
|
|
writeblock(part.startBlock + b, buf, error, devId);
|
|
if error <> 0 then
|
|
writeln('Error in writeblock ', b, ': ', error);
|
|
end;
|
|
writeln(blkCount, ' boot blocks written.');
|
|
end;
|
|
close(bootfile);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure showMenu;
|
|
begin
|
|
writeln;
|
|
writeln('L)ist partitions V)alidate partition table T)oggle default volume flag');
|
|
writeln('A)dd volume R)ename volume D)elete volume I)nitialize volume');
|
|
writeln('Read N)ew card Initialize C)ard');
|
|
writeln('E)dit partition Install B)oot file eX)it');
|
|
write('> ');
|
|
end;
|
|
|
|
procedure invalidCommand;
|
|
begin
|
|
writeln('Invalid command.');
|
|
end;
|
|
|
|
procedure command(cmd:char; var done:boolean);
|
|
begin
|
|
case cmd of
|
|
'L': printPartTable;
|
|
'A': addVolume;
|
|
'R': renameVolume;
|
|
'D': deleteVolume;
|
|
'V': validatePartTable;
|
|
'T': toggleDefaultFlag;
|
|
'I': initializeVolume;
|
|
'N': checkNewCard;
|
|
'C': initializeCard;
|
|
'B': installBoot;
|
|
'E': rawEdit;
|
|
'X',#24: done := true;
|
|
else invalidCommand;
|
|
end;
|
|
end;
|
|
|
|
function changesPending:boolean;
|
|
var i:integer;
|
|
begin
|
|
changesPending := false;
|
|
for i := 0 to LastPartBlock do
|
|
if changed[i] then changesPending := true;
|
|
end;
|
|
|
|
procedure checkNewCard;
|
|
begin
|
|
if changesPending then
|
|
writeln('WARNING: Discarding partition table changes.');
|
|
initDevices;
|
|
detectedCardSize := cardsize;
|
|
writeln('Detected card size: ', detectedCardSize);
|
|
readPartTable;
|
|
printPartTable;
|
|
end;
|
|
|
|
begin
|
|
checkNewCard;
|
|
|
|
repeat
|
|
showMenu;
|
|
read(cmd);
|
|
writeln;
|
|
command(Upcase(cmd), done);
|
|
until done;
|
|
writePartitions;
|
|
end.
|