<> <> <> <> DIRECTORY AIS USING [CloseFile, CloseWindow, CreateFile, FRef, OpenWindow, Raster, RasterPart, WRef, WriteSample], Basics USING [BITOR, BytePair], RefText USING [New], BasicTime USING [OutOfRange, GMT, nullGMT, Pack, Unpacked, Now, Unpack], <> PrincOpsUtils USING [LongCopy], Real USING [Round], CedarProcess USING [CheckAbort], Rope USING [Cat, Fetch, FromProc, Length, ROPE, Substr], TapesCommon, CrosfieldTape, UnixTapeOps; CrosfieldTapeImpl: CEDAR PROGRAM IMPORTS AIS, Basics, BasicTime, PrincOpsUtils, Rope, UnixTapeOps, CedarProcess, Real, RefText EXPORTS CrosfieldTape ~ BEGIN OPEN CrosfieldTape, TapesCommon; <> <> <<15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 --tape documentation 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 --Cedar bit addressing 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --normal Cedar bit addressing>> <<>> <<>> ROPE: TYPE ~ Rope.ROPE; <> <> <> <> <> <<];>> IdentityOnTape: TYPE ~ MACHINE DEPENDENT RECORD [ <> checkWord1(0): WORD _ 0, tapeID(1): WORD _ 0, dateYMD(2): WORD _ 0, dateHM(3): DateHM _ [0,0], name(4:0..159): PACKED ARRAY [0..20) OF CHAR _ ALL [' ], ws2(14): WORD _ 0, checkWord2(15): WORD _ 0 ]; nameLen: NAT ~ (159+1)/16*2; <> <> <> <> <> <> <> <> <> <> <> <> <> <<];>> DirectoryOnTape: TYPE ~ MACHINE DEPENDENT RECORD [ <> idn(0): WORD _ 0, -- File number on tape (range 1- 70) ws1(1): WORD _ 0, ws3(2:0..10): [0..1024] _ 2, att(2:11..11): BOOL _ FALSE, -- TRUE if contone, FALSE if screen ws2(2:12..15): [0..15] _ 0, color(3): Color _ [], ws4(4:0..31): CARD _ 0, hRes(6): WORD _ 0, vRes(7): WORD _ 0, pixelsPerLine(8): WORD _ 0, lines(9): WORD _ 0, linesPerCylinder(10): WORD _ 0, sectorsPerLine(11): WORD _ 0, createTimeYMD(12): WORD _ 0, createTimeHM(13): DateHM _ [0, 0], ws5(14:0..31): CARD _ 0, note(16:0..159): PACKED ARRAY [0..20) OF CHAR _ ALL[' ], ws6(26:0..111): ARRAY [0..7) OF WORD _ ALL[0], cylinders(33): WORD _ 0, blocks(34): WORD _ 0, tapeID(35): WORD _ 0, ws7(36:0..31): CARD _ 0, ws8(38:0..31): CARD _ 0 ]; noteLen: NAT ~ (159+1)/16*2; <> <> <> <> <> <> <> <> <> <> <> <> <> <<];>> <> DateYMD: TYPE ~ MACHINE DEPENDENT RECORD [ <> year(0:9..15): [0..127], -- Year-1900 month(0:5..8): [0..15], day(0:0..4): [0..31] ]; DateHM: TYPE ~ MACHINE DEPENDENT RECORD [ minute(0:0..7): [0..255], hour(0:8..15): [0..255] ]; State: TYPE ~ REF StateRep; StateRep: TYPE ~ RECORD [ newState: BOOLEAN _ TRUE, tapeHandle: TapeHandle _ NIL, dir: Directory, text: REF TEXT, --one record (32 sectors) worth atByte: INT _ -1, -- the current byte offset in text sector: [0..32) _ 31, --sector in the current record recordsLeftInCylinder: [0..19] _ 0, linesLeftInCylinder: NAT _ 0, --dir.linesPerCylinder bytesLeftInSector: [0..512] _ 0, linesLeftInFile: NAT _ 0, mapPixel: PixelProc, dirAsText: REF TEXT _ NIL ]; Pixel: TYPE ~ MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM cmky => [cyan, magenta, yellow, black: BYTE], rgb => [red, green, blue, blank: BYTE], ENDCASE ]; Colors: TYPE ~ {cyan, magenta, yellow, black}; AISInfo: TYPE ~ RECORD [fileName: ROPE, raster: AIS.Raster, fRef: AIS.FRef, wRef: AIS.WRef]; suffix: ARRAY Colors OF ROPE = ["cyan", "magenta", "yellow", "black"]; <> WriteTape: PUBLIC PROC [tapeHandle: TapeHandle, files: FileSpecList, tapeNumber: [1..999] _ 102, name: ROPE] RETURNS [FileHandleList] = { <> <> fileHandleList: FileHandleList _ NIL; fileNumber: NAT _ 1; reverse: PROC[files: FileHandleList] RETURNS[r: FileHandleList] = { r _ NIL; FOR f: FileHandleList _ files, f.rest UNTIL f=NIL DO r _ CONS[f.first, r]; ENDLOOP; }; InitializeTape[tapeHandle, files, tapeNumber, name]; <> FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO fileSpec: FileSpec _ f.first; fileHandleList _ CONS[FileHandleFromFileSpec[fileSpec, tapeHandle, fileNumber, tapeNumber], fileHandleList]; fileNumber _ fileNumber+1; ENDLOOP; <> fileHandleList _ reverse[fileHandleList]; RETURN[fileHandleList]; }; InitializeTape: PUBLIC PROC [tapeHandle: TapeHandle, files: FileSpecList, tapeNumber: [1..999] _ 102, name: ROPE] = { <> identity: Identity _ NEW[IdentityRep _ [tapeNumber: tapeNumber, date: BasicTime.Now[], name: name]]; idAsText: REF TEXT; masterDir: REF TEXT; fileNumber: NAT _ 1; <> idAsText _ TextFromIdentity[identity]; UnixTapeOps.Rewind[tapeHandle]; UnixTapeOps.WriteRecord[tapeHandle, idAsText]; <> masterDir _ AllocateBuffer[sizeOfMasterDirectory]; FOR i: NAT IN [0..sizeOfMasterDirectory) DO masterDir[i] _ VAL[377B]; --end of directories mark ENDLOOP; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO fileSpec: FileSpec _ f.first; dirAsText: REF TEXT _ TextFromDirectory[DirectoryFromFileSpec[fileSpec, fileNumber, tapeNumber]]; offset: NAT _ (fileNumber-1)*bytesPerDirectory; FOR i: NAT IN [0..bytesPerDirectory) DO --put directory into the master directory masterDir[offset+i] _ dirAsText[i]; ENDLOOP; fileNumber _ fileNumber+1; ENDLOOP; <> UnixTapeOps.WriteRecord[tapeHandle, masterDir]; UnixTapeOps.WriteFileMark[tapeHandle]; }; AddFileToTape: PUBLIC PROC [tapeHandle: TapeHandle, file: FileSpec, fileNumber, tapeNumber: NAT, inPosition: BOOLEAN _ FALSE] RETURNS [TapesCommon.FileHandle] = { <> <> <> IF NOT inPosition THEN { UnixTapeOps.Rewind[tapeHandle]; UnixTapeOps.ForwardSpaceFile[tapeHandle]; --skip the master directory FOR i: NAT IN [1..fileNumber) DO UnixTapeOps.ForwardSpaceFile[tapeHandle]; ENDLOOP; }; RETURN[FileHandleFromFileSpec[file,tapeHandle, fileNumber, tapeNumber]]; }; FileHandleFromFileSpec: PROC [fileSpec: FileSpec, tapeHandle: TapeHandle, fileNumber, tapeNumber: NAT] RETURNS [FileHandle] ~ { dir: Directory _ DirectoryFromFileSpec[fileSpec, fileNumber, tapeNumber]; state: State; linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT; [linesPerCylinder, nCylinders, sectorsPerLine, nRecords] _ FileStructures[fileSpec]; state _ NEW[StateRep _ [ newState: TRUE, tapeHandle: tapeHandle, dir: dir, text: AllocateBuffer[bytesPerRecord], atByte: 0, sector: 0, recordsLeftInCylinder: recordsPerCylinder, linesLeftInCylinder: linesPerCylinder, bytesLeftInSector: bytesPerSector, linesLeftInFile: fileSpec.nLines, mapPixel: fileSpec.mapPixel, dirAsText: TextFromDirectory[dir] ]]; RETURN[NEW[FileHandleRep _ [newLine: WriteNewLine, data: state]]]; }; DirectoryFromFileSpec: PROC [fileSpec: FileSpec, fileNumber, tapeNumber: NAT] RETURNS [dir: Directory] ~ { linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT; [linesPerCylinder, nCylinders, sectorsPerLine, nRecords] _ FileStructures[fileSpec]; dir _ NEW[DirectoryRep _ [ fileNumber: fileNumber, contone: FALSE, <> hRes: fileSpec.pixelsPerInch, vRes: fileSpec.pixelsPerInch, pixelsPerLine: fileSpec.nPixels, lines: fileSpec.nLines, createDate: BasicTime.Now[], note: fileSpec.note, tapeNumber: tapeNumber, linesPerCylinder: linesPerCylinder, sectorsPerLine: sectorsPerLine, cylinders: nCylinders, blocks: nRecords ]]; }; <> ReadTapeIdentity: PUBLIC PROC [tapeHandle: TapeHandle] RETURNS [Identity] = { <> tapeMark: BOOL; text: REF TEXT; UnixTapeOps.Rewind[tapeHandle]; <> [tapeMark, text] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: text]; IF tapeMark THEN ERROR; -- something wrong here RETURN[IdentityFromText[text]]; }; ReadMasterDirectory: PUBLIC PROC[tapeHandle: TapeHandle] RETURNS [nFiles: NAT, masterDirectory: LIST OF Directory] = { id: Identity _ ReadTapeIdentity[tapeHandle]; mDirAsText: REF TEXT _ NIL; tapeMark: BOOL; [tapeMark, mDirAsText] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: NIL]; IF tapeMark THEN ERROR; -- something wrong here <> [tapeMark, ] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: NIL]; IF NOT tapeMark THEN ERROR; -- something wrong here [nFiles, masterDirectory] _ ParseMasterDirectory[mDirAsText]; }; ParseMasterDirectory: PROC [dir: REF TEXT] RETURNS [nFiles: NAT, masterDirectory: LIST OF Directory] = { directory: Directory; list: LIST OF Directory; start, nRecords: NAT _ 0; reverse: PROC[l: LIST OF Directory] RETURNS[r: LIST OF Directory] = { FOR d: LIST OF Directory _ l, d.rest UNTIL d=NIL DO r _ CONS[d.first, r]; ENDLOOP; }; nFiles _ 0; DO directory _ DirectoryFromText[text: dir, start: start]; IF directory=NIL THEN EXIT; nFiles _ nFiles+1; nRecords _ nRecords+directory.blocks; start _ start+bytesPerDirectory; list _ CONS[directory, list]; ENDLOOP; list _ reverse[list]; RETURN[nFiles, list]; }; ReadFile: PUBLIC PROC [tapeHandle: TapeHandle, fileNumber: [1..70] _ 1] RETURNS[Directory] ~ { stateProc: PROC[line: NAT, state: State] = {}; RETURN[ReadAndProcessFile[tapeHandle, fileNumber, stateProc]]; }; ReadAndProcessFile: PROC [tape: TapeHandle, fileNumber: [1..70] _ 1, stateProc: PROC[line: NAT, state: State]] RETURNS[Directory] ~ { <> state: State _ NEW[StateRep _ [tapeHandle: tape]]; nFiles: NAT; tapeMark: BOOL; [nFiles, ] _ ReadMasterDirectory[tape]; IF fileNumber > nFiles THEN ERROR; FOR i: NAT IN [1..fileNumber) DO UnixTapeOps.ForwardSpaceFile[tape]; --skip over the unwanted files ENDLOOP; <> [tapeMark, state.dirAsText] _ UnixTapeOps.ReadRecord[tapeHandle: tape, buffer: NIL]; IF tapeMark THEN ERROR; -- something wrong here state.dir _ DirectoryFromText[text: state.dirAsText, start: 0]; state.linesLeftInFile _ state.dir.lines; FOR line: NAT IN [0 .. state.dir.lines) DO ReadNewLine[state]; stateProc[line, state]; CedarProcess.CheckAbort[]; ENDLOOP; FOR i: NAT IN [0..state.recordsLeftInCylinder) DO ReadNewRecord[state]; ENDLOOP; [tapeMark, state.text] _ UnixTapeOps.ReadRecord[tapeHandle: tape, buffer: state.text]; IF NOT tapeMark THEN ERROR; -- something wrong here RETURN[state.dir]; }; FileTo4AIS: PUBLIC PROC [tapeHandle: TapeHandle, fileNumber: [1..70] _ 1, aisRootName: ROPE, mapPixel: PixelProc, reduce: NAT _ 4] RETURNS [Directory] ~ { < 75 spi)>> ais: ARRAY Colors OF AISInfo; dir: Directory; Done: PROC [c: Colors] = { AIS.CloseWindow[w: ais[c].wRef]; AIS.CloseFile[f: ais[c].fRef]; }; stateProc: PROC[line: NAT, state: State] = { SetUp: PROC [c: Colors] = { ais[c].raster _ NEW[AIS.RasterPart _ [scanCount: 1+state.dir.lines/reduce, scanLength: 1+state.dir.pixelsPerLine/reduce, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]]; ais[c].fileName _ Rope.Cat[r1: aisRootName, r2: "-", r3: suffix[c], r4: ".ais"]; ais[c].fRef _ AIS.CreateFile[name: ais[c].fileName, raster: ais[c].raster]; ais[c].wRef _ AIS.OpenWindow[f: ais[c].fRef]; MakeScreenToByte[mapPixel]; }; IF state.newState THEN { SetUp[cyan]; SetUp[magenta]; SetUp[yellow]; SetUp[black]; state.newState _ FALSE; }; FOR pixel: NAT IN [0 .. state.dir.pixelsPerLine) DO p: Pixel _ ReadPixel[state]; store: PROC [c: Colors, data: BYTE] = { lastPixel: NAT _ state.dir.pixelsPerLine-1; data _ ScreenToByte[data]; AIS.WriteSample[w: ais[c].wRef, value: data, line: line/reduce, pixel: (lastPixel-pixel)/reduce]; }; IF line MOD reduce #0 OR pixel MOD reduce #0 THEN LOOP; store[cyan, p.cyan]; store[magenta, p.magenta]; store[yellow, p.yellow]; store[black, p.black]; ENDLOOP; }; dir _ ReadAndProcessFile[tapeHandle, fileNumber, stateProc]; Done[cyan]; Done[magenta]; Done[yellow]; Done[black]; RETURN[dir]; }; <> bytesPerSector: NAT ~ 512; bytesPerRecord: NAT ~ 16384; sizeOfMasterDirectory: NAT ~5602; bytesPerDirectory: NAT ~80; recordsPerCylinder: NAT ~ 19; sectorsPerRecord: NAT ~ 32; lastSector: NAT _ sectorsPerRecord-1; <> WriteNewLine: PROC[self: FileHandle, line: PixelBuffer] RETURNS[eof: BOOLEAN]= { state: State _ NARROW[self.data]; IF line.samplesPerPixel # 4 THEN ERROR; IF state.newState THEN { --first time with this state MakeByteToScreen[state.mapPixel]; --make the byte translation table UnixTapeOps.WriteRecord[state.tapeHandle, state.dirAsText]; --write the directory state.newState _ FALSE; }; SELECT TRUE FROM state.linesLeftInCylinder=0 => WriteNewCylinder[state]; state.atByte#0 AND state.linesLeftInCylinder#0 => WriteNewSector[state]; ENDCASE; --state.atByte=0 AND linesLeftInCylinder#0 FOR i: NAT DECREASING IN [0..line.length) DO p: Pixel; p.cyan _ ByteToScreen[line[0][i]]; p.magenta _ ByteToScreen[line[1][i]]; p.yellow _ ByteToScreen[line[2][i]]; p.black _ ByteToScreen[line[3][i]]; WritePixel[state,p]; ENDLOOP; state.linesLeftInFile _ state.linesLeftInFile-1; state.linesLeftInCylinder _ state.linesLeftInCylinder-1; IF state.linesLeftInFile = 0 THEN { --write the rest of the records and the tape mark UNTIL state.recordsLeftInCylinder=0 DO WriteNewRecord[state]; ENDLOOP; UnixTapeOps.WriteFileMark[state.tapeHandle]; eof _ TRUE; } ELSE eof _ FALSE; }; WritePixel: PROC [state: State, p: Pixel] ~ { IF state.bytesLeftInSector=0 THEN WriteNewSector[state]; state.text[state.atByte] _ VAL[p.cyan]; state.text[state.atByte+1] _ VAL[p.magenta]; state.text[state.atByte+2] _ VAL[p.yellow]; state.text[state.atByte+3] _ VAL[ p.black]; state.atByte_state.atByte+4; state.bytesLeftInSector_ state.bytesLeftInSector-4; }; WriteNewSector: PROC [state: State] ~ { IF state.sector>=lastSector THEN WriteNewRecord[state] ELSE {state.sector _ state.sector+1; state.atByte_state.sector*bytesPerSector}; state.bytesLeftInSector _ bytesPerSector; }; WriteNewCylinder: PROC [state: State] ~ { UNTIL state.recordsLeftInCylinder=0 DO WriteNewRecord[state]; ENDLOOP; state.recordsLeftInCylinder _ recordsPerCylinder; state.atByte_ 0; state.sector _ 0; state.bytesLeftInSector _ bytesPerSector; state.linesLeftInCylinder _ state.dir.linesPerCylinder; }; WriteNewRecord: PROC [state: State] ~ { UnixTapeOps.WriteRecord[state.tapeHandle, state.text]; state.atByte_ 0; state.sector _ 0; state.recordsLeftInCylinder _ state.recordsLeftInCylinder-1; }; <> ReadNewLine: PROC [state: State] ~ { --aligns state.atByte to start of next line IF state.linesLeftInCylinder=0 THEN ReadNewCylinder[state] ELSE ReadNewSector[state]; state.linesLeftInCylinder _ state.linesLeftInCylinder-1; state.linesLeftInFile _ state.linesLeftInFile-1; }; ReadNewCylinder: PROC [state: State] ~ { UNTIL state.recordsLeftInCylinder=0 DO ReadNewRecord[state]; ENDLOOP; state.recordsLeftInCylinder _ recordsPerCylinder; ReadNewRecord[state]; --sets atByte and sector state.bytesLeftInSector _ bytesPerSector; state.linesLeftInCylinder _ state.dir.linesPerCylinder; }; ReadNewSector: PROC [state: State] ~ { --aligns state.atByte to start of next Sector IF state.sector>=lastSector THEN ReadNewRecord[state] ELSE {state.sector _ state.sector+1; state.atByte_state.sector*512}; state.bytesLeftInSector _ bytesPerSector; }; ReadNewRecord: PROC [state: State] ~ { --reads a tape record tapeMark: BOOL; [tapeMark, state.text] _ UnixTapeOps.ReadRecord[state.tapeHandle, state.text]; IF tapeMark THEN ERROR; -- out of sync state.atByte_ 0; state.sector _ 0; state.recordsLeftInCylinder _ state.recordsLeftInCylinder-1; }; ReadPixel: PROC [state: State] RETURNS [p: Pixel] ~ { --gets pixels out of the line IF state.bytesLeftInSector=0 THEN ReadNewSector[state]; p.cyan _ ORD[state.text[state.atByte]]; p.magenta _ ORD[state.text[state.atByte+1]]; p.yellow _ ORD[state.text[state.atByte+2]]; p.black _ ORD[state.text[state.atByte+3]]; state.atByte_state.atByte+4; state.bytesLeftInSector_ state.bytesLeftInSector-4; }; <> Error: PUBLIC ERROR [r: ROPE] = CODE; TextFromIdentity: PROC [identity: Identity] RETURNS [text: REF TEXT] ~ { onTape: IdentityOnTape _ []; size: NAT _ SIZE[IdentityOnTape]; text _ AllocateBuffer[size*2]; onTape.checkWord1 _ ByteSwap[648Ch]; onTape.tapeID _ ByteSwap[identity.tapeNumber]; [onTape.dateYMD, onTape.dateHM] _ ToDate[identity.date]; onTape.name _ RopeToArray[identity.name]; onTape.checkWord2 _ ByteSwap[7D47h]; TRUSTED { placeToPut: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; placeToStart: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: size, to: placeToPut]; }; }; IdentityFromText: PROC [text: REF TEXT] RETURNS [identity: Identity] ~ { i: NAT _ 0; P: PROC RETURNS [c: CHAR] = {c _ onTape.name[i]; i _ i + 1}; placeToStart: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; onTape: IdentityOnTape; TRUSTED { placeToPut: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: SIZE[IdentityOnTape], to: placeToPut]; }; IF ByteSwap[onTape.checkWord1] # 648Ch THEN ERROR Error["bad Identity checkword 1"]; IF ByteSwap[onTape.checkWord2] # 7D47h THEN ERROR Error["bad Identity checkword 2"]; identity _ NEW[IdentityRep]; identity.tapeNumber _ ByteSwap[onTape.tapeID]; identity.date _ FromDate[onTape.dateYMD, onTape.dateHM]; identity.name _ Rope.FromProc[nameLen, P]; identity.name _ StripRope[identity.name]; }; ValHRes: ARRAY PixelsPerInch OF WORD _ [ppi300: 006000B, ppi450: 010000B, ppi600: 014000B]; ValVRes: ARRAY PixelsPerInch OF WORD _ [ppi300: 005717B, ppi450: 010667B, ppi600: 013637B]; HResFromVal: PROC [val: WORD] RETURNS [ppi: PixelsPerInch] ~ { ppi _ SELECT val FROM 006000B => ppi300, 010000B => ppi450, 014000B => ppi600, ENDCASE => ERROR; }; VResFromVal: PROC [val: WORD] RETURNS [ppi: PixelsPerInch] ~ { ppi _ SELECT val FROM 005717B => ppi300, 010667B => ppi450, 013637B => ppi600, ENDCASE => ERROR; }; TextFromDirectory: PROC [directory: Directory] RETURNS [text: REF TEXT] ~ { size: NAT _ SIZE[DirectoryOnTape]; onTape: DirectoryOnTape _ []; text _ AllocateBuffer[size*2]; onTape.idn _ ByteSwap[directory.fileNumber]; onTape.att _ directory.contone; onTape.color _ directory.color; onTape.hRes _ ByteSwap[ValHRes[directory.hRes]]; onTape.vRes _ ByteSwap[ValVRes[directory.vRes]]; onTape.pixelsPerLine _ ByteSwap[directory.pixelsPerLine]; onTape.lines _ ByteSwap[directory.lines]; [onTape.createTimeYMD, onTape.createTimeHM] _ ToDate[directory.createDate]; onTape.note _ RopeToArray[directory.note]; onTape.tapeID _ ByteSwap[directory.tapeNumber]; onTape.linesPerCylinder _ ByteSwap[directory.linesPerCylinder]; onTape.sectorsPerLine _ ByteSwap[directory.sectorsPerLine]; onTape.cylinders _ ByteSwap[directory.cylinders]; onTape.blocks _ ByteSwap[directory.blocks]; TRUSTED { placeToPut: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; placeToStart: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: size, to: placeToPut]; }; }; DirectoryFromText: PROC [text: REF TEXT, start: CARD] RETURNS [directory: Directory] ~ { placeToStart: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]] + start/2; i: NAT _ 0; P: PROC RETURNS [c: CHAR] = {c _ onTape.note[i]; i _ i + 1}; onTape: DirectoryOnTape _ []; TRUSTED { placeToPut: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[placeToStart, SIZE[DirectoryOnTape], placeToPut]; }; IF onTape.idn = 0FFFFh THEN RETURN [NIL]; -- The last one directory _ NEW[DirectoryRep]; directory.fileNumber _ ByteSwap[onTape.idn]; directory.contone _ onTape.att; directory.color _ onTape.color; directory.hRes _ HResFromVal[ByteSwap[onTape.hRes]]; directory.vRes _ VResFromVal[ByteSwap[onTape.vRes]]; directory.pixelsPerLine _ ByteSwap[onTape.pixelsPerLine]; directory.lines _ ByteSwap[onTape.lines]; directory.createDate _ FromDate[onTape.createTimeYMD, onTape.createTimeHM]; directory.note _ Rope.FromProc[noteLen, P]; directory.note _ StripRope[directory.note]; directory.tapeNumber _ ByteSwap[onTape.tapeID]; directory.linesPerCylinder _ ByteSwap[onTape.linesPerCylinder]; directory.sectorsPerLine _ ByteSwap[onTape.sectorsPerLine]; directory.cylinders _ ByteSwap[onTape.cylinders]; directory.blocks _ ByteSwap[onTape.blocks]; }; ByteSwap: PROC [in: WORD] RETURNS [WORD] = INLINE { RETURN[Basics.BITOR[LOOPHOLE[in, Basics.BytePair].low*256, LOOPHOLE[in, Basics.BytePair].high]]; }; ByteSwapProc: PROC [in: WORD] RETURNS [out: WORD] = { out _ Basics.BITOR[LOOPHOLE[in, Basics.BytePair].low*256, LOOPHOLE[in, Basics.BytePair].high]; }; StripRope: PROC [rope: ROPE] RETURNS [ROPE] ~ { last: INT _ Rope.Length[rope]; FOR i: INT DECREASING IN [0 .. Rope.Length[rope]) DO IF Rope.Fetch[base: rope, index: i] = '\000 THEN last _ last - 1 ELSE EXIT; ENDLOOP; RETURN[Rope.Substr[base: rope, start: 0, len: last]]; }; RopeToArray: PROC [rope: ROPE] RETURNS [array: PACKED ARRAY [0..20) OF CHAR] ~ { length: NAT _ Rope.Length[rope]; FOR i: NAT IN [0..length) DO array[i] _ Rope.Fetch[base: rope, index: i]; ENDLOOP; FOR i: NAT IN [length..20) DO array[i] _ '\000; ENDLOOP; }; ToDate: PROC [t: BasicTime.GMT] RETURNS [rawYMD: WORD, dateHM: DateHM] ~ { unpacked: BasicTime.Unpacked _ BasicTime.Unpack[t]; dateYMD: DateYMD; dateYMD.year _ unpacked.year - 1900; dateYMD.month _ SELECT unpacked.month FROM January => 1, February=> 2, March=> 3, April=> 4, May => 5, June => 6, July=> 7, August => 8, September => 9, October => 10, November => 11, December => 12, ENDCASE => ERROR; dateYMD.day _ unpacked.day; dateHM.hour _ unpacked.hour; dateHM.minute _ unpacked.minute; rawYMD _ ByteSwapProc[LOOPHOLE[dateYMD]]; }; FromDate: PROC [rawYMD: WORD, dateHM: DateHM] RETURNS [t: BasicTime.GMT] ~ { unpacked: BasicTime.Unpacked; dateYMD: DateYMD _ LOOPHOLE[ByteSwapProc[rawYMD]]; unpacked.year _ dateYMD.year+1900; unpacked.month _ SELECT dateYMD.month FROM 1 => January, 2=> February, 3=> March, 4=> April, 5 => May, 6 => June, 7=> July, 8 => August, 9 => September, 10 => October, 11 => November, 12 => December, ENDCASE => unspecified; IF dateYMD.day NOT IN [1..31] THEN dateYMD.day _ 1; IF dateHM.hour NOT IN [0..12] THEN dateHM.hour _ 0; IF dateHM.minute NOT IN [0..60] THEN dateHM.minute _ 0; unpacked.day _ dateYMD.day; unpacked.hour _ dateHM.hour; unpacked.minute _ dateHM.minute; t _ BasicTime.nullGMT; t _ BasicTime.Pack[unpacked ! BasicTime.OutOfRange => CONTINUE]; RETURN[t]; }; <> ScreenToByte: ARRAY[0..255] OF BYTE _ ALL[0]; ByteToScreen: ARRAY[0..255] OF BYTE _ ALL[0]; MakeScreenToByte: PROC[pixelProc: PixelProc] = { FOR i: NAT IN[0..255] DO ScreenToByte[i] _ pixelProc[i]; ENDLOOP; }; MakeByteToScreen: PROC[pixelProc: PixelProc] = { FOR i: NAT IN[0..255] DO ByteToScreen[i] _ pixelProc[i]; ENDLOOP; }; CrosfieldToByte: PUBLIC PixelProc = { <> <> SELECT TRUE FROM pixel =21 => RETURN[0]; pixel =229 => RETURN[255]; pixel IN [25..225] => RETURN[ Real.Round[255*0.01* (pixel -25)/2.0]]; pixel < 25 => RETURN[0]; pixel > 225 => RETURN[255]; ENDCASE => ERROR; }; KOToByte: PUBLIC PixelProc = { <> dotPercent: REAL _ (pixel -6)*(100.0/239.0); dotPercent _ MIN[MAX[dotPercent, 0], 255]; RETURN[Real.Round[dotPercent*2.55]]; }; ByteToCrosfield: PUBLIC PixelProc = { SELECT TRUE FROM pixel =0 => RETURN[21]; pixel =255 => RETURN[229]; pixel IN [1..254] => RETURN[ Real.Round[(100.0*pixel /255.0)*2+25]]; ENDCASE => ERROR; }; IdentityByte: PUBLIC PixelProc = {RETURN[pixel]}; --use for read for KedieOrent tapes AllocateBuffer: PROC[sizeInBytes: INT] RETURNS[t: REF TEXT] = { t _ RefText.New[sizeInBytes]; t.length _ sizeInBytes; }; FileStructures: PROC [fileSpec: FileSpec] RETURNS [linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT] = { sectorsPerLine _ Real.Round[0.5+(fileSpec.nPixels*4.0)/bytesPerSector]; linesPerCylinder _ (recordsPerCylinder*sectorsPerRecord)/sectorsPerLine; nCylinders _ Real.Round[0.5+REAL[fileSpec.nLines]/linesPerCylinder]; nRecords _ nCylinders*recordsPerCylinder; }; sizeRecordGap: REAL _ 0.8; --the inter-record gap, in inches sizeFileGap: REAL _ 2; --the inter-file gap, in feet SizeOfHeader: PUBLIC PROC [density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { d: INTEGER _ SELECT density FROM GCR6250 => 6250, PE1600 => 1600, ENDCASE => ERROR; id: REAL _ sizeRecordGap+ SIZE[IdentityOnTape]/d; masterDir: REAL _ sizeRecordGap+ sizeOfMasterDirectory/d; RETURN[sizeFileGap+(id+masterDir)/12.0]; }; SizeOfFile: PUBLIC PROC [fileSpec: FileSpec, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { d: INTEGER _ SELECT density FROM GCR6250 => 6250, PE1600 => 1600, ENDCASE => ERROR; dir: REAL _ sizeRecordGap+ SIZE[DirectoryOnTape]/d; records: REAL; nCylinders: NAT; [,nCylinders] _ FileStructures[fileSpec]; records _ recordsPerCylinder*nCylinders*(sizeRecordGap+REAL[bytesPerRecord]/d); RETURN[sizeFileGap+(dir+records)/12.0]; }; SizeOfList: PUBLIC PROC [files: FileSpecList, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { <> sizeInFeet _ SizeOfHeader[density]; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO sizeInFeet _ SizeOfFile[f.first, density]+sizeInFeet; ENDLOOP; }; END.