// TableMaker.bcpl -- converts a file into a table contained in a loadable .br file
// Last modified April 6, 1982  1:12 PM by Taft

// Bldr TableMaker GP

get "Streams.d"

external
[
SetupReadParam; ReadParam
OpenFile; Closes; Resets; Endofs; Puts; ReadBlock; WriteBlock; FileLength; Wl
MoveBlock; Zero
]

//----------------------------------------------------------------------------
structure BRFile:	// Bcpl binary format
//----------------------------------------------------------------------------
[
bcplVersionNumber word
fileLength word
blank word		// must be zero
nameTableOffset word
blank word		// must be zero
labelTableOffset word
blank word		// must be zero
codeOffset word
blank word		// must be zero
chainTableOffset word
blank word		// must be zero
zchainTableOffset word
blank word 3		// must be zero
labelTable word 3 =
   [
   labelCount word	// must be 1
   labelNameNumber word	// must be 1
   labelPC word		// must be 1
   ]
chainTable word 1	// must be zero
zchainTable word 1	// must be zero
nameTable word 3 =
   [
   nameCount word	// must be 1
   nameDescriptor word	// must be 140b
   blank word
   nameString word 0	// actually @String
   ]
code word 2 =
   [
   codeLength word
   codeFirst word 1		// should be zero
   data word 0
   ]
]

manifest
[
lenBRFile = offset BRFile.nameString/16  // + name string + data
bcplVersionNumber = 2 lshift 8
]

structure String: [ length byte; char↑1,1 byte ]

//----------------------------------------------------------------------------
let TableMaker() be
//----------------------------------------------------------------------------
[
let inputFileName, outputFileName, staticName = vec 127, vec 127, vec 127
inputFileName!0 = 0; outputFileName!0 = 0; staticName!0 = 0
let string, switchVec = vec 127, vec 127
SetupReadParam()

   [ // repeat
   if ReadParam($P, 0, string, switchVec, true) eq -1 then break
   let destString = selecton switchVec!0 into
      [
      case 0: inputFileName
      case 1:
         selecton switchVec!1 into
            [
            case $I: case $i: inputFileName
            case $O: case $o: outputFileName
            case $S: case $s: staticName
            default: Fail("Undefined switch")
            ]
      default: Fail("Too many switches")
      ]
   MoveBlock(destString, string, string>>String.length rshift 1 +1)
   ] repeat

let inputFile = 0
if inputFileName!0 ne 0 then
   inputFile = OpenFile(inputFileName, ksTypeReadOnly, wordItem)
   if inputFile eq 0 then Wl("Input file not found.*n")
if inputFile eq 0 then
   inputFile = ReadParam("IW", "Input file: ", inputFileName)

for i = 1 to inputFileName>>String.length do
   if inputFileName>>String.char↑i eq $. then
      [ inputFileName>>String.length = i-1; break ]
if staticName!0 eq 0 then
   MoveBlock(staticName, inputFileName, inputFileName>>String.length rshift 1 +1)

if outputFileName!0 eq 0 then outputFileName = inputFileName
let dot = 0
for i = 1 to outputFileName>>String.length do
   if outputFileName>>String.char↑i eq $. then
      [ dot = i; break ]
if dot eq 0 then
   [
   dot = outputFileName>>String.length +1
   outputFileName>>String.char↑dot = $.
   outputFileName>>String.char↑(dot+1) = $b
   outputFileName>>String.char↑(dot+2) = $r
   outputFileName>>String.length = dot+2
   ]
let outputFile = OpenFile(outputFileName, ksTypeWriteOnly, wordItem)
if outputFile eq 0 then Fail("Can't open output file")

let fileLength = vec 1
FileLength(inputFile, fileLength)
let dataLength = fileLength!0 lshift 15 + fileLength!1 rshift 1
if (fileLength!1 & 1) ne 0 then dataLength = dataLength+1
Resets(inputFile)
WriteBRFileHeader(outputFile, dataLength, staticName)

until Endofs(inputFile) do
   [
   manifest lenDataVec = 12*256
   let data = vec lenDataVec
   let words = ReadBlock(inputFile, data, lenDataVec)
   WriteBlock(outputFile, data, words)
   ]

Closes(inputFile)
Closes(outputFile)
]

//----------------------------------------------------------------------------
and Fail(message) be
//----------------------------------------------------------------------------
[
Wl(message)
Wl("Command syntax is:  TableMaker inputFile outputFile/o staticName/s")
abort
]

//----------------------------------------------------------------------------
and WriteBRFileHeader(stream, dataLength, labelName) be
//----------------------------------------------------------------------------
// ripped off from LoadMB.bcpl, courtesy of Peter Deutsch
[
let brFile = vec lenBRFile; Zero(brFile, lenBRFile)
let nameLength = labelName>>String.length/2+1
brFile>>BRFile.bcplVersionNumber = bcplVersionNumber
brFile>>BRFile.fileLength = offset BRFile.data/16 + nameLength + dataLength
brFile>>BRFile.nameTableOffset = offset BRFile.nameTable/16
brFile>>BRFile.labelTableOffset = offset BRFile.labelTable/16
brFile>>BRFile.codeOffset = offset BRFile.code/16 + nameLength
brFile>>BRFile.chainTableOffset = offset BRFile.chainTable/16
brFile>>BRFile.zchainTableOffset = offset BRFile.zchainTable/16
brFile>>BRFile.labelCount = 1
brFile>>BRFile.labelNameNumber = 1
brFile>>BRFile.labelPC = 1
brFile>>BRFile.nameCount = 1
brFile>>BRFile.nameDescriptor = 140b
WriteBlock(stream, brFile, lenBRFile)
WriteBlock(stream, labelName, nameLength)
Puts(stream, dataLength+1)		// codeLength
Puts(stream, 0)				// codeFirst
]