<> <> <> <> DIRECTORY AIS, AISFormat, Basics, CountedVM, FS, IO, PrincOps, Rope, PrincOpsUtils, SafeStorage, VM; AISImpl: CEDAR PROGRAM IMPORTS FS, IO, CountedVM, PrincOpsUtils, Basics, SafeStorage, Rope, VM EXPORTS AIS = {OPEN AIS; Error: PUBLIC SIGNAL [type: ErrorType] = CODE; catch: BOOLEAN = TRUE; wordsPerPage: CARDINAL = PrincOps.wordsPerPage; bitsPerWord: CARDINAL = Basics.bitsPerWord; aisWordsPerPage: CARDINAL = AISFormat.aisWordsPerPage; pagesPerAISPage: CARDINAL = aisWordsPerPage/wordsPerPage; defaultBSize: CARDINAL _ 4*pagesPerAISPage; bbflags: PrincOps.BitBltFlags ~ [direction: forward, disjoint: TRUE, disjointItems: FALSE, gray: FALSE, srcFunc: null, dstFunc: null]; DivUp: PROC [n: LONG CARDINAL, d: CARDINAL] RETURNS [CARDINAL] = INLINE { q, r: CARDINAL; [q,r] _ Basics.LongDivMod[n,d]; RETURN[IF r>0 THEN q+1 ELSE q] }; LongCopy: UNSAFE PROC [from: LONG POINTER, to: LONG POINTER, nwords: LONG CARDINAL] = UNCHECKED INLINE { wpb: CARDINAL _ 177777B; q,r: CARDINAL; [q, r] _ Basics.LongDivMod[num: nwords, den: wpb]; FOR i: CARDINAL IN [0..q) DO PrincOpsUtils.LongCopy[from: from, to: to, nwords: wpb]; from _ from+wpb; ENDLOOP; PrincOpsUtils.LongCopy[from: from, to: to, nwords: r]; }; CreateFile: PUBLIC PROC [name: ROPE, raster: Raster, attributeLength: CARDINAL] RETURNS [f: FRef] = { file: FS.OpenFile _ FS.nullOpenFile; filesize: PrincOps.PageCount _ 0; bpp: CARDINAL _ 0; -- bits per pixel ppw: CARDINAL _ 0; -- pixels per word wpl: CARDINAL _ 0; -- words per scan line lines: CARDINAL _ 0; -- raster.scanLength pixels: CARDINAL _ 0; -- raster.scanCount apages, rpages, bpages: CARDINAL _ 0; -- AIS pages in attribute, raster, and block abase, rbase: VM.PageNumber; -- starting file pages aRef: ARef _ NEW[ARep]; <> SELECT raster.bitsPerPixel FROM 0 => { bpp _ 1; ppw _ bitsPerWord }; 1,2,4,8,16 => { bpp _ raster.bitsPerPixel; ppw _ bitsPerWord/bpp }; 3,5,6,7,IN[9..15] => Error[notImplemented]; -- not expected ENDCASE => Error[invalidParameter]; -- 0 or out of range IF raster.scanCount>0 AND raster.scanLength>0 THEN { lines _ raster.scanCount; pixels _ raster.scanLength; wpl _ DivUp[pixels,ppw]; } ELSE Error[invalidParameter]; -- bad scanCount or scanLength apages _ (IF attributeLength=0 THEN 1 ELSE DivUp[attributeLength, aisWordsPerPage]); attributeLength _ LONG[aisWordsPerPage]*apages; <> IF raster.linesPerBlock>0 THEN { blocks: CARDINAL _ DivUp[lines, raster.linesPerBlock]; -- number of blocks bwords: LONG CARDINAL _ Basics.LongMult[wpl, raster.linesPerBlock]; bpages _ DivUp[bwords, aisWordsPerPage]; -- ais pages per block rpages _ blocks*bpages; --total ais pages in raster part } ELSE { rwords: LONG CARDINAL _ Basics.LongMult[wpl, lines]; rpages _ DivUp[rwords, aisWordsPerPage] --total ais pages in raster part }; abase _ 0; rbase _ abase + pagesPerAISPage*apages; filesize _ rbase + pagesPerAISPage*rpages; file _ FS.Create[name: name, pages: filesize]; FS.SetByteCountAndCreatedTime[file: file, bytes: FS.BytesForPages[filesize]]; f _ NEW[FRep _ [file: file, write: TRUE, abase: abase, rbase: rbase, raster: raster, attributes: aRef, wordsPerLine: wpl, buffersize: 0]]; IF raster.linesPerBlock>0 THEN f.buffersize _ bpages*pagesPerAISPage ELSE SetBufferSize[f]; aRef.header _ NEW[AISFormat.Header _ [password: AISFormat.aisPassword, attributeLength: attributeLength]]; aRef.raster _ NEW[AISFormat.RasterPart.uca _ [ part: AISFormat.PartHeader[type: raster, length: SIZE[AISFormat.RasterPart.uca]], scanCount: raster.scanCount, scanLength: raster.scanLength, scanDir: (SELECT raster.scanMode FROM ru => 2, rd => 3, lu => 6, ld => 7, ul => 9, ur => 8, dl => 13, dr => 12, ENDCASE => ERROR), samplesPerPixel: 1, coding: uca[bitsPerSample: raster.bitsPerPixel, wordsPerScanLine: wpl, scanLinesPerBlock: raster.linesPerBlock, paddingPerBlock: raster.paddingPerBlock] ]]; RETURN[f]; }; DeleteFile: PUBLIC PROC [name: ROPE] = { FS.Delete[name]; }; OpenFile: PUBLIC PROC[name: ROPE, write: BOOLEAN _ FALSE] RETURNS [f: FRef] = { file: FS.OpenFile _ FS.Open[ name: name, lock: IF write THEN $write ELSE $read ]; scanmode: ScanMode _ ru; apages: PrincOps.PageCount; f _ NEW[FRep]; f.file _ file; f.abase _ 0; ReadAttributes[f]; --sets up f.attributes apages _ f.attributes.header.attributeLength/wordsPerPage; f.rbase _ f.abase + apages; scanmode _ SELECT f.attributes.raster.scanDir FROM 2 => ru, 3 => rd, 6 => lu, 7 => ld, 9 => ul, 8 => ur, 13 => dl, 12 => dr, ENDCASE => rd; f.raster _ NEW[AIS.RasterPart _ [scanCount: f.attributes.raster.scanCount, scanLength: f.attributes.raster.scanLength, scanMode: scanmode, bitsPerPixel: f.attributes.raster.bitsPerSample, linesPerBlock: f.attributes.raster.scanLinesPerBlock, paddingPerBlock: f.attributes.raster.paddingPerBlock]]; f.wordsPerLine _ f.attributes.raster.wordsPerScanLine; f.write _ write; IF f.attributes.raster.scanLinesPerBlock>0 THEN { bwords: LONG CARDINAL _ Basics.LongMult[f.attributes.raster.wordsPerScanLine, f.attributes.raster.scanLinesPerBlock]; bpages: LONG CARDINAL _ DivUp[bwords,aisWordsPerPage]; -- ais pages per block temp: LONG CARDINAL _ bpages*pagesPerAISPage; IF Basics.HighHalf[temp]=0 THEN f.buffersize _ Basics.LowHalf[temp] ELSE ERROR; } ELSE SetBufferSize[f]; RETURN[f]; }; CloseFile: PUBLIC PROC [f: FRef] = { attributesTruncated: BOOLEAN _ FALSE; IF f.write THEN attributesTruncated _ FlushBuffersAndClose[f]; IF attributesTruncated THEN Error[attributesTooLong] }; SetBufferSize: PROC [f: FRef] = { rsize: CARDINAL _ DivUp[Basics.LongMult[f.wordsPerLine, f.raster.scanCount], wordsPerPage]; f.buffersize _ IF rsize < defaultBSize THEN rsize ELSE defaultBSize; }; <> ReadComment: PUBLIC PROC [f: FRef] RETURNS [ROPE] = { IF f.attributes.comment = NIL THEN RETURN [NIL] ELSE { chars: NAT ~ ORD[f.attributes.comment[0]]; words: NAT ~ f.attributes.comment.part.length - SIZE[AISFormat.PartHeader]; text: REF TEXT _ NEW[TEXT[chars]]; IF chars+1 > words*Basics.bytesPerWord THEN Error[invalidParameter]; FOR i: NAT IN [0..chars) DO text[i] _ f.attributes.comment[i+1]; ENDLOOP; text.length _ chars; RETURN [Rope.FromRefText[text]] }; }; WriteComment: PUBLIC PROC [f: FRef, comment: ROPE] = { length: [0..256) _ Rope.Length[comment]; IF ~f.write THEN Error[readOnlyFile]; f.attributes.comment _ NEW[AISFormat.CommentPart[length+1]]; f.attributes.comment.part _ [type: comment, length: SIZE[AISFormat.CommentPart[length+1]]]; f.attributes.comment[0] _ VAL[length]; FOR i: NAT IN [0..length) DO f.attributes.comment[i+1] _ comment.Fetch[i]; ENDLOOP; }; ReadPlacement: PUBLIC PROC [f: FRef] RETURNS [Placement] = { IF f.attributes.placement = NIL THEN RETURN [NIL]; RETURN [NEW[PlacementPart _ [ xLeft: f.attributes.placement.xLeft, yBottom: f.attributes.placement.yBottom, xWidth: f.attributes.placement.xWidth, yHeight: f.attributes.placement.yHeight ]]]; }; WritePlacement: PUBLIC PROC [f: FRef, placement: Placement] = { IF ~f.write THEN Error[readOnlyFile]; IF placement = NIL THEN f.attributes.placement _ NIL ELSE { IF f.attributes.placement=NIL THEN f.attributes.placement _ NEW[AISFormat.PlacementPart]; f.attributes.placement^ _ [ part: [type: placement, length: SIZE[AISFormat.PlacementPart]], xLeft: placement.xLeft, yBottom: placement.yBottom, xWidth: placement.xWidth, yHeight: placement.yHeight ]; }; }; ReadRaster: PUBLIC PROC [f: FRef] RETURNS [Raster] = { RETURN [NEW[RasterPart _ f.raster^]]; }; ReadPhotometry: PUBLIC PROC [f: FRef] RETURNS [photometry: Photometry] = { IF f.attributes.photometry=NIL THEN RETURN [NIL]; RETURN[NEW[PhotometryPart _ [ signal: f.attributes.photometry.signal, sense: f.attributes.photometry.sense, scaleType: f.attributes.photometry.scaleType, pointA: f.attributes.photometry.pointA, pointB: f.attributes.photometry.pointB, pointC: f.attributes.photometry.pointC, spotType: f.attributes.photometry.spotType, spotWidth: f.attributes.photometry.spotWidth, spotLength: f.attributes.photometry.spotLength, sampleMin: f.attributes.photometry.sampleMin, sampleMax: f.attributes.photometry.sampleMax, histogramLength: f.attributes.photometry.histogramLength ]]]; }; ReadHistogram: PUBLIC PROC [f: FRef] RETURNS [histogram: Histogram] = { IF f.attributes.photometry=NIL OR f.attributes.photometry.histogramLength<=0 THEN RETURN [NIL]; histogram _ NEW[HistogramRep[f.attributes.photometry.histogramLength]]; FOR i: NAT IN [0..histogram.length) DO histogram[i] _ f.attributes.photometry.histogram[i]; ENDLOOP; }; WritePhotometry: PUBLIC PROC [f: FRef, photometry: Photometry, histogram: Histogram _ NIL] = { histogramLength: INTEGER _ 0; p: REF AISFormat.PhotometryPart _ NIL; IF ~f.write THEN Error[readOnlyFile]; IF photometry = NIL AND histogram = NIL THEN {f.attributes.photometry _ NIL; RETURN}; <> IF histogram#NIL THEN histogramLength _ histogram.length; IF histogramLength # photometry.histogramLength THEN ERROR; p _ NEW[AISFormat.PhotometryPart[histogramLength]]; p.part _ [type: photometry, length: SIZE[AISFormat.PhotometryPart[histogramLength]]]; p.signal _ photometry.signal; p.sense _ photometry.sense; p.scaleType _ photometry.scaleType; p.pointA _ photometry.pointA; p.pointB _ photometry.pointB; p.pointC _ photometry.pointC; p.spotType _ photometry.spotType; p.spotWidth _ photometry.spotWidth; p.spotLength _ photometry.spotLength; p.sampleMin _ photometry.sampleMin; p.sampleMax _ photometry.sampleMax; p.histogramLength _ photometry.histogramLength; FOR i: INTEGER IN [0..histogramLength) DO p[i] _ histogram[i]; ENDLOOP; f.attributes.photometry _ p; }; <> OpenWindow: PUBLIC PROC [f: FRef, firstScan: CARDINAL _ 0, lastScan: CARDINAL _ LAST[CARDINAL], firstPixel: CARDINAL _ 0, lastPixel: CARDINAL _ LAST[CARDINAL]] RETURNS [w: WRef] = { bref: BRef _ NEW[BRep]; wpl, ppw: CARDINAL; IF lastScan=LAST[CARDINAL] THEN lastScan _ f.raster.scanCount; IF lastPixel=LAST[CARDINAL] THEN lastPixel _ f.raster.scanLength; lastScan _ MIN[f.raster.scanCount-1,lastScan]; lastPixel _ MIN[f.raster.scanLength-1,lastPixel]; IF firstScan>lastScan THEN Error[badWindow]; IF firstPixel>lastPixel THEN Error[badWindow]; bref^ _ [countedVM: CountedVM.Allocate[VM.WordsForPages[f.buffersize]], addr: NIL, first: 0, last: 0, firstPage: 0, nPages: 0]; ppw _ IF f.raster.bitsPerPixel=0 THEN bitsPerWord ELSE bitsPerWord/f.raster.bitsPerPixel; wpl _ DivUp[lastPixel-firstPixel+1,ppw]; w _ NEW[WRep _ [fref: f, bref: bref, firstScan: firstScan, lastScan: lastScan, firstPixel: firstPixel, lastPixel: lastPixel, wordsPerLine: wpl, pixelsPerWord: ppw]]; MapBuffer[w, w.firstScan]; }; CloseWindow: PUBLIC PROC [w: WRef] = { w.fref _ NIL; w.bref _ NIL; SafeStorage.ReclaimCollectibleObjects[]; }; <> MapBuffer: PROC [w: WRef, scan: CARDINAL _ 0] = INLINE { IF scan>w.lastScan THEN Error[outOfRange]; IF w.bref.addr#NIL AND scan IN [w.bref.first..w.bref.last] AND (w.fref.outputBuffer = NIL OR w.fref.outputBuffer = w.bref) THEN RETURN; --already mapped in GMBuff[w, scan]; }; GMBuff: PROC [w: WRef, scan: CARDINAL _ 0] = TRUSTED { page, offset, lnum, loff: CARDINAL; IF w.fref.outputBuffer # NIL THEN { FS.Write[ file: w.fref.file, to: w.fref.outputBuffer.firstPage, nPages: w.fref.outputBuffer.nPages, from: w.fref.outputBuffer.countedVM.pointer ]; w.fref.outputBuffer _ NIL; }; <> [page, offset] _ Basics.LongDivMod[num: Basics.LongMult[scan, w.fref.wordsPerLine], den: wordsPerPage]; w.bref.firstPage _ w.fref.rbase+page; w.bref.nPages _ MIN[w.bref.countedVM.interval.count, MAX[FS.GetInfo[w.fref.file].pages-(w.fref.rbase+page), 0]]; FS.Read[ file: w.fref.file, from: w.bref.firstPage, nPages: w.bref.nPages, to: w.bref.countedVM.pointer ]; <> [lnum,loff] _ Basics.LongDivMod[num: offset, den: w.fref.wordsPerLine]; w.bref.addr _ w.bref.countedVM.pointer+loff; w.bref.first _ scan-lnum; w.bref.last _ w.bref.first + Basics.LongDiv[num: Basics.LongMult[w.fref.buffersize, wordsPerPage]-loff, den: w.fref.wordsPerLine] - 1; }; GetWindowParams: PUBLIC PROC [w: WRef] RETURNS [firstScan: CARDINAL, lastScan: CARDINAL, firstPixel: CARDINAL, lastPixel: CARDINAL] = { firstScan _ w.firstScan; lastScan _ w.lastScan; firstPixel _ w.firstPixel; lastPixel _ w.lastPixel; }; MinBufferSize: PUBLIC PROC [w: WRef] RETURNS [length: CARDINAL] = {RETURN[w.wordsPerLine]}; EndOfWindow: PUBLIC PROC [w: WRef] RETURNS [BOOLEAN] = {RETURN[w.nextScanLine=(w.lastScan-w.firstScan)]}; UnsafeReadLine: PUBLIC UNSAFE PROC [w: WRef, buffer: Buffer, line: INTEGER _ -1] = UNCHECKED { XFerLine[w,buffer,line,TRUE]; }; UnsafeWriteLine: PUBLIC UNSAFE PROC [w: WRef, buffer: Buffer, line: INTEGER _ -1] = UNCHECKED { IF NOT w.fref.write THEN Error[readOnlyFile]; XFerLine[w,buffer,line,FALSE]; w.fref.outputBuffer _ w.bref; }; XFerLine: UNSAFE PROC [w: WRef, buffer: Buffer, line: INTEGER _ -1, read: BOOLEAN] = UNCHECKED { woff, poff, absline: CARDINAL; word: LONG POINTER; IF w.wordsPerLine > buffer.length THEN Error[bufferTooSmall]; IF line=-1 THEN line _ w.nextScanLine; absline _ CARDINAL[line]+w.firstScan; IF absline > w.lastScan THEN Error[outOfRange]; MapBuffer[w, absline]; [woff,poff] _ Basics.LongDivMod[num: w.firstPixel, den: w.pixelsPerWord]; word _ w.bref.addr+Basics.LongMult[w.fref.wordsPerLine, (absline-w.bref.first)]+woff; IF poff=0 THEN { IF read THEN LongCopy[from: word, to: buffer.addr, nwords: w.wordsPerLine] ELSE LongCopy[from: buffer.addr, to: word, nwords: w.wordsPerLine] } ELSE { src, dst: PrincOps.BitAddress; bit: CARDINAL _ IF w.fref.raster.bitsPerPixel=0 THEN poff ELSE poff*w.fref.raster.bitsPerPixel; bufferwidth: CARDINAL _ w.wordsPerLine*bitsPerWord; filebpl: CARDINAL _ w.fref.wordsPerLine*bitsPerWord; bbspace: PrincOps.BBTableSpace; bbptr: PrincOps.BBptr _ PrincOpsUtils.AlignedBBTable[@bbspace]; IF read THEN { src _ [word: word, bit: bit]; dst _ [word: buffer.addr, bit: 0]; bbptr^ _ [dst: dst, dstBpl: bufferwidth, src: src, srcDesc: [srcBpl[srcBpl: filebpl]], width: bufferwidth, height: 1, flags: bbflags]; } ELSE { dst _ [word: word, bit: bit]; src _ [word: buffer.addr, bit: 0]; bbptr^ _ [dst: dst, dstBpl: filebpl, src: src, srcDesc: [srcBpl[srcBpl: bufferwidth]], width: bufferwidth, height: 1, flags: bbflags]; }; PrincOpsUtils.BITBLT[bbptr]; }; w.nextScanLine _ MIN[line+1,w.lastScan]; }; Line1: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..1B]]; Line2: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..3B]]; Line4: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..17B]]; Line8: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..377B]]; Line16: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF WORD]; ReadSample: PUBLIC PROC [w: WRef, line, pixel: CARDINAL] RETURNS [value: CARDINAL] = TRUSTED { lineaddr: LONG POINTER; absline: CARDINAL _ line+w.firstScan; IF absline # w.currline THEN { IF absline > w.lastScan THEN Error[outOfRange]; MapBuffer[w, absline]; w.currline_absline; w.clineaddr _ w.bref.addr+Basics.LongMult[(absline-w.bref.first),w.fref.wordsPerLine]; } ELSE {IF pixel+ w.firstPixel > w.lastPixel THEN Error[outOfRange]}; lineaddr_w.clineaddr; SELECT w.fref.raster.bitsPerPixel FROM 0,1 => RETURN[LOOPHOLE[lineaddr,LONG POINTER TO Line1][pixel]]; 2 => RETURN[LOOPHOLE[lineaddr,LONG POINTER TO Line2][pixel]]; 4 => RETURN[LOOPHOLE[lineaddr,LONG POINTER TO Line4][pixel]]; 8 => RETURN[LOOPHOLE[lineaddr,LONG POINTER TO Line8][pixel]]; 16 => RETURN[LOOPHOLE[lineaddr,LONG POINTER TO Line16][pixel]]; ENDCASE => Error[notImplemented]; w.nextScanLine _ MIN[line+1,w.lastScan]; }; WriteSample: PUBLIC PROC [w: WRef, value: CARDINAL, line, pixel: CARDINAL] = TRUSTED { lineaddr: LONG POINTER; absline: CARDINAL _ line+w.firstScan; IF NOT w.fref.write THEN Error[readOnlyFile]; IF absline # w.currline THEN { IF absline > w.lastScan THEN Error[outOfRange]; MapBuffer[w, absline]; w.currline _ absline; w.clineaddr _ w.bref.addr+Basics.LongMult[(absline-w.bref.first),w.fref.wordsPerLine]; } ELSE {IF pixel+ w.firstPixel > w.lastPixel THEN Error[outOfRange]}; lineaddr_w.clineaddr; SELECT w.fref.raster.bitsPerPixel FROM 0,1 => LOOPHOLE[lineaddr,LONG POINTER TO Line1][pixel] _ value; 2 => LOOPHOLE[lineaddr,LONG POINTER TO Line2][pixel] _ value; 4 => LOOPHOLE[lineaddr,LONG POINTER TO Line4][pixel] _ value; 8 => LOOPHOLE[lineaddr,LONG POINTER TO Line8][pixel] _ value; 16 => LOOPHOLE[lineaddr,LONG POINTER TO Line16][pixel] _ value; ENDCASE => Error[notImplemented]; w.nextScanLine _ MIN[line+1,w.lastScan]; w.fref.outputBuffer _ w.bref; }; ReadAttributes: PROC [f: FRef] = TRUSTED { addr: LONG POINTER _ NIL; -- address of first word of attributes words: LONG CARDINAL _ 0; -- number of words read from attributes header: LONG POINTER TO AISFormat.Header _ NIL; part: LONG POINTER TO AISFormat.PartHeader _ NIL; raster: LONG POINTER TO uca AISFormat.RasterPart _ NIL; placement: LONG POINTER TO AISFormat.PlacementPart _ NIL; photometry: LONG POINTER TO AISFormat.PhotometryPart _ NIL; comment: LONG POINTER TO AISFormat.CommentPart _ NIL; space: CountedVM.Handle _ CountedVM.Allocate[AISFormat.aisWordsPerPage]; attributes: ARef _ NEW[ARep]; f.attributes _ attributes; FS.Read[ file: f.file, from: 0, nPages: space.interval.count, to: space.pointer ]; header _ space.pointer; IF header.password#AISFormat.aisPassword THEN Error[invalidFile]; -- wrong password IF header.attributeLength > space.words THEN { header _ NIL; space _ CountedVM.Allocate[header.attributeLength]; FS.Read[ file: f.file, from: 0, nPages: space.interval.count, to: space.pointer ]; }; addr _ space.pointer; header _ addr; IF header.password#AISFormat.aisPassword THEN Error[invalidFile]; -- wrong password IF (header.attributeLength MOD wordsPerPage) # 0 THEN Error[invalidFile]; -- illegal attribute length attributes.header _ NEW[AISFormat.Header _ header^]; words _ SIZE[AISFormat.Header]; DO -- read the parts part _ addr + words; words _ words + part.length; IF words>header.attributeLength THEN Error[invalidFile]; -- part overflows end of attribute section SELECT part.type FROM nil => EXIT; -- no more parts raster => IF raster=NIL THEN {raster _ LOOPHOLE[part]; attributes.raster _ NEW[AISFormat.RasterPart.uca _ raster^]} ELSE Error[invalidFile]; -- multiple raster parts placement => IF placement=NIL THEN {placement _ LOOPHOLE[part]; attributes.placement _ NEW[AISFormat.PlacementPart _ placement^]} ELSE Error[invalidFile]; -- multiple placement parts photometry => { IF photometry=NIL THEN { hlen: INTEGER _ 0; words: NAT _ 0; photometry _ LOOPHOLE[part]; hlen _ photometry.histogramLength; IF hlen<0 THEN hlen _ 0; words _ SIZE[AISFormat.PhotometryPart[hlen]]; IF words > photometry.part.length THEN Error[invalidFile]; attributes.photometry _ NEW[AISFormat.PhotometryPart[hlen]]; PrincOpsUtils.LongCopy[from: photometry, to: LOOPHOLE[attributes.photometry], nwords: words]; } ELSE Error[invalidFile]; -- multiple photometry parts }; comment => { IF comment=NIL THEN { words: NAT _ 0; comment _ LOOPHOLE[part]; words _ SIZE[AISFormat.CommentPart[ORD[comment[0]+1]]]; IF comment.part.length < words THEN Error[invalidFile]; attributes.comment _ NEW[AISFormat.CommentPart[ORD[comment[0]+1]]]; PrincOpsUtils.LongCopy[from: comment, to: LOOPHOLE[attributes.comment], nwords: words]; } ELSE Error[invalidFile]; -- multiple comment parts }; ENDCASE => Error[invalidFile]; -- unknown part type ENDLOOP; IF raster=NIL THEN Error[invalidFile]; -- raster part missing IF raster.samplesPerPixel#1 THEN ERROR; -- Can't handle multiple samples per pixel IF raster.codingType#uca THEN ERROR; -- Coding type not UCA space _ NIL; SafeStorage.ReclaimCollectibleObjects[]; }; FlushBuffersAndClose: PROC [f: FRef] RETURNS [truncated: BOOLEAN _ FALSE] = TRUSTED { stream: IO.STREAM _ NIL; wordsWritten: INT _ 0; zero: CARDINAL _ 0; WriteBlock: UNSAFE PROC [addr: LONG POINTER, words: NAT] ~ UNCHECKED { IF wordsWritten + words > f.attributes.header.attributeLength THEN truncated _ TRUE ELSE { IO.UnsafePutBlock[stream, [base: addr, startIndex: 0, count: Basics.bytesPerWord*words]]; wordsWritten _ wordsWritten + words; }; }; IF f.outputBuffer # NIL THEN { FS.Write[ file: f.file, to: f.outputBuffer.firstPage, nPages: f.outputBuffer.nPages, from: f.outputBuffer.countedVM.pointer ]; f.outputBuffer _ NIL; }; stream _ FS.StreamFromOpenFile[f.file, $write]; WriteBlock[LOOPHOLE[f.attributes.header], SIZE[AISFormat.Header]]; IF f.attributes.raster#NIL THEN WriteBlock[LOOPHOLE[f.attributes.raster], f.attributes.raster.part.length]; IF f.attributes.placement # NIL THEN WriteBlock[LOOPHOLE[f.attributes.placement], f.attributes.placement.part.length]; IF f.attributes.photometry # NIL THEN WriteBlock[LOOPHOLE[f.attributes.photometry], f.attributes.photometry.part.length]; IF f.attributes.comment#NIL THEN WriteBlock[LOOPHOLE[f.attributes.comment], f.attributes.comment.part.length]; WriteBlock[@zero, SIZE[CARDINAL]]; IO.Close[stream]; }; }. <> <> <> <> <> <> <>