Compare commits

...

2 commits

Author SHA1 Message Date
Jeremy Penner a5b8712ec8 Refactor decoder / renderer / display pages into Javascript modules
Revert limb patterns, as they're breaking MP.bin
2024-01-03 02:50:44 -05:00
Jeremy Penner 70f2d0338a Clean commit of historical griddle / fred source 2024-01-02 19:43:12 -05:00
31 changed files with 7219 additions and 905 deletions

View file

@ -15,7 +15,6 @@
line-height:1.2
}
</style>
<script src="index.js?v=4"></script>
</head>
<body>
<h1 id="filename"></h1>
@ -33,7 +32,10 @@
</div>
<div id="errors"></div>
<a href="index.html">Back</a>
<script>
<script type="module">
import { decodeBody, choreographyActions } from "./codec.js"
import { docBuilder, decodeBinary, showAll, actionShower, limbAnimationShower, celShower, textNode } from "./show.js"
const propFilter = (key, value) => {
if (key != "bitmap" && key != "data" && key != "canvas") {
return value
@ -51,31 +53,31 @@
const q = new URLSearchParams(window.location.search)
const filename = q.get("f")
document.getElementById("filename").innerText = filename
const doc = docBuilder({ errorContainer: document.getElementById("errors") })
try {
const body = await decodeBinary(filename, BodyImpl)
const body = await decodeBinary(filename, decodeBody)
dumpProp(body, document.getElementById("data"))
if (body.error) {
showError(body.error, filename)
doc.showError(body.error, filename)
} else {
const celContainer = document.getElementById("cels")
const limbContainer = document.getElementById("limbs")
for (const [ilimb, limb] of body.limbs.entries()) {
labelLimb(celContainer, ilimb)
showCels(limb, celContainer)
labelLimb(celContainer, ilimb)
showAll(doc, celContainer, filename, limb.cels, celShower())
labelLimb(limbContainer, ilimb)
showAnimations(limb, limbContainer, LimbImpl)
showAll(doc, limbContainer, filename, limb.animations, limbAnimationShower(limb))
}
const actionContainer = document.getElementById("actions")
for (const action of choreographyActions) {
showAll(doc, actionContainer, filename, choreographyActions, (action) => {
if (action == "stand" || body.actions[action] != body.actions["stand"]) {
actionContainer.appendChild(textNode(action, "div"))
actionContainer.appendChild(createAnimation(action, body, BodyImpl))
return [textNode(action, "div"), actionShower(body)(action)]
}
}
})
}
} catch (e) {
showError(e, filename)
doc.showError(e, filename)
}
}
onload()

497
codec.js Normal file
View file

@ -0,0 +1,497 @@
const LE = true // little-endian
// JS bitmap format: array of scanlines, each scanline being an array of numbers from 0-3
export const emptyBitmap = (w, h, color = 0) => {
const bitmap = []
for (let y = 0; y < h; y ++) {
const scanline = []
for (let x = 0; x < w; x ++) {
scanline.push(color)
scanline.push(color)
scanline.push(color)
scanline.push(color)
}
bitmap.push(scanline)
}
return bitmap
}
export const drawByte = (bitmap, x, y, byte) => {
bitmap[y][x] = (byte & 0xc0) >> 6
bitmap[y][x + 1] = (byte & 0x30) >> 4
bitmap[y][x + 2] = (byte & 0x0c) >> 2
bitmap[y][x + 3] = (byte & 0x03)
}
const signedByte = (byte) => {
if ((byte & 0x80) != 0) {
const complement = (byte ^ 0xff) + 1
return -complement
} else {
return byte
}
}
const decodeHowHeld = (byte) => {
const heldVal = byte & 0xc0
if (heldVal == 0) {
return "swing"
} else if (heldVal == 0x40) {
return "out"
} else if (heldVal == 0x80) {
return "both"
} else {
return "at_side"
}
}
const encodeHowHeld = (howHeld) => {
if (howHeld == "swing") {
return 0x00
} else if (howHeld == "out") {
return 0x40
} else if (howHeld == "both") {
return 0x80
} else if (howHeld == "at_side") {
return 0xc0
} else {
throw new Error(`Unknown hold "${howHeld}"`)
}
}
const decodeCelType = (byte) => {
const typeVal = byte & 0xc0
if (typeVal == 0x00) {
if ((byte & 0x20) == 0) {
return "bitmap"
} else {
return "text"
}
} else if (typeVal == 0x40) {
return "trap"
} else if (typeVal == 0x80) {
return "box"
} else {
return "circle"
}
}
const encodeCelType = (type) => {
if (type == "bitmap") {
return 0x00
} else if (type == "text") {
return 0x20
} else if (type == "trap") {
return 0x40
} else if (type == "box") {
return 0x80
} else if (type == "circle") {
return 0xc0
} else {
throw new Error(`Unknown cel type "${type}"`)
}
}
const celDecoder = {}
const celEncoder = {}
celDecoder.bitmap = (data, cel) => {
// bitmap cells are RLE-encoded vertical strips of bytes. Decoding starts from the bottom-left
// and proceeds upwards until the top of the bitmap is hit; then then next vertical strip is decoded.
// Each byte describes four 2-bit pixels.
const bitmap = emptyBitmap(cel.width, cel.height)
let ibmp = 0
const end = cel.width * cel.height
const putByte = (byte) => {
const x = Math.floor(ibmp / cel.height) * 4
const y = (cel.height - (ibmp % cel.height)) - 1
drawByte(bitmap, x, y, byte)
ibmp ++
}
let i = 6
while (ibmp < end) {
const byte = data.getUint8(i)
i ++
if (byte == 0) {
// A zero byte denotes the start of a run of identical bytes. The second
// byte denotes the number of repetitions.
const count = data.getUint8(i)
i ++
if ((count & 0x80) == 0) {
// if the high bit of the count is not set, we read a third byte to
// determine the byte to repeat.
const val = data.getUint8(i)
i ++
for (let repeat = 0; repeat < count; repeat ++) {
putByte(val)
}
} else {
// if the high bit of the count is set, the lower 7 bits are used as
// the count, and a fully transparent byte is repeated.
for (let repeat = 0; repeat < (count & 0x7f); repeat ++) {
putByte(0)
}
}
} else {
// non-zero bytes are raw bitmap data
putByte(byte)
}
}
cel.bitmap = bitmap
}
celDecoder.box = (data, cel) => {
const bitmap = emptyBitmap(cel.width, cel.height)
cel.borderLR = (data.getUint8(0) & 0x20) != 0
cel.borderTB = (data.getUint8(0) & 0x10) != 0
cel.pattern = data.getUint8(6)
for (let y = 0; y < cel.height; y ++) {
for (let x = 0; x < cel.width; x ++) {
if (cel.borderTB && (y == 0 || y == (cel.height - 1))) {
drawByte(bitmap, x * 4, y, 0xaa)
} else {
drawByte(bitmap, x * 4, y, cel.pattern)
}
}
if (cel.borderLR) {
const line = bitmap[y]
line[0] = 2
line[line.length - 1] = 2
}
}
cel.bitmap = bitmap
}
const horizontalLine = (bitmap, xa, xb, y, patternByte) => {
const xStart = xa - (xa % 4)
const xEnd = (xb + (3 - (xb % 4))) - 3
for (let x = xStart + 4; x < xEnd; x += 4) {
drawByte(bitmap, x, y, patternByte)
}
const startBit = ((xa - xStart) * 2)
const startByte = (0xff >> startBit) & patternByte
drawByte(bitmap, xStart, y, startByte)
const endBit = (((xEnd + 3) - xb) * 2)
const endByte = (0xff << endBit) & patternByte
drawByte(bitmap, xEnd, y, endByte)
}
celDecoder.trap = (data, cel) => {
let border = false
// trap.m:21 - high-bit set means "draw a border"
// It looks like this was used as a flag and the real height
// was ORed with 0x80 - see house2.m, sign2.m
// There are also trapezoids that use 0x80 as their height -
// bwall6.m, bwall7.m, bwall9.m, magic_wall.m
// This appears to be special-cased to mean "no border" at trap.m:26
// mix.m:253 appears to have the logic to calculate y2, extracting
// the height by ANDing with 0x7f (when not 0x80)
if ((cel.height & 0x80) != 0 && cel.height != 0x80) {
border = true
cel.height = cel.height & 0x7f
}
if ((data.getUint8(0) & 0x10) == 0) {
// shape_pattern is a repeating 4-pixel colour, same as box
cel.pattern = data.getUint8(6)
} else {
// shape_pattern is 0xff, and the pattern is a bitmap that follows
// the trapezoid definition
// dline.m:103 - first two bytes are bitmasks used for efficiently calculating
// offsets into the texture. This means that the dimensions will be a power of
// two, and we can get the width and height simply by adding one to the mask.
const texW = data.getUint8(11) + 1
const texH = data.getUint8(12) + 1
cel.texture = emptyBitmap(texW, texH)
let i = 13
// dline.m:111 - the y position into the texture is calculated by
// ANDing y1 with the height mask; thus, unlike prop bitmaps, we decode
// from the top down
for (let y = 0; y < texH; y ++) {
for (let x = 0; x < texW; x ++) {
drawByte(cel.texture, x * 4, y, data.getUint8(i))
i ++
}
}
}
cel.x1a = data.getUint8(7)
cel.x1b = data.getUint8(8)
cel.x2a = data.getUint8(9)
cel.x2b = data.getUint8(10)
// trapezoid-drawing algorithm:
// draw_line: draws a line from x1a,y1 to x1b, y1
// handles border drawing (last/first line, edges)
// decreases vcount, then jumps to cycle1 if there
// are more lines
// cycle1: run bresenham, determine if x1a (left edge) needs to be incremented
// or decremented (self-modifying code! the instruction in inc_dec1 is
// written at trap.m:52)
// has logic to jump back to cycle1 if we have a sharp enough angle that
// we need to move more than one pixel horizontally
// cycle2: same thing, but for x2a (right edge)
// at the end, increments y1 and jumps back to the top of draw_line
cel.width = Math.floor((Math.max(cel.x1a, cel.x1b, cel.x2a, cel.x2b) + 3) / 4)
// trap.m:32 - delta_y and vcount are calculated by subtracting y2 - y1.
// mix.m:253: y2 is calculated as cel_y + cel_height
// mix.m:261: y1 is calculated as cel_y + 1
// So for a one-pixel tall trapezoid, deltay is 0, because y1 == y2.
// vcount is decremented until it reaches -1, compensating for the off-by-one.
const deltay = cel.height - 1
cel.bitmap = emptyBitmap(cel.width, cel.height)
const dxa = Math.abs(cel.x1a - cel.x2a)
const dxb = Math.abs(cel.x1b - cel.x2b)
const countMaxA = Math.max(dxa, deltay)
const countMaxB = Math.max(dxb, deltay)
const inca = cel.x1a < cel.x2a ? 1 : -1
const incb = cel.x1b < cel.x2b ? 1 : -1
let x1aLo = Math.floor(countMaxA / 2)
let y1aLo = x1aLo
let x1bLo = Math.floor(countMaxB / 2)
let y1bLo = x1bLo
let xa = cel.x1a
let xb = cel.x1b
for (let y = 0; y < cel.height; y ++) {
const line = cel.bitmap[y]
if (border && (y == 0 || y == (cel.height - 1))) {
// top and bottom border line
horizontalLine(cel.bitmap, xa, xb, y, 0xaa, true)
} else {
if (cel.texture) {
const texLine = cel.texture[y % cel.texture.length]
for (let x = xa; x <= xb; x ++) {
line[x] = texLine[x % texLine.length]
}
} else {
horizontalLine(cel.bitmap, xa, xb, y, cel.pattern, border)
}
}
if (border) {
line[xa] = 2
line[xb] = 2
}
// cycle1: move xa
do {
x1aLo += dxa
if (x1aLo >= countMaxA) {
x1aLo -= countMaxA
xa += inca
}
y1aLo += deltay
} while (y1aLo < countMaxA)
y1aLo -= countMaxA
// cycle2: move xb
do {
x1bLo += dxb
if (x1bLo >= countMaxB) {
x1bLo -= countMaxB
xb += incb
}
y1bLo += deltay
} while (y1bLo < countMaxA)
y1bLo -= countMaxA
}
}
const decodeCel = (data, changesColorRam) => {
const cel = {
data: data,
changesColorRam: changesColorRam,
type: decodeCelType(data.getUint8(0)),
// wild: (data.getUint8(0) & 0x10) == 0 ? "color" : "pattern",
width: data.getUint8(0) & 0x0f,
height: data.getUint8(1),
xOffset: data.getInt8(2),
yOffset: data.getInt8(3),
xRel: data.getInt8(4),
yRel: data.getInt8(5)
}
if (celDecoder[cel.type]) {
celDecoder[cel.type](data, cel)
}
return cel
}
const decodeSide = (byte) => {
const side = byte & 0x03
if (side == 0x00) {
return "left"
} else if (side == 0x01) {
return "right"
} else if (side == 0x02) {
return "up"
} else {
return "down"
}
}
const encodeSide = (side) => {
if (side == "left") {
return 0x00
} else if (side == "right") {
return 0x01
} else if (side == "up") {
return 0x02
} else if (side == "down") {
return 0x03
} else {
throw new Error(`Unknown side "${side}"`)
}
}
const decodeWalkto = (byte) => {
return { fromSide: decodeSide(byte), offset: signedByte(byte & 0xfc) }
}
const encodeWalkto = ({ fromSide, offset }) => {
return encodeSide(fromSide) | (offset & 0xfc)
}
const decodeAnimations = (data, startEndTableOff, firstCelOff, stateCount) => {
const animations = []
// The prop structure also does not encode a count for how many frames there are, so we simply
// stop parsing once we find one that doesn't make sense.
// We also use the heuristic that this structure always precedes the first cel, as that seems to be
// consistently be the case with all the props in the Habitat source tree. We'll stop reading
// animation data if we cross that boundary. If we encounter a prop that has the animation data
// _after_ the cel data, which would be legal but doesn't happen in practice, then we ignore this
// heuristic rather than failing to parse any animation data.
// It's possible for there to be no frames, which is represented by an offset of 0 (no_animation)
if (startEndTableOff != 0) {
for (let frameOff = startEndTableOff; (startEndTableOff > firstCelOff) || (frameOff < firstCelOff); frameOff += 2) {
// each animation is two bytes: the starting state, and the ending state
// the first byte can have its high bit set to indicate that the animation should cycle
const cycle = (data.getUint8(frameOff) & 0x80) != 0
const startState = data.getUint8(frameOff) & 0x7f
const endState = data.getUint8(frameOff + 1)
if (startState >= stateCount || endState >= stateCount) {
break
}
animations.push({ cycle: cycle, startState: startState, endState: endState })
}
}
return animations
}
export const decodeProp = (data) => {
const prop = {
data: data,
howHeld: decodeHowHeld(data.getUint8(0)),
colorBitmask: data.getUint8(1),
containerXYOff: data.getUint8(3), // TODO: parse this when nonzero
walkto: { left: decodeWalkto(data.getUint8(4)), right: decodeWalkto(data.getUint8(5)), yoff: data.getInt8(6) },
celmasks: [],
cels: []
}
const stateCount = (data.getUint8(0) & 0x3f) + 1
const graphicStateOff = data.getUint8(2)
const celMasksOff = 7
const celOffsetsOff = celMasksOff + stateCount
// The prop structure does not directly encode a count for how many cels there are, but each
// "graphic state" is defined by a bitmask marking which cels are present, and we do know how
// many states there are. We can assume that all cels are referenced by at least one state,
// and use that to determine the cel count.
let allCelsMask = 0
for (let icelmask = 0; icelmask < stateCount; icelmask ++) {
const celmask = data.getUint8(celMasksOff + icelmask)
prop.celmasks.push(celmask)
allCelsMask |= celmask
}
if (allCelsMask != 0x80 && allCelsMask != 0xc0 && allCelsMask != 0xe0 && allCelsMask != 0xf0 &&
allCelsMask != 0xf8 && allCelsMask != 0xfc && allCelsMask != 0xfe && allCelsMask != 0xff) {
throw new Error("Inconsistent graphic state cel masks - implies unused cel data")
}
let firstCelOff = Number.POSITIVE_INFINITY
for (let celOffsetOff = celOffsetsOff; allCelsMask != 0; celOffsetOff += 2) {
const icel = prop.cels.length
const celbit = 0x80 >> icel
const celOff = data.getUint16(celOffsetOff, LE)
firstCelOff = Math.min(celOff, firstCelOff)
prop.cels.push(decodeCel(new DataView(data.buffer, celOff), (prop.colorBitmask & celbit) != 0))
allCelsMask = (allCelsMask << 1) & 0xff
}
prop.animations = decodeAnimations(data, graphicStateOff, firstCelOff, stateCount)
return prop
}
const decodeLimb = (data, limb) => {
let frameCount = data.getUint8(0) + 1
limb.frames = []
for (let iframe = 0; iframe < frameCount; iframe ++) {
limb.frames.push(data.getInt8(3 + iframe))
}
const celOffsetsOff = 3 + frameCount
const maxCelIndex = Math.max(...limb.frames)
limb.cels = []
let firstCelOff
for (let icel = 0; icel <= maxCelIndex; icel ++) {
const celOff = data.getUint16(celOffsetsOff + (icel * 2), LE)
if (icel == 0) {
firstCelOff = celOff
}
limb.cels.push(decodeCel(new DataView(data.buffer, data.byteOffset + celOff)))
}
limb.animations = decodeAnimations(data, data.getUint8(2), firstCelOff, limb.frames.length)
}
export const choreographyActions = [
"init", "stand", "walk", "hand_back", "sit_floor", "sit_chair", "bend_over",
"bend_back", "point", "throw", "get_shot", "jump", "punch", "wave",
"frown", "stand_back", "walk_front", "walk_back", "stand_front",
"unpocket", "gimme", "knife", "arm_get", "hand_out", "operate",
"arm_back", "shoot1", "shoot2", "nop", "sit_front"
]
export const decodeBody = (data) => {
const body = {
data: data,
headCelNumber: data.getUint8(19),
frozenWhenStands: data.getUint8(20),
frontFacingLimbOrder: [],
backFacingLimbOrder: [],
limbs: [],
choreography: [],
actions: {}
}
for (let ilimb = 0; ilimb < 6; ilimb ++) {
body.frontFacingLimbOrder.push(data.getUint8(27 + ilimb))
body.backFacingLimbOrder.push(data.getUint8(33 + ilimb))
const limb = {
pattern: data.getUint8(21 + ilimb),
affectedByHeight: data.getUint8(39 + ilimb)
}
const limbOff = data.getUint16(7 + (ilimb * 2), LE)
decodeLimb(new DataView(data.buffer, limbOff), limb)
body.limbs.push(limb)
}
const choreographyIndexOff = data.getUint16(0, LE)
const choreographyTableOff = data.getUint16(2, LE)
const indexToChoreography = new Map()
for (const [i, action] of choreographyActions.entries()) {
let tableIndex = data.getUint8(choreographyIndexOff + i)
let choreographyIndex = indexToChoreography.get(tableIndex)
if (choreographyIndex == undefined) {
choreographyIndex = body.choreography.length
indexToChoreography.set(tableIndex, choreographyIndex)
const choreography = []
body.choreography.push(choreography)
for (;; tableIndex ++) {
const state = data.getUint8(choreographyTableOff + tableIndex)
let limb = (state & 0x70) >> 4
let animation = state & 0x0f
if (limb == 6) {
limb = 5
animation += 0x10
}
choreography.push({ limb, animation })
if ((state & 0x80) != 0) {
break
}
}
}
body.actions[action] = choreographyIndex
}
return body
}

View file

@ -15,7 +15,6 @@
line-height:1.2
}
</style>
<script src="index.js?v=4"></script>
</head>
<body>
<h1 id="filename"></h1>
@ -33,7 +32,10 @@
</div>
<div id="errors"></div>
<a href="index.html">Back</a>
<script>
<script type="module">
import { decodeProp } from "./codec.js"
import { docBuilder, decodeBinary, showAll, textNode, propAnimationShower, celmaskShower, celShower } from "./show.js"
const propFilter = (key, value) => {
if (key != "bitmap" && key != "data" && key != "canvas") {
return value
@ -47,15 +49,16 @@
const q = new URLSearchParams(window.location.search)
const filename = q.get("f")
document.getElementById("filename").innerText = filename
const doc = docBuilder({ errorContainer: document.getElementById("errors") })
try {
const prop = await decodeBinary(filename, PropImpl)
const prop = await decodeBinary(filename, decodeProp)
dumpProp(prop, document.getElementById("data"))
if (prop.error) {
showError(prop.error, filename)
doc.showError(prop.error, filename)
} else {
showAnimations(prop, document.getElementById("animations"), PropImpl)
showStates(prop, document.getElementById("states"))
showCels(prop, document.getElementById("cels"))
showAll(doc, document.getElementById("animations"), filename, prop.animations, propAnimationShower(prop))
showAll(doc, document.getElementById("states"), filename, prop.celmasks, celmaskShower(prop))
showAll(doc, document.getElementById("cels"), filename, prop.cels, celShower())
}
} catch (e) {
showError(e, filename)

View file

@ -15,18 +15,19 @@
line-height:1.2
}
</style>
<script src="index.js?v=4"></script>
<script>
function showErrors() {
<script type="module">
import { displayBodies, displayProps } from "./index.js"
window.showErrors = () => {
document.getElementById('errors').style.display = 'block'
}
const displayEverything = async () => {
await displayList("bodies.json", "bodies", BodyImpl)
await displayList("heads.json", "heads", PropImpl)
await displayList("props.json", "props", PropImpl)
await displayList("misc.json", "misc", PropImpl)
await displayList("beta.json", "beta", PropImpl)
const displayEverything = () => {
displayBodies("bodies.json", "bodies")
displayProps("heads.json", "heads")
displayProps("props.json", "props")
displayProps("misc.json", "misc")
displayProps("beta.json", "beta")
}
displayEverything()

903
index.js
View file

@ -1,893 +1,34 @@
const LE = true // little-endian
import { decodeProp, decodeBody } from "./codec.js"
import { docBuilder, showAll, decodeBinary, textNode, propAnimationShower, celmaskShower, actionShower } from "./show.js"
const readBinary = async (url) => {
const response = await fetch(url)
if (!response.ok) {
console.log(response)
throw Error(`Failed to download ${url}`)
}
return new DataView(await response.arrayBuffer())
}
const makeCanvas = (w, h) => {
const canvas = document.createElement("canvas")
canvas.width = w
canvas.height = h
return canvas
}
// C64 RGB values taken from https://www.c64-wiki.com/wiki/Color
const c64Colors = [
0x000000, 0xffffff, 0x880000, 0xaaffee, 0xcc44cc, 0x00cc55,
0x0000aa, 0xeeee77, 0xdd8855, 0x664400, 0xff7777, 0x333333,
0x777777, 0xaaff66, 0x0088ff, 0xbbbbbb
]
// from paint.m:447
const celPatterns = [
[0x00, 0x00, 0x00, 0x00],
[0xaa, 0xaa, 0xaa, 0xaa],
[0xff, 0xff, 0xff, 0xff],
[0xe2, 0xe2, 0xe2, 0xe2],
[0x8b, 0xbe, 0x0f, 0xcc],
[0xee, 0x00, 0xee, 0x00],
[0xf0, 0xf0, 0x0f, 0x0f],
[0x22, 0x88, 0x22, 0x88],
[0x32, 0x88, 0x23, 0x88],
[0x00, 0x28, 0x3b, 0x0c],
[0x33, 0xcc, 0x33, 0xcc],
[0x08, 0x80, 0x0c, 0x80],
[0x3f, 0x3f, 0xf3, 0xf3],
[0xaa, 0x3f, 0xaa, 0xf3],
[0xaa, 0x00, 0xaa, 0x00],
[0x55, 0x55, 0x55, 0x55]
]
const defaultColors = {
wildcard: 6,
skin: 10,
pattern: 15
}
const canvasFromBitmap = (bitmap, colors = {}) => {
if (bitmap.length == 0 || bitmap[0].length == 0) {
return null
}
const { wildcard, pattern, skin } = { ...defaultColors, ...colors }
const patternColors = [6, wildcard, 0, skin]
const h = bitmap.length
const w = bitmap[0].length * 2
const canvas = makeCanvas(w, h)
const ctx = canvas.getContext("2d")
const img = ctx.createImageData(w, h)
const putpixel = (x, y, r, g, b, a) => {
const i = (x * 8) + (y * w * 4)
img.data[i] = r
img.data[i + 1] = g
img.data[i + 2] = b
img.data[i + 3] = a
img.data[i + 4] = r
img.data[i + 5] = g
img.data[i + 6] = b
img.data[i + 7] = a
}
for (let y = 0; y < bitmap.length; y ++) {
const line = bitmap[y]
const patbyte = celPatterns[pattern][y % 4]
for (let x = 0; x < line.length; x ++) {
const pixel = line[x]
let color = null
if (pixel == 0) { // transparent
putpixel(x, y, 0, 0, 0, 0)
} else if (pixel == 1) { // wild
const shift = (x % 4) * 2
color = patternColors[(patbyte & (0xc0 >> shift)) >> (6 - shift)]
} else {
color = patternColors[pixel]
}
if (color != null) {
const rgb = c64Colors[color]
putpixel(x, y, (rgb & 0xff0000) >> 16, (rgb & 0xff00) >> 8, rgb & 0xff, 0xff)
}
}
}
ctx.putImageData(img, 0, 0)
return canvas
}
const signedByte = (byte) => {
if ((byte & 0x80) != 0) {
const complement = (byte ^ 0xff) + 1
return -complement
const showProp = (prop) => {
if (prop.filename == 'heads/fhead.bin') {
return textNode("CW: Pixel genitals")
} else if (prop.animations.length > 0) {
return prop.animations.map(propAnimationShower(prop))
} else {
return byte
return prop.celmasks.map(celmaskShower(prop))
}
}
// JS bitmap format: array of scanlines, each scanline being an array of numbers from 0-3
const emptyBitmap = (w, h, color = 0) => {
const bitmap = []
for (let y = 0; y < h; y ++) {
const scanline = []
for (let x = 0; x < w; x ++) {
scanline.push(color)
scanline.push(color)
scanline.push(color)
scanline.push(color)
}
bitmap.push(scanline)
}
return bitmap
}
const showBody = (body) => actionShower(body)("walk")
const drawByte = (bitmap, x, y, byte) => {
bitmap[y][x] = (byte & 0xc0) >> 6
bitmap[y][x + 1] = (byte & 0x30) >> 4
bitmap[y][x + 2] = (byte & 0x0c) >> 2
bitmap[y][x + 3] = (byte & 0x03)
}
// Prop decoding functions
const decodeHowHeld = (byte) => {
const heldVal = byte & 0xc0
if (heldVal == 0) {
return "swing"
} else if (heldVal == 0x40) {
return "out"
} else if (heldVal == 0x80) {
return "both"
} else {
return "at_side"
}
}
const encodeHowHeld = (howHeld) => {
if (howHeld == "swing") {
return 0x00
} else if (howHeld == "out") {
return 0x40
} else if (howHeld == "both") {
return 0x80
} else if (howHeld == "at_side") {
return 0xc0
} else {
throw new Error(`Unknown hold "${howHeld}"`)
}
}
const decodeCelType = (byte) => {
const typeVal = byte & 0xc0
if (typeVal == 0x00) {
if ((byte & 0x20) == 0) {
return "bitmap"
} else {
return "text"
}
} else if (typeVal == 0x40) {
return "trap"
} else if (typeVal == 0x80) {
return "box"
} else {
return "circle"
}
}
const encodeCelType = (type) => {
if (type == "bitmap") {
return 0x00
} else if (type == "text") {
return 0x20
} else if (type == "trap") {
return 0x40
} else if (type == "box") {
return 0x80
} else if (type == "circle") {
return 0xc0
} else {
throw new Error(`Unknown cel type "${type}"`)
}
}
const celDecoder = {}
const celEncoder = {}
celDecoder.bitmap = (data, cel) => {
// bitmap cells are RLE-encoded vertical strips of bytes. Decoding starts from the bottom-left
// and proceeds upwards until the top of the bitmap is hit; then then next vertical strip is decoded.
// Each byte describes four 2-bit pixels.
const bitmap = emptyBitmap(cel.width, cel.height)
let ibmp = 0
const end = cel.width * cel.height
const putByte = (byte) => {
const x = Math.floor(ibmp / cel.height) * 4
const y = (cel.height - (ibmp % cel.height)) - 1
drawByte(bitmap, x, y, byte)
ibmp ++
}
let i = 6
while (ibmp < end) {
const byte = data.getUint8(i)
i ++
if (byte == 0) {
// A zero byte denotes the start of a run of identical bytes. The second
// byte denotes the number of repetitions.
const count = data.getUint8(i)
i ++
if ((count & 0x80) == 0) {
// if the high bit of the count is not set, we read a third byte to
// determine the byte to repeat.
const val = data.getUint8(i)
i ++
for (let repeat = 0; repeat < count; repeat ++) {
putByte(val)
}
} else {
// if the high bit of the count is set, the lower 7 bits are used as
// the count, and a fully transparent byte is repeated.
for (let repeat = 0; repeat < (count & 0x7f); repeat ++) {
putByte(0)
}
}
} else {
// non-zero bytes are raw bitmap data
putByte(byte)
}
}
cel.bitmap = bitmap
}
celDecoder.box = (data, cel) => {
const bitmap = emptyBitmap(cel.width, cel.height)
cel.borderLR = (data.getUint8(0) & 0x20) != 0
cel.borderTB = (data.getUint8(0) & 0x10) != 0
cel.pattern = data.getUint8(6)
for (let y = 0; y < cel.height; y ++) {
for (let x = 0; x < cel.width; x ++) {
if (cel.borderTB && (y == 0 || y == (cel.height - 1))) {
drawByte(bitmap, x * 4, y, 0xaa)
} else {
drawByte(bitmap, x * 4, y, cel.pattern)
}
}
if (cel.borderLR) {
const line = bitmap[y]
line[0] = 2
line[line.length - 1] = 2
}
}
cel.bitmap = bitmap
}
const horizontalLine = (bitmap, xa, xb, y, patternByte) => {
const xStart = xa - (xa % 4)
const xEnd = (xb + (3 - (xb % 4))) - 3
for (let x = xStart + 4; x < xEnd; x += 4) {
drawByte(bitmap, x, y, patternByte)
}
const startBit = ((xa - xStart) * 2)
const startByte = (0xff >> startBit) & patternByte
drawByte(bitmap, xStart, y, startByte)
const endBit = (((xEnd + 3) - xb) * 2)
const endByte = (0xff << endBit) & patternByte
drawByte(bitmap, xEnd, y, endByte)
}
celDecoder.trap = (data, cel) => {
let border = false
// trap.m:21 - high-bit set means "draw a border"
// It looks like this was used as a flag and the real height
// was ORed with 0x80 - see house2.m, sign2.m
// There are also trapezoids that use 0x80 as their height -
// bwall6.m, bwall7.m, bwall9.m, magic_wall.m
// This appears to be special-cased to mean "no border" at trap.m:26
// mix.m:253 appears to have the logic to calculate y2, extracting
// the height by ANDing with 0x7f (when not 0x80)
if ((cel.height & 0x80) != 0 && cel.height != 0x80) {
border = true
cel.height = cel.height & 0x7f
}
if ((data.getUint8(0) & 0x10) == 0) {
// shape_pattern is a repeating 4-pixel colour, same as box
cel.pattern = data.getUint8(6)
} else {
// shape_pattern is 0xff, and the pattern is a bitmap that follows
// the trapezoid definition
// dline.m:103 - first two bytes are bitmasks used for efficiently calculating
// offsets into the texture. This means that the dimensions will be a power of
// two, and we can get the width and height simply by adding one to the mask.
const texW = data.getUint8(11) + 1
const texH = data.getUint8(12) + 1
cel.texture = emptyBitmap(texW, texH)
let i = 13
// dline.m:111 - the y position into the texture is calculated by
// ANDing y1 with the height mask; thus, unlike prop bitmaps, we decode
// from the top down
for (let y = 0; y < texH; y ++) {
for (let x = 0; x < texW; x ++) {
drawByte(cel.texture, x * 4, y, data.getUint8(i))
i ++
}
}
}
cel.x1a = data.getUint8(7)
cel.x1b = data.getUint8(8)
cel.x2a = data.getUint8(9)
cel.x2b = data.getUint8(10)
// trapezoid-drawing algorithm:
// draw_line: draws a line from x1a,y1 to x1b, y1
// handles border drawing (last/first line, edges)
// decreases vcount, then jumps to cycle1 if there
// are more lines
// cycle1: run bresenham, determine if x1a (left edge) needs to be incremented
// or decremented (self-modifying code! the instruction in inc_dec1 is
// written at trap.m:52)
// has logic to jump back to cycle1 if we have a sharp enough angle that
// we need to move more than one pixel horizontally
// cycle2: same thing, but for x2a (right edge)
// at the end, increments y1 and jumps back to the top of draw_line
cel.width = Math.floor((Math.max(cel.x1a, cel.x1b, cel.x2a, cel.x2b) + 3) / 4)
// trap.m:32 - delta_y and vcount are calculated by subtracting y2 - y1.
// mix.m:253: y2 is calculated as cel_y + cel_height
// mix.m:261: y1 is calculated as cel_y + 1
// So for a one-pixel tall trapezoid, deltay is 0, because y1 == y2.
// vcount is decremented until it reaches -1, compensating for the off-by-one.
const deltay = cel.height - 1
cel.bitmap = emptyBitmap(cel.width, cel.height)
const dxa = Math.abs(cel.x1a - cel.x2a)
const dxb = Math.abs(cel.x1b - cel.x2b)
const countMaxA = Math.max(dxa, deltay)
const countMaxB = Math.max(dxb, deltay)
const inca = cel.x1a < cel.x2a ? 1 : -1
const incb = cel.x1b < cel.x2b ? 1 : -1
let x1aLo = Math.floor(countMaxA / 2)
let y1aLo = x1aLo
let x1bLo = Math.floor(countMaxB / 2)
let y1bLo = x1bLo
let xa = cel.x1a
let xb = cel.x1b
for (let y = 0; y < cel.height; y ++) {
const line = cel.bitmap[y]
if (border && (y == 0 || y == (cel.height - 1))) {
// top and bottom border line
horizontalLine(cel.bitmap, xa, xb, y, 0xaa, true)
} else {
if (cel.texture) {
const texLine = cel.texture[y % cel.texture.length]
for (let x = xa; x <= xb; x ++) {
line[x] = texLine[x % texLine.length]
}
} else {
horizontalLine(cel.bitmap, xa, xb, y, cel.pattern, border)
}
}
if (border) {
line[xa] = 2
line[xb] = 2
}
// cycle1: move xa
do {
x1aLo += dxa
if (x1aLo >= countMaxA) {
x1aLo -= countMaxA
xa += inca
}
y1aLo += deltay
} while (y1aLo < countMaxA)
y1aLo -= countMaxA
// cycle2: move xb
do {
x1bLo += dxb
if (x1bLo >= countMaxB) {
x1bLo -= countMaxB
xb += incb
}
y1bLo += deltay
} while (y1bLo < countMaxA)
y1bLo -= countMaxA
}
}
const decodeCel = (data, changesColorRam) => {
const cel = {
data: data,
changesColorRam: changesColorRam,
type: decodeCelType(data.getUint8(0)),
// wild: (data.getUint8(0) & 0x10) == 0 ? "color" : "pattern",
width: data.getUint8(0) & 0x0f,
height: data.getUint8(1),
xOffset: data.getInt8(2),
yOffset: data.getInt8(3),
xRel: data.getInt8(4),
yRel: data.getInt8(5)
}
if (celDecoder[cel.type]) {
celDecoder[cel.type](data, cel)
}
return cel
}
const decodeSide = (byte) => {
const side = byte & 0x03
if (side == 0x00) {
return "left"
} else if (side == 0x01) {
return "right"
} else if (side == 0x02) {
return "up"
} else {
return "down"
}
}
const encodeSide = (side) => {
if (side == "left") {
return 0x00
} else if (side == "right") {
return 0x01
} else if (side == "up") {
return 0x02
} else if (side == "down") {
return 0x03
} else {
throw new Error(`Unknown side "${side}"`)
}
}
const decodeWalkto = (byte) => {
return { fromSide: decodeSide(byte), offset: signedByte(byte & 0xfc) }
}
const encodeWalkto = ({ fromSide, offset }) => {
return encodeSide(fromSide) | (offset & 0xfc)
}
const decodeAnimations = (data, startEndTableOff, firstCelOff, stateCount) => {
const animations = []
// The prop structure also does not encode a count for how many frames there are, so we simply
// stop parsing once we find one that doesn't make sense.
// We also use the heuristic that this structure always precedes the first cel, as that seems to be
// consistently be the case with all the props in the Habitat source tree. We'll stop reading
// animation data if we cross that boundary. If we encounter a prop that has the animation data
// _after_ the cel data, which would be legal but doesn't happen in practice, then we ignore this
// heuristic rather than failing to parse any animation data.
// It's possible for there to be no frames, which is represented by an offset of 0 (no_animation)
if (startEndTableOff != 0) {
for (let frameOff = startEndTableOff; (startEndTableOff > firstCelOff) || (frameOff < firstCelOff); frameOff += 2) {
// each animation is two bytes: the starting state, and the ending state
// the first byte can have its high bit set to indicate that the animation should cycle
const cycle = (data.getUint8(frameOff) & 0x80) != 0
const startState = data.getUint8(frameOff) & 0x7f
const endState = data.getUint8(frameOff + 1)
if (startState >= stateCount || endState >= stateCount) {
break
}
animations.push({ cycle: cycle, startState: startState, endState: endState })
}
}
return animations
}
const decodeProp = (data) => {
const prop = {
data: data,
howHeld: decodeHowHeld(data.getUint8(0)),
colorBitmask: data.getUint8(1),
containerXYOff: data.getUint8(3), // TODO: parse this when nonzero
walkto: { left: decodeWalkto(data.getUint8(4)), right: decodeWalkto(data.getUint8(5)), yoff: data.getInt8(6) },
celmasks: [],
cels: []
}
const stateCount = (data.getUint8(0) & 0x3f) + 1
const graphicStateOff = data.getUint8(2)
const celMasksOff = 7
const celOffsetsOff = celMasksOff + stateCount
// The prop structure does not directly encode a count for how many cels there are, but each
// "graphic state" is defined by a bitmask marking which cels are present, and we do know how
// many states there are. We can assume that all cels are referenced by at least one state,
// and use that to determine the cel count.
let allCelsMask = 0
for (let icelmask = 0; icelmask < stateCount; icelmask ++) {
const celmask = data.getUint8(celMasksOff + icelmask)
prop.celmasks.push(celmask)
allCelsMask |= celmask
}
if (allCelsMask != 0x80 && allCelsMask != 0xc0 && allCelsMask != 0xe0 && allCelsMask != 0xf0 &&
allCelsMask != 0xf8 && allCelsMask != 0xfc && allCelsMask != 0xfe && allCelsMask != 0xff) {
throw new Error("Inconsistent graphic state cel masks - implies unused cel data")
}
let firstCelOff = Number.POSITIVE_INFINITY
for (let celOffsetOff = celOffsetsOff; allCelsMask != 0; celOffsetOff += 2) {
const icel = prop.cels.length
const celbit = 0x80 >> icel
const celOff = data.getUint16(celOffsetOff, LE)
firstCelOff = Math.min(celOff, firstCelOff)
prop.cels.push(decodeCel(new DataView(data.buffer, celOff), (prop.colorBitmask & celbit) != 0))
allCelsMask = (allCelsMask << 1) & 0xff
}
prop.animations = decodeAnimations(data, graphicStateOff, firstCelOff, stateCount)
return prop
}
const decodeLimb = (data, limb) => {
let frameCount = data.getUint8(0) + 1
limb.frames = []
for (let iframe = 0; iframe < frameCount; iframe ++) {
limb.frames.push(data.getInt8(3 + iframe))
}
const celOffsetsOff = 3 + frameCount
const maxCelIndex = Math.max(...limb.frames)
limb.cels = []
let firstCelOff
for (let icel = 0; icel <= maxCelIndex; icel ++) {
const celOff = data.getUint16(celOffsetsOff + (icel * 2), LE)
if (icel == 0) {
firstCelOff = celOff
}
limb.cels.push(decodeCel(new DataView(data.buffer, data.byteOffset + celOff)))
}
limb.animations = decodeAnimations(data, data.getUint8(2), firstCelOff, limb.frames.length)
}
const choreographyActions = [
"init", "stand", "walk", "hand_back", "sit_floor", "sit_chair", "bend_over",
"bend_back", "point", "throw", "get_shot", "jump", "punch", "wave",
"frown", "stand_back", "walk_front", "walk_back", "stand_front",
"unpocket", "gimme", "knife", "arm_get", "hand_out", "operate",
"arm_back", "shoot1", "shoot2", "nop", "sit_front"
]
const decodeBody = (data) => {
const body = {
data: data,
headCelNumber: data.getUint8(19),
frozenWhenStands: data.getUint8(20),
frontFacingLimbOrder: [],
backFacingLimbOrder: [],
limbs: [],
choreography: [],
actions: {}
}
for (let ilimb = 0; ilimb < 6; ilimb ++) {
body.frontFacingLimbOrder.push(data.getUint8(27 + ilimb))
body.backFacingLimbOrder.push(data.getUint8(33 + ilimb))
const limb = {
pattern: data.getUint8(21 + ilimb),
affectedByHeight: data.getUint8(39 + ilimb)
}
const limbOff = data.getUint16(7 + (ilimb * 2), LE)
decodeLimb(new DataView(data.buffer, limbOff), limb)
body.limbs.push(limb)
}
const choreographyIndexOff = data.getUint16(0, LE)
const choreographyTableOff = data.getUint16(2, LE)
const indexToChoreography = new Map()
for (const [i, action] of choreographyActions.entries()) {
let tableIndex = data.getUint8(choreographyIndexOff + i)
let choreographyIndex = indexToChoreography.get(tableIndex)
if (choreographyIndex == undefined) {
choreographyIndex = body.choreography.length
indexToChoreography.set(tableIndex, choreographyIndex)
const choreography = []
body.choreography.push(choreography)
for (;; tableIndex ++) {
const state = data.getUint8(choreographyTableOff + tableIndex)
let limb = (state & 0x70) >> 4
let animation = state & 0x0f
if (limb == 6) {
limb = 5
animation += 0x10
}
choreography.push({ limb, animation })
if ((state & 0x80) != 0) {
break
}
}
}
body.actions[action] = choreographyIndex
}
return body
}
const celsFromMask = (prop, celMask) => {
const cels = []
for (let icel = 0; icel < 8; icel ++) {
const celbit = 0x80 >> icel
if ((celMask & celbit) != 0) {
cels.push(prop.cels[icel])
}
}
return cels
}
const compositeCels = (cels, celColors = null, paintOrder = null) => {
if (cels.length == 0) {
return null
}
let minX = Number.POSITIVE_INFINITY
let minY = Number.POSITIVE_INFINITY
let maxX = Number.NEGATIVE_INFINITY
let maxY = Number.NEGATIVE_INFINITY
let xRel = 0
let yRel = 0
let layers = []
for (const [icel, cel] of cels.entries()) {
if (cel) {
const x = cel.xOffset + xRel
const y = -(cel.yOffset + yRel)
minX = Math.min(minX, x)
minY = Math.min(minY, y)
maxX = Math.max(maxX, cel.width + x)
maxY = Math.max(maxY, cel.height + y)
if (cel.bitmap) {
const colors = Array.isArray(celColors) ? celColors[icel] : (celColors ?? {})
layers.push({ canvas: canvasFromBitmap(cel.bitmap, colors), x, y })
} else {
layers.push(null)
}
xRel += cel.xRel
yRel += cel.yRel
} else {
layers.push(null)
}
}
if (paintOrder) {
const reordered = []
for (const ilayer of paintOrder) {
reordered.push(layers[ilayer])
}
layers = reordered
}
const w = (maxX - minX) * 8
const h = maxY - minY
const canvas = makeCanvas(w, h)
const ctx = canvas.getContext("2d")
for (const layer of layers) {
if (layer && layer.canvas) {
ctx.drawImage(layer.canvas, (layer.x - minX) * 8, layer.y - minY)
}
}
return { canvas: canvas, xOffset: minX * 8, yOffset: minY, w: w, h: h }
}
const imageFromCanvas = (canvas) => {
const img = document.createElement("img")
img.src = canvas.toDataURL()
img.width = canvas.width * 3
img.height = canvas.height * 3
img.style.imageRendering = "pixelated"
return img
}
const imageFromBitmap = (bitmap, colors = {}) => imageFromCanvas(canvasFromBitmap(bitmap, colors))
const textNode = (text, type = "span") => {
const node = document.createElement(type)
node.innerText = text
return node
}
const wrapLink = (element, href) => {
const link = document.createElement("a")
link.href = href
link.appendChild(element)
return link
}
const PropImpl = {
decode: decodeProp,
detailHref: (filename) => `detail.html?f=${filename}`,
celsForAnimationState: (prop, istate) => celsFromMask(prop, prop.celmasks[istate]),
}
const LimbImpl = {
celsForAnimationState: (limb, istate) => {
const iframe = limb.frames[istate]
if (iframe >= 0) {
return [limb.cels[iframe]]
} else {
return []
}
}
}
const actionOrientations = {
"stand_back": "back",
"walk_front": "front",
"walk_back": "back",
"stand_front": "front",
"sit_front": "front"
}
const BodyImpl = {
decode: decodeBody,
detailHref: (filename) => `body.html?f=${filename}`,
generateFrames: (action, body, frames) => {
const chore = body.choreography[body.actions[action]]
const animations = []
const orientation = actionOrientations[action] ?? "side"
const limbOrder = orientation == "front" ? body.frontFacingLimbOrder :
orientation == "back" ? body.backFacingLimbOrder :
null // side animations are always displayed in standard limb order
for (const limb of body.limbs) {
if (limb.animations.length > 0) {
animations.push({ ...limb.animations[0] })
} else {
animations.push({ startState: 0, endState: 0 })
}
}
for (const override of chore) {
const ilimb = override.limb
const newAnim = body.limbs[ilimb].animations[override.animation]
animations[ilimb].startState = newAnim.startState
animations[ilimb].endState = newAnim.endState
}
while (true) {
const cels = []
const celColors = []
let restartedCount = 0
for (const [ilimb, limb] of body.limbs.entries()) {
const animation = animations[ilimb]
if (animation.current == undefined) {
animation.current = animation.startState
} else {
animation.current ++
if (animation.current > animation.endState) {
animation.current = animation.startState
restartedCount ++
}
}
const istate = limb.frames[animation.current]
if (istate >= 0) {
cels.push(limb.cels[istate])
} else {
cels.push(null)
}
celColors.push({ pattern: limb.pattern })
}
if (restartedCount == animations.length) {
break
}
frames.push(compositeCels(cels, celColors, limbOrder))
}
}
}
const linkDetail = (element, filename, impl) => {
return impl && impl.detailHref ? wrapLink(element, impl.detailHref(filename)) : element
}
const createAnimation = (animation, value, impl) => {
const frames = []
if (impl.generateFrames) {
impl.generateFrames(animation, value, frames)
} else {
for (let istate = animation.startState; istate <= animation.endState; istate ++) {
const frame = compositeCels(impl.celsForAnimationState(value, istate))
if (frame != null) {
frames.push(frame)
}
}
}
if (frames.length == 0) {
return textNode("")
} else if (frames.length == 1) {
return imageFromCanvas(frames[0].canvas)
}
let minX = Number.POSITIVE_INFINITY
let minY = Number.POSITIVE_INFINITY
let maxX = Number.NEGATIVE_INFINITY
let maxY = Number.NEGATIVE_INFINITY
for (const frame of frames) {
minX = Math.min(minX, frame.xOffset)
minY = Math.min(minY, frame.yOffset)
maxX = Math.max(maxX, frame.xOffset + frame.w)
maxY = Math.max(maxY, frame.yOffset + frame.h)
}
const w = maxX - minX
const h = maxY - minY
const canvas = makeCanvas(w, h)
canvas.style.imageRendering = "pixelated"
canvas.style.width = `${w * 3}px`
canvas.style.height = `${h * 3}px`
let iframe = 0
const ctx = canvas.getContext("2d")
const nextFrame = () => {
const frame = frames[iframe]
ctx.clearRect(0, 0, w, h)
ctx.drawImage(frame.canvas, frame.xOffset - minX, frame.yOffset - minY)
iframe = (iframe + 1) % frames.length
}
nextFrame()
setInterval(nextFrame, 250)
return canvas
}
const showAnimations = (value, container, impl) => {
for (const animation of value.animations) {
container.appendChild(linkDetail(createAnimation(animation, value, impl), value.filename, impl))
}
}
const showStates = (prop, container) => {
for (const celmask of prop.celmasks) {
const state = compositeCels(celsFromMask(prop, celmask))
if (state) {
const img = imageFromCanvas(state.canvas)
img.alt = prop.filename
container.appendChild(linkDetail(img, prop.filename))
}
}
}
const showCels = (prop, container) => {
for (const cel of prop.cels) {
if (cel.canvas) {
container.appendChild(imageFromCanvas(cel.canvas))
}
}
}
const decodeBinary = async (filename, impl) => {
try {
const prop = impl.decode(await readBinary(filename))
prop.filename = filename
return prop
} catch (e) {
return { filename: filename, error: e }
}
}
const showError = (e, filename, impl) => {
const container = document.getElementById("errors")
const errNode = document.createElement("p")
console.error(e)
errNode.appendChild(linkDetail(textNode(filename, "b"), filename, impl))
errNode.appendChild(textNode(e.toString(), "p"))
if (e.stack) {
errNode.appendChild(textNode(e.stack.toString(), "pre"))
}
container.appendChild(errNode)
}
const displayFile = async (filename, container, impl) => {
const value = await decodeBinary(filename, impl)
const displayFile = async (doc, container, filename, decode, show) => {
const value = await decodeBinary(filename, decode)
if (value.error) {
container.parentNode.removeChild(container)
showError(value.error, value.filename, impl)
doc.showError(value.error, filename)
} else {
try {
impl.display(value, container)
showAll(doc, container, filename, [value], show)
} catch (e) {
container.parentNode.removeChild(container)
showError(e, value.filename, impl)
doc.showError(e, filename)
}
}
}
PropImpl.display = (prop, container) => {
if (prop.filename == 'heads/fhead.bin') {
container.appendChild(textNode("CW: Pixel genitals"))
} else if (prop.animations.length > 0) {
showAnimations(prop, container, PropImpl)
} else {
showStates(prop, container)
}
}
BodyImpl.display = (body, container) => {
container.appendChild(linkDetail(createAnimation("walk", body, BodyImpl), body.filename, BodyImpl))
}
const displayList = async (indexFile, containerId, impl) => {
const displayList = async (doc, indexFile, containerId, decode, show) => {
const response = await fetch(indexFile, { cache: "no-cache" })
const filenames = await response.json()
const container = document.getElementById(containerId)
@ -897,8 +38,16 @@ const displayList = async (indexFile, containerId, impl) => {
fileContainer.style.margin = "2px"
fileContainer.style.padding = "2px"
fileContainer.style.display = "inline-block"
fileContainer.appendChild(linkDetail(textNode(filename, "div"), filename, impl))
fileContainer.appendChild(doc.linkDetail(textNode(filename, "div"), filename))
container.appendChild(fileContainer)
displayFile(filename, fileContainer, impl)
displayFile(doc, fileContainer, filename, decode, show)
}
}
}
export const displayBodies = (indexFile, containerId) =>
displayList(docBuilder({ detailHref: "body.html", errorContainer: document.getElementById("errors") }),
indexFile, containerId, decodeBody, showBody)
export const displayProps = (indexFile, containerId) =>
displayList(docBuilder({ detailHref: "detail.html", errorContainer: document.getElementById("errors") }),
indexFile, containerId, decodeProp, showProp)

View file

@ -0,0 +1,7 @@
alias r run -i test.i -g test.gri
alias c cont
alias n next
alias s step
alias p print
alias l list
alias q quit

23
mamelink/griddle/DOC Normal file
View file

@ -0,0 +1,23 @@
/u0/chip/habitat/griddle:
This directory contains the Griddle and Fred utilities. They share
most of their source, since they are mostly the same program, so they are both
together here. Fred uses Fastlink, so some stuff for it is elsewhere.
These are the files here:
DOC - This file
regiontools.t - Documentation for Fred and Griddle
Makefile - Makefile to compile everything
*.c, *.y, *.h - Source files
fred - Executable for Fred
griddle - Executable for Griddle
reno.out - Binary for C64 half of Fred (gets uploaded through Fastlink)
util - A directory containing some minor utilities that work with
Fred. Fred keeps a stat file of what commands are used so
We can collect frequency info for improving the command
interface. These utilities deal with this stat file. The
utilities are all short and their source is their
documentation.

84
mamelink/griddle/Makefile Normal file
View file

@ -0,0 +1,84 @@
.SUFFIXES: .o .c .h .run .y .l
GOBJ = griddle.o gmain.o glexer.o build.o cv.o gexpr.o gexec.o debug.o indir.o
FOBJ = griddle.o fmain.o flexer.o build.o cv.o fexpr.o fexec.o debug.o fred.o fred2.o fscreen.o sun.o map.o
.c.o:
cc -c -g -DYYDEBUG $*.c
.y.c:
yacc -vd $*.y
mv y.tab.c $*.c
.l.c:
lex $*.l
mv lex.yy.c $*.c
.c.run:
cc -o $* $*.c
griddle: $(GOBJ)
cc -g $(GOBJ) -o griddle
fred: $(FOBJ)
cc -g $(FOBJ) -o fred -lcurses -ltermlib
all: griddle fred
dumpfstats: dumpfstats.c
cc -g dumpfstats.c -o dumpfstats
flushfstats: flushfstats.c
cc -g flushfstats.c -o flushfstats
griddle.o: griddle.c griddleDefs.h
#griddle.c: griddle.y
alloc.o: alloc.c
build.o: build.c griddleDefs.h
gmain.o: main.c griddleDefs.h
cc -c -g -DYYDEBUG main.c
mv main.o gmain.o
fmain.o: main.c griddleDefs.h
cc -c -g -DYYDEBUG -DFRED main.c
mv main.o fmain.o
gexec.o: exec.c griddleDefs.h
cc -c -g -DYYDEBUG exec.c
mv exec.o gexec.o
fexec.o: exec.c griddleDefs.h
cc -c -g -DYYDEBUG -DFRED exec.c
mv exec.o fexec.o
debug.o: debug.c griddleDefs.h
cv.o: cv.c griddleDefs.h
glexer.o: lexer.c griddleDefs.h y.tab.h
cc -c -g -DYYDEBUG lexer.c
mv lexer.o glexer.o
flexer.o: lexer.c griddleDefs.h y.tab.h
cc -c -g -DYYDEBUG -DFRED lexer.c
mv lexer.o flexer.o
gexpr.o: expr.c griddleDefs.h y.tab.h
cc -c -g -DYYDEBUG expr.c
mv expr.o gexpr.o
fexpr.o: expr.c griddleDefs.h y.tab.h
cc -c -g -DYYDEBUG -DFRED expr.c
mv expr.o fexpr.o
indir.o: indir.c griddleDefs.h
fred.o: fred.c griddleDefs.h prot.h
cc -c -g -DDATE=\""`date`\"" fred.c
fred2.o: fred2.c griddleDefs.h
fscreen.o: fscreen.c griddleDefs.h

50
mamelink/griddle/alloc.c Normal file
View file

@ -0,0 +1,50 @@
#include <stdio.h>
typedef struct {
int tag;
} alloc;
#define MAXTAG 40;
static int allocCount[MAXTAG] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int freeCount[MAXTAG] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int highTag = 0;
char *
mymalloc(size, tag)
int size;
int tag;
{
alloc *result;
result = (alloc *) malloc(size + sizeof(alloc));
if (tag > highTag)
highTag = tag;
allocCount[tag]++;
result->tag = tag;
++result;
return((char *) result);
}
void
myfree(ptr)
alloc *ptr;
{
--ptr;
freeCount[ptr->tag]--;
free(ptr);
}
void
dumpAllocOrphans()
{
int i;
for (i=0; i<=highTag; ++i)
printf("%d %d/%d\n", i, allocCount[i], freeCount[i]);
}

199
mamelink/griddle/build.c Normal file
View file

@ -0,0 +1,199 @@
#include "griddleDefs.h"
value *evaluate();
genericListHead *
buildGenericList(list, new)
genericListHead *list;
int *new;
{
genericListHead *result;
genericList *newList;
if (list == NULL) {
result = typeAlloc(genericListHead);
result->thing = new;
result->next = NULL;
result->last = (genericList *)result;
} else {
newList = typeAlloc(genericList);
newList->thing = new;
newList->next = NULL;
list->last->next = newList;
list->last = newList;
result = list;
}
return(result);
}
fieldList *
buildFieldList(list, new)
fieldList *list;
field *new;
{
return((fieldList *)buildGenericList(list, new));
}
valueList *
buildValueList(list, new)
valueList *list;
value *new;
{
return((valueList *)buildGenericList(list, new));
}
objectList *
buildObjectList(list, new)
objectList *list;
object *new;
{
return((objectList *)buildGenericList(list, new));
}
exprList *
buildExprList(list, new)
exprList *list;
expression *new;
{
return((exprList *)buildGenericList(list, new));
}
propertyList *
buildPropertyList(list, new)
propertyList *list;
property *new;
{
return((propertyList *)buildGenericList(list, new));
}
stringList *
buildStringList(list, new)
stringList *list;
char *new;
{
return((stringList *)buildGenericList(list, new));
}
expression *
buildExpr(type, arg1, arg2, arg3)
exprType type;
int arg1;
int arg2;
int arg3;
{
expression *result;
result = typeAlloc(expression);
result->type = type;
result->part1 = arg1;
result->part2 = arg2;
result->part3 = arg3;
return(result);
}
property *
buildProperty(fieldName, data)
symbol *fieldName;
exprList *data;
{
property *result;
result = typeAlloc(property);
result->fieldName = fieldName;
result->data = data;
return(result);
}
value *
buildValue(vtype, val)
valueType vtype;
int val;
{
value *result;
result = typeAlloc(value);
result->value = val;
result->dataType = vtype;
return(result);
}
value *
buildNumber(val)
int val;
{
return(buildValue(VAL_INTEGER, val));
}
value *
buildString(val)
char *val;
{
return(buildValue(VAL_STRING, (int)val));
}
value *
buildBitString(val)
byte *val;
{
return(buildValue(VAL_BITSTRING, (int)val));
}
field *
buildField(name, dimension, type, initList)
symbol *name;
expression *dimension;
fieldType type;
exprList *initList;
{
field *result;
value *val;
result = typeAlloc(field);
result->name = name;
result->type = type;
result->offset = 0;
result->invisible = FALSE;
val = evaluate(dimension);
if (isInteger(val) && val->value > 0)
result->dimension = val->value;
else {
error("illegal data type for field dimension\n");
result->dimension = 1;
}
freeValue(val);
result->initValues = initList;
return(result);
}
field *
invisifyField(aField)
field *aField;
{
aField->invisible = TRUE;
return(aField);
}
objectTail *
buildObjectTail(idExpr, propList)
expression *idExpr;
propertyList *propList;
{
objectTail *result;
result = typeAlloc(objectTail);
result->idExpr = idExpr;
result->properties = propList;
return(result);
}
objectStub *
buildObjectStub(obj)
object *obj;
{
objectStub *result;
result = typeAlloc(objectStub);
result->class = obj->class;
result->id = getLong(obj->stateVector, 0);
return(result);
}

346
mamelink/griddle/cv.c Normal file
View file

@ -0,0 +1,346 @@
#include "griddleDefs.h"
#define CLASS_MAX 256
#define SIZE_OFFSET 12
int classSize[CLASS_MAX];
byte cv[512];
int cvLength;
static FILE *cvFyle;
static object *inNoid[MAXNOID];
void
cvByte(n)
byte n;
{
cv[cvLength++] = n;
}
byte
readByte(fyle)
FILE *fyle;
{
return((byte)getc(fyle));
}
word
readWord(fyle)
FILE *fyle;
{
byte lo;
byte hi;
lo = readByte(fyle);
hi = readByte(fyle);
return((hi << 8) + lo);
}
void
readClassFile()
{
int i;
FILE *classFyle;
if ((classFyle = fopen(classFileName, "r")) == NULL)
systemError("can't open class file '%s'\n", classFileName);
for (i=0; i<CLASS_MAX; ++i)
classSize[i] = readWord(classFyle);
for (i=0; i<CLASS_MAX; ++i)
if (classSize[i] == 0xFFFF)
classSize[i] = 0;
else {
fseek(classFyle, classSize[i]+SIZE_OFFSET, 0);
classSize[i] = (readByte(classFyle) & 0x7F) - 6;
}
fclose(classFyle);
}
void
cvNoidClass()
{
int i;
for (i=1; i<objectCount; ++i) {
if (noidArray[i] != NULL) {
cvByte(i);
cvByte(noidArray[i]->class);
}
}
}
void
cvProperties(class, buf)
int class;
byte *buf;
{
int container;
int i;
if (class == 0) {
cvByte(0); /* style */
cvByte(getWord(buf, LIGHTLEVEL_OFFSET_REG));
cvByte(getWord(buf, DEPTH_OFFSET_REG));
cvByte(getWord(buf, CLASSGROUP_OFFSET_REG));
cvByte(0); /* who am I? */
cvByte(0); /* bank balance */
cvByte(0);
cvByte(0);
cvByte(0);
} else if (class == 1) {
container = getLong(buf, CONTAINER_OFFSET_AVA);
if (container != 0 && (container < -1000-MAXNOID ||
-1000 <= container)) {
error("specified avatar container out of range\n");
return;
}
cvByte(getByte(buf, STYLE_OFFSET_AVA));
cvByte(getWord(buf, X_OFFSET_AVA));
cvByte(getWord(buf, Y_OFFSET_AVA));
cvByte(getWord(buf, ORIENT_OFFSET_AVA));
cvByte(getWord(buf, GRSTATE_OFFSET_AVA));
if (container == 0)
cvByte(0);
else
cvByte(-container-1001);
for (i=0; i<AVATAR_PROPERTY_COUNT; ++i)
cvByte(getWord(buf, PROP_BASE_AVA + i*2));
} else {
container = getLong(buf, CONTAINER_OFFSET_OBJ);
if (container != 0 && (container < -1000-MAXNOID ||
-1000 <= container)) {
error("specified object container out of range\n");
return;
}
cvByte(getWord(buf, STYLE_OFFSET_OBJ));
cvByte(getWord(buf, X_OFFSET_OBJ));
cvByte(getWord(buf, Y_OFFSET_OBJ));
cvByte(getWord(buf, ORIENT_OFFSET_OBJ));
cvByte(getWord(buf, GRSTATE_OFFSET_OBJ));
if (container == 0)
cvByte(0);
else
cvByte(-container-1001);
for (i=0; i<classSize[class]; ++i)
cvByte(getWord(buf, objectBase + i*2));
}
}
void
cvProps()
{
int i;
for (i=1; i<objectCount; ++i)
if (noidArray[i] != NULL)
cvProperties(noidArray[i]->class, noidArray[i]->stateVector);
}
boolean
generateContentsVector()
{
int i;
cvLength = 0;
if (noidArray[0] == NULL || noidArray[0]->class != 0) {
error("first object is not a region\n");
return(FALSE);
} else for (i=1; i<objectCount; ++i) {
if (noidArray[i] != NULL && noidArray[i]->class == 0) {
error("more than one region given\n");
return(FALSE);
}
}
/* cvProperties(0, noidArray[0]->stateVector);*/
cvNoidClass();
cvByte(0);
cvProps();
cvByte(0);
return(TRUE);
}
void
outputContentsVector()
{
if (generateContentsVector())
fwrite(cv, 1, cvLength, cvFile);
}
void
fillFromCvFile(obj)
object *obj;
{
byte *buf;
int class;
int container;
int i;
buf = obj->stateVector;
class = obj->class;
if (class == 0) {
readByte(cvFyle); /* skip style */
fillWord(buf, LIGHTLEVEL_OFFSET_REG, readByte(cvFyle));
fillWord(buf, DEPTH_OFFSET_REG, readByte(cvFyle));
fillWord(buf, CLASSGROUP_OFFSET_REG, readByte(cvFyle));
readByte(cvFyle); /* skip who am i */
for (i=0; i<4; ++i)
readByte(cvFyle); /* skip bank balance */
} else if (class == 1) {
fillByte(buf, STYLE_OFFSET_AVA, readByte(cvFyle));
fillWord(buf, X_OFFSET_AVA, readByte(cvFyle));
fillWord(buf, Y_OFFSET_AVA, readByte(cvFyle));
fillWord(buf, ORIENT_OFFSET_AVA, readByte(cvFyle));
fillWord(buf, GRSTATE_OFFSET_AVA, readByte(cvFyle));
container = getLong(inNoid[readByte(cvFyle)]->stateVector, 0);
fillLong(buf, CONTAINER_OFFSET_AVA, container);
for (i=0; i<AVATAR_PROPERTY_COUNT; ++i)
fillWord(buf, PROP_BASE_AVA + i*2, readByte(cvFyle));
} else {
fillWord(buf, STYLE_OFFSET_OBJ, readByte(cvFyle));
fillWord(buf, X_OFFSET_OBJ, readByte(cvFyle));
fillWord(buf, Y_OFFSET_OBJ, readByte(cvFyle));
fillWord(buf, ORIENT_OFFSET_OBJ, readByte(cvFyle));
fillWord(buf, GRSTATE_OFFSET_OBJ, readByte(cvFyle));
container = getLong(inNoid[readByte(cvFyle)]->stateVector, 0);
fillLong(buf, CONTAINER_OFFSET_OBJ, container);
for (i=0; i<classSize[class]; ++i)
fillWord(buf, objectBase + i*2, readByte(cvFyle));
}
}
void
inputContentsVector(filename)
char *filename;
{
int i;
int noid;
int class;
int highNoid;
int noidBase;
byte noidMap[MAXNOID];
object *initObject();
if ((cvFyle = fopen(filename, "r")) == NULL) {
error("can't open contents vector input file '%s'\n",
filename);
return;
}
for (i=0; i<MAXNOID; ++i)
inNoid[i] = NULL;
noidMap[0] = 0;
inNoid[0] = initObject(0, -globalIdCounter++);
noidBase = 1;
do {
for (highNoid=noidBase; (noid = readByte(cvFyle)) != 0; ++highNoid) {
class = readByte(cvFyle);
noidMap[highNoid] = noid;
inNoid[noid] = initObject(class, -globalIdCounter++);
}
for (i=noidBase; i<highNoid; ++i)
fillFromCvFile(inNoid[noidMap[i]]);
for (i=0; i<highNoid; ++i) {
if (objectCount < MAXNOID)
noidArray[objectCount++] = inNoid[noidMap[i]];
if (griFile != NULL)
dumpObject(inNoid[noidMap[i]]);
if (rawFile != NULL)
outputRawObject(inNoid[noidMap[i]]);
}
noidBase = 0;
} while (readByte(cvFyle) != 0 && !feof(cvFyle));
fclose(cvFyle);
}
int
deCvNoidClass(offset)
int offset;
{
int i;
for (; cv[offset] != 0; offset += 2)
;
return(offset + 1);
}
int
deCvProperties(class, buf, offset)
int class;
byte *buf;
int offset;
{
int i;
if (class == 0) {
fillWord(buf, LIGHTLEVEL_OFFSET_REG, cv[offset + 1]);
fillWord(buf, DEPTH_OFFSET_REG, cv[offset + 2]);
fillWord(buf, CLASSGROUP_OFFSET_REG, cv[offset + 3]);
return(9);
} else if (class == 1) {
fillByte(buf, STYLE_OFFSET_AVA, cv[offset + 0]);
fillWord(buf, X_OFFSET_AVA, cv[offset + 1]);
fillWord(buf, Y_OFFSET_AVA, cv[offset + 2]);
fillWord(buf, ORIENT_OFFSET_AVA, cv[offset + 3]);
fillWord(buf, GRSTATE_OFFSET_AVA, cv[offset + 4]);
if (cv[offset + 5] == 0)
fillLong(buf, CONTAINER_OFFSET_AVA, 0);
else
fillLong(buf, CONTAINER_OFFSET_AVA, -1001-cv[offset+5]);
for (i=0; i<AVATAR_PROPERTY_COUNT; ++i)
fillWord(buf, PROP_BASE_AVA + i*2, cv[offset + 6+i]);
return(6 + AVATAR_PROPERTY_COUNT);
} else {
fillWord(buf, STYLE_OFFSET_OBJ, cv[offset + 0]);
fillWord(buf, X_OFFSET_OBJ, cv[offset + 1]);
fillWord(buf, Y_OFFSET_OBJ, cv[offset + 2]);
fillWord(buf, ORIENT_OFFSET_OBJ, cv[offset + 3]);
fillWord(buf, GRSTATE_OFFSET_OBJ, cv[offset + 4]);
fillLong(buf, CONTAINER_OFFSET_OBJ, -1001-cv[offset + 5]);
for (i=0; i<classSize[class]; ++i)
fillWord(buf, objectBase + i*2, cv[offset + 6 + i]);
return(6 + classSize[class]);
}
}
int
deCvProps(noidOffset, stateOffset)
int noidOffset;
int stateOffset;
{
int noid;
while ((noid = cv[noidOffset]) != 0) {
if (cv[noidOffset + 1] != noidArray[noid]->class) {
error("class mismatch: cv says %d, we say %d\n",
cv[noidOffset + 1], noidArray[noid]->class);
return(0);
}
stateOffset += deCvProperties(noidArray[noid]->class,
noidArray[noid]->stateVector, stateOffset);
noidOffset += 2;
noidAlive[noid] = TRUE;
}
if (cv[stateOffset] == 0)
return(0);
else
return(stateOffset + 1);
}
void
degenerateContentsVector()
{
int offset;
int noid;
noidAlive[0] = TRUE;
for (noid=1; noid<MAXNOID; ++noid)
noidAlive[noid] = FALSE;
offset = 0;
while ((offset = deCvProps(offset, deCvNoidClass(offset))) != 0)
;
for (noid=1; noid<MAXNOID; ++noid)
if (!noidAlive[noid] && noidArray[noid] != NULL) {
freeObject(noidArray[noid]);
noidArray[noid] = NULL;
}
}

170
mamelink/griddle/debug.c Normal file
View file

@ -0,0 +1,170 @@
#include "griddleDefs.h"
int
getByte(buf, offset)
byte *buf;
int offset;
{
return((char)buf[offset]);
}
int
getWord(buf, offset)
byte *buf;
int offset;
{
return((short)( buf[offset ] * 256 +
buf[offset + 1] ));
}
int
getLong(buf, offset)
byte *buf;
int offset;
{
return( buf[offset ] * 256 * 256 * 256 +
buf[offset + 1] * 256 * 256 +
buf[offset + 2] * 256 +
buf[offset + 3] );
}
char
contCode(ctype)
int ctype;
{
if (ctype == 0) return('r');
else if (ctype == 1) return('a');
else /* if (ctype == 2) */ return('o');
}
void
dumpField(aField, buf)
field *aField;
byte *buf;
{
char str[512];
if (fieldString(aField, buf, str))
fprintf(griFile, " %s\n", str);
}
boolean
fieldString(aField, buf, str)
field *aField;
byte *buf;
char *str;
{
int i;
int offset, bitOffset;
int val;
char *escape();
char temp[80];
if (aField->invisible) {
*str = '\0';
return(FALSE);
}
sprintf(str, "%s: ", aField->name->name);
offset = aField->offset & 0x3FFF;
bitOffset = aField->offset >> 14;
for (i=0; i<aField->dimension; ++i) {
switch (aField->type) {
Case FIELD_ENTITY:
sprintf(temp, "%c %d",
contCode(getWord(buf, offset + 6*i + 4)),
getLong(buf, offset + 6*i));
Case FIELD_BIN31:
val = getLong(buf, offset + 4*i);
sprintf(temp, "%d", val);
Case FIELD_AVAID:
val = getLong(buf, offset + 4*i);
sprintf(temp, "a %d", val);
Case FIELD_OBJID:
val = getLong(buf, offset + 4*i);
sprintf(temp, "o %d", val);
Case FIELD_REGID:
val = getLong(buf, offset + 4*i);
sprintf(temp, "r %d", val);
Case FIELD_FATWORD:
val = getWord(buf, offset + 4*i) +
getWord(buf, offset + 4*i + 2) * 256;
sprintf(temp, "%d", val);
Case FIELD_BIN15:
val = getWord(buf, offset + 2*i);
sprintf(temp, "%d", val);
Case FIELD_WORDS:
val = getWord(buf, offset + 2*i);
sprintf(temp, "%s%s%s",
(i == 0) ? "\"" : "",
escape(val),
(i == aField->dimension-1) ? "\"" : ""
);
Case FIELD_BYTE:
val = getByte(buf, offset);
sprintf(temp, "%d", val);
Case FIELD_VARSTRING:
val = getWord(buf, offset);
strcpy(temp, "\"");
strncat(temp, buf + offset + 2, val);
strcat(temp, "\"");
i = aField->dimension - 1;
Case FIELD_CHARACTER:
val = getByte(buf, offset + i);
sprintf(temp, "%s%s%s",
(i == 0) ? "\"" : "",
escape(val),
(i == aField->dimension-1) ? "\"" : ""
);
Case FIELD_BIT:
val = getByte(buf, offset + ((i+bitOffset)>>3));
val &= 1 << (7 - ((i+bitOffset)&7));
sprintf(temp, "%s%c%s",
(i == 0) ? "'" : "" ,
(val) ? '1' : '0',
(i == aField->dimension-1) ? "'b" : ""
);
}
strcat(str, temp);
if (aField->type != FIELD_WORDS && aField->type !=
FIELD_BIT && aField->type != FIELD_CHARACTER &&
i != aField->dimension-1)
strcat(str, ", ");
}
return(TRUE);
}
void
dumpObject(obj)
object *obj;
{
fieldList *fields;
if (obj == NULL)
return;
fprintf(griFile, "use %s {\n", classDefs[obj->class+1]->className->
name);
if (obj->class > 1) {
fields = classDefs[0]->fields;
while (fields != NULL) {
dumpField(fields->field, obj->stateVector);
fields = fields->nextField;
}
}
fields = classDefs[obj->class+1]->fields;
while (fields != NULL) {
dumpField(fields->field, obj->stateVector);
fields = fields->nextField;
}
fprintf(griFile, "}\n");
}

771
mamelink/griddle/exec.c Normal file
View file

@ -0,0 +1,771 @@
#include "griddleDefs.h"
void
executeInclude(filename)
char *filename;
{
fileList *newFile;
if (announceIncludes) {
fprintf(stderr, "->%s\n", filename);
fflush(stderr);
}
newFile = typeAlloc(fileList);
newFile->next = inputStack;
newFile->saveName = currentFileName;
newFile->saveLine = currentLineNumber;
newFile->fyle = currentInput;
inputStack = newFile;
if ((currentInput = fopen(filename, "r")) == NULL) {
error("unable to open include file '%s'\n", filename);
exit(1);
}
currentFileName = filename;
currentLineNumber = 1;
free(filename);
}
void
executeAssignment(name, expr)
symbol *name;
expression *expr;
{
value *evaluate();
if (name->type != NON_SYM && name->type != VARIABLE_SYM) {
error("illegal assignment to '%s'\n", name->name);
} else {
if (name->type == VARIABLE_SYM)
freeValue(name->def.value);
name->type = VARIABLE_SYM;
name->def.value = evaluate(expr);
}
}
void
fillByte(buf, offset, value)
byte *buf;
int offset;
int value;
{
buf[offset] = value & 0xFF;
}
void
fillWord(buf, offset, value)
byte *buf;
int offset;
int value;
{
buf[offset ] = (value >> 8) & 0xFF;
buf[offset + 1] = value & 0xFF;
}
void
fillLong(buf, offset, value)
byte *buf;
int offset;
long value;
{
buf[offset ] = (value >> 24) & 0xFF;
buf[offset + 1] = (value >> 16) & 0xFF;
buf[offset + 2] = (value >> 8) & 0xFF;
buf[offset + 3] = value & 0xFF;
}
value *
nextValue(dataptr)
exprList **dataptr;
{
exprList *data;
value *val;
value *buildNumber();
if (dataptr == NULL || *dataptr == NULL)
return(buildNumber(0));
data = *dataptr;
val = evaluate(data->expr);
*dataptr = data->nextExpr;
return(val);
}
value *
nextIntValue(dataptr)
exprList **dataptr;
{
value *val;
val = nextValue(dataptr);
if (!isInteger(val))
val = buildNumber(0);
return(val);
}
char *
nextStringValue(dataptr)
exprList **dataptr;
{
value *val;
char *result;
if (dataptr == NULL || *dataptr == NULL)
return(NULL);
val = nextValue(dataptr);
if (isString(val))
result = (char *)(val->value);
else
result = NULL;
freeValue(val);
return(result);
}
int
contNum(vtype)
valueType vtype;
{
if (vtype == VAL_AVATAR) return (1);
else if (vtype == VAL_OBJECT) return (2);
else return (0);
}
void
adjustValue(val)
value *val;
{
if (indirFile != NULL && val->value < -1000)
val->value -= globalIdAdjustment;
}
void
fillField(buf, data, aField, nextInt, nextString, nextBit)
byte *buf;
exprList *data;
field *aField;
value *(*nextInt)();
char *(*nextString)();
value *(*nextBit)();
{
int i, j;
int offset, bitOffset;
int len;
value *val;
char *string;
byte *bitString;
int bitLength;
int bufIndex;
byte theBit;
offset = aField->offset & 0x3FFF;
if (indirFile != NULL && offset == IDENT_OFFSET)
return;
bitOffset = aField->offset >> 14;
for (i=0; i<aField->dimension; ++i) {
switch (aField->type) {
Case FIELD_ENTITY:
val = (*nextInt)(&data);
adjustValue(val);
fillLong(buf, offset + 6*i, val->value);
fillWord(buf, offset + 6*i + 4, contNum(val->dataType));
freeValue(val);
Case FIELD_AVAID:
val = (*nextInt)(&data);
if (val->dataType != VAL_AVATAR && val->dataType !=
VAL_INTEGER)
error("illegal avatar id value\n");
adjustValue(val);
fillLong(buf, offset + 4*i, val->value);
freeValue(val);
Case FIELD_OBJID:
val = (*nextInt)(&data);
if (val->dataType != VAL_OBJECT && val->dataType !=
VAL_INTEGER)
error("illegal object id value\n");
adjustValue(val);
fillLong(buf, offset + 4*i, val->value);
freeValue(val);
Case FIELD_REGID:
val = (*nextInt)(&data);
if (val->dataType != VAL_REGION && val->dataType !=
VAL_INTEGER)
error("illegal region id value\n");
adjustValue(val);
fillLong(buf, offset + 4*i, val->value);
freeValue(val);
Case FIELD_BIN31:
val = (*nextInt)(&data);
fillLong(buf, offset + 4*i, val->value);
freeValue(val);
Case FIELD_FATWORD:
val = (*nextInt)(&data);
fillWord(buf, offset + 4*i, val->value & 0xFF);
fillWord(buf, offset + 4*i + 2, (val->value >> 8) &
0xFF);
freeValue(val);
Case FIELD_BIN15:
val = (*nextInt)(&data);
fillWord(buf, offset + 2*i, val->value);
freeValue(val);
Case FIELD_WORDS:
string = (*nextString)(&data);
if (string != NULL)
for (; i<aField->dimension && string != NULL
&& string[i] != '\0'; ++i)
fillWord(buf, offset + i*2, string[i]);
for (; i<aField->dimension; ++i)
fillWord(buf, offset + i*2, ' ');
free(string);
Case FIELD_BYTE:
val = (*nextInt)(&data);
fillByte(buf, offset + i, val->value);
freeValue(val);
Case FIELD_CHARACTER:
string = (*nextString)(&data);
if (string != NULL)
for (; i<aField->dimension && string != NULL
&& string[i] != '\0'; ++i)
fillByte(buf, offset + i, string[i]);
for (; i<aField->dimension; ++i)
fillByte(buf, offset + i, ' ');
freeValue(string);
Case FIELD_VARSTRING:
string = (*nextString)(&data);
if (string != NULL) {
len = strlen(string);
fillWord(buf, offset, len);
for (; i<aField->dimension && string != NULL
&& string[i] != '\0'; ++i)
fillByte(buf, offset+2+i, string[i]);
free(string);
} else
fillWord(buf, offset, 0);
i = aField->dimension;
Case FIELD_BIT:
val = (*nextBit)(&data);
if (isInteger(val)) {
bufIndex = offset + ((i+bitOffset)>>3);
theBit = 1 << (7 - ((i+bitOffset)&7));
if (val->value & 1)
buf[bufIndex] |= theBit;
else
buf[bufIndex] &= ~theBit;
} else if (val->dataType == VAL_BITSTRING) {
bitString = (byte *)val->value;
bitLength = *bitString++;
for (j=0; j < bitLength && i+j <
aField->dimension; ++j) {
bufIndex = offset + ((i+j+bitOffset)>>3);
theBit = 1 << (7 - ((i+j+bitOffset)&7));
if ((bitString[j>>3] >> (7-(j&7))) & 1)
buf[bufIndex] |= theBit;
else
buf[bufIndex] &= ~theBit;
}
for (i+=bitLength; i<aField->dimension; ++i) {
bufIndex = offset + ((i+bitOffset)>>3);
theBit = 1 << (7 - ((i+bitOffset)&7));
buf[bufIndex] &= ~theBit;
}
} else
error("invalid data type for bit field\n");
freeValue(val);
}
}
if (data != NULL)
error("too many initialization values\n");
}
void
fillPrototype(buf, fields, class)
byte *buf;
fieldList *fields;
int class;
{
while (fields != NULL) {
fillField(buf, fields->field->initValues, fields->field,
nextIntValue, nextStringValue, nextValue);
fields = fields->nextField;
}
}
void
fillProperty(buf, prop, fields, class)
byte *buf;
property *prop;
fieldList *fields;
int class;
{
while (fields != NULL) {
if (fields->field->name == prop->fieldName) {
fillField(buf, prop->data, fields->field,
nextIntValue, nextStringValue, nextValue);
return;
} else {
fields = fields->nextField;
}
}
if (class > 1)
fillProperty(buf, prop, classDefs[0]->fields, -1);
else
error("no match for field '%s'\n", prop->fieldName->name);
}
void
fillData(buf, fields, properties, class)
byte *buf;
fieldList *fields;
propertyList *properties;
int class;
{
while (properties != NULL) {
fillProperty(buf, properties->property, fields, class);
properties = properties->nextProp;
}
}
object *
initObject(class, globalId)
int class;
int globalId;
{
object *result;
objectList *buildObjectList();
int i;
if (indirectPass == 1) {
if (class == CLASS_REGION)
indirTable[indirRegion].region = globalId;
result = NULL;
} else {
result = typeAlloc(object);
result->class = class;
result->stateVector = byteAlloc(classDefs[class+1]->size);
for (i=0; i<classDefs[class+1]->size; ++i)
result->stateVector[i] = classDefs[class+1]->prototype[i];
if (class > 1)
fillLong(result->stateVector, 4, class);
fillLong(result->stateVector, 0, globalId);
}
return(result);
}
object *
generateObject(class, tail)
int class;
objectTail *tail;
{
value *val;
int globalId;
object *result;
if (tail->idExpr == NULL || indirFile != NULL) {
globalId = -globalIdCounter++;
} else {
val = evaluate(tail->idExpr);
if (!isInteger(val) || val->value <= 0) {
error("illegal global id number\n");
return(NULL);
}
globalId = val->value;
freeValue(val);
}
result = initObject(class, globalId);
if (indirectPass != 1)
fillData(result->stateVector, classDefs[class+1]->fields,
tail->properties, class);
return(result);
}
void
outputRawObject(obj)
object *obj;
{
int i;
if (obj == NULL)
return;
fprintf(rawFile, "/%d ", obj->class);
for (i=0; i<classDefs[obj->class+1]->size; ++i) {
if ((i & 31) == 31)
fprintf(rawFile, "\\\n");
fprintf(rawFile, "%02x", obj->stateVector[i]);
}
fprintf(rawFile, "\n");
}
int
fieldCode(ftype)
fieldType ftype;
{
if (ftype == FIELD_REGID)
return(0);
else if (ftype == FIELD_AVAID)
return(1);
else if (ftype == FIELD_OBJID)
return(2);
else
return(-1);
}
int
relativeId(id, contnum)
int id;
int contnum;
{
char scratchName[15];
symbol *scratchSymbol;
symbol *lookupSymbol();
sprintf(scratchName, "%c_%d", contCode(contnum), id);
scratchSymbol = lookupSymbol(scratchName);
if (scratchSymbol->type != VARIABLE_SYM) {
if (id < -1000 || 0 < id) {
error("undefined %c %d\n", contCode(contnum), id);
return(0);
} else
return(id);
} else
return(scratchSymbol->def.value->value);
}
void
shiftField(aField, buf, adjust)
field *aField;
byte *buf;
int adjust;
{
int i;
int offset;
int val;
offset = aField->offset & 0x3FFF;
for (i=0; i<aField->dimension; ++i) {
if (aField->type == FIELD_ENTITY) {
val = getLong(buf, offset + 6*i);
if (val < -1000)
fillLong(buf, offset + 6*i, val - adjust);
else if (assignRelativeIds)
fillLong(buf, offset + 6*i,
relativeId(val, getWord(buf, offset +
6*i + 4)));
} else if (aField->type == FIELD_AVAID || aField->type ==
FIELD_OBJID || aField->type == FIELD_REGID) {
val = getLong(buf, offset + 4*i);
if (val < -1000)
fillLong(buf, offset + 4*i, val - adjust);
else if (assignRelativeIds)
fillLong(buf, offset + 4*i, relativeId(val,
fieldCode(aField->type)));
}
}
}
void
shiftRelativeGlobalIds(obj, adjust)
object *obj;
int adjust;
{
fieldList *fields;
if (obj->class > 1) {
fields = classDefs[0]->fields;
while (fields != NULL) {
shiftField(fields->field, obj->stateVector, adjust);
fields = fields->nextField;
}
}
fields = classDefs[obj->class+1]->fields;
while (fields != NULL) {
shiftField(fields->field, obj->stateVector, adjust);
fields = fields->nextField;
}
}
void
generateScratchId(class, id, relativeId)
int class;
int id;
int relativeId;
{
char scratchName[15];
symbol *scratchSymbol;
valueType vtype;
value *buildValue();
symbol *lookupSymbol();
sprintf(scratchName, "%c_%d", contCode(class), id);
scratchSymbol = lookupSymbol(scratchName);
scratchSymbol->type = VARIABLE_SYM;
if (class == 0)
vtype = VAL_REGION;
else if (class == 1)
vtype = VAL_AVATAR;
else
vtype = VAL_OBJECT;
scratchSymbol->def.value = buildValue(vtype, relativeId);
}
void
executeRawline(obj)
object *obj;
{
if (assignRelativeIds)
generateScratchId(obj->class, getLong(obj->stateVector, 0),
-1001 - objectCount);
shiftRelativeGlobalIds(obj, objectCount - rawCount++);
if (objectCount < MAXNOID)
noidArray[objectCount++] = obj;
}
void
freeObjectTail(tail)
objectTail *tail;
{
propertyList *properties;
propertyList *oldProperties;
exprList *expr;
exprList *oldExpr;
properties = tail->properties;
while (properties != NULL) {
expr = properties->property->data;
while (expr != NULL) {
if (indirectPass == 1)
freeExpr(expr->expr);
oldExpr = expr;
expr = expr->nextExpr;
free(oldExpr);
}
free(properties->property);
oldProperties = properties;
properties = properties->nextProp;
free(oldProperties);
}
free(tail);
}
void
executeUse(className, tagName, tail)
symbol *className;
symbol *tagName;
objectTail *tail;
{
object *ultimate;
objectStub *buildObjectStub();
if (className->type != CLASS_SYM)
error("non-class identifier %s used for class name\n",
className->name);
else {
ultimate = generateObject(className->def.class, tail);
freeObjectTail(tail);
if (ultimate != NULL && tagName != NULL) {
if (ultimate->class == 0)
globalIdAdjustment = 0;
if (tagName->type == OBJECT_SYM)
free(tagName->def.object);
else if(tagName->type == VARIABLE_SYM)
freeValue(tagName->def.value);
tagName->type = OBJECT_SYM;
tagName->def.object = buildObjectStub(ultimate);
}
/*#ifndef FRED*/
if (ultimate != NULL) {
if (ultimate->class == CLASS_DOOR ||
ultimate->class == CLASS_BUILDING)
sortObjects = TRUE;
else if (ultimate->class == CLASS_REGION)
flushNoidArray();
if (objectCount < MAXNOID && indirectPass != 1)
noidArray[objectCount++] = ultimate;
else if (indirectPass != 0)
error("more than 256 objects in region\n");
}
/*#endif*/
}
}
cmpObjects(ob1, ob2)
object **ob1;
object **ob2;
{
int class1, class2;
class1 = (*ob1)->class;
class2 = (*ob2)->class;
if (class1 == CLASS_REGION)
return(-1);
else if (class2 == CLASS_REGION)
return(1);
else if (class1 != CLASS_DOOR && class1 != CLASS_BUILDING &&
class2 != CLASS_DOOR && class2 != CLASS_BUILDING)
return(0);
else if (class1 != CLASS_DOOR && class1 != CLASS_BUILDING)
return(1);
else if (class2 != CLASS_DOOR && class2 != CLASS_BUILDING)
return(-1);
return(getLong((*ob1)->stateVector, X_OFFSET_OBJ) -
getLong((*ob2)->stateVector, X_OFFSET_OBJ));
}
flushNoidArray()
{
int i;
#ifndef FRED
if (indirectPass == 2) {
for (i=0; i<objectCount; ++i)
altNoidArray[i] = noidArray[i];
if (sortObjects)
qsort(altNoidArray, objectCount, sizeof(object *),
cmpObjects);
for (i=0; i<objectCount; ++i)
replaceIndirectArgs(altNoidArray[i], i);
}
sortObjects = FALSE;
#endif
for (i=0; i<objectCount; ++i) {
#ifndef FRED
if (griFile != NULL && indirectPass != 1)
dumpObject(noidArray[i]);
if (rawFile != NULL && indirectPass != 1)
outputRawObject(noidArray[i]);
#endif
freeObject(noidArray[i]);
}
objectCount = 0;
}
freeObject(obj)
object *obj;
{
free(obj->stateVector);
free(obj);
}
freeValue(val)
value *val;
{
if (val != NULL) {
if (val->dataType == VAL_STRING ||
val->dataType == VAL_BITSTRING)
free(val->value);
free(val);
}
}
void
executeDefine(classExpr, name, fields)
expression *classExpr;
char *name;
fieldList *fields;
{
value *val;
int class;
symbol *symb;
symbol *lookupSymbol();
int size;
val = evaluate(classExpr);
class = val->value;
if (!isInteger(val))
error("non-integer value used for class number\n");
else if (class < -1 || MAXCLASS <= class)
error("class value %d out of range\n", class);
else if (classDefs[class+1] != NULL)
error("class %d already defined\n", class);
else {
translate(name, ' ', '_');
symb = lookupSymbol(name);
symb->type = CLASS_SYM;
symb->def.class = class;
classDefs[class+1] = typeAlloc(classDescriptor);
size = computeFieldOffsets(fields, class);
classDefs[class+1]->size = size;
classDefs[class+1]->fields = fields;
classDefs[class+1]->className = symb;
classDefs[class+1]->prototype = (byte *)malloc(size);
fillPrototype(classDefs[class+1]->prototype, fields, class);
}
freeValue(val);
free(name);
}
int
computeFieldOffsets(fields, class)
fieldList *fields;
int class;
{
int offset;
int bitOffset;
field *aField;
if (class > 1)
offset = objectBase;
else
offset = 0;
if (fields == NULL)
return(offset);
bitOffset = 0;
while (fields != NULL) {
aField = fields->field;
aField->offset = offset + (bitOffset << 14);
if (class == -1)
objectBase = offset;
switch (aField->type) {
Case FIELD_ENTITY:
offset += 6 * aField->dimension;
Case FIELD_AVAID:
case FIELD_BIN31:
case FIELD_OBJID:
case FIELD_REGID:
case FIELD_FATWORD:
offset += 4 * aField->dimension;
Case FIELD_BIN15:
case FIELD_WORDS:
offset += 2 * aField->dimension;
Case FIELD_BYTE:
case FIELD_CHARACTER:
offset += aField->dimension;
Case FIELD_VARSTRING:
offset += aField->dimension + 2;
Case FIELD_BIT:
bitOffset += aField->dimension;
if (bitOffset > 15) {
offset += 2 * (bitOffset >> 4);
bitOffset -= (bitOffset & ~0xF);
}
}
if (aField->type != FIELD_BIT && bitOffset > 0) {
bitOffset = 0;
offset += 2;
} else if (aField->type != FIELD_CHARACTER &&
aField->type != FIELD_BYTE && (offset & 1) == 1)
++offset;
fields = fields->nextField;
}
if (bitOffset > 0)
offset += 2;
return(offset);
}

252
mamelink/griddle/expr.c Normal file
View file

@ -0,0 +1,252 @@
#include "y.tab.h"
#include "griddleDefs.h"
value *
integerize(val)
value *val;
{
char *string;
int i;
value *buildNumber();
if (val == NULL) {
val = buildNumber(0);
} else if (val->dataType == VAL_UNDEFINED) {
val->value = 0;
val->dataType = VAL_INTEGER;
} else if (val->dataType == VAL_STRING) {
string = (char *)val->value;
val->value = 0;
for (i=0; i < 4 && string[i] != '\0'; ++i)
val->value = val->value * 256 + string[i];
val->dataType = VAL_INTEGER;
} else if (val->dataType == VAL_BITSTRING) {
/* do something */
val->value = 0;
val->dataType = VAL_INTEGER;
}
return(val);
}
boolean
isInteger(val)
value *val;
{
return(val != NULL &&
(val->dataType == VAL_INTEGER || val->dataType == VAL_AVATAR ||
val->dataType == VAL_OBJECT || val->dataType == VAL_REGION));
}
boolean
isString(val)
value *val;
{
return(val->dataType == VAL_STRING);
}
value *
evaluate(expr)
expression *expr;
{
value *result;
value *evaluateName();
value *evaluateUnop();
value *evaluateBin();
value *buildNumber();
value *buildString();
value *buildBitString();
switch (expr->type) {
Case ID_EXPR:
result = evaluateName(expr->part1);
Case NUM_EXPR:
result = buildNumber((int) expr->part1);
Case EXPR_EXPR:
result = evaluate(expr->part1);
Case UNOP_EXPR:
result = evaluateUnop(expr->part1, evaluate(expr->part2));
Case BIN_EXPR:
result = evaluateBin(evaluate(expr->part1), expr->part2,
evaluate(expr->part3));
Case STRING_EXPR:
result = buildString((char *) expr->part1);
Case BITSTRING_EXPR:
result = buildBitString((byte *) expr->part1);
Default:
printf("bad expr type leaked thru!\n");
exit(1);
}
free(expr);
return(result);
}
void
freeExpr(expr)
expression *expr;
{
switch (expr->type) {
Case EXPR_EXPR:
freeExpr(expr->part1);
Case UNOP_EXPR:
freeExpr(expr->part2);
Case BIN_EXPR:
freeExpr(expr->part1);
freeExpr(expr->part3);
Case STRING_EXPR:
free(expr->part1);
Case BITSTRING_EXPR:
free(expr->part1);
}
free(expr);
}
value *
evaluateUnop(oper, opnd)
int oper;
value *opnd;
{
opnd = integerize(opnd);
switch(oper) {
Case NOT:
opnd->value = ~opnd->value;
Case UMINUS:
opnd->value = -opnd->value;
Case A:
if (opnd->dataType == VAL_OBJECT ||
opnd->dataType == VAL_REGION)
error("incompatible type coercion\n");
opnd->dataType = VAL_AVATAR;
Case O:
if (opnd->dataType == VAL_AVATAR ||
opnd->dataType == VAL_REGION)
error("incompatible type coercion\n");
opnd->dataType = VAL_OBJECT;
Case R:
if (opnd->dataType == VAL_OBJECT ||
opnd->dataType == VAL_AVATAR)
error("incompatible type coercion\n");
opnd->dataType = VAL_REGION;
Default:
printf("bad unop leaked thru!\n");
exit(1);
}
return(opnd);
}
value *
evaluateBin(opnd1, oper, opnd2)
value *opnd1;
int oper;
value *opnd2;
{
opnd1 = integerize(opnd1);
opnd2 = integerize(opnd2);
switch(oper) {
case ADD:
opnd1->value += opnd2->value;
break;
case SUB:
opnd1->value -= opnd2->value;
break;
case MUL:
opnd1->value *= opnd2->value;
break;
case DIV:
opnd1->value /= opnd2->value;
break;
case MOD:
opnd1->value %= opnd2->value;
break;
case AND:
opnd1->value &= opnd2->value;
break;
case OR:
opnd1->value |= opnd2->value;
break;
case XOR:
opnd1->value ^= opnd2->value;
break;
default:
printf("bad binop leaked thru!\n");
exit(1);
}
if (opnd1->dataType != opnd2->dataType) {
if (opnd1->dataType == VAL_INTEGER)
opnd1->dataType = opnd2->dataType;
else if (opnd2->dataType != VAL_INTEGER)
error("incompatible type combination");
}
free(opnd2);
return(opnd1);
}
#ifdef FRED
value *
valueFromName(name)
char *name;
{
int len;
int i;
int result;
value *buildValue();
if ((len = strlen(name)) < 3 || name[1] != '_')
return(NULL);
result = 0;
for (i=2; i<len; ++i)
if ('0' <= name[i] && name[i] <= '9')
result = result * 10 + name[i] - '0';
else
return(NULL);
if (name[0] == 'r')
return(buildValue(VAL_REGION, result));
else if (name[0] == 'o')
return(buildValue(VAL_OBJECT, result));
else if (name[0] == 'a')
return(buildValue(VAL_AVATAR, result));
else
return(NULL);
}
#endif
value *
evaluateName(name)
symbol *name;
{
value *buildNumber();
value *buildValue();
value *result;
switch(name->type) {
case VARIABLE_SYM:
return(buildValue(name->def.value->dataType,
name->def.value->value));
case MACRO_SYM:
return(evaluate(name->def.expr));
case OBJECT_SYM:
result = buildNumber(name->def.object->id);
if (name->def.object->class == 0)
result->dataType = VAL_REGION;
else if (name->def.object->class == 1)
result->dataType = VAL_AVATAR;
else
result->dataType = VAL_OBJECT;
return(result);
case NON_SYM:
#ifdef FRED
if ((result = valueFromName(name->name)) != NULL)
return(result);
#endif
printf("symbol %s undefined\n", name->name);
return(buildNumber(0));
default:
printf("bad symbol type leaked thru!\n");
exit(1);
}
}

900
mamelink/griddle/fred.c Normal file
View file

@ -0,0 +1,900 @@
#include <curses.h>
#include <signal.h>
#include "griddleDefs.h"
#include "prot.h"
#include "y.tab.h"
static int selectedField = 0;
static int selectedPath = 0;
static int editX = 0;
static int editY = 0;
static char *editPath;
static field *editField = NULL;
static boolean changedFlag = FALSE;
static char paths[10][80];
static char pathFileName[80];
int fredStats[128];
typedef struct {
char commandKey;
char *echoName;
boolean (*commandFunction)();
char *helpInfo;
} command;
void echoLine();
void lineError();
boolean getString();
boolean processCommand();
void displayOneObject();
boolean saveGriddle(), initC64editor(), loadRegion(), quit(), saveRaw(), sh();
boolean refreshScreen(), showNoids(), displayObject(), incDisplayObject();
boolean decDisplayObject(), createObject(), help(), touch(), undeleteObject();
boolean deleteObject(), foreground(), background(), incX_4(), decX_4();
boolean incY_1(), decY_1(), incY_10(), decY_10(), zeroGrState(), incGrState();
boolean toggleOrient(), editObject(), nightMode(), walkto(), showFlatTypes();
boolean eraseBackground(), incPattern(), decPattern(), flatChange();
boolean twinObject(), peekContainer(), changeContainer(), trapEdit();
boolean borderToggle(), containerOffsetRight(), containerOffsetLeft();
boolean containerOffsetUp(), containerOffsetDown(), changeHold(), pauseFred();
boolean editPathlist(), displayPathlist();
command commandTable[] = {
'b', "background object", background, "move object to background",
'B', "background display suppression", eraseBackground,
"display without background",
ctrl(B), "border/unborder trapezoid trap", borderToggle,
"border/unborder trapezoid",
'c', "create new object", createObject, "create a new object",
'C', "container peek", peekContainer, "look inside container",
'd', "display object", displayObject, "display an object",
'D', "drop into container", changeContainer,
"drop object into container",
ctrl(D), "display pathlist", displayPathlist,
"display current pathlist",
'e', "edit object", editObject, "edit an object's state info",
'E', "edit trapezoid", trapEdit, "edit a trapezoid's corner info",
'f', "foreground object", foreground, "move object to foreground",
'F', "flat type change", flatChange, "change type of flat",
'g', "griddle format save", saveGriddle, "save in griddle format",
'G', "show flat types", showFlatTypes, "show flat types",
'h', "help", help, "display help info",
'H', "offset left", containerOffsetLeft, NULL,
'i', "initialize region editor", initC64editor, "init Reno",
'I', "change how held", changeHold, NULL,
'J', "offset up", containerOffsetUp, NULL,
'K', "offset down", containerOffsetDown, NULL,
'L', "offset right", containerOffsetRight, NULL,
'n', "night mode", nightMode, "switch display to night mode",
'o', "object noid list", showNoids, "show noids and classes",
'O', "orientation flip", toggleOrient, "toggle object's orientation",
'p', "inc color/pattern", incPattern, "inc object's color/pattern",
'P', "dec color/pattern", decPattern, "dec object's color/pattern",
ctrl(P), "path list edit", editPathlist, "edit path list",
'q', "quit", quit, "exit Fred",
'r', "read region from file", loadRegion, "read region from a file",
's', "set grState to 0", zeroGrState, "set object's grState to 0",
'S', "inc grState", incGrState, "inc object's grState",
't', "touch object under cursor", touch, "touch object under cursor",
'T', "twin object", twinObject, "twin object",
'u', "undelete deleted object", undeleteObject,
"undo object deletion",
'w', "walk to indicated object", walkto, "walk avatar to object",
'x', "delete object", deleteObject, "delete an object",
'z', "raw format save", saveRaw, "save in raw format",
'+', "display object", incDisplayObject, "inc display noid",
'-', "display object", decDisplayObject, "dec display noid",
'!', "unix command", sh, "execute a Unix command",
'.', "inc X", incX_4, "inc object's X coordinate",
',', "dec X", decX_4, "dec object's X coordinate",
'?', "inc Y", incY_1, "inc object's Y coordinate",
'/', "dec Y", decY_1, "dec object's Y coordinate",
'>', "inc Y by 10", incY_10, "inc object's Y coordinate by 10",
'<', "dec Y by 10", decY_10, "dec object's Y coordinate by 10",
'\r', "refresh Fred screen", refreshScreen, "refresh screen",
ctrl(Z), "pause Fred", pauseFred, "pause Fred",
'\0', NULL, NULL, NULL
};
extern char *getenv();
/* flags */
#define NCARDS 3
int card = 0;
int port = 0;
void
c64_override_command(cmd)
byte cmd;
{
if (!testMode) {
down(&cmd, (word) 1, KEYBOARD_OVERRIDE);
Cont();
sleep(1);
}
}
void
c64_key_command(cmd)
byte cmd;
{
byte buf;
buf = cmd;
if (!testMode) {
down(&buf, (word) 1, KEYBOARD_KEYPRESS);
Cont();
sleep(1);
}
}
void
c64_touch_command(arg)
byte arg;
{
byte buf;
buf = arg;
if (!testMode) {
down(&buf, (word) 1, TOUCH_SLOT);
Cont();
}
}
void
readFredStats()
{
FILE *statFyle;
int i;
if ((statFyle = fopen("/u0/habitat/fredStats", "r")) != NULL) {
for (i=0; i<128; ++i)
fredStats[i] = getw(statFyle);
fclose(statFyle);
}
if (statFyle == NULL || feof(statFyle)) {
for (i=0; i<128; ++i)
fredStats[i] = 0;
}
}
void
writeFredStats()
{
FILE *statFyle;
int i;
if ((statFyle = fopen("/u0/habitat/fredStats", "w")) != NULL) {
for (i=0; i<128; ++i)
putw(fredStats[i], statFyle);
fclose(statFyle);
}
}
int
touchObject()
{
char buf;
if (!testMode) {
c64_key_command('t');
up(&buf, (word) 1, TOUCHED_OBJECT);
Cont();
return(buf);
} else
return(displayNoid);
}
boolean
touch()
{
displayNoid = touchObject();
displayOneObject(displayNoid);
return(TRUE);
}
boolean
help()
{
int i;
int y, x;
int line, col;
line = 1;
col = 0;
getyx(curscr, y, x);
clearDisplay();
for (i=0; commandTable[i].commandKey!='\0'; ++i) {
if (commandTable[i].helpInfo != NULL) {
mvprintw(line, col, "%s -- %s",
keyName(commandTable[i].commandKey),
commandTable[i].helpInfo);
nextlc(line, col, 40);
}
}
move(y, x);
return(TRUE);
}
boolean
displayField(line, col, aField, buf, highlight)
int line;
int col;
field *aField;
byte *buf;
boolean highlight;
{
char str[512];
if (fieldString(aField, buf, str)) {
if (highlight) {
editX = col;
editY = line;
editField = aField;
}
mvaddstr(line, col, " ");
if (highlight)
standout();
addstr(str);
if (highlight)
standend();
return(TRUE);
} else
return(FALSE);
}
int
displayFieldList(fields, buf, lineptr, colptr, fieldNum)
fieldList *fields;
byte *buf;
int *lineptr;
int *colptr;
int fieldNum;
{
while (fields != NULL) {
if (displayField(*lineptr, *colptr, fields->field, buf,
fieldNum == selectedField)) {
nextlc(*lineptr, *colptr, 40);
++fieldNum;
}
fields = fields->nextField;
}
return(fieldNum);
}
void
revalueField(firstc, buf)
char firstc;
byte *buf;
{
ungetch(firstc);
fredModeLexingOn();
fillFieldPrompt(editY, editX, editField, buf, TRUE);
fredModeLexingOff();
}
bool
showObject(noid)
int noid;
{
object *obj;
int line, col;
int y, x;
int fieldNum;
line = 1;
col = 0;
fieldNum = 1;
obj = noidArray[noid];
getyx(curscr, y, x);
clearDisplay();
if (obj->class > 1)
fieldNum = displayFieldList(classDefs[0]->fields,
obj->stateVector, &line, &col, fieldNum);
fieldNum = displayFieldList(classDefs[obj->class+1]->fields,
obj->stateVector, &line, &col, fieldNum);
move(y, x);
refresh();
return(fieldNum - 1 == selectedField);
}
void
editOneObject(noid)
int noid;
{
char c;
boolean lastField;
showObject(noid);
selectedField = 1;
changedFlag = FALSE;
do {
lastField = showObject(noid);
c = mygetch();
if (c == DEL)
break;
else if (c == '\b') {
selectedField = selectedField < 2 ?
0 : selectedField - 2;
lastField = FALSE;
} else if (c != '\r' && c != '\n' && c != ' ')
revalueField(c, noidArray[noid]->stateVector);
++selectedField;
} while (!lastField);
selectedField = 0;
showObject(noid);
}
boolean
editObject()
{
int y, x;
echoLine("editing object %d (%s)", displayNoid,
classDefs[noidArray[displayNoid]->class+1]->className->name);
getyx(curscr, y, x);
editOneObject(displayNoid);
if (changedFlag)
uploadRegion();
move(y, x);
return(TRUE);
}
void
displayOneObject(noid)
int noid;
{
echoLine("object %d (%s)", noid,
classDefs[noidArray[noid]->class+1]->className->name);
showObject(noid);
}
boolean
displayObject()
{
int noid;
noid = promptInt("noid #", displayNoid);
if (noid == -1)
echoLine("aborted");
else if (noid < 0 || MAXNOID <= noid)
lineError("noid out of range");
else if (noidArray[noid] == NULL)
lineError("there is no object #%d", noid);
else {
displayNoid = noid;
if (noid != 0)
c64_touch_command(noid);
displayOneObject(noid);
}
return(TRUE);
}
boolean
incDisplayObject()
{
int count;
count = 0;
if (displayNoid == MAXNOID - 1) displayNoid = -1;
while (noidArray[++displayNoid] == NULL && ++count < MAXNOID)
if (displayNoid == MAXNOID - 1) displayNoid = -1;
if (noidArray[displayNoid] != NULL) {
if (displayNoid != 0)
c64_touch_command(displayNoid);
displayOneObject(displayNoid);
} else
lineError("no objects to display!");
return(TRUE);
}
boolean
decDisplayObject()
{
int count;
count = 0;
if (displayNoid == 0) displayNoid = MAXNOID;
while (noidArray[--displayNoid] == NULL && ++count < MAXNOID)
if (displayNoid == 0) displayNoid = MAXNOID;
if (noidArray[displayNoid] != NULL) {
if (displayNoid != 0)
c64_touch_command(displayNoid);
displayOneObject(displayNoid);
} else
lineError("no objects to display!");
return(TRUE);
}
boolean
deleteObject()
{
if (displayNoid == 0)
lineError("can't delete the region!");
else {
if (undeleteBuffer != NULL)
freeObject(undeleteBuffer);
undeleteBuffer = noidArray[displayNoid];
noidArray[displayNoid] = NULL;
if (displayNoid == objectCount)
--objectCount;
echoLine("object %d is gone", displayNoid);
incDisplayObject();
uploadRegion();
}
return(TRUE);
}
boolean
undeleteObject()
{
int noid;
if (undeleteBuffer == NULL)
lineError("no deleted object to restore");
else {
noid = nextFreeNoid();
noidArray[noid] = undeleteBuffer;
undeleteBuffer = NULL;
echoLine("object restored to noid %d", noid);
uploadRegion();
}
return(TRUE);
}
boolean
fillFieldPrompt(line, col, aField, buf, editMode)
int line;
int col;
field *aField;
byte *buf;
boolean editMode;
{
char temp[80];
value *parseBit();
value *parseInt();
char *parseString();
if (aField->invisible)
return(FALSE);
fieldPrompt(line, col, aField, buf, editMode);
if (mygetstr(temp) && temp[0] != '\0') {
fillField(buf, temp, aField, parseInt, parseString, parseBit);
changedFlag = TRUE;
}
return(TRUE);
}
void
fillDataPrompt(lineptr, colptr, buf, fields, class)
int *lineptr;
int *colptr;
byte *buf;
fieldList *fields;
int class;
{
fredModeLexingOn();
while (fields != NULL) {
if (fillFieldPrompt(*lineptr, *colptr, fields->field, buf,
FALSE))
nextlc(*lineptr, *colptr, 40);
fields = fields->nextField;
}
fredModeLexingOff();
}
void
generateFredObject(class, twinFlag)
int class;
boolean twinFlag;
{
object *obj;
object *initObject();
int noid;
int i;
obj = initObject(class, -globalIdCounter++);
if (twinFlag)
for (i=4; i<classDefs[class+1]->size; ++i)
obj->stateVector[i] =
noidArray[displayNoid]->stateVector[i];
noid = nextFreeNoid();
displayNoid = noid;
noidArray[noid] = obj;
clearDisplay();
if (twinFlag)
displayOneObject(noid);
else
editOneObject(noid);
}
boolean
createObject()
{
int class;
void displayRegion();
class = promptInt("-- class", previousClass);
if (class == -1)
echoLine("aborted");
else if (class < 1 || MAXCLASS <= class)
lineError("class value %d is out of range", class);
else {
echoLine("creating class %d (%s)", class,
classDefs[class+1]->className->name);
previousClass = class;
generateFredObject(class, FALSE);
uploadRegion();
echoLine("created object %d, class %d (%s)", displayNoid,
class, classDefs[class+1]->className->name);
c64_touch_command(displayNoid);
}
return(TRUE);
}
boolean
twinObject()
{
int class;
if (displayNoid == 0)
lineError("can't twin region");
else {
class = noidArray[displayNoid]->class;
previousClass = class;
generateFredObject(class, TRUE);
/* announceObject(displayNoid, class);*/
uploadRegion();
echoLine("created object %d, class %d (%s)", displayNoid,
class, classDefs[class+1]->className->name);
c64_touch_command(displayNoid);
}
return(TRUE);
}
boolean
showNoids()
{
int line, col;
int y, x;
int i;
line = 1;
col = 0;
getyx(curscr, y, x);
clearDisplay();
for (i=0; i<objectCount; ++i)
if (noidArray[i] != NULL) {
mvprintw(line, col, "%3d %s", i,
classDefs[noidArray[i]->class+1]->className->name);
nextlc(line, col, 20);
}
move(y, x);
refresh();
return(TRUE);
}
void
readPathlist()
{
char *pathstr;
FILE *fyle;
int i;
sprintf(pathFileName, "%s/.fredpaths", getenv("HOME"));
if ((fyle = fopen(pathFileName,"r")) != NULL) {
for (i=1; i<10; i++)
fscanf(fyle, "%s\n", paths[i]);
fclose(fyle);
} else {
for (i=1; i<10; i++)
strcpy(paths[i], "./");
}
if ((pathstr = getenv("FREDPATH")) != NULL)
strcpy(paths[0], pathstr);
else
strcpy(paths[0], "./");
}
void
writePathlist()
{
FILE *fyle;
int i;
if ((fyle = fopen(pathFileName, "w")) != NULL) {
for (i=1; i<10; i++)
fprintf(fyle, "%s\n", paths[i]);
fclose(fyle);
} else
lineError("can't write to pathlist file %s", pathFileName);
}
void
showPathlist()
{
int i;
int y, x;
getyx(curscr, y, x);
clearDisplay();
for (i=0; i<10; ++i) {
mvaddstr(i + 1, 0, " ");
if (i == selectedPath) {
editX = 0;
editY = i + 1;
editPath = paths[i];
standout();
}
addstr(paths[i]);
if (i == selectedPath) standend();
clrtoeol();
}
move(y, x);
refresh();
}
boolean
displayPathlist()
{
showPathlist();
return(TRUE);
}
void
revaluePath(firstc)
char firstc;
{
char temp[80];
ungetch(firstc);
mvaddstr(selectedPath + 1, 0, " ");
clrtoeol();
refresh();
if (mygetstr(temp) && temp[0] != '\0') {
strcpy(editPath, temp);
changedFlag = TRUE;
}
}
boolean
editPathlist()
{
int y, x;
char c;
echoLine("editing pathlist");
getyx(curscr, y, x);
showPathlist();
changedFlag = FALSE;
for (;;) {
c = mygetch();
if (c == DEL)
break;
else if (c == '\b') {
if (selectedPath > 0)
--selectedPath;
} else if (c == '\r' || c == '\n' || c == ' ') {
if (selectedPath < 9)
++selectedPath;
} else
revaluePath(c);
showPathlist();
}
if (changedFlag)
writePathlist();
move(y, x);
strcpy(pathname, paths[selectedPath]);
return(TRUE);
}
void
snarfRegion()
{
int i;
byte *p;
byte buf[10000];
int regionSize;
FILE *fyle;
c64_override_command(CMD_SAVE_CV);
up(buf, (word) 2, CV_SIZE_SLOT);
regionSize = buf[0] + buf[1]*256;
up(buf, (word)(regionSize), CV_DATA_SLOT);
Cont();
p = buf;
for (i=0; i<regionSize; ++i)
cv[i] = *p++;
}
boolean
quit()
{
echoLine("quit");
clearDisplay();
refresh();
writeFredStats();
return(FALSE);
}
void
setupFastlinkPort()
{
char *portstr;
portstr = getenv("FASTPORT");
if (portstr != NULL) {
if (strlen(portstr) < 3) {
error("FASTPORT env variable must be of form: c;p\n");
exit(1);
} else {
card = portstr[0] - '0';
port = portstr[2] - '0';
}
}
if (card < 0 || NCARDS <= card) {
error("card number out of range\n");
exit(1);
}
if (port < 0 || 2 < port) {
error("port number out of range\n");
exit(1);
}
if (!testMode && !Init(card, port)) {
error("unable to access device\n");
Finish();
exit(1);
}
}
void
doFredStuff()
{
char *pathstr;
FILE *fyle;
pathname[0] = '\0';
if ((pathstr = getenv("FREDPATH")) != NULL)
strcpy(pathname, pathstr);
readFredStats();
readPathlist();
if (!testMode)
setupFastlinkPort();
setupTerminal();
displayNoid = 0;
undeleteBuffer = NULL;
previousClass = 2;
strcpy(regionName, "/u0/habitat/empty.raw");
echoLine("Fred version 1.0 (%s) -- type 'h' for help", DATE);
while (processCommand())
;
refresh();
endwin();
}
boolean
initC64editor()
{
/* system("down -S < /u0/aric/mic/Gr/all.out");*/
system("down -S < /u0/habitat/reno.out");
/* system("down -S < /u0/chip/reno.out");*/
return(TRUE);
}
boolean
sh()
{
char commandBuf[80];
int y, x;
if (!getString(":", commandBuf)) {
echoLine("aborted");
return(TRUE);
}
getyx(curscr, y, x);
clearDisplay();
move(1, 0);
refresh();
echo(); noraw(); nl();
system(commandBuf);
noecho(); raw(); nonl();
move(1, 47);
refresh();
echoLine("unix command:%s", commandBuf);
refresh();
return(TRUE);
}
boolean
loadRegion()
{
if (!getRegionName())
echoLine("aborted");
else if (readRegion() && generateContentsVector()) {
displayRegion();
echoLine("loaded %s", regionName);
}
return(TRUE);
}
boolean
saveRaw()
{
if (!getRegionName())
echoLine("aborted");
else {
if (!testMode) {
snarfRegion();
degenerateContentsVector();
}
if (writeRegionRaw())
echoLine("saved raw file %s", regionName);
}
return(TRUE);
}
boolean
saveGriddle()
{
if (!getRegionName())
echoLine("aborted");
else {
if (!testMode) {
snarfRegion();
degenerateContentsVector();
}
if (writeRegionGriddle())
echoLine("saved griddle file %s", regionName);
}
return(TRUE);
}
boolean
processCommand()
{
char c;
int i;
static char previousC = '\0';
if (previousC != '\r' && previousC != '\32')
addstr(" -- Next?");
refresh();
c = mygetch();
for (i=0; commandTable[i].commandKey != '\0'; ++i)
if (commandTable[i].commandKey == c) {
if (c != '\r' && c != '\32')
echoLine(commandTable[i].echoName);
fredStats[c]++;
previousC = c;
return (*(commandTable[i].commandFunction))();
}
lineError("'%s' is not a Fred command", keyName(c));
previousC = c;
return(TRUE);
}
boolean
pauseFred()
{
echo(); noraw(); nl();
kill(0, SIGTSTP);
noecho(); raw(); nonl();
move(1, 47);
refresh();
reallyClearScreen();
return(TRUE);
}
boolean
refreshScreen()
{
reallyClearScreen();
c64_key_command('r');
return(TRUE);
}

602
mamelink/griddle/fred2.c Normal file
View file

@ -0,0 +1,602 @@
#include <curses.h>
#include "griddleDefs.h"
#include <sys/types.h>
#include <sys/stat.h>
void
mydown(buf, len, addr)
byte *buf;
word len;
word addr;
{
if (!testMode)
down(buf, len, addr);
}
void
displayRegion()
{
mydown(cv, (word)(cvLength), CV_DATA_SLOT);
c64_override_command(CMD_LOAD_CV);
}
boolean
incColorPattern(dp, cmd)
int dp;
byte cmd;
{
byte *buf;
if (displayNoid == 0)
lineError("region has no color/pattern!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, ORIENT_OFFSET_AVA,
(getWord(buf, ORIENT_OFFSET_AVA) + dp) & 0xFF);
else
fillWord(buf, ORIENT_OFFSET_OBJ,
(getWord(buf, ORIENT_OFFSET_OBJ) + dp) & 0xFF);
c64_key_command(cmd);
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
incPattern()
{
return(incColorPattern(8, 'p'));
}
boolean
decPattern()
{
return(incColorPattern(-8, 'P'));
}
boolean
incX(dx, cmd)
int dx;
byte cmd;
{
byte *buf;
if (displayNoid == 0)
lineError("region has no X-coordinate!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, X_OFFSET_AVA,
(getWord(buf, X_OFFSET_AVA) + dx) & 0xFF);
else
fillWord(buf, X_OFFSET_OBJ,
(getWord(buf, X_OFFSET_OBJ) + dx) & 0xFF);
c64_key_command(cmd);
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
incY(dy, cmd)
int dy;
byte cmd;
{
byte *buf;
if (displayNoid == 0)
lineError("region has no Y-coordinate!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, Y_OFFSET_AVA,
(getWord(buf, Y_OFFSET_AVA) + dy) & 0xFF);
else
fillWord(buf, Y_OFFSET_OBJ,
(getWord(buf, Y_OFFSET_OBJ) + dy) & 0xFF);
c64_key_command(cmd);
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
incX_4()
{
return(incX(4, '.'));
}
boolean
decX_4()
{
return(incX(-4, ','));
}
boolean
incY_1()
{
return(incY(1, '?'));
}
boolean
incY_10()
{
return(incY(10, '>'));
}
boolean
decY_1()
{
return(incY(-1, '/'));
}
boolean
decY_10()
{
return(incY(-10, '<'));
}
boolean
zeroGrState()
{
byte *buf;
if (displayNoid == 0)
lineError("region does not have grState!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, GRSTATE_OFFSET_AVA, 0);
else
fillWord(buf, GRSTATE_OFFSET_OBJ, 0);
c64_key_command('s');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
incGrState()
{
byte *buf;
if (displayNoid == 0)
lineError("region does not have grState!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, GRSTATE_OFFSET_AVA,
getWord(buf, GRSTATE_OFFSET_AVA) + 1);
else
fillWord(buf, GRSTATE_OFFSET_OBJ,
getWord(buf, GRSTATE_OFFSET_OBJ) + 1);
c64_key_command('S');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
toggleOrient()
{
byte *buf;
if (displayNoid == 0)
lineError("region does not have displayed orientation!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, ORIENT_OFFSET_AVA,
getWord(buf, ORIENT_OFFSET_AVA) ^ 0x01);
else
fillWord(buf, ORIENT_OFFSET_OBJ,
getWord(buf, ORIENT_OFFSET_OBJ) ^ 0x01);
c64_key_command('o');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
borderToggle()
{
byte *buf;
if (noidArray[displayNoid]->class != CLASS_TRAP &&
noidArray[displayNoid]->class != CLASS_SUPER_TRAP)
lineError("current object is not a trapezoid!");
else {
buf = noidArray[displayNoid]->stateVector;
fillWord(buf, HEIGHT_OFFSET_TRAP,
getWord(buf, HEIGHT_OFFSET_TRAP) ^ 0x80);
c64_key_command('B');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
flatChange()
{
byte *buf;
int class;
class = noidArray[displayNoid]->class;
if (class != CLASS_FLAT && class != CLASS_TRAP && class !=
CLASS_SUPER_TRAP)
lineError("inappropriate object for flat type change!");
else {
buf = noidArray[displayNoid]->stateVector;
fillWord(buf, TYPE_OFFSET_FLAT,
(getWord(buf, TYPE_OFFSET_FLAT) + 1) & 3);
c64_key_command('m');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
foreground()
{
byte *buf;
if (displayNoid == 0)
lineError("region does not have foreground/background!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, Y_OFFSET_AVA,
getWord(buf, Y_OFFSET_AVA) | 0x80);
else
fillWord(buf, Y_OFFSET_OBJ,
getWord(buf, Y_OFFSET_OBJ) | 0x80);
c64_key_command('f');
displayOneObject(displayNoid);
}
return(TRUE);
}
boolean
background()
{
byte *buf;
if (displayNoid == 0)
lineError("region does not have foreground/background!");
else {
buf = noidArray[displayNoid]->stateVector;
if (isAvatar(displayNoid))
fillWord(buf, Y_OFFSET_AVA,
getWord(buf, Y_OFFSET_AVA) & 0x7F);
else
fillWord(buf, Y_OFFSET_OBJ,
getWord(buf, Y_OFFSET_OBJ) & 0x7F);
c64_key_command('b');
displayOneObject(displayNoid);
}
return(TRUE);
}
void
uploadRegion()
{
generateContentsVector();
displayRegion();
}
void
homogenize(filename)
char *filename;
{
char tempstr[80];
char *tildeptr;
char *index();
if ((tildeptr = index(filename, '~')) != NULL) {
strncpy(tempstr, filename, tildeptr - filename);
sprintf(tempstr + (tildeptr - filename), "%s%s",
getenv("HOME"), tildeptr + 1);
strcpy(filename, tempstr);
}
}
boolean
readRegion()
{
char regionFileName[80];
sprintf(regionFileName, "%s%s", pathname, regionName);
homogenize(regionFileName);
queueInputFile(saveString(regionFileName));
if (openFirstFile(TRUE)) {
resetRegionCounters();
yyparse();
echoLine("reading %d objects", objectCount);
globalIdCounter += objectCount;
displayNoid = 0;
if (objectCount > 127)
lineError("too many objects in region");
return(TRUE);
} else {
lineError("can't open '%s'", regionFileName);
return(FALSE);
}
}
boolean
writeRegionRaw()
{
char regionFileName[80];
int i;
struct stat statBuf;
sprintf(regionFileName, "%s%s", pathname, regionName);
homogenize(regionFileName);
if (!promptDefault)
if (stat(regionFileName, &statBuf) == 0)
if (!promptYN("file already exists; replace? ")) {
echoLine("write aborted");
return(FALSE);
}
if ((rawFile = fopen(regionFileName, "w")) != NULL) {
for (i=0; i<objectCount; ++i)
outputRawObject(noidArray[i]);
fclose(rawFile);
rawFile = NULL;
return(TRUE);
} else {
lineError("can't open '%s'", regionFileName);
return(FALSE);
}
}
boolean
writeRegionGriddle()
{
char regionFileName[80];
int i;
struct stat statBuf;
sprintf(regionFileName, "%s%s", pathname, regionName);
homogenize(regionFileName);
if (!promptDefault)
if (stat(regionFileName, &statBuf) == 0)
if (!promptYN("file already exists; replace? ")) {
echoLine("write aborted");
return(FALSE);
}
if ((griFile = fopen(regionFileName, "w")) != NULL) {
for (i=0; i<objectCount; ++i)
dumpObject(noidArray[i]);
fclose(griFile);
griFile = NULL;
return(TRUE);
} else {
lineError("can't open '%s'", regionFileName);
return(FALSE);
}
}
char *
keyName(key)
char key;
{
static char result[10];
int i;
static struct {
char key;
char *name;
} keyList[] = {
' ', "SPACE", '\b', "BACKSPACE",
'\t', "TAB", '\n', "LINE FEED",
'\r', "RETURN", '\33', "ESC",
'\177', "DEL"
};
if ('!' <= key && key <= '~') {
sprintf(result, "%c", key);
return(result);
}
for (i=0; keyList[i].key != '\0'; ++i)
if (keyList[i].key == key)
return(keyList[i].name);
if (key < ' ') {
sprintf(result, "^%c", key + 0x40);
return(result);
}
sprintf(result, "\\%o", key);
return(result);
}
int
nextFreeNoid()
{
int noid;
for (noid=0; noid<objectCount; ++noid)
if (noidArray[noid] == NULL)
break;
if (noid == objectCount)
++objectCount;
return(noid);
}
void
announceObject(noid, class)
int noid;
int class;
{
byte createPacket[7];
byte *buf;
void mydown();
if (class == 0)
return;
buf = noidArray[noid]->stateVector;
createPacket[0] = class;
createPacket[1] = 0;
if (class == 1) {
createPacket[2] = getByte(buf, STYLE_OFFSET_AVA);
createPacket[3] = getWord(buf, X_OFFSET_AVA);
createPacket[4] = getWord(buf, Y_OFFSET_AVA);
createPacket[5] = getWord(buf, ORIENT_OFFSET_AVA);
createPacket[6] = getWord(buf, GRSTATE_OFFSET_AVA);
} else {
createPacket[2] = getWord(buf, STYLE_OFFSET_OBJ);
createPacket[3] = getWord(buf, X_OFFSET_OBJ);
createPacket[4] = getWord(buf, Y_OFFSET_OBJ);
createPacket[5] = getWord(buf, ORIENT_OFFSET_OBJ);
createPacket[6] = getWord(buf, GRSTATE_OFFSET_OBJ);
}
mydown(createPacket, (word) 7, CREATE_DATA_SLOT);
c64_override_command(CMD_CREATE);
}
boolean
nightMode()
{
c64_key_command('n');
return(TRUE);
}
boolean
eraseBackground()
{
c64_key_command('e');
return(TRUE);
}
boolean
walkto()
{
c64_key_command('w');
return(TRUE);
}
boolean
showFlatTypes()
{
c64_key_command('g');
return(TRUE);
}
boolean
peekContainer()
{
c64_key_command('l');
return(TRUE);
}
boolean
containerOffsetRight()
{
c64_key_command('+');
return(TRUE);
}
boolean
containerOffsetLeft()
{
c64_key_command('-');
return(TRUE);
}
boolean
containerOffsetUp()
{
c64_key_command('@');
return(TRUE);
}
boolean
containerOffsetDown()
{
c64_key_command('*');
return(TRUE);
}
boolean
changeHold()
{
c64_key_command('=');
return(TRUE);
}
boolean
changeContainer()
{
int newContainer;
int contCode;
byte *cbuf;
byte *obuf;
newContainer = touchObject();
c64_touch_command(displayNoid);
c64_key_command('C');
c64_key_command('r');
obuf = noidArray[displayNoid]->stateVector;
cbuf = noidArray[newContainer]->stateVector;
if (noidArray[newContainer]->class == 0)
contCode = 0;
else if (noidArray[newContainer]->class == 1)
contCode = 1;
else
contCode = 2;
if (isAvatar(displayNoid)) {
if (contCode == 1)
lineError("can't put avatar in another avatar!");
else if (contCode == 0)
fillLong(obuf, CONTAINER_OFFSET_AVA, 0);
else
fillLong(obuf, CONTAINER_OFFSET_AVA,
getLong(cbuf, IDENT_OFFSET_OBJ));
} else {
fillLong(obuf, CONTAINER_OFFSET_OBJ,
getLong(cbuf, IDENT_OFFSET_OBJ));
fillWord(obuf, CONTAINER_TYPE_OFFSET_OBJ, contCode);
}
}
boolean
trapEdit()
{
char c;
if (noidArray[displayNoid]->class != CLASS_TRAP &&
noidArray[displayNoid]->class != CLASS_SUPER_TRAP) {
lineError("current object is not a trapezoid!");
return(TRUE);
}
clearDisplay();
mvprintw(1, 0, "L -- upper left corner");
mvprintw(2, 0, "l -- lower left corner");
mvprintw(3, 0, "R -- upper right corner");
mvprintw(4, 0, "r -- lower right corner");
mvprintw(5, 0, "x -- exit trapezoid edit mode");
refresh();
c64_key_command(TRAP_EDIT_KEY);
while ((c = mygetch()) != 'x') {
if (c == 'L') {
echoLine("upper left");
c64_key_command(UPPER_LEFT_KEY);
} else if (c == 'l') {
echoLine("lower left");
c64_key_command(LOWER_LEFT_KEY);
} else if (c == 'R') {
echoLine("upper right");
c64_key_command(UPPER_RIGHT_KEY);
} else if (c == 'r') {
echoLine("lower right");
c64_key_command(LOWER_RIGHT_KEY);
} else {
lineError("not a trapezoid edit command!");
}
}
echoLine("done editing trapezoid");
c64_key_command(TRAP_EDIT_KEY);
snarfRegion();
degenerateContentsVector();
displayOneObject(displayNoid);
return(TRUE);
}

350
mamelink/griddle/fscreen.c Normal file
View file

@ -0,0 +1,350 @@
#include <curses.h>
#include "griddleDefs.h"
#include "y.tab.h"
static boolean unsavedFlag = FALSE;
static char unsavedChar;
extern int yylval;
void
echoLine(fmt, arg1, arg2, arg3)
char *fmt;
int arg1;
int arg2;
int arg3;
{
move(0, 0);
refresh();
clrtoeol();
printw(fmt, arg1, arg2, arg3);
refresh();
}
void
lineError(fmt, arg1, arg2, arg3)
char *fmt;
int arg1;
int arg2;
int arg3;
{
echoLine(fmt, arg1, arg2, arg3);
putchar('\7');
}
char
mygetch()
{
if (unsavedFlag) {
unsavedFlag = FALSE;
return(unsavedChar);
} else
return(getch());
}
void
ungetch(c)
char c;
{
unsavedChar = c;
unsavedFlag = TRUE;
}
boolean
mygetstr(buf)
char *buf;
{
char c;
char *originalBuf;
int x, y;
int originalX;
originalBuf = buf;
getyx(curscr, y, originalX);
x = originalX;
for (;;) {
c = mygetch();
if (c == CTRL_C || c == ESCAPE)
return(FALSE);
else if (c == '\n' || c == '\r' || c == EOF) {
addch('\r');
refresh();
*buf = '\0';
return(TRUE);
} else if (c == '\b' || c == DEL) {
if (buf > originalBuf) {
mvclrtoeol(y, --x);
--buf;
}
} else if (c == CTRL_U) {
mvclrtoeol(y, x = originalX);
buf = originalBuf;
} else {
addch(c);
*buf++ = c;
++x;
}
refresh();
}
}
boolean
getString(prompt, buf)
char *prompt;
char *buf;
{
addstr(prompt);
refresh();
return(mygetstr(buf));
}
int
promptInt(prompt, defval)
char *prompt;
int defval;
{
char answer[80];
char newPrompt[80];
symbol *symb;
symbol *lookupSymbol();
sprintf(newPrompt, " %s [%d] ? ", prompt, defval);
if (!getString(newPrompt, answer))
return(0);
if (answer[0] == '\0')
return(defval);
else if (answer[0] == ESCAPE || answer[0] == CTRL_C)
return(-1);
else if ('0' <= answer[0] && answer[0] <= '9')
return(atoi(answer));
else {
symb = lookupSymbol(answer);
if (symb->type != CLASS_SYM) {
lineError("not a class name!");
return(-1);
}
return(symb->def.class);
}
}
boolean
promptStr(prompt, defval, resultBuf)
char *prompt;
char *defval;
char *resultBuf;
{
char answer[80];
char newPrompt[80];
sprintf(newPrompt, " %s [\"%s\"] ? ", prompt, defval);
if (!getString(newPrompt, answer))
return(FALSE);
if (answer[0] == '\0') {
promptDefault = TRUE;
if (defval != resultBuf)
strcpy(resultBuf, defval);
} else {
promptDefault = FALSE;
strcpy(resultBuf, answer);
}
return(TRUE);
}
boolean
promptYN(prompt)
char *prompt;
{
char answer[80];
mvclrtoeol(0, 0);
for (;;) {
if (getString(prompt, answer)) {
if (answer[0] == 'Y' || answer[0] == 'y')
return(TRUE);
else if (answer[0] == 'N' || answer[0] == 'n')
return(FALSE);
}
lineError("please answer yes or no!");
}
}
void
fieldPrompt(line, col, aField, buf, highlight)
int line;
int col;
field *aField;
byte *buf;
boolean highlight;
{
mvaddstr(line, col, " ");
if (highlight)
standout();
printw("%s:", aField->name->name);
if (highlight)
standend();
addstr(" ");
clrtoeol();
refresh();
}
value *
parseValue(dataptr)
char **dataptr;
{
char *data;
value *val;
value *buildValue();
valueType resultType, newType;
boolean typeTest;
int sign;
if (dataptr == NULL || *dataptr == NULL)
return(buildValue(VAL_INTEGER, 0));
fredLexString = *dataptr;
resultType = newType = VAL_INTEGER;
val = NULL;
sign = 1;
for (;;) {
typeTest = FALSE;
switch (yylex()) {
Case Number:
val = buildValue(resultType, yylval*sign);
Case String:
val = buildValue(VAL_STRING, yylval);
typeTest = TRUE;
Case BitString:
val = buildValue(VAL_BITSTRING, yylval);
typeTest = TRUE;
Case '-':
sign = -sign;
Case A:
newType = VAL_AVATAR;
typeTest = TRUE;
Case O:
newType = VAL_OBJECT;
typeTest = TRUE;
Case R:
newType = VAL_REGION;
typeTest = TRUE;
Case ',':
if (resultType != VAL_INTEGER)
lineError("dangling type!");
*dataptr = fredLexString;
return(val);
Case 0:
if (resultType != VAL_INTEGER)
lineError("dangling type!");
*dataptr = NULL;
return(val);
Default:
lineError("syntax error!");
if (val == NULL)
val = buildValue(VAL_INTEGER, 0);
}
if (typeTest && resultType != VAL_INTEGER)
lineError("type mismatch!");
else
resultType = newType;
}
}
value *
parseInt(dataptr)
char **dataptr;
{
value *val;
value *buildNumber();
val = parseValue(dataptr);
if (val != NULL && !isInteger(val)) {
lineError("invalid data type for integer value!");
val = buildNumber(0);
}
return(val);
}
value *
parseBit(dataptr)
char **dataptr;
{
value *val;
value *buildNumber();
val = parseValue(dataptr);
if (val != NULL && !isInteger(val) && val->dataType != VAL_BITSTRING){
lineError("invalid data type for bitstring value!");
val = buildNumber(0);
}
return(val);
}
char *
parseString(dataptr)
char **dataptr;
{
value *val;
if (dataptr == NULL || *dataptr == NULL)
return(NULL);
val = parseValue(dataptr);
if (isString(val))
return((char *)(val->value));
else {
lineError("invalid data type for string value!");
return(NULL);
}
}
boolean
getRegionName()
{
return(promptStr("name", regionName, regionName));
}
void
hexDump(buf, len)
byte *buf;
int len;
{
int i;
int line;
clearDisplay();
for (i=0, line=1; i<len; ++i) {
if ((i & 7) == 0) mvprintw(line, 0, "0x%04x: ", i);
printw("0x%02x ", buf[i]);
if ((i & 7) == 7) ++line;
if (line == LINES-1) {
mvprintw(line, 0, "--more--");
refresh();
mygetch();
clearDisplay();
line = 1;
}
}
}
WINDOW *clearWindow;
void
setupTerminal()
{
WINDOW *newwin();
initscr();
noecho();
raw();
nonl();
erase();
refresh();
clearWindow = newwin(0, 0, 0, 0);
}
void
reallyClearScreen()
{
touchwin(clearWindow);
wrefresh(clearWindow);
touchwin(stdscr);
refresh();
}

535
mamelink/griddle/griddle.c Normal file
View file

@ -0,0 +1,535 @@
# line 2 "griddle.y"
#include "griddleDefs.h"
# define Name 257
# define Number 258
# define String 259
# define BitString 260
# define Rawline 261
# define INCLUDE 262
# define DEFINE 263
# define ENDDEFINE 264
# define USE 265
# define AVAID 266
# define BIN15 267
# define BIN31 268
# define BIT 269
# define BYTE 270
# define CHARACTER 271
# define ENTITY 272
# define FATWORD 273
# define OBJID 274
# define REGID 275
# define VARSTRING 276
# define WORDS 277
# define A 278
# define O 279
# define R 280
# define OR 281
# define XOR 282
# define AND 283
# define ADD 284
# define SUB 285
# define MUL 286
# define DIV 287
# define MOD 288
# define UMINUS 289
# define NOT 290
#define yyclearin yychar = -1
#define yyerrok yyerrflag = 0
extern int yychar;
extern short yyerrflag;
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 150
#endif
#ifndef YYSTYPE
#define YYSTYPE int
#endif
YYSTYPE yylval, yyval;
# define YYERRCODE 256
short yyexca[] ={
-1, 1,
0, -1,
-2, 0,
};
# define YYNPROD 60
# define YYLAST 287
short yyact[]={
21, 73, 29, 36, 37, 35, 30, 31, 32, 33,
34, 95, 37, 35, 30, 31, 32, 33, 34, 62,
32, 33, 34, 15, 36, 37, 35, 30, 31, 32,
33, 34, 35, 30, 31, 32, 33, 34, 30, 31,
32, 33, 34, 85, 79, 80, 81, 88, 78, 87,
86, 84, 83, 89, 82, 8, 52, 52, 96, 12,
9, 10, 74, 11, 67, 53, 27, 46, 102, 93,
77, 46, 65, 92, 94, 14, 99, 66, 76, 72,
16, 51, 50, 45, 28, 97, 2, 48, 13, 7,
6, 38, 39, 40, 41, 42, 43, 71, 5, 4,
54, 55, 56, 57, 58, 59, 60, 61, 3, 1,
0, 0, 0, 0, 0, 0, 64, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 63, 47,
0, 69, 0, 47, 70, 0, 0, 0, 0, 0,
0, 0, 90, 75, 0, 0, 91, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 36,
37, 35, 30, 31, 32, 33, 34, 100, 98, 75,
101, 0, 0, 0, 0, 0, 103, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67, 0, 0, 0, 67, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 17, 18, 19,
20, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 24, 25,
26, 0, 0, 0, 0, 22, 0, 0, 0, 0,
23, 36, 37, 35, 30, 31, 32, 33, 34, 36,
37, 35, 30, 31, 32, 33, 34, 44, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 53, 53,
0, 0, 0, 0, 0, 68, 49 };
short yypact[]={
-202,-202,-1000,-1000,-1000,-1000,-1000,-1000, 14,-236,
-40,-191,-1000,-1000, -40,-1000,-257,-1000,-1000,-1000,
-1000, -40, -40, -40, -40, -40, -40, 10,-278, 22,
-40, -40, -40, -40, -40, -40, -40, -40, -22,-1000,
-1000,-278,-278,-278, 6,-1000, -40,-193, 21,-1000,
-1000,-1000,-192, 39,-266,-266,-1000,-1000,-1000,-246,
-270,-251,-1000,-1000,-122, -63,-1000, 20,-1000,-1000,
-1000,-223, -40,-193,-1000,-1000, -40, 13,-1000,-1000,
-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
-30, -67, 41,-278, -40, 18,-1000, -40, 41,-223,
-278, 7, -40, 41 };
short yypgo[]={
0, 109, 86, 108, 99, 98, 90, 89, 69, 87,
82, 81, 70, 73, 83, 72, 77 };
short yyr1[]={
0, 1, 1, 2, 2, 2, 2, 2, 7, 3,
4, 5, 5, 9, 9, 10, 10, 11, 11, 11,
11, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 6, 6, 14, 14, 15, 15, 16,
13, 13, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
short yyr2[]={
0, 1, 2, 1, 1, 1, 1, 1, 1, 3,
2, 5, 4, 1, 2, 1, 2, 3, 6, 5,
8, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 4, 3, 5, 3, 1, 2, 3,
1, 3, 1, 1, 1, 1, 3, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
short yychk[]={
-1000, -1, -2, -3, -4, -5, -6, -7, 257, 262,
263, 265, 261, -2, 61, 259, -8, 257, 258, 259,
260, 40, 285, 290, 278, 279, 280, 257, -8, 259,
284, 285, 286, 287, 288, 283, 281, 282, -8, -8,
-8, -8, -8, -8, 257, -14, 61, 123, -9, 264,
-10, -11, 35, 257, -8, -8, -8, -8, -8, -8,
-8, -8, 41, -14, -8, -15, -16, 257, 264, -10,
-11, 58, 40, 123, 125, -16, 58, -12, 271, 267,
268, 269, 277, 275, 274, 266, 273, 272, 270, 276,
-8, -15, -13, -8, 61, 41, 125, 44, -13, 58,
-8, -12, 61, -13 };
short yydef[]={
0, -2, 1, 3, 4, 5, 6, 7, 0, 0,
0, 0, 8, 2, 0, 10, 0, 42, 43, 44,
45, 0, 0, 0, 0, 0, 0, 0, 9, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 47,
48, 49, 50, 51, 0, 34, 0, 0, 0, 12,
13, 15, 0, 0, 52, 53, 54, 55, 56, 57,
58, 59, 46, 33, 0, 0, 37, 0, 11, 14,
16, 0, 0, 0, 36, 38, 0, 17, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
0, 0, 39, 40, 0, 0, 35, 0, 19, 0,
41, 18, 0, 20 };
#ifndef lint
static char yaccpar_sccsid[] = "@(#)yaccpar 1.1 83/07/20 SMI"; /* from UCB 4.1 83/02/11 */
#endif
#
# define YYFLAG -1000
# define YYERROR goto yyerrlab
# define YYACCEPT return(0)
# define YYABORT return(1)
/* parser for yacc output */
#ifdef YYDEBUG
int yydebug = 0; /* 1 for debugging */
#endif
YYSTYPE yyv[YYMAXDEPTH]; /* where the values are stored */
int yychar = -1; /* current input token number */
int yynerrs = 0; /* number of errors */
short yyerrflag = 0; /* error recovery flag */
yyparse() {
short yys[YYMAXDEPTH];
short yyj, yym;
register YYSTYPE *yypvt;
register short yystate, *yyps, yyn;
register YYSTYPE *yypv;
register short *yyxi;
yystate = 0;
yychar = -1;
yynerrs = 0;
yyerrflag = 0;
yyps= &yys[-1];
yypv= &yyv[-1];
yystack: /* put a state and value onto the stack */
#ifdef YYDEBUG
if( yydebug ) printf( "state %d, char 0%o\n", yystate, yychar );
#endif
if( ++yyps> &yys[YYMAXDEPTH] ) { yyerror( "yacc stack overflow" ); return(1); }
*yyps = yystate;
++yypv;
*yypv = yyval;
yynewstate:
yyn = yypact[yystate];
if( yyn<= YYFLAG ) goto yydefault; /* simple state */
if( yychar<0 ) if( (yychar=yylex())<0 ) yychar=0;
if( (yyn += yychar)<0 || yyn >= YYLAST ) goto yydefault;
if( yychk[ yyn=yyact[ yyn ] ] == yychar ){ /* valid shift */
yychar = -1;
yyval = yylval;
yystate = yyn;
if( yyerrflag > 0 ) --yyerrflag;
goto yystack;
}
yydefault:
/* default state action */
if( (yyn=yydef[yystate]) == -2 ) {
if( yychar<0 ) if( (yychar=yylex())<0 ) yychar = 0;
/* look through exception table */
for( yyxi=yyexca; (*yyxi!= (-1)) || (yyxi[1]!=yystate) ; yyxi += 2 ) ; /* VOID */
while( *(yyxi+=2) >= 0 ){
if( *yyxi == yychar ) break;
}
if( (yyn = yyxi[1]) < 0 ) return(0); /* accept */
}
if( yyn == 0 ){ /* error */
/* error ... attempt to resume parsing */
switch( yyerrflag ){
case 0: /* brand new error */
yyerror( "syntax error" );
yyerrlab:
++yynerrs;
case 1:
case 2: /* incompletely recovered error ... try again */
yyerrflag = 3;
/* find a state where "error" is a legal shift action */
while ( yyps >= yys ) {
yyn = yypact[*yyps] + YYERRCODE;
if( yyn>= 0 && yyn < YYLAST && yychk[yyact[yyn]] == YYERRCODE ){
yystate = yyact[yyn]; /* simulate a shift of "error" */
goto yystack;
}
yyn = yypact[*yyps];
/* the current yyps has no shift onn "error", pop stack */
#ifdef YYDEBUG
if( yydebug ) printf( "error recovery pops state %d, uncovers %d\n", *yyps, yyps[-1] );
#endif
--yyps;
--yypv;
}
/* there is no state on the stack with an error shift ... abort */
yyabort:
return(1);
case 3: /* no shift yet; clobber input char */
#ifdef YYDEBUG
if( yydebug ) printf( "error recovery discards char %d\n", yychar );
#endif
if( yychar == 0 ) goto yyabort; /* don't discard EOF, quit */
yychar = -1;
goto yynewstate; /* try again in the same state */
}
}
/* reduction by production yyn */
#ifdef YYDEBUG
if( yydebug ) printf("reduce %d\n",yyn);
#endif
yyps -= yyr2[yyn];
yypvt = yypv;
yypv -= yyr2[yyn];
yyval = yypv[1];
yym=yyn;
/* consult goto table to find next state */
yyn = yyr1[yyn];
yyj = yypgo[yyn] + *yyps + 1;
if( yyj>=YYLAST || yychk[ yystate = yyact[yyj] ] != -yyn ) yystate = yyact[yypgo[yyn]];
switch(yym){
case 8:
# line 35 "griddle.y"
{
executeRawline(yypvt[-0]);
} break;
case 9:
# line 42 "griddle.y"
{
executeAssignment(yypvt[-2], yypvt[-0]);
} break;
case 10:
# line 49 "griddle.y"
{
executeInclude(yypvt[-0]);
} break;
case 11:
# line 56 "griddle.y"
{
yyval = executeDefine(yypvt[-3], yypvt[-2], yypvt[-1]);
} break;
case 12:
# line 60 "griddle.y"
{
yyval = executeDefine(yypvt[-2], yypvt[-1], NULL);
} break;
case 13:
# line 67 "griddle.y"
{
yyval = buildFieldList(NULL, yypvt[-0]);
} break;
case 14:
# line 71 "griddle.y"
{
yyval = buildFieldList(yypvt[-1], yypvt[-0]);
} break;
case 15:
# line 78 "griddle.y"
{
yyval = yypvt[-0];
} break;
case 16:
# line 82 "griddle.y"
{
yyval = invisifyField(yypvt[-0]);
} break;
case 17:
# line 89 "griddle.y"
{
yyval = buildField(yypvt[-2], buildExpr(NUM_EXPR, 1), yypvt[-0], NULL);
} break;
case 18:
# line 93 "griddle.y"
{
yyval = buildField(yypvt[-5], yypvt[-3], yypvt[-0], NULL);
} break;
case 19:
# line 97 "griddle.y"
{
yyval = buildField(yypvt[-4], buildExpr(NUM_EXPR, 1), yypvt[-2], yypvt[-0]);
} break;
case 20:
# line 101 "griddle.y"
{
yyval = buildField(yypvt[-7], yypvt[-5], yypvt[-2], yypvt[-0]);
} break;
case 21:
# line 107 "griddle.y"
{ yyval = (int) FIELD_CHARACTER; } break;
case 22:
# line 108 "griddle.y"
{ yyval = (int) FIELD_BIN15; } break;
case 23:
# line 109 "griddle.y"
{ yyval = (int) FIELD_BIN31; } break;
case 24:
# line 110 "griddle.y"
{ yyval = (int) FIELD_BIT; } break;
case 25:
# line 111 "griddle.y"
{ yyval = (int) FIELD_WORDS; } break;
case 26:
# line 112 "griddle.y"
{ yyval = (int) FIELD_REGID; } break;
case 27:
# line 113 "griddle.y"
{ yyval = (int) FIELD_OBJID; } break;
case 28:
# line 114 "griddle.y"
{ yyval = (int) FIELD_AVAID; } break;
case 29:
# line 115 "griddle.y"
{ yyval = (int) FIELD_FATWORD; } break;
case 30:
# line 116 "griddle.y"
{ yyval = (int) FIELD_ENTITY; } break;
case 31:
# line 117 "griddle.y"
{ yyval = (int) FIELD_BYTE; } break;
case 32:
# line 118 "griddle.y"
{ yyval = (int) FIELD_VARSTRING; } break;
case 33:
# line 123 "griddle.y"
{
executeUse(yypvt[-2], yypvt[-1], yypvt[-0]);
} break;
case 34:
# line 127 "griddle.y"
{
executeUse(yypvt[-1], NULL, yypvt[-0]);
} break;
case 35:
# line 134 "griddle.y"
{
yyval = buildObjectTail(yypvt[-3], yypvt[-1]);
} break;
case 36:
# line 138 "griddle.y"
{
yyval = buildObjectTail(NULL, yypvt[-1]);
} break;
case 37:
# line 145 "griddle.y"
{
yyval = buildPropertyList(NULL, yypvt[-0]);
} break;
case 38:
# line 149 "griddle.y"
{
yyval = buildPropertyList(yypvt[-1], yypvt[-0]);
} break;
case 39:
# line 156 "griddle.y"
{
yyval = buildProperty(yypvt[-2], yypvt[-0]);
} break;
case 40:
# line 163 "griddle.y"
{
yyval = buildExprList(NULL, yypvt[-0]);
} break;
case 41:
# line 167 "griddle.y"
{
yyval = buildExprList(yypvt[-2], yypvt[-0]);
} break;
case 42:
# line 174 "griddle.y"
{
yyval = buildExpr(ID_EXPR, yypvt[-0]);
} break;
case 43:
# line 178 "griddle.y"
{
yyval = buildExpr(NUM_EXPR, yypvt[-0]);
} break;
case 44:
# line 182 "griddle.y"
{
yyval = buildExpr(STRING_EXPR, yypvt[-0]);
} break;
case 45:
# line 186 "griddle.y"
{
yyval = buildExpr(BITSTRING_EXPR, yypvt[-0]);
} break;
case 46:
# line 190 "griddle.y"
{
yyval = buildExpr(EXPR_EXPR, yypvt[-1]);
} break;
case 47:
# line 194 "griddle.y"
{
yyval = buildExpr(UNOP_EXPR, UMINUS, yypvt[-0]);
} break;
case 48:
# line 198 "griddle.y"
{
yyval = buildExpr(UNOP_EXPR, NOT, yypvt[-0]);
} break;
case 49:
# line 202 "griddle.y"
{
yyval = buildExpr(UNOP_EXPR, A, yypvt[-0]);
} break;
case 50:
# line 206 "griddle.y"
{
yyval = buildExpr(UNOP_EXPR, O, yypvt[-0]);
} break;
case 51:
# line 210 "griddle.y"
{
yyval = buildExpr(UNOP_EXPR, R, yypvt[-0]);
} break;
case 52:
# line 214 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], ADD, yypvt[-0]);
} break;
case 53:
# line 218 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], SUB, yypvt[-0]);
} break;
case 54:
# line 222 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], MUL, yypvt[-0]);
} break;
case 55:
# line 226 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], DIV, yypvt[-0]);
} break;
case 56:
# line 230 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], MOD, yypvt[-0]);
} break;
case 57:
# line 234 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], AND, yypvt[-0]);
} break;
case 58:
# line 238 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], OR, yypvt[-0]);
} break;
case 59:
# line 242 "griddle.y"
{
yyval = buildExpr(BIN_EXPR, yypvt[-2], XOR, yypvt[-0]);
} break;
}
goto yystack; /* stack new state and value */
}

245
mamelink/griddle/griddle.y Normal file
View file

@ -0,0 +1,245 @@
%{
#include "griddleDefs.h"
%}
%token Name Number String BitString Rawline
%token INCLUDE DEFINE ENDDEFINE USE
%token AVAID BIN15 BIN31 BIT BYTE CHARACTER ENTITY FATWORD OBJID REGID
%token VARSTRING WORDS
%left A O R
%left OR
%left XOR
%left AND
%left ADD SUB
%left MUL DIV MOD
%right UMINUS NOT
%%
statementList:
statement /* na */
| statementList statement /* na */
;
statement:
assignmentStatement /* na */
| includeStatement /* na */
| defineStatement /* na */
| objectUseStatement /* na */
| rawStatement /* na */
;
rawStatement:
Rawline
{
executeRawline($1);
}
;
assignmentStatement:
Name '=' expr
{
executeAssignment($1, $3);
}
;
includeStatement:
INCLUDE String
{
executeInclude($2);
}
;
defineStatement:
DEFINE expr String fieldList ENDDEFINE
{
$$ = executeDefine($2, $3, $4);
}
| DEFINE expr String ENDDEFINE
{
$$ = executeDefine($2, $3, NULL);
}
;
fieldList:
field
{
$$ = buildFieldList(NULL, $1);
}
| fieldList field
{
$$ = buildFieldList($1, $2);
}
;
field:
basicField
{
$$ = $1;
}
| '#' basicField
{
$$ = invisifyField($2);
}
;
basicField:
Name ':' fieldType
{
$$ = buildField($1, buildExpr(NUM_EXPR, 1), $3, NULL);
}
| Name '(' expr ')' ':' fieldType
{
$$ = buildField($1, $3, $6, NULL);
}
| Name ':' fieldType '=' exprList
{
$$ = buildField($1, buildExpr(NUM_EXPR, 1), $3, $5);
}
| Name '(' expr ')' ':' fieldType '=' exprList
{
$$ = buildField($1, $3, $6, $8);
}
;
fieldType:
CHARACTER { $$ = (int) FIELD_CHARACTER; }
| BIN15 { $$ = (int) FIELD_BIN15; }
| BIN31 { $$ = (int) FIELD_BIN31; }
| BIT { $$ = (int) FIELD_BIT; }
| WORDS { $$ = (int) FIELD_WORDS; }
| REGID { $$ = (int) FIELD_REGID; }
| OBJID { $$ = (int) FIELD_OBJID; }
| AVAID { $$ = (int) FIELD_AVAID; }
| FATWORD { $$ = (int) FIELD_FATWORD; }
| ENTITY { $$ = (int) FIELD_ENTITY; }
| BYTE { $$ = (int) FIELD_BYTE; }
| VARSTRING { $$ = (int) FIELD_VARSTRING; }
;
objectUseStatement:
USE Name Name objectTail
{
executeUse($2, $3, $4);
}
| USE Name objectTail
{
executeUse($2, NULL, $3);
}
;
objectTail:
'=' expr '{' properties '}'
{
$$ = buildObjectTail($2, $4);
}
| '{' properties '}'
{
$$ = buildObjectTail(NULL, $2);
}
;
properties:
property
{
$$ = buildPropertyList(NULL, $1);
}
| properties property
{
$$ = buildPropertyList($1, $2);
}
;
property:
Name ':' exprList
{
$$ = buildProperty($1, $3);
}
;
exprList:
expr
{
$$ = buildExprList(NULL, $1);
}
| exprList ',' expr
{
$$ = buildExprList($1, $3);
}
;
expr:
Name
{
$$ = buildExpr(ID_EXPR, $1);
}
| Number
{
$$ = buildExpr(NUM_EXPR, $1);
}
| String
{
$$ = buildExpr(STRING_EXPR, $1);
}
| BitString
{
$$ = buildExpr(BITSTRING_EXPR, $1);
}
| '(' expr ')'
{
$$ = buildExpr(EXPR_EXPR, $2);
}
| SUB expr %prec UMINUS
{
$$ = buildExpr(UNOP_EXPR, UMINUS, $2);
}
| NOT expr
{
$$ = buildExpr(UNOP_EXPR, NOT, $2);
}
| A expr
{
$$ = buildExpr(UNOP_EXPR, A, $2);
}
| O expr
{
$$ = buildExpr(UNOP_EXPR, O, $2);
}
| R expr
{
$$ = buildExpr(UNOP_EXPR, R, $2);
}
| expr ADD expr
{
$$ = buildExpr(BIN_EXPR, $1, ADD, $3);
}
| expr SUB expr
{
$$ = buildExpr(BIN_EXPR, $1, SUB, $3);
}
| expr MUL expr
{
$$ = buildExpr(BIN_EXPR, $1, MUL, $3);
}
| expr DIV expr
{
$$ = buildExpr(BIN_EXPR, $1, DIV, $3);
}
| expr MOD expr
{
$$ = buildExpr(BIN_EXPR, $1, MOD, $3);
}
| expr AND expr
{
$$ = buildExpr(BIN_EXPR, $1, AND, $3);
}
| expr OR expr
{
$$ = buildExpr(BIN_EXPR, $1, OR, $3);
}
| expr XOR expr
{
$$ = buildExpr(BIN_EXPR, $1, XOR, $3);
}
;

View file

@ -0,0 +1,298 @@
#include <stdio.h>
#define Case break; case
#define Default break; default
typedef unsigned char byte;
typedef unsigned short word;
typedef int boolean;
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
#define CLASS_REGION 0
#define CLASS_DOOR 23
#define CLASS_BUILDING 132
#define CLASS_FLAT 93
#define CLASS_TRAP 87
#define CLASS_SUPER_TRAP 92
/* These constants ought to be extracted from the defines, but they're not.
Anytime the definition for the base object changes, these may have to be
changed also. */
#define IDENT_OFFSET 0
#define IDENT_OFFSET_OBJ IDENT_OFFSET
#define CONTAINER_OFFSET_OBJ 8
#define CONTAINER_TYPE_OFFSET_OBJ 12
#define X_OFFSET_OBJ 14
#define Y_OFFSET_OBJ 16
#define STYLE_OFFSET_OBJ 18
#define GRSTATE_OFFSET_OBJ 20
#define ORIENT_OFFSET_OBJ 22
#define TYPE_OFFSET_FLAT 42
#define HEIGHT_OFFSET_TRAP 52
#define CONNECTION_OFFSET_DOOR 48
#define CONTAINER_OFFSET_AVA 8
#define X_OFFSET_AVA 12
#define Y_OFFSET_AVA 14
#define GRSTATE_OFFSET_AVA 20
#define ORIENT_OFFSET_AVA 28
#define STYLE_OFFSET_AVA 32
#define PROP_BASE_AVA 80
#define AVATAR_PROPERTY_COUNT 6
#define IDENT_OFFSET_REG IDENT_OFFSET
#define LIGHTLEVEL_OFFSET_REG 8
#define DEPTH_OFFSET_REG 10
#define EAST_OFFSET_REG 12
#define WEST_OFFSET_REG 16
#define NORTH_OFFSET_REG 20
#define SOUTH_OFFSET_REG 24
#define CLASSGROUP_OFFSET_REG 28
#define ORIENT_OFFSET_REG 30
typedef enum {
VARIABLE_SYM, MACRO_SYM, OBJECT_SYM, NON_SYM, CLASS_SYM
} symbolType;
typedef enum {
ID_EXPR, NUM_EXPR, EXPR_EXPR, UNOP_EXPR, BIN_EXPR, STRING_EXPR,
BITSTRING_EXPR
} exprType;
typedef enum {
FIELD_AVAID, FIELD_BIN15, FIELD_BIN31, FIELD_BIT, FIELD_BYTE,
FIELD_CHARACTER, FIELD_ENTITY, FIELD_FATWORD, FIELD_OBJID,
FIELD_REGID, FIELD_WORDS, FIELD_VARSTRING
} fieldType;
typedef enum {
VAL_UNDEFINED, VAL_INTEGER, VAL_STRING, VAL_AVATAR, VAL_OBJECT,
VAL_REGION, VAL_BITSTRING
} valueType;
typedef struct {
exprType type;
int part1;
int part2;
int part3;
} expression;
typedef struct exprListStruct {
expression *expr;
struct exprListStruct *nextExpr;
} exprList;
typedef struct stringListStruct {
char *string;
struct stringListStruct *nextString;
} stringList;
typedef struct {
int value;
valueType dataType;
} value;
typedef struct valueListStruct {
value *value;
struct valueListStruct *nextValue;
} valueList;
typedef struct genericListStruct {
int *thing;
struct genericListStruct *next;
} genericList;
typedef struct {
int *thing;
genericList *next;
genericList *last;
} genericListHead;
typedef struct {
int class;
byte *stateVector;
} object;
typedef struct {
int class;
int id;
} objectStub;
typedef struct objectListStruct {
object *object;
struct objectListStruct *nextObject;
} objectList;
typedef struct symbolStruct {
char *name;
int codeNumber;
symbolType type;
union {
value *value;
expression *expr;
objectStub *object;
int class;
} def;
struct symbolStruct *next;
} symbol;
typedef struct {
symbol *fieldName;
exprList *data;
} property;
typedef struct propertyListStruct {
property *property;
struct propertyListStruct *nextProp;
} propertyList;
typedef struct {
expression *idExpr;
propertyList *properties;
} objectTail;
typedef struct {
symbol *name;
int dimension;
fieldType type;
int offset;
boolean invisible;
exprList *initValues;
} field;
typedef struct fieldListStruct {
field *field;
struct fieldListStruct *nextField;
} fieldList;
typedef struct {
fieldList *fields;
int size;
symbol *className;
byte *prototype;
} classDescriptor;
#define HASH_MAX 512
symbol *symbolTable[HASH_MAX];
char *malloc();
#define typeAlloc(t) ((t *)malloc(sizeof(t)))
#define typeAllocMulti(t,n) ((t *)malloc((n)*sizeof(t)))
#define byteAlloc(n) ((byte *)malloc(n))
typedef struct fileListStruct {
FILE *fyle;
struct fileListStruct *next;
int saveLine;
char *saveName;
} fileList;
fileList *inputStack;
fileList *bottomOfInputStack;
FILE *currentInput;
int currentLineNumber;
char *currentFileName;
int globalIdCounter;
int globalIdAdjustment;
int objectBase;
FILE *griFile;
FILE *rawFile;
FILE *cvFile;
FILE *indirFile;
int indirectPass;
stringList *cvInput;
char *classFileName;
boolean debug;
boolean testMode;
boolean assignRelativeIds;
int useStartCount;
boolean insideDefinition;
boolean announceIncludes;
#define MAXCLASS 256
classDescriptor *classDefs[MAXCLASS+1];
#define MAXNOID 256
int objectCount;
int rawCount;
object *noidArray[MAXNOID];
object *altNoidArray[MAXNOID];
boolean noidAlive[MAXNOID];
boolean fredModeLexing;
char *fredLexString;
boolean promptDefault;
char pathname[80];
char regionName[80];
byte cv[512];
int cvLength;
int displayNoid;
object *undeleteBuffer;
int previousClass;
/* C64 locations */
#define KEYBOARD_OVERRIDE (word)0x0010
#define KEYBOARD_KEYPRESS (word)0x0011
#define TOUCH_SLOT (word)0x0012
#define TOUCHED_OBJECT (word)0x0013
#define TOUCHED_ADDRES (word)0x0014
#define CREATE_DATA_SLOT (word)0x0801
#define CV_SIZE_SLOT (word)0xea07
#define CV_DATA_SLOT (word)0xea09
/* C64 override commands */
#define CMD_CREATE 1
#define CMD_SAVE_CV 2
#define CMD_LOAD_CV 3
#define TRAP_EDIT_KEY 141
#define UPPER_LEFT_KEY 18
#define UPPER_RIGHT_KEY 19
#define LOWER_LEFT_KEY 20
#define LOWER_RIGHT_KEY 21
#define DEL '\177'
#define ESCAPE '\033'
#define ctrl(c) ('c'&0x1F)
#define CTRL_C ctrl(C)
#define CTRL_U ctrl(U)
#define mvclrtobot(y,x) { move((y),(x)); clrtobot(); }
#define mvclrtoeol(y,x) { move((y),(x)); clrtoeol(); }
#define clearDisplay() mvclrtobot(1,0)
#define nextlc(line,col,dy) {++(line);if((line)>LINES-1){\
(line)=1;(col)+=(dy);}}
#define myCont() if (!testMode) Cont();
#define fredModeLexingOn() fredModeLexing = TRUE;
#define fredModeLexingOff() { strcpy(fredLexString, "\n"); \
yylex(); fredModeLexing = FALSE; }
#define isAvatar(n) (noidArray[(n)]->class == 0)
typedef struct {
int west;
int north;
int east;
int south;
int rot;
int *multi;
int multiCount;
int region;
} indirectEntry;
indirectEntry *indirTable;
int indirCount;
char indirName[80];
int indirArgc;
char *indirArgv[50];
int indirRegion;
boolean sortObjects;

344
mamelink/griddle/indir.c Normal file
View file

@ -0,0 +1,344 @@
#include "griddleDefs.h"
#define MAXLINE 500
char *
scan_number(args, resultptr)
char *args;
int *resultptr;
{
*resultptr = 0;
while ('0' <= *args && *args <= '9')
*resultptr = *resultptr * 10 + *args++ - '0';
return(args);
}
char *
scan_connections(args, multiOK, resultptr, multiptr, countptr)
char *args;
boolean multiOK;
int *resultptr;
int **multiptr;
int *countptr;
{
int multiArray[20];
int multiCount;
int dummy;
int i;
if (*args == '(') {
*args = ' ';
multiCount = 0;
while (*args == ' ')
args = scan_number(args+1, &multiArray[multiCount++]);
*resultptr = multiArray[multiCount - 1];
if (multiOK) {
*multiptr = typeAllocMulti(int, multiCount);
for (i=0; i<multiCount; ++i)
(*multiptr)[i] = multiArray[i];
*countptr = multiCount;
}
return(args + 1);
} else {
args = scan_number(args, resultptr);
if (multiOK) {
*multiptr = typeAlloc(int);
(*multiptr)[0] = *resultptr;
*countptr = 1;
}
return(args);
}
}
void
scanIndirectFilePass1()
{
char line[MAXLINE];
char *argptr;
char *iptr;
char *index();
char *skipArg();
boolean stringFlag;
int rot;
indirRegion = 0;
indirectPass = 1;
while (fgets(line, MAXLINE, indirFile) != NULL) {
iptr = indirName;
argptr = line;
while (*argptr != ' ')
*iptr++ = *argptr++;
*iptr = '\0';
argptr += 3;
argptr = scan_number(argptr, &rot);
indirTable[indirRegion].rot = rot;
argptr = scan_connections(argptr + 3, rot == 0,
&indirTable[indirRegion].west,
&indirTable[indirRegion].multi,
&indirTable[indirRegion].multiCount);
argptr = scan_connections(argptr + 3, rot == 1,
&indirTable[indirRegion].north,
&indirTable[indirRegion].multi,
&indirTable[indirRegion].multiCount);
argptr = scan_connections(argptr + 3, rot == 2,
&indirTable[indirRegion].east,
&indirTable[indirRegion].multi,
&indirTable[indirRegion].multiCount);
argptr = scan_connections(argptr + 3, rot == 3,
&indirTable[indirRegion].south,
&indirTable[indirRegion].multi,
&indirTable[indirRegion].multiCount);
argptr = index(argptr, '/') + 1;
indirArgc = 0;
while (argptr != NULL && *argptr != '\0' && *argptr != '\n') {
while (*argptr == ' ')
++argptr;
if (stringFlag = (*argptr == '"'))
++argptr;
indirArgv[indirArgc++] = argptr;
argptr = skipArg(argptr, stringFlag);
if (argptr != NULL)
*argptr++ = '\0';
}
queueInputFile(strcat(indirName, ".gri"));
if (!openFirstFile(FALSE)) {
error("can't continue from here!");
exit(1);
}
globalIdAdjustment = globalIdCounter - 1001;
yyparse();
++indirRegion;
}
}
void
scanIndirectFilePass2()
{
char line[MAXLINE];
char *argptr;
char *iptr;
char *index();
char *skipArg();
boolean stringFlag;
int dummy;
indirRegion = 0;
indirectPass = 2;
globalIdCounter = 1001;
while (fgets(line, MAXLINE, indirFile) != NULL) {
iptr = indirName;
argptr = line;
while (*argptr != ' ')
*iptr++ = *argptr++;
*iptr = '\0';
argptr += 3;
argptr = scan_number(argptr, &dummy);
argptr = scan_connections(argptr, FALSE, &dummy, NULL,&dummy);
argptr = scan_connections(argptr, FALSE, &dummy, NULL,&dummy);
argptr = scan_connections(argptr, FALSE, &dummy, NULL,&dummy);
argptr = scan_connections(argptr, FALSE, &dummy, NULL,&dummy);
argptr = index(argptr, '/') + 1;
indirArgc = 0;
while (argptr != NULL && *argptr != '\0' && *argptr != '\n') {
while (*argptr == ' ')
++argptr;
if (stringFlag = (*argptr == '"'))
++argptr;
indirArgv[indirArgc++] = argptr;
argptr = skipArg(argptr, stringFlag);
if (argptr != NULL)
*argptr++ = '\0';
}
queueInputFile(strcat(indirName, ".gri"));
if (!openFirstFile(FALSE)) {
error("can't continue from here!");
exit(1);
}
globalIdAdjustment = globalIdCounter - 1001;
yyparse();
++indirRegion;
}
}
void
replaceIndirectArgs(obj, index)
object *obj;
int index;
{
int i;
byte *buf;
int reg;
reg = indirRegion - 1;
buf = obj->stateVector;
if (obj->class == CLASS_REGION) {
fillLong(buf, WEST_OFFSET_REG,
getIdent(indirTable[reg].west));
fillLong(buf, NORTH_OFFSET_REG,
getIdent(indirTable[reg].north));
fillLong(buf, EAST_OFFSET_REG,
getIdent(indirTable[reg].east));
fillLong(buf, SOUTH_OFFSET_REG,
getIdent(indirTable[reg].south));
fillWord(buf, ORIENT_OFFSET_REG, indirTable[reg].rot);
} else if (obj->class == CLASS_DOOR || obj->class == CLASS_BUILDING) {
if (index <= indirTable[reg].multiCount) {
fillLong(buf, CONNECTION_OFFSET_DOOR,
getIdent(indirTable[reg].multi[index-1]));
if (index == indirTable[reg].multiCount) {
buf = altNoidArray[0]->stateVector;
switch (indirTable[reg].rot) {
Case 0: fillLong(buf, WEST_OFFSET_REG, -1);
Case 1: fillLong(buf, NORTH_OFFSET_REG, -1);
Case 2: fillLong(buf, EAST_OFFSET_REG, -1);
Case 3: fillLong(buf, SOUTH_OFFSET_REG, -1);
}
}
} else
fillLong(buf, CONNECTION_OFFSET_DOOR, -1);
}
}
int
getIdent(num)
int num;
{
if (num == 0)
return(-1);
else
return(indirTable[num - 1].region);
}
char *
skipArg(line, stringFlag)
char *line;
boolean stringFlag;
{
char *index();
if (stringFlag) {
while ((line = index(line+1, '"')) != NULL)
if (*(line - 1) != '\\')
return(line);
} else for (; *line != ' ' && *line != '\n' && *line != '\0'; ++line)
;
return(line);
}
void
replaceParams(line)
char *line;
{
char scratchBuf[500];
char *outptr;
char *argptr;
char *inptr;
int offset;
enum { LEFT, CENTER, RIGHT, NONE } format;
int width;
int i;
int len;
outptr = scratchBuf;
inptr = line;
while (*inptr != '\0') {
if (*inptr == '`') {
offset = scanNumber(&inptr);
if (*inptr == 'l' || *inptr == 'r' || *inptr == 'c') {
format = (*inptr == 'l') ? LEFT :
((*inptr == 'r') ? RIGHT : CENTER);
width = scanNumber(&inptr) + 1;
} else
format = NONE;
if (indirArgc < offset)
error("parameter offset %d out of range\n",
offset);
else if (offset == -1)
*outptr++ = '`';
else {
len = strlen(indirArgv[offset]);
switch (format) {
Case NONE:
for (argptr=indirArgv[offset];
*argptr != '\0'; )
*outptr++ = *argptr++;
Case LEFT:
for (argptr=indirArgv[offset], i=0;
*argptr != '\0' &&
i < width; ++i)
*outptr++ = *argptr++;
for (; i < width; ++i)
*outptr++ = ' ';
Case RIGHT:
argptr = indirArgv[offset];
if (len < width)
for (i=width-len; i > 0; --i)
*outptr++ = ' ';
else
argptr += len - width;
while (*argptr != '\0')
*outptr++ = *argptr++;
Case CENTER:
argptr = indirArgv[offset];
i = 0;
if (len < width)
for (; i < (width-len)/2; ++i)
*outptr++ = ' ';
else
argptr += (len - width) / 2;
for (; i<width && *argptr!='\0'; ++i)
*outptr++ = *argptr++;
for (; i<width; ++i)
*outptr++ = ' ';
}
}
} else {
if (*inptr == '\\') {
++inptr;
if (*inptr != '`')
*outptr++ = '\\';
}
*outptr++ = *inptr++;
}
}
*outptr = '\0';
strcpy(line, scratchBuf);
}
int
scanNumber(lineptr)
char **lineptr;
{
int result;
char *line;
result = 0;
line = *lineptr + 1;
while ('0' <= *line && *line <= '9')
result = result * 10 + *line++ - '0';
*lineptr = line;
return(result - 1);
}
void
indirectGriddle()
{
char line[80];
int i;
fgets(line, 80, indirFile);
sscanf(line, "%d", &indirCount);
indirTable = typeAllocMulti(indirectEntry, indirCount);
scanIndirectFilePass1();
rewind(indirFile);
flushNoidArray();
fgets(line, 80, indirFile);
scanIndirectFilePass2();
flushNoidArray();
}

594
mamelink/griddle/lexer.c Normal file
View file

@ -0,0 +1,594 @@
#include "griddleDefs.h"
#include "y.tab.h"
extern int yylval;
int yydebug;
typedef enum {
C_SKIP, C_ZERO, C_DIG, C_ALPH, C_QUOTE, C_APOSTROPHE, C_LIT, C_SLASH,
C_NL
} charType;
static charType char_type[128] = {
/* EOF Soh Stx Etx Eot Enq Ack Bell */
/* 0 */ C_LIT, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP,
/* Backsp HTab Newline VTab Newpage Return So Si */
/* 8 */ C_SKIP, C_SKIP, C_NL, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP,
/* Dle Dc1 Dc2 Dc3 Dc4 Nak Syn Etb */
/* 16 */ C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP,
/* Can Em Sub Escape Fs Gs Rs Us */
/* 24 */ C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP, C_SKIP,
/* Space ! " # $ % & ' */
/* 32 */ C_SKIP, C_SKIP, C_QUOTE, C_LIT, C_SKIP, C_LIT, C_LIT, C_APOSTROPHE,
/* ( ) * + , - . / */
/* 40 */ C_LIT, C_LIT, C_LIT, C_LIT, C_LIT, C_LIT, C_SKIP, C_SLASH,
/* 0 1 2 3 4 5 6 7 */
/* 48 */ C_DIG, C_DIG, C_DIG, C_DIG, C_DIG, C_DIG, C_DIG, C_DIG,
/* 8 9 : ; < = > ? */
/* 56 */ C_DIG, C_DIG, C_LIT, C_SKIP, C_SKIP, C_LIT, C_SKIP, C_SKIP,
/* @ A B C D E F G */
/* 64 */ C_SKIP, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* H I J J L M N O */
/* 72 */ C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* P Q R S T U V W */
/* 80 */ C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* X Y Z [ \ ] ^ _ */
/* 88 */ C_ALPH, C_ALPH, C_ALPH, C_SKIP, C_SKIP, C_SKIP, C_LIT, C_ALPH,
/* ` a b c d e f g */
/* 96 */ C_SKIP, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* h i j k l m n o */
/*104 */ C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* p q r s t u v w */
/*112 */ C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH, C_ALPH,
/* x y z { | } ` Del */
/*120 */ C_ALPH, C_ALPH, C_ALPH, C_LIT, C_LIT, C_LIT, C_SKIP, C_SKIP
};
static int litCode[128] = {
/* EOF Soh Stx Etx Eot Enq Ack Bell */
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* Backsp HTab Newline VTab Newpage Return So Si */
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* Dle Dc1 Dc2 Dc3 Dc4 Nak Syn Etb */
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* Can Em Sub Escape Fs Gs Rs Us */
/* 24 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* Space ! " # $ % & ' */
/* 32 */ 0, 0, 0, '#', 0, MOD, AND, 0,
/* ( ) * + , - . / */
/* 40 */ '(', ')', MUL, ADD, ',', SUB, 0, 0,
/* 0 1 2 3 4 5 6 7 */
/* 48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 8 9 : ; < = > ? */
/* 56 */ 0, 0, ':', 0, 0, '=', 0, 0,
/* @ A B C D E F G */
/* 64 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* H I J J L M N O */
/* 72 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* P Q R S T U V W */
/* 80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* X Y Z [ \ ] ^ _ */
/* 88 */ 0, 0, 0, 0, 0, 0, XOR, 0,
/* ` a b c d e f g */
/* 96 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* h i j k l m n o */
/*104 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* p q r s t u v w */
/*112 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* x y z { | } ` Del */
/*120 */ 0, 0, 0, '{', OR, '}', 0, 0
};
static boolean newline = TRUE;
static boolean oldnewline;
char yytext[256];
yylex()
{
char c;
for (;;) {
c = input();
oldnewline = newline;
newline = FALSE;
switch (char_type[c]) {
Case C_SLASH:
if (oldnewline) {
parseRawline();
return(Rawline);
}
c = input();
if (c == '*')
comment();
else {
unput(c);
return(DIV);
}
Case C_ZERO:
c = input();
if (c == 'x' || c == 'X')
parseNumber(16, input());
else if (c == 'b' || c == 'B')
parseNumber(2, input());
else if (c == 'q' || c == 'Q')
parseNumber(4, input());
else
parseNumber(8, c);
return(Number);
Case C_DIG:
parseNumber(10, c);
return(Number);
Case C_ALPH:
parseName(c);
if ((yylval = matchKeyword(yytext)) != 0)
return(yylval);
yylval = lookupSymbol(yytext);
/* if (debug)
printf("lexer: Name '%s'\n", yytext);*/
return(Name);
Case C_QUOTE:
lexString('"');
string();
return(String);
Case C_APOSTROPHE:
lexString('\'');
c = input();
if (c == 'b' || c == 'B') {
bitString();
return(BitString);
} else {
string();
unput(c);
return(String);
}
Case C_LIT:
return(litCode[c]);
Case C_NL:
newline = TRUE;
}
}
}
parseName(c)
char c;
{
char *cptr;
cptr = yytext;
while (char_type[c] == C_DIG || char_type[c] == C_ALPH) {
*cptr++ = c;
c = input();
}
*cptr = '\0';
unput(c);
}
comment()
{
char c, c1;
loop:
while ((c = input()) != '*' && c != 0)
;
if ((c1 = input()) != '/' && c != 0)
{
unput(c1);
goto loop;
}
}
parseNumber(base, c)
int base;
char c;
{
yylval = 0;
while (isDigit(c, base)) {
yylval = yylval * base + digitValue(c);
c = input();
}
unput(c);
/* if (debug)
printf("lexer: Number (%d) = %d\n", base, yylval);*/
}
skipToEol()
{
char c;
while ((c = input()) != '\n' && c != 0)
;
unput(c);
}
int
getHexByte()
{
char c1, c2;
while ((c1 = input()) == '\\' || c1 == ' ')
if (c1 == '\\')
c1 = input();
if (c1 == '\n' || c1 == 0) {
unput(c1);
return(-1);
} else if (!isDigit(c1, 16)) {
error("illegal hex digit '%c'\n", c1);
skipToEol();
return(-2);
}
c2 = input();
if (c2 == '\n' || c2 == 0) {
unput(c1);
error("bad hex digit parity\n");
return(-2);
} else if (!isDigit(c2, 16)) {
error("illegal hex digit '%c'\n", c2);
skipToEol();
return(-2);
}
return(digitValue(c1) * 16 + digitValue(c2));
}
getHexString(buf, size)
byte *buf;
int size;
{
int i;
int aByte;
char c;
for (i=0; i<size; ++i) {
aByte = getHexByte();
if (aByte < 0) break;
buf[i] = aByte;
}
if (aByte < 0) {
error("raw line short\n");
for (; i<size; ++i)
buf[i] = 0;
} else if (getHexByte() >= 0) {
error("raw line long\n");
skipToEol();
}
}
parseRawline()
{
object *result;
char hexString[1500];
int length;
result = typeAlloc(object);
parseNumber(10, input());
result->class = yylval;
result->stateVector = (byte *)malloc(classDefs[yylval+1]->size);
getHexString(result->stateVector, classDefs[yylval+1]->size);
yylval = (int)result;
/* if (debug)
printf("lexer: Rawline class=%d addr=%x\n", result->class, result);*/
}
boolean
isDigit(c, base)
char c;
int base;
{
if (base <= 10)
return('0' <= c && c <= '0' - 1 + base);
else
return( ('0' <= c && c <= '9') ||
('a' <= c && c <= 'f') ||
('A' <= c && c <= 'F') );
}
int
digitValue(c)
char c;
{
if ('0'<=c && c<='9')
return(c - '0');
else if ('a'<=c && c<='f')
return(c - 'a' + 10);
else
return(c - 'A' + 10);
}
#define FORMFEED 12
#define HALF_SPACE 128
#define DOUBLE_SPACE 143
#define INC_WIDTH 129
#define DEC_WIDTH 130
#define INC_HEIGHT 131
#define DEC_HEIGHT 132
#define HALF_SIZE 133
#define CHAR_RETURN 134
#define DN_HALF_CHAR 139
#define INVERSE 140
#define CURSOR_RIGHT 135
#define CURSOR_LEFT 136
#define CURSOR_UP 137
#define CURSOR_DOWN 138
static struct {
char inChar;
char outChar;
} escapes[] = {
'n', '\n', 't', '\t', 'b', '\b',
'r', '\r', 'f', FORMFEED, 'e', ESCAPE,
'\\', '\\', '\'', '\'', '"', '"',
' ', HALF_SPACE, '#', DOUBLE_SPACE, '+', INC_WIDTH,
'-', DEC_WIDTH, '(', INC_HEIGHT, ')', DEC_HEIGHT,
'h', HALF_SIZE, 'd', DN_HALF_CHAR, 'i', INVERSE,
'>', CURSOR_LEFT, '<', CURSOR_RIGHT, '^', CURSOR_UP,
'v', CURSOR_DOWN, 'R', CHAR_RETURN, '\0', 0
};
char
unescape()
{
int i;
char result;
char c;
c = input();
if (c == '^') {
result = input() & 0x3F;
} else if (c == 'x') {
result = 0;
for (i=0; i<2; ++i) {
c = input();
if (isDigit(c, 16))
result = result * 16 + digitValue(c);
else
unput(c);
}
} else if ('0' <= c && c <= '7') {
result = digitValue(c);
for (i=0; i<2; ++i) {
c = input();
if ('0' <= c && c <= '7')
result = result * 8 + digitValue(c);
else
unput(c);
}
} else for (i=0; escapes[i].inChar!='\0'; ++i) {
result = c;
if (c == escapes[i].inChar) {
result = escapes[i].outChar;
break;
}
}
return(result);
}
char *
escape(c)
char c;
{
int i;
static char result[5];
if (' ' <= c && c <= '~')
sprintf(result, "%c", c);
else if (c == 0)
sprintf(result, "\\0");
else {
sprintf(result, "\\%o", (byte)c);
for (i=0; escapes[i].outChar!='\0'; ++i)
if (c == escapes[i].outChar) {
sprintf(result, "\\%c", escapes[i].inChar);
break;
}
}
return(result);
}
lexString(end)
char end;
{
char *str;
char c;
str = yytext;
for (c = input(); c != end; c = input())
if (c == '\\')
*str++ = unescape();
else
*str++ = c;
*str = '\0';
}
string()
{
int len;
char *str;
char *inptr;
str = malloc(strlen(yytext) + 1);
yylval = (int)str;
strcpy(str, yytext);
/* if (debug)
printf("lexer: String '%s'\n", yylval);*/
}
bitString()
{
int len;
byte *str;
int i;
len = strlen(yytext);
str = (byte *)malloc(((len+7) >> 3) + 1);
yylval = (int)str;
*str++ = len;
for (i=0; i<((len+7) >> 3); ++i)
str[i] = 0;
for (i=0; i<len; ++i)
str[i>>3] |= (yytext[i] == '0' ? 0 : 1) << (7 - (i&7));
/* if (debug)
printf("lexer: BitString, length=%d\n", len);*/
}
static char lineBuf[500] = "";
static char *inptr = lineBuf;
static char ungot;
static boolean ungetFlag = FALSE;
purgeUnget()
{
ungetFlag = FALSE;
}
input()
{
char result;
fileList *oldInputStack;
if (fredModeLexing)
return(*fredLexString++);
else if (ungetFlag) {
ungetFlag = FALSE;
return(ungot);
}
result = *inptr++;
if (result == '\0') {
if (fgets(lineBuf, 500, currentInput) == NULL) {
result = EOF;
--inptr;
} else {
inptr = lineBuf;
#ifndef FRED
if (indirFile != NULL)
replaceParams(lineBuf);
#endif
result = *inptr++;
}
}
if (result == '\n')
++currentLineNumber;
else if (result == EOF) {
oldInputStack = inputStack;
inputStack = inputStack->next;
free(oldInputStack);
ungetFlag = FALSE;
if (announceIncludes) {
fprintf(stderr, "<-\n");
fflush(stderr);
}
fclose(currentInput);
if (inputStack == NULL) {
/* if (debug) printf("in(EOF)\n");*/
return(0);
} else {
currentInput = inputStack->fyle;
currentFileName = inputStack->saveName;
if (currentInput == NULL) {
if (strcmp(inputStack->saveName, "-") == 0) {
currentInput = stdin;
currentFileName = "<standard input>";
} else if ((currentInput = fopen(inputStack->
saveName, "r")) == NULL) {
systemError("can't open input file %s\n",
inputStack->saveName);
}
inputStack->fyle = currentInput;
}
currentLineNumber = inputStack->saveLine;
return(input());
}
}
/* if (debug) printf("in(%c)\n", result);*/
return(result);
}
unput(c)
char c;
{
/* if (debug) printf("un(%c)\n", c);*/
if (fredModeLexing)
--fredLexString;
else {
ungetFlag = TRUE;
ungot = c;
}
}
static struct {
char *string;
int token;
int minlength;
} keywords[] = {
/*0*/ "a", A, 0, "ava/id", AVAID, 0,
/*2*/ "bin15", BIN15, 0, "bin31", BIN31, 0,
/*4*/ "bit", BIT, 0, "byte", BYTE, 0,
/*6*/ "ch/aracter", CHARACTER, 0, "de/fine", DEFINE, 0,
/*8*/ "endd/efine", ENDDEFINE, 0, "ent/ity", ENTITY, 0,
/*10*/ "fat/word", FATWORD, 0, "include", INCLUDE, 0,
/*12*/ "int/eger", BIN15, 0, "lo/ng", BIN31, 0,
/*14*/ "o", O, 0, "obj/id", OBJID, 0,
/*16*/ "r", R, 0, "reg/id", REGID, 0,
/*18*/ "use", USE, 0, "var/string", VARSTRING, 0,
/*20*/ "wo/rds", WORDS, 0, NULL, 0, 0
};
static int keystart[26] = {
/* a */ 0, /* b */ 2, /* c */ 6, /* d */ 7, /* e */ 8,
/* f */ 10, /* g */ -1, /* h */ -1, /* i */ 11, /* j */ -1,
/* k */ -1, /* l */ 13, /* m */ -1, /* n */ -1, /* o */ 14,
/* p */ -1, /* q */ -1, /* r */ 16, /* s */ -1, /* t */ -1,
/* u */ 18, /* v */ 19, /* w */ 20, /* x */ -1, /* y */ -1,
/* z */ -1 };
static int keyend[26] = {
/* a */ 1, /* b */ 5, /* c */ 6, /* d */ 7, /* e */ 9,
/* f */ 10, /* g */ -1, /* h */ -1, /* i */ 12, /* j */ -1,
/* k */ -1, /* l */ 13, /* m */ -1, /* n */ -1, /* o */ 15,
/* p */ -1, /* q */ -1, /* r */ 17, /* s */ -1, /* t */ -1,
/* u */ 18, /* v */ 19, /* w */ 20, /* x */ -1, /* y */ -1,
/* z */ -1 };
setKeywordMinlengths()
{
char *trailer;
char *index();
int i;
for (i = 0; keywords[i].string != NULL; ++i) {
trailer = index(keywords[i].string, '/');
if (trailer == NULL) {
keywords[i].minlength = strlen(keywords[i].string);
} else {
keywords[i].minlength = trailer - keywords[i].string;
strcpy(trailer, trailer + 1);
}
}
}
int
matchKeyword(s)
char *s;
{
register int i;
register int firstc;
int len;
firstc = *s - 'a';
if (firstc < 0 || 25 < firstc || keystart[firstc] == -1)
return(0);
len = strlen(s);
for (i = keystart[firstc]; i <= keyend[firstc]; ++i) {
if (len >= keywords[i].minlength &&
strncmp(s, keywords[i].string, len) == 0)
return(keywords[i].token);
}
return(0);
}

355
mamelink/griddle/main.c Normal file
View file

@ -0,0 +1,355 @@
/*
griddle -- Ghu's Region Internal Database Description LanguagE
*/
#include "griddleDefs.h"
int yydebug;
#define argcheck(i,m) if (++i >= argc) { error(m); exit(1); }
#define argfile(fd,m,t) { \
fileName = *args++; \
if (strcmp(fileName, "-") == 0) \
fd = stdout; \
else if ((fd = fopen(fileName, t)) == NULL) \
systemError(m, fileName); \
}
#define argfiler(fd,m) argfile(fd,m,"r")
#define argfilew(fd,m) argfile(fd,m,"w")
main(argc, argv)
int argc;
char *argv[];
{
int i;
int yyparse();
boolean initialize();
if (!initialize(argc, argv))
exit(1);
#ifndef FRED
if (indirFile == NULL)
yyparse();
else
indirectGriddle();
#else
yyparse();
#endif
readClassFile();
#ifndef FRED
while (cvInput != NULL) {
inputContentsVector(cvInput->string);
cvInput = cvInput->nextString;
}
if (cvFile != NULL)
outputContentsVector();
#else
doFredStuff();
#endif
}
void
resetRegionCounters()
{
int i;
for (i=0; i<objectCount; ++i) {
if (noidArray[i] != NULL) {
freeObject(noidArray[i]);
noidArray[i] = NULL;
}
}
globalIdCounter = 1001;
objectCount = 0;
rawCount = 0;
useStartCount = 0;
}
boolean
initialize(argc, argv)
int argc;
char *argv[];
{
int i;
int j;
char **args;
char *arg;
boolean inputFilesGiven;
char *defineFileName;
char *fileName;
char *getenv();
void queueInputFile();
boolean openFirstFile();
stringList *buildStringList();
griFile = NULL;
rawFile = NULL;
indirFile = NULL;
cvFile = NULL;
cvInput = NULL;
fredModeLexing = FALSE;
debug = FALSE;
yydebug = FALSE;
testMode = FALSE;
objectBase = 0;
sortObjects = FALSE;
insideDefinition = FALSE;
objectCount = 0;
resetRegionCounters();
inputStack = NULL;
announceIncludes = FALSE;
inputFilesGiven = FALSE;
assignRelativeIds = FALSE;
indirectPass = 0;
setKeywordMinlengths();
for (i=0; i<MAXCLASS+1; ++i)
classDefs[i] = NULL;
args = argv + 1;
if ((defineFileName = getenv("GHUDEFINES")) == NULL)
queueInputFile("/u0/habitat/defines.ghu");
else
queueInputFile(defineFileName);
if ((classFileName = getenv("CLASSINFO")) == NULL)
classFileName = "/u0/habitat/class.dat";
for (i=1; i<argc; i++) {
arg = *args++;
if (*arg != '-') {
#ifndef FRED
queueInputFile(arg);
inputFilesGiven = TRUE;
continue;
#else
error("illegal parameter '%s'\n", arg);
exit(1);
#endif
}
for (j=1; arg[j]!='\0'; j++) switch (arg[j]) {
case 'D':
debug = TRUE;
continue;
case 'I':
announceIncludes = TRUE;
continue;
case 'R':
assignRelativeIds = TRUE;
continue;
#ifndef FRED
case 'c':
argcheck(i, "no cv output file name after -c\n");
argfilew(cvFile, "can't open cv file %s\n");
continue;
case 'v':
argcheck(i, "no cv input file name after -v\n");
cvInput = buildStringList(cvInput, *args++);
continue;
case 'g':
case 'l':
argcheck(i,"no gri file name after -g\n");
argfilew(griFile, "can't open gri output file %s\n");
continue;
case 'i':
argcheck(i, "no indirect file name after -i\n");
argfiler(indirFile, "can't open indirect file %s\n");
continue;
case 'r':
case 'o':
argcheck(i,"no raw file name after -r\n");
argfilew(rawFile, "can't open raw output file %s\n");
continue;
#endif
case 'Y':
yydebug = TRUE;
continue;
case 't':
testMode = TRUE;
continue;
default:
error("bad command line flag -%c\n", arg[j]);
exit(1);
}
}
#ifndef FRED
if(inputFilesGiven && indirFile != NULL) {
error("input files and indirect file given at the same time");
exit(1);
} if (!inputFilesGiven && cvInput == NULL && indirFile == NULL)
queueInputFile("-");
#endif
for (i=0; i<MAXNOID; ++i)
noidArray[i] = NULL;
if (indirFile == NULL)
return(openFirstFile(FALSE));
}
int
hash(s)
char *s;
{
int result;
result = 0;
while (*s != '\0')
result = (result << 1) ^ *s++;
return(result & (HASH_MAX - 1));
}
symbol *
insertSymbol(name)
char *name;
{
symbol *newSymbol;
int hashval;
symbol *mptr;
symbol *oldmptr;
int cmp;
newSymbol = typeAlloc(symbol);
newSymbol->name = malloc(strlen(name) + 1);
newSymbol->type = NON_SYM;
newSymbol->codeNumber = 0;
strcpy(newSymbol->name, name);
hashval = hash(name);
mptr = symbolTable[hashval];
oldmptr = NULL;
while (mptr != NULL) {
if ((cmp = strcmp(name, mptr->name)) == 0) {
error("Hey, symbol %s already in table!", name);
exit(1);
} else if (cmp > 0) {
break;
} else {
oldmptr = mptr;
mptr = mptr->next;
}
}
if (oldmptr == NULL)
symbolTable[hashval] = newSymbol;
else
oldmptr->next = newSymbol;
newSymbol->next = mptr;
return(newSymbol);
}
symbol *
lookupSymbol(name)
char *name;
{
symbol *result;
int cmp;
result = symbolTable[hash(name)];
while (result != NULL) {
if ((cmp = strcmp(name, result->name)) == 0)
return(result);
else if (cmp > 0)
result = NULL;
else
result = result->next;
}
return(insertSymbol(name));
}
void
yyerror(s)
char *s;
{
error("\"%s\", line %d: %s\n", currentFileName, currentLineNumber, s);
}
void
queueInputFile(name)
char *name;
{
fileList *newFileName;
newFileName = typeAlloc(fileList);
newFileName->saveName = name;
newFileName->fyle = NULL;
newFileName->next = NULL;
newFileName->saveLine = 1;
if (inputStack == NULL) {
inputStack = bottomOfInputStack = newFileName;
} else {
bottomOfInputStack->next = newFileName;
bottomOfInputStack = newFileName;
}
}
boolean
openFirstFile(fredMode)
boolean fredMode;
{
if (inputStack == NULL || strcmp(inputStack->saveName, "-") == 0) {
inputStack = typeAlloc(fileList);
inputStack->saveName = "<standard input>";
inputStack->fyle = stdin;
inputStack->saveLine = 1;
inputStack->next = NULL;
} else {
if ((inputStack->fyle = fopen(inputStack->saveName,
"r")) == NULL) {
if (!fredMode)
error("can't open input file %s\n",
inputStack->saveName);
free(inputStack);
inputStack = NULL;
return(FALSE);
}
}
currentInput = inputStack->fyle;
currentLineNumber = inputStack->saveLine;
currentFileName = inputStack->saveName;
purgeUnget();
return(TRUE);
}
error(msg, arg1, arg2, arg3)
char *msg;
int arg1, arg2, arg3;
{
fprintf(stderr, "error: ");
fprintf(stderr, msg, arg1, arg2, arg3);
}
systemError(msg, arg1, arg2, arg3)
char *msg;
int arg1, arg2, arg3;
{
error(msg, arg1, arg2, arg3);
perror("Unix says");
exit(1);
}
void
translate(s, c1, c2)
char *s;
char c1;
char c2;
{
for (; *s != '\0'; ++s)
if (*s == c1)
*s = c2;
}
char *
saveString(s)
char *s;
{
char *result;
result = (char *)malloc(strlen(s) + 1);
strcpy(result, s);
return(result);
}

41
mamelink/griddle/prot.h Normal file
View file

@ -0,0 +1,41 @@
extern char readc();
#define SC 033
#define REPLY 0
#define STORE 1
#define LOW 2
#define HIGH 3
#define JUMP 4
#define LOAD 5
#define BADREPLY 6
#define CONT 7
#define HALTMAIN 8
#define HALTALL 9
#define HALTCONT 10
#define JUMPSUB 11
#define RESET 12
typedef int Boolean;
typedef struct
{
byte portB;
byte portA;
byte control;
byte portC;
} Port;
extern Port *portopen();
#define CLOWIN 1
#define CLOWOUT 0
#define BIN 02
#define BOUT 0
#define AIN 0x10
#define AOUT 0
#define CUPOUT 0
#define CUPIN 08
#define BMODE0 0
#define AMODE0 0
#define MODESET 0x80
#define NCARDS 3
#define PADDR { 0x70l, 0x60l }

BIN
mamelink/griddle/reno.out Normal file

Binary file not shown.

View file

@ -0,0 +1,69 @@
#include <stdio.h>
int fredStats[128];
char *
keyName(key)
char key;
{
static char result[10];
int i;
static struct {
char key;
char *name;
} keyList[] = {
' ', "SPACE", '\b', "BACKSPACE",
'\t', "TAB", '\n', "LINE FEED",
'\r', "RETURN", '\33', "ESC",
'\177', "DEL"
};
if ('!' <= key && key <= '~') {
sprintf(result, "%c", key);
return(result);
}
for (i=0; keyList[i].key != '\0'; ++i)
if (keyList[i].key == key)
return(keyList[i].name);
if (key < ' ') {
sprintf(result, "^%c", key + 0x40);
return(result);
}
sprintf(result, "\\%o", key);
return(result);
}
void
readFredStats(argc, argv)
int argc;
char *argv[];
{
FILE *statFyle;
int i;
char temp[256];
if (argc > 1)
sprintf(temp, "%s/u0/habitat/fredStats", argv[1]);
else
strcpy(temp, "/u0/habitat/fredStats");
if ((statFyle = fopen(temp, "r")) != NULL) {
for (i=0; i<128; ++i)
fredStats[i] = getw(statFyle);
fclose(statFyle);
}
if (statFyle == NULL || feof(statFyle)) {
for (i=0; i<128; ++i)
fredStats[i] = 0;
}
}
main(argc, argv)
int argc;
char *argv[];
{
int i;
readFredStats(argc, argv);
for (i=0; i<128; ++i)
printf("%s -- %d\n", keyName(i), fredStats[i]);
}

View file

@ -0,0 +1,14 @@
#include <stdio.h>
main()
{
FILE *statFyle;
int i;
if ((statFyle = fopen("/u0/habitat/fredStats", "w")) != NULL) {
for (i=0; i<128; ++i)
putw(0, statFyle);
fclose(statFyle);
} else
fprintf(stderr, "couldn't open stat file!\n");
}

View file

@ -0,0 +1,19 @@
#include <stdio.h>
main()
{
char c;
int i;
i = 0;
while ((c = getchar()) != EOF) {
putchar(c);
if (c == '\n')
i = 0;
else if (++i > 78) {
putchar('\\');
putchar('\n');
i = 0;
}
}
}

View file

@ -0,0 +1,19 @@
#include <stdio.h>
main(argc, argv)
int argc;
char *argv[];
{
int limit;
int count;
char c;
limit = atoi(argv[1]);
count = 0;
while ((c = getchar()) != EOF) {
if (count++ < limit || c == '\n')
putchar(c);
if (c == '\n')
count = 0;
}
}

288
render.js Normal file
View file

@ -0,0 +1,288 @@
// C64 RGB values taken from https://www.c64-wiki.com/wiki/Color
const c64Colors = [
0x000000, 0xffffff, 0x880000, 0xaaffee, 0xcc44cc, 0x00cc55,
0x0000aa, 0xeeee77, 0xdd8855, 0x664400, 0xff7777, 0x333333,
0x777777, 0xaaff66, 0x0088ff, 0xbbbbbb
]
// from paint.m:447
const celPatterns = [
[0x00, 0x00, 0x00, 0x00],
[0xaa, 0xaa, 0xaa, 0xaa],
[0xff, 0xff, 0xff, 0xff],
[0xe2, 0xe2, 0xe2, 0xe2],
[0x8b, 0xbe, 0x0f, 0xcc],
[0xee, 0x00, 0xee, 0x00],
[0xf0, 0xf0, 0x0f, 0x0f],
[0x22, 0x88, 0x22, 0x88],
[0x32, 0x88, 0x23, 0x88],
[0x00, 0x28, 0x3b, 0x0c],
[0x33, 0xcc, 0x33, 0xcc],
[0x08, 0x80, 0x0c, 0x80],
[0x3f, 0x3f, 0xf3, 0xf3],
[0xaa, 0x3f, 0xaa, 0xf3],
[0xaa, 0x00, 0xaa, 0x00],
[0x55, 0x55, 0x55, 0x55]
]
const makeCanvas = (w, h) => {
const canvas = document.createElement("canvas")
canvas.width = w
canvas.height = h
return canvas
}
const defaultColors = {
wildcard: 6,
skin: 10,
pattern: 15
}
export const canvasFromBitmap = (bitmap, colors = {}) => {
if (bitmap.length == 0 || bitmap[0].length == 0) {
return null
}
const { wildcard, pattern, skin } = { ...defaultColors, ...colors }
const patternColors = [6, wildcard, 0, skin]
const h = bitmap.length
const w = bitmap[0].length * 2
const canvas = makeCanvas(w, h)
const ctx = canvas.getContext("2d")
const img = ctx.createImageData(w, h)
const putpixel = (x, y, r, g, b, a) => {
const i = (x * 8) + (y * w * 4)
img.data[i] = r
img.data[i + 1] = g
img.data[i + 2] = b
img.data[i + 3] = a
img.data[i + 4] = r
img.data[i + 5] = g
img.data[i + 6] = b
img.data[i + 7] = a
}
for (let y = 0; y < bitmap.length; y ++) {
const line = bitmap[y]
// TODO: What is pattern 255?
const patbyte = celPatterns[pattern < 0 || pattern > 15 ? 15 : pattern][y % 4]
for (let x = 0; x < line.length; x ++) {
const pixel = line[x]
let color = null
if (pixel == 0) { // transparent
putpixel(x, y, 0, 0, 0, 0)
} else if (pixel == 1) { // wild
const shift = (x % 4) * 2
color = patternColors[(patbyte & (0xc0 >> shift)) >> (6 - shift)]
} else {
color = patternColors[pixel]
}
if (color != null) {
const rgb = c64Colors[color]
putpixel(x, y, (rgb & 0xff0000) >> 16, (rgb & 0xff00) >> 8, rgb & 0xff, 0xff)
}
}
}
ctx.putImageData(img, 0, 0)
return canvas
}
export const celsFromMask = (prop, celMask) => {
const cels = []
for (let icel = 0; icel < 8; icel ++) {
const celbit = 0x80 >> icel
if ((celMask & celbit) != 0) {
cels.push(prop.cels[icel])
}
}
return cels
}
export const frameFromCels = (cels, celColors = null, paintOrder = null) => {
if (cels.length == 0) {
return null
}
let minX = Number.POSITIVE_INFINITY
let minY = Number.POSITIVE_INFINITY
let maxX = Number.NEGATIVE_INFINITY
let maxY = Number.NEGATIVE_INFINITY
let xRel = 0
let yRel = 0
let layers = []
for (const [icel, cel] of cels.entries()) {
if (cel) {
const x = cel.xOffset + xRel
const y = -(cel.yOffset + yRel)
minX = Math.min(minX, x)
minY = Math.min(minY, y)
maxX = Math.max(maxX, cel.width + x)
maxY = Math.max(maxY, cel.height + y)
if (cel.bitmap) {
const colors = (Array.isArray(celColors) ? celColors[icel] : celColors) ?? {}
layers.push({ canvas: canvasFromBitmap(cel.bitmap, colors), x, y })
} else {
layers.push(null)
}
xRel += cel.xRel
yRel += cel.yRel
} else {
layers.push(null)
}
}
if (paintOrder) {
const reordered = []
for (const ilayer of paintOrder) {
reordered.push(layers[ilayer])
}
layers = reordered
}
const w = (maxX - minX) * 8
const h = maxY - minY
const canvas = makeCanvas(w, h)
const ctx = canvas.getContext("2d")
for (const layer of layers) {
if (layer && layer.canvas) {
ctx.drawImage(layer.canvas, (layer.x - minX) * 8, layer.y - minY)
}
}
return { canvas: canvas, xOffset: minX * 8, yOffset: minY, w: w, h: h }
}
const framesFromAnimation = (animation, frameFromState) => {
const frames = []
for (let istate = animation.startState; istate <= animation.endState; istate ++) {
const frame = frameFromState(istate)
if (frame != null) {
frames.push(frame)
}
}
return frames
}
export const framesFromPropAnimation = (animation, prop, colors = null) => {
const frameFromState = (istate) =>
frameFromCels(celsFromMask(prop, prop.celmasks[istate]), colors)
return framesFromAnimation(animation, frameFromState)
}
export const framesFromLimbAnimation = (animation, limb, colors = null) => {
const frameFromState = (istate) => {
const iframe = limb.frames[istate]
if (iframe >= 0) {
return frameFromCels([limb.cels[iframe]], colors)
} else {
return null
}
}
return framesFromAnimation(animation, frameFromState)
}
const actionOrientations = {
"stand_back": "back",
"walk_front": "front",
"walk_back": "back",
"stand_front": "front",
"sit_front": "front"
}
export const framesFromAction = (action, body, limbColors = null) => {
const frames = []
const chore = body.choreography[body.actions[action]]
const animations = []
const orientation = actionOrientations[action] ?? "side"
const limbOrder = orientation == "front" ? body.frontFacingLimbOrder :
orientation == "back" ? body.backFacingLimbOrder :
null // side animations are always displayed in standard limb order
for (const limb of body.limbs) {
if (limb.animations.length > 0) {
animations.push({ ...limb.animations[0] })
} else {
animations.push({ startState: 0, endState: 0 })
}
}
for (const override of chore) {
const ilimb = override.limb
const newAnim = body.limbs[ilimb].animations[override.animation]
animations[ilimb].startState = newAnim.startState
animations[ilimb].endState = newAnim.endState
}
while (true) {
const cels = []
// const celColors = []
let restartedCount = 0
for (const [ilimb, limb] of body.limbs.entries()) {
const animation = animations[ilimb]
if (animation.current == undefined) {
animation.current = animation.startState
} else {
animation.current ++
if (animation.current > animation.endState) {
animation.current = animation.startState
restartedCount ++
}
}
const istate = limb.frames[animation.current]
if (istate >= 0) {
cels.push(limb.cels[istate])
} else {
cels.push(null)
}
// limb.pattern is not a pattern index, it's a LIMB pattern index
// celColors.push({ pattern: limb.pattern })
}
if (restartedCount == animations.length) {
break
}
frames.push(frameFromCels(cels, null, limbOrder))
}
return frames
}
export const imageFromCanvas = (canvas) => {
const img = document.createElement("img")
img.src = canvas.toDataURL()
img.width = canvas.width * 3
img.height = canvas.height * 3
img.style.imageRendering = "pixelated"
return img
}
export const animate = (frames) => {
if (frames.length == 0) {
return textNode("")
} else if (frames.length == 1) {
return imageFromCanvas(frames[0].canvas)
}
let minX = Number.POSITIVE_INFINITY
let minY = Number.POSITIVE_INFINITY
let maxX = Number.NEGATIVE_INFINITY
let maxY = Number.NEGATIVE_INFINITY
for (const frame of frames) {
minX = Math.min(minX, frame.xOffset)
minY = Math.min(minY, frame.yOffset)
maxX = Math.max(maxX, frame.xOffset + frame.w)
maxY = Math.max(maxY, frame.yOffset + frame.h)
}
const w = maxX - minX
const h = maxY - minY
const canvas = makeCanvas(w, h)
canvas.style.imageRendering = "pixelated"
canvas.style.width = `${w * 3}px`
canvas.style.height = `${h * 3}px`
let iframe = 0
const ctx = canvas.getContext("2d")
const nextFrame = () => {
const frame = frames[iframe]
ctx.clearRect(0, 0, w, h)
ctx.drawImage(frame.canvas, frame.xOffset - minX, frame.yOffset - minY)
iframe = (iframe + 1) % frames.length
}
nextFrame()
setInterval(nextFrame, 250)
return canvas
}

87
show.js Normal file
View file

@ -0,0 +1,87 @@
import { animate, celsFromMask, frameFromCels, framesFromAction, framesFromLimbAnimation, framesFromPropAnimation } from "./render.js"
const readBinary = async (url) => {
const response = await fetch(url)
if (!response.ok) {
console.log(response)
throw Error(`Failed to download ${url}`)
}
return new DataView(await response.arrayBuffer())
}
export const textNode = (text, type = "span") => {
const node = document.createElement(type)
node.innerText = text
return node
}
export const wrapLink = (element, href) => {
const link = document.createElement("a")
link.href = href
link.appendChild(element)
return link
}
export const docBuilder = ({ detailHref, errorContainer }) => {
const linkDetail = (element, filename, impl) => {
return detailHref ? wrapLink(element, `${detailHref}?f=${filename}`) : element
}
const showError = (e, filename) => {
if (errorContainer) {
const errNode = document.createElement("p")
console.error(e)
errNode.appendChild(linkDetail(textNode(filename, "b"), filename))
errNode.appendChild(textNode(e.toString(), "p"))
if (e.stack) {
errNode.appendChild(textNode(e.stack.toString(), "pre"))
}
errorContainer.appendChild(errNode)
}
}
return { linkDetail, showError }
}
export const showAll = (doc, container, filename, values, f) => {
for (const value of values) {
try {
let elements = f(value)
if (elements && !Array.isArray(elements)) {
elements = [elements]
}
if (elements) {
for (const element of elements) {
container.appendChild(doc.linkDetail(element, filename))
}
}
} catch (e) {
doc.showError(e, filename)
}
}
}
export const propAnimationShower = (prop, colors = {}) =>
(animation) => animate(framesFromPropAnimation(animation, prop, colors))
export const limbAnimationShower = (limb, colors = {}) =>
(animation) => animate(framesFromLimbAnimation(animation, limb, colors))
export const actionShower = (body, limbColors = null) =>
(action) => animate(framesFromAction(action, body, limbColors))
export const celmaskShower = (prop, colors = null) =>
(celmask) => animate([frameFromCels(celsFromMask(prop, celmask), colors)])
export const celShower = (colors = null) =>
(cel) => animate([frameFromCels([cel], colors)])
export const decodeBinary = async (filename, decode) => {
try {
const value = decode(await readBinary(filename))
value.filename = filename
return value
} catch (e) {
return { filename: filename, error: e }
}
}