MODULE IsoImages;
IMPORT SYSTEM, Commands, Streams, Files, Dates, Strings;
CONST
Ok* = 0;
FileNotFound* = 1;
CouldNotCreateFile* = 2;
MaxPathLen = 256;
ISO9660Id = "CD001";
CDSectorSize = 2048;
NumSystemSectors = 16;
ElToritoSysId = "EL TORITO SPECIFICATION";
Platform80x86 = 0X;
PlatformPowerPC = 0X;
PlatformMac = 0X;
Bootable = 88X;
NotBootable = 00X;
EmulationNone = 0X;
Emulation12Floppy = 1X;
Emulation144Floppy = 2X;
Emulation288Floppy = 3X;
EmulationHDD = 4X;
BBVolumeId = "BLUEBOTTLE";
BBPublisher = "ETH_ZURICH";
TYPE
BootCatalogEntry = ARRAY 32 OF CHAR;
BCValidationEntry = RECORD
HeaderId: CHAR;
PlatformId: CHAR;
Reserved: INTEGER;
IdString: ARRAY 24 OF CHAR;
Checksum: INTEGER;
KeyBytes: ARRAY 2 OF CHAR;
END;
BCInitialDefaultEntry = RECORD
BootIndicator: CHAR;
BootMediaType: CHAR;
LoadSegment: INTEGER;
SystemType: CHAR;
Unused1: CHAR;
SectorCount: INTEGER;
LoadRBA: LONGINT;
Unused2: ARRAY 20 OF CHAR;
END;
PROCEDURE WriteImage(w: Streams.Writer; r: Streams.Reader; imageSize: LONGINT);
VAR read, padLen: LONGINT; buf: ARRAY CDSectorSize OF CHAR;
BEGIN
padLen := CDSectorSize - (imageSize MOD CDSectorSize);
r.Bytes(buf, 0, CDSectorSize, read);
WHILE (read > 0) DO
w.Bytes(buf, 0, read);
r.Bytes(buf, 0, CDSectorSize, read);
END;
WriteByteRep(w, 0X, padLen);
END WriteImage;
PROCEDURE WriteElToritoDescriptor(w: Streams.Writer);
BEGIN
w.Char(0X);
w.String(ISO9660Id);
w.Char(1X);
WriteStringWithPadding(w, ElToritoSysId, 0X, 32);
WriteByteRep(w, 0X, 32);
w.RawLInt(NumSystemSectors + 1 + 1 + 1);
WriteByteRep(w, 0X, 1973);
END WriteElToritoDescriptor;
PROCEDURE WriteBootCatalog(w: Streams.Writer);
VAR entry: BCValidationEntry; entry2: BCInitialDefaultEntry; len: LONGINT;
BEGIN
len := 0;
entry.HeaderId := 1X;
entry.PlatformId := Platform80x86;
entry.Reserved := 0;
entry.IdString := BBVolumeId;
entry.Checksum := 0;
entry.KeyBytes[0] := 55X; entry.KeyBytes[1] := 0AAX;
entry.Checksum := CalcChecksum16(SYSTEM.VAL(BootCatalogEntry, entry));
w.Bytes(SYSTEM.VAL(BootCatalogEntry, entry), 0, 32);
INC(len, 32);
entry2.BootIndicator := Bootable;
entry2.BootMediaType := Emulation144Floppy;
entry2.LoadSegment := 0;
entry2.SystemType := 0X;
entry2.Unused1 := 0X;
entry2.SectorCount := 1;
entry2.LoadRBA := NumSystemSectors + 7;
w.Bytes(SYSTEM.VAL(BootCatalogEntry, entry2), 0, 32);
INC(len, 32);
WriteByteRep(w, 0X, CDSectorSize - len);
END WriteBootCatalog;
PROCEDURE WriteIsoFSData(w: Streams.Writer);
VAR now: Dates.DateTime;
BEGIN
now := Dates.Now();
w.Char(22X);
w.Char(0X);
w.RawLInt(NumSystemSectors + 4); w.Net32(NumSystemSectors + 4);
w.RawLInt(CDSectorSize); w.Net32(CDSectorSize);
WriteByteRep(w, 0X, 7);
w.Char(2X);
w.Char(0X);
w.Char(0X);
w.RawInt(1); w.Net16(1);
w.Char(1X);
w.Char(0X);
w.Char(22X);
w.Char(0X);
w.RawLInt(NumSystemSectors + 4); w.Net32(NumSystemSectors + 4);
w.RawLInt(CDSectorSize); w.Net32(CDSectorSize);
WriteByteRep(w, 0X, 7);
w.Char(2X);
w.Char(0X);
w.Char(0X);
w.RawInt(1); w.Net16(1);
w.Char(1X);
w.Char(1X);
WriteByteRep(w, 0X, CDSectorSize - (2 * 22H));
WriteTypeLPathTable(w);
WriteTypeMPathTable(w);
END WriteIsoFSData;
PROCEDURE WriteTypeLPathTable(w: Streams.Writer);
BEGIN
w.Char(1X);
w.Char(0X);
w.RawLInt(NumSystemSectors + 4);
w.RawInt(1);
w.Char(0X);
WriteByteRep(w, 0X, CDSectorSize - 9);
END WriteTypeLPathTable;
PROCEDURE WriteTypeMPathTable(w: Streams.Writer);
BEGIN
w.Char(1X);
w.Char(0X);
w.Net32(NumSystemSectors + 4);
w.Net16(1);
w.Char(0X);
WriteByteRep(w, 0X, CDSectorSize - 9);
END WriteTypeMPathTable;
PROCEDURE WritePrimaryVolumeDescriptor(w: Streams.Writer; isoImageSectorCount: LONGINT);
VAR now: Dates.DateTime; dtBuf: ARRAY 20 OF CHAR;
BEGIN
now := Dates.Now();
Strings.FormatDateTime("yyyymmddhhnnss00", now, dtBuf);
w.Char(1X);
w.String(ISO9660Id);
w.Char(1X);
w.Char(0X);
WriteByteRep(w, ' ', 32);
WriteStringWithPadding(w, BBVolumeId, ' ', 32);
WriteByteRep(w, 0X, 8);
WriteBothByteOrder32(w, isoImageSectorCount);
WriteByteRep(w, 0X, 32);
WriteBothByteOrder16(w, 1);
WriteBothByteOrder16(w, 1);
WriteBothByteOrder16(w, CDSectorSize);
WriteBothByteOrder32(w, 10);
w.RawLInt(NumSystemSectors + 1 + 1 + 1 + 1 + 1);
w.RawLInt(0);
w.Net32(NumSystemSectors + 1 + 1 + 1 + 1 + 1 + 1);
w.RawLInt(0);
WriteDirectoryRecord(w);
WriteByteRep(w, ' ', 128);
WriteStringWithPadding(w, BBPublisher, ' ', 128);
WriteByteRep(w, ' ', 128 + 128);
WriteByteRep(w, ' ', 37 + 37 + 37);
w.String(dtBuf); w.Char(0X);
w.String(dtBuf); w.Char(0X);
dtBuf := "0000000000000000";
w.String(dtBuf); w.Char(0X);
w.String(dtBuf); w.Char(0X);
w.Char(1X);
w.Char(0X);
WriteByteRep(w, 0X, 512 + 653);
END WritePrimaryVolumeDescriptor;
PROCEDURE WriteSetTerminatorDescriptor(w: Streams.Writer);
BEGIN
w.Char(0FFX);
w.String(ISO9660Id);
w.Char(1X);
WriteByteRep(w, 0X, 2041);
END WriteSetTerminatorDescriptor;
PROCEDURE WriteDirectoryRecord(w: Streams.Writer);
VAR now: Dates.DateTime;
BEGIN
now := Dates.Now();
w.RawSInt(22H);
w.Char(0X);
WriteBothByteOrder32(w, NumSystemSectors + 1 + 1 + 1 + 1);
WriteBothByteOrder32(w, CDSectorSize);
w.RawSInt(SHORT(SHORT(now.year - 1900)));
w.RawSInt(SHORT(SHORT(now.month + 1)));
w.RawSInt(SHORT(SHORT(now.day)));
w.RawSInt(SHORT(SHORT(now.hour)));
w.RawSInt(SHORT(SHORT(now.minute)));
w.RawSInt(SHORT(SHORT(now.second)));
w.Char(0X);
w.Char(2X);
w.Char(0X);
w.Char(0X);
WriteBothByteOrder16(w, 1);
w.Char(1X);
w.Char(0X);
END WriteDirectoryRecord;
PROCEDURE CalcIsoImageSectorCount(inputImageSize: LONGINT): LONGINT;
VAR imageSectors: LONGINT;
BEGIN
imageSectors := inputImageSize DIV CDSectorSize;
IF (inputImageSize MOD CDSectorSize # 0) THEN
INC(imageSectors);
END;
RETURN NumSystemSectors +
1 +
1 +
1 +
1 +
3 +
imageSectors;
END CalcIsoImageSectorCount;
PROCEDURE WriteBothByteOrder32(w: Streams.Writer; x: LONGINT);
BEGIN
w.RawLInt(x);
w.Net32(x);
END WriteBothByteOrder32;
PROCEDURE WriteBothByteOrder16(w: Streams.Writer; x:INTEGER);
BEGIN
w.RawInt(x);
w.Net16(x);
END WriteBothByteOrder16;
PROCEDURE WriteByteRep(w: Streams.Writer; b: CHAR; n: LONGINT);
VAR i: LONGINT;
BEGIN
FOR i := 1 TO n DO
w.Char(b);
END;
END WriteByteRep;
PROCEDURE WriteStringWithPadding(w: Streams.Writer; CONST str: ARRAY OF CHAR; padChar: CHAR; len: LONGINT);
VAR strLen: LONGINT;
BEGIN
strLen := LEN(str) - 1;
w.String(str);
WriteByteRep(w, padChar, len - strLen);
END WriteStringWithPadding;
PROCEDURE WriteEmptySectors(w: Streams.Writer; n: LONGINT);
VAR i, s, nLongs: LONGINT;
BEGIN
nLongs := CDSectorSize DIV 4;
FOR s := 1 TO n DO
FOR i := 1 TO nLongs DO
w.RawLInt(0);
END;
END;
END WriteEmptySectors;
PROCEDURE CalcChecksum16(CONST buf: ARRAY OF CHAR): INTEGER;
VAR checksum, i, numWords: LONGINT;
BEGIN
checksum := 0;
numWords := LEN(buf) DIV 2;
FOR i := 0 TO numWords - 1 DO
checksum := (checksum + SYSTEM.VAL(INTEGER, buf[i * 2])) MOD 10000H;
END;
RETURN SHORT(10000H - checksum);
END CalcChecksum16;
PROCEDURE MakeImage*(CONST input, output: ARRAY OF CHAR; VAR imageSize , res : LONGINT);
VAR fOut, fIn: Files.File; out: Files.Writer; in: Files.Reader; numSectors: LONGINT;
BEGIN
res := Ok;
fIn := Files.Old(input);
IF (fIn = NIL) THEN
res := FileNotFound;
RETURN;
END;
fOut := Files.New(output);
IF (fOut = NIL) THEN
res := CouldNotCreateFile;
RETURN;
END;
numSectors := CalcIsoImageSectorCount(fIn.Length());
Files.Register(fOut);
Files.OpenWriter(out, fOut, 0);
WriteEmptySectors(out, NumSystemSectors);
WritePrimaryVolumeDescriptor(out, numSectors);
WriteElToritoDescriptor(out);
WriteSetTerminatorDescriptor(out);
WriteBootCatalog(out);
WriteIsoFSData(out);
Files.OpenReader(in, fIn, 0);
WriteImage(out, in, fIn.Length());
out.Update;
fOut.Update;
imageSize := fOut.Length();
END MakeImage;
PROCEDURE Make*(context : Commands.Context);
VAR
imageSource, isoDest: ARRAY MaxPathLen OF CHAR;
imageSize, res : LONGINT;
BEGIN
context.arg.SkipWhitespace; context.arg.String(isoDest);
context.arg.SkipWhitespace; context.arg.String(imageSource);
context.out.String("Making ISO-9660 Bootable Image"); context.out.Ln;
context.out.String("Input image is ");
context.out.String(imageSource); context.out.Ln;
context.out.String("Writing Bootable ISO Image to ");
context.out.String(isoDest); context.out.String(" ... ");
MakeImage(imageSource, isoDest, imageSize, res);
IF (res = Ok) THEN
context.out.String("done."); context.out.Ln;
context.out.String("Bootable ISO Image successfully written (Size: ");
context.out.Int(imageSize DIV 1024, 0); context.out.String(" KB)"); context.out.Ln;
ELSIF (res = FileNotFound) THEN
context.error.String("Disk image file "); context.error.String(imageSource);
context.error.String(" not found."); context.error.Ln;
ELSIF (res = CouldNotCreateFile) THEN
context.error.String("Could not create image file "); context.error.String(isoDest);
context.error.String("."); context.error.Ln;
ELSE
context.error.String("Error, res: "); context.error.Int(res, 0); context.error.Ln;
END;
END Make;
END IsoImages.
(*
IsoImages.Make AosCDPrivate.iso AosCDPrivate.Dsk ~
SystemTools.Free IsoImages ~
*)