Much better error and warning pinpointing during pack process.

This commit is contained in:
Martin Haye 2014-09-11 08:54:37 -07:00
parent a480dc8c96
commit c43f381296
2 changed files with 157 additions and 55 deletions

3
.gitignore vendored
View File

@ -25,3 +25,6 @@
/Platform/Apple/tools/PLASMA/src/plvm /Platform/Apple/tools/PLASMA/src/plvm
/Platform/Apple/tools/PLASMA/src/PLVM02.SYSTEM.sys /Platform/Apple/tools/PLASMA/src/PLVM02.SYSTEM.sys
/Platform/Apple/tools/PLASMA/src/*#* /Platform/Apple/tools/PLASMA/src/*#*
# Packer sometimes produces an error text file.
error_stack.txt

View File

@ -50,6 +50,47 @@ class PackPartitions
def javascriptOut = null def javascriptOut = null
def currentContext = []
def nWarnings = 0
/**
* Keep track of context within the XML file, so we can spit out more useful
* error and warning messages.
*/
def withContext(name, closure)
{
def threw = false
try {
currentContext << name
closure()
}
catch (Throwable e) {
threw = true
throw e
}
finally {
// Preserve context in the case of an exception
if (!threw)
currentContext.pop()
}
}
def getContextStr()
{
return currentContext.join(" -> ")
}
def printError(str)
{
System.out.format("Error in ${getContextStr()}: %s\n", str)
}
def printWarning(str)
{
System.out.format("Warning in ${getContextStr()}: %s\n", str)
++nWarnings
}
def parseMap(map, tiles) def parseMap(map, tiles)
{ {
// Parse each row of the map // Parse each row of the map
@ -460,7 +501,7 @@ class PackPartitions
texMap[id] |= 0x80; // hi-bit flag to mark sprite cells texMap[id] |= 0x80; // hi-bit flag to mark sprite cells
} }
else if (id) { else if (id) {
println "Warning: can't match tile name '$name' to any image; treating as blank." printWarning("can't match tile name '$name' to any image; treating as blank.")
texMap[id] = 0 texMap[id] = 0
} }
} }
@ -559,7 +600,7 @@ class PackPartitions
{ {
def num = frames.size() + 1 def num = frames.size() + 1
def name = imgEl.@name ?: "img$num" def name = imgEl.@name ?: "img$num"
println "Packing frame image #$num named '${imgEl.@name}'." //println "Packing frame image #$num named '${imgEl.@name}'."
def buf = parseFrameData(imgEl) def buf = parseFrameData(imgEl)
frames[imgEl.@name] = [num:num, buf:buf] frames[imgEl.@name] = [num:num, buf:buf]
return buf return buf
@ -620,12 +661,14 @@ class PackPartitions
{ {
def name = mapEl.@name ?: "map$num" def name = mapEl.@name ?: "map$num"
def num = mapNames[name][1] def num = mapNames[name][1]
println "Packing 3D map #$num named '$name'." //println "Packing 3D map #$num named '$name'."
def (scriptModule, locationsWithTriggers) = packScripts(mapEl, name) withContext("map '$name'") {
def rows = parseMap(mapEl, tileEls) def (scriptModule, locationsWithTriggers) = packScripts(mapEl, name)
def buf = ByteBuffer.allocate(50000) def rows = parseMap(mapEl, tileEls)
write3DMap(buf, name, rows, scriptModule, locationsWithTriggers) def buf = ByteBuffer.allocate(50000)
maps3D[name] = [num:num, buf:buf] write3DMap(buf, name, rows, scriptModule, locationsWithTriggers)
maps3D[name] = [num:num, buf:buf]
}
} }
def packScripts(mapEl, mapName) def packScripts(mapEl, mapName)
@ -636,7 +679,7 @@ class PackPartitions
module.packScripts(mapEl.scripts[0]) module.packScripts(mapEl.scripts[0])
def num = modules.size() + 1 def num = modules.size() + 1
def name = "mapScript$num" def name = "mapScript$num"
println "Packing scripts for map $mapName, to module $num." //println "Packing scripts for map $mapName, to module $num."
modules[name] = [num:num, buf:wrapByteList(module.data)] modules[name] = [num:num, buf:wrapByteList(module.data)]
bytecodes[name] = [num:num, buf:wrapByteList(module.bytecode)] bytecodes[name] = [num:num, buf:wrapByteList(module.bytecode)]
fixups[name] = [num:num, buf:wrapByteList(module.fixups)] fixups[name] = [num:num, buf:wrapByteList(module.fixups)]
@ -1059,7 +1102,7 @@ class PackPartitions
mapNames[shortName] = ['3D', num3D] mapNames[shortName] = ['3D', num3D]
} }
else else
println "Warning: map name '${map?.@name}' should contain '2D' or '3D'. Skipping." printWarning "map name '${map?.@name}' should contain '2D' or '3D'. Skipping."
} }
// Pack each map This uses the image and tile maps filled earlier. // Pack each map This uses the image and tile maps filled earlier.
@ -1070,7 +1113,7 @@ class PackPartitions
else if (map?.@name =~ /3D/) else if (map?.@name =~ /3D/)
pack3DMap(map, dataIn.tile) pack3DMap(map, dataIn.tile)
else else
println "Warning: map name '${map?.@name}' should contain '2D' or '3D'. Skipping." printWarning "map name '${map?.@name}' should contain '2D' or '3D'. Skipping."
} }
// Ready to start writing the output file. // Ready to start writing the output file.
@ -1095,6 +1138,9 @@ class PackPartitions
static void main(String[] args) static void main(String[] args)
{ {
// Set auto-flushing for stdout
System.out = new PrintStream(new BufferedOutputStream(System.out), true)
// Verify that assertions are enabled // Verify that assertions are enabled
def flag = false def flag = false
try { try {
@ -1114,9 +1160,42 @@ class PackPartitions
println " (where intcastMap.js is to aid in debugging the Javascript raycaster)" println " (where intcastMap.js is to aid in debugging the Javascript raycaster)"
System.exit(1); System.exit(1);
} }
// If there's an existing error file, remote it.
def errorFile = new File("pack_error.txt")
if (errorFile.exists())
errorFile.delete()
// Go for it. // Go for it.
new PackPartitions().pack(args[0], args[1], args.size() > 2 ? args[2] : null) def inst = new PackPartitions()
try {
inst.pack(args[0], args[1], args.size() > 2 ? args[2] : null)
}
catch (Throwable t) {
errorFile.withWriter { out ->
out.println "Packing error: ${t.message}"
out.println "\nContext:"
out.println " ${inst.getContextStr()}"
out.println "\nGroovy call stack:"
t.getStackTrace().each {
if (it.toString().contains(".groovy:"))
out.println " $it"
}
}
def msg = "Fatal error encountered in ${inst.getContextStr()}.\nDetails written to file 'pack_error.txt'."
println msg
System.out.flush()
javax.swing.JOptionPane.showMessageDialog(null, msg, "Fatal packing error",
javax.swing.JOptionPane.ERROR_MESSAGE)
System.exit(1)
}
if (inst.nWarnings > 0) {
javax.swing.JOptionPane.showMessageDialog(null,
"${inst.nWarnings} warning(s) noted during packing.\nCheck console for details.",
"Pack warnings",
javax.swing.JOptionPane.ERROR_MESSAGE)
}
} }
class ScriptModule class ScriptModule
@ -1183,24 +1262,32 @@ class PackPartitions
def name = script.name[0].text() def name = script.name[0].text()
if (name.toLowerCase() == "init") // this special script gets processed later if (name.toLowerCase() == "init") // this special script gets processed later
return return
println " Script '$name'" //println " Script '$name'"
withContext("script '$name'")
{
if (script.block.size() == 0) {
printWarning("empty script found; skipping.")
return
}
if (script.block.size() == 0) // Record the function's start address in its corresponding stub
return startFunc(scriptNum+1)
// Record the function's start address in its corresponding stub // Process the code inside it
startFunc(scriptNum+1) def proc = script.block[0]
assert proc.@type == "procedures_defreturn"
// Process the code inside it if (proc.statement.size() > 0) {
def proc = script.block[0] assert proc.statement.size() == 1
assert proc.@type == "procedures_defreturn" def stmt = proc.statement[0]
assert proc.statement.size() == 1 assert stmt.@name == "STACK"
def stmt = proc.statement[0] stmt.block.each { packBlock(it) }
assert stmt.@name == "STACK" }
stmt.block.each { packBlock(it) } else
printWarning "empty statement found; skipping."
// And complete the function
finishFunc() // And complete the function
finishFunc()
}
} }
def finishFunc() def finishFunc()
@ -1212,24 +1299,25 @@ class PackPartitions
def packBlock(blk) def packBlock(blk)
{ {
println " Block '${blk.@type}'" withContext("${blk.@type}") {
switch (blk.@type) switch (blk.@type)
{ {
case 'text_print': case 'text_print':
case 'text_println': case 'text_println':
packTextPrint(blk); break packTextPrint(blk); break
case 'controls_if': case 'controls_if':
packIfStmt(blk); break packIfStmt(blk); break
case 'events_set_map': case 'events_set_map':
packSetMap(blk); break packSetMap(blk); break
case 'events_set_sky': case 'events_set_sky':
packSetSky(blk); break packSetSky(blk); break
case 'events_set_ground': case 'events_set_ground':
packSetGround(blk); break packSetGround(blk); break
case 'events_teleport': case 'events_teleport':
packTeleport(blk); break packTeleport(blk); break
default: default:
println "Warning: don't know how to pack block of type '${blk.@type}'" printWarning "don't know how to pack block of type '${blk.@type}'"
}
} }
// Strangely, blocks seem to be chained together, but hierarchically. Whatever. // Strangely, blocks seem to be chained together, but hierarchically. Whatever.
@ -1283,7 +1371,10 @@ class PackPartitions
def packTextPrint(blk) def packTextPrint(blk)
{ {
assert blk.value.size() == 1 if (blk.value.size() == 0) {
printWarning "empty text_print block, skipping."
return
}
def val = blk.value[0] def val = blk.value[0]
assert val.@name == 'VALUE' assert val.@name == 'VALUE'
assert val.block.size() == 1 assert val.block.size() == 1
@ -1293,7 +1384,7 @@ class PackPartitions
def fld = valBlk.field[0] def fld = valBlk.field[0]
assert fld.@name == 'TEXT' assert fld.@name == 'TEXT'
def text = fld.text() def text = fld.text()
println " text: '$text'" //println " text: '$text'"
emitCodeByte(0x26) // LA emitCodeByte(0x26) // LA
def textAddr = addString(text) def textAddr = addString(text)
@ -1305,13 +1396,17 @@ class PackPartitions
def packIfStmt(blk) def packIfStmt(blk)
{ {
if (blk.value.size() == 0) {
printWarning "missing condition; skipping."
return
}
assert blk.value.size() == 1 assert blk.value.size() == 1
def cond = blk.value[0] def cond = blk.value[0]
assert cond.@name == 'IF0' assert cond.@name == 'IF0'
assert cond.block.size() == 1 assert cond.block.size() == 1
assert cond.block[0].@type == 'text_getboolean' assert cond.block[0].@type == 'text_getboolean'
print " Conditional on getboolean," //print " Conditional on getboolean,"
emitCodeByte(0x54) // CALL emitCodeByte(0x54) // CALL
emitCodeWord(vec_getYN) emitCodeWord(vec_getYN)
@ -1338,7 +1433,11 @@ class PackPartitions
assert fld.@name == 'NAME' assert fld.@name == 'NAME'
def mapName = fld.text() def mapName = fld.text()
def mapNum = mapNames[mapName] def mapNum = mapNames[mapName]
println " Set map to '$mapName' (num $mapNum)" if (!mapNum) {
printWarning "map '$mapName' not found; skipping set_map."
return
}
//println " Set map to '$mapName' (num $mapNum)"
assert mapNum : "Map $mapName not found!" assert mapNum : "Map $mapName not found!"
emitCodeByte(0x2A) // CB emitCodeByte(0x2A) // CB
@ -1358,7 +1457,7 @@ class PackPartitions
assert fld.@name == 'COLOR' assert fld.@name == 'COLOR'
def color = fld.text().toInteger() def color = fld.text().toInteger()
assert color >= 0 && color <= 15 assert color >= 0 && color <= 15
println " Set sky to $color" //println " Set sky to $color"
emitCodeByte(0x2A) // CB emitCodeByte(0x2A) // CB
emitCodeByte(color) emitCodeByte(color)
@ -1374,7 +1473,7 @@ class PackPartitions
assert fld.@name == 'COLOR' assert fld.@name == 'COLOR'
def color = fld.text().toInteger() def color = fld.text().toInteger()
assert color >= 0 && color <= 15 assert color >= 0 && color <= 15
println " Set ground to $color" //println " Set ground to $color"
emitCodeByte(0x2A) // CB emitCodeByte(0x2A) // CB
emitCodeByte(color) emitCodeByte(color)
@ -1393,7 +1492,7 @@ class PackPartitions
def y = blk.field[1].text().toInteger() def y = blk.field[1].text().toInteger()
def facing = blk.field[2].text().toInteger() def facing = blk.field[2].text().toInteger()
assert facing >= 0 && facing <= 15 assert facing >= 0 && facing <= 15
println " Teleport to ($x,$y) facing $facing" //println " Teleport to ($x,$y) facing $facing"
emitCodeByte(0x2C) // CW emitCodeByte(0x2C) // CW
emitCodeWord(x) emitCodeWord(x)
@ -1408,7 +1507,7 @@ class PackPartitions
def makeInit(scripts) def makeInit(scripts)
{ {
println " Script: special 'init'" //println " Script: special 'init'"
startFunc(0) startFunc(0)
scripts.script.eachWithIndex { script, idx -> scripts.script.eachWithIndex { script, idx ->
def name = script.name[0].text() def name = script.name[0].text()