From 7445ee219d4e336cf1513c59566e4e1dd2c1e62b Mon Sep 17 00:00:00 2001 From: Martin Haye Date: Fri, 13 Sep 2013 08:40:53 -0700 Subject: [PATCH] Added new tool: PackMap, to convert an Outlaw XML file to an Apple II packed map file. --- .gitignore | 6 +- Platform/Apple/tools/PackMap/build.xml | 73 + Platform/Apple/tools/PackMap/manifest.mf | 3 + .../tools/PackMap/nbproject/build-impl.xml | 1403 +++++++++++++++++ .../PackMap/nbproject/genfiles.properties | 11 + .../tools/PackMap/nbproject/groovy-build.xml | 93 ++ .../PackMap/nbproject/project.properties | 75 + .../Apple/tools/PackMap/nbproject/project.xml | 20 + .../tools/PackMap/src/org/demo/PackMap.groovy | 274 ++++ 9 files changed, 1955 insertions(+), 3 deletions(-) create mode 100644 Platform/Apple/tools/PackMap/build.xml create mode 100644 Platform/Apple/tools/PackMap/manifest.mf create mode 100644 Platform/Apple/tools/PackMap/nbproject/build-impl.xml create mode 100644 Platform/Apple/tools/PackMap/nbproject/genfiles.properties create mode 100644 Platform/Apple/tools/PackMap/nbproject/groovy-build.xml create mode 100644 Platform/Apple/tools/PackMap/nbproject/project.properties create mode 100644 Platform/Apple/tools/PackMap/nbproject/project.xml create mode 100644 Platform/Apple/tools/PackMap/src/org/demo/PackMap.groovy diff --git a/.gitignore b/.gitignore index 9742c2cb..9d0e528d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,12 +5,12 @@ # Skip build directories **/build/ /OutlawEditor/target/ -/Platform/Apple/tools/A2Copy/nbproject/private/ -/Platform/Apple/tools/A2Copy/dist/ +/Platform/Apple/tools/*/nbproject/private/ +/Platform/Apple/tools/*/dist/ # Don't check in data specific to any particular game /Platform/Apple/virtual/data/images/*.bin # Only check in sample.build.props; each person's build.props will be different. /Platform/Apple/virtual/src/include/build.props -/Platform/Apple/tools/A2Copy/build/ \ No newline at end of file +/Platform/Apple/tools/A2Copy/build/ diff --git a/Platform/Apple/tools/PackMap/build.xml b/Platform/Apple/tools/PackMap/build.xml new file mode 100644 index 00000000..643a4cf7 --- /dev/null +++ b/Platform/Apple/tools/PackMap/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project PackMap. + + + diff --git a/Platform/Apple/tools/PackMap/manifest.mf b/Platform/Apple/tools/PackMap/manifest.mf new file mode 100644 index 00000000..328e8e5b --- /dev/null +++ b/Platform/Apple/tools/PackMap/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/Platform/Apple/tools/PackMap/nbproject/build-impl.xml b/Platform/Apple/tools/PackMap/nbproject/build-impl.xml new file mode 100644 index 00000000..ac114625 --- /dev/null +++ b/Platform/Apple/tools/PackMap/nbproject/build-impl.xml @@ -0,0 +1,1403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Platform/Apple/tools/PackMap/nbproject/genfiles.properties b/Platform/Apple/tools/PackMap/nbproject/genfiles.properties new file mode 100644 index 00000000..22cdbdcd --- /dev/null +++ b/Platform/Apple/tools/PackMap/nbproject/genfiles.properties @@ -0,0 +1,11 @@ +build.xml.data.CRC32=69409762 +build.xml.script.CRC32=89726637 +build.xml.stylesheet.CRC32=8064a381@1.68.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=69409762 +nbproject/build-impl.xml.script.CRC32=12156ac0 +nbproject/build-impl.xml.stylesheet.CRC32=cdba79fa@1.68.1.46 +nbproject/groovy-build.xml.data.CRC32=69409762 +nbproject/groovy-build.xml.script.CRC32=629135a5 +nbproject/groovy-build.xml.stylesheet.CRC32=919c82d5@1.4.1 diff --git a/Platform/Apple/tools/PackMap/nbproject/groovy-build.xml b/Platform/Apple/tools/PackMap/nbproject/groovy-build.xml new file mode 100644 index 00000000..a15403dd --- /dev/null +++ b/Platform/Apple/tools/PackMap/nbproject/groovy-build.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.binarytestincludes + + + + Some tests failed; see details above. + + + diff --git a/Platform/Apple/tools/PackMap/nbproject/project.properties b/Platform/Apple/tools/PackMap/nbproject/project.properties new file mode 100644 index 00000000..a349791a --- /dev/null +++ b/Platform/Apple/tools/PackMap/nbproject/project.properties @@ -0,0 +1,75 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=PackMap +application.vendor=mhaye +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form,**/*.groovy +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/PackMap.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +includes=** +jar.compress=false +javac.classpath=\ + ${libs.groovy-all.classpath} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=org.demo.PackMap +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test +compile.on.save.unsupported.groovy=true diff --git a/Platform/Apple/tools/PackMap/nbproject/project.xml b/Platform/Apple/tools/PackMap/nbproject/project.xml new file mode 100644 index 00000000..a9ba3dfe --- /dev/null +++ b/Platform/Apple/tools/PackMap/nbproject/project.xml @@ -0,0 +1,20 @@ + + + org.netbeans.modules.java.j2seproject + + + + + + + + PackMap + + + + + + + + + diff --git a/Platform/Apple/tools/PackMap/src/org/demo/PackMap.groovy b/Platform/Apple/tools/PackMap/src/org/demo/PackMap.groovy new file mode 100644 index 00000000..0e6b3e00 --- /dev/null +++ b/Platform/Apple/tools/PackMap/src/org/demo/PackMap.groovy @@ -0,0 +1,274 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.demo + +import java.nio.ByteBuffer +import java.nio.channels.Channels + +/** + * + * @author mhaye + */ +class PackMap +{ + def parseMap(tiles, map) + { + // Parse each row of the map + map.chunk.row.collect + { + // The map data is a space-separated string of tile ids. Look up those + // tiles. + // + it.text().split(" ").collect { tileId -> + (tileId == "_") ? null : tiles.find{ it.@id == tileId } + } + } + } + + def pixelize(dataEl) + { + def nBytes = dataEl.@width as int + def nLines = dataEl.@height as int + def hexStr = dataEl.text() + return (0.. + def outRow = [] + def pix = 0 + def pixBits = 0 + for (def byteNum in 0.. + rowNum++ + row.collect { pix -> + (rowNum <= 25 && pix == 3) ? 7 : + (rowNum <= 25 && pix == 0) ? 4 : + (rowNum > 25 && pix == 4) ? 0 : + (rowNum > 25 && pix == 7) ? 3 : + (rowNum > 25 && pix == 5) ? 0 : + pix + } + } + } + + return result + } + + class MultiPix + { + def colorValues = [:] + + def add(color, value) { + colorValues[color] = colorValues.get(color,0) + value + } + + def getHighColor() { + def kv = colorValues.grep().sort { a, b -> b.value <=> a.value }[0] + return [kv.key, kv.value] + } + + def addError(pix, skipColor, frac) { + pix.colorValues.each { color, value -> + if (color != skipColor) + add(color, value * frac) + } + } + } + + /** + * Produce a new mip-map level by halving the image's resolution. + */ + def reduceImage(imgIn) + { + def inWidth = imgIn[0].size() + def inHeight = imgIn.size() + + def outWidth = inWidth >> 1 + def outHeight = inHeight >> 1 + + def pixBuf = new MultiPix[outHeight][outWidth] + + // Distribute the input pixels to the output pixels + imgIn.eachWithIndex { row, sy -> + def dy = sy >> 1 + row.eachWithIndex { color, sx -> + def dx = sx >> 1 + if (!pixBuf[dy][dx]) + pixBuf[dy][dx] = new MultiPix() + pixBuf[dy][dx].add(color, 1) + } + } + + // Apply error diffusion to form the final result + def outRows = [] + pixBuf.eachWithIndex { row, dy -> + def outRow = [] + row.eachWithIndex { pix, dx -> + def (color, value) = pix.getHighColor() + outRow.add(color) + // Distribute the error: 50% to the right, 25% down, 25% down-right + /* + if (dx+1 < outWidth) + pixBuf[dy][dx+1].addError(pix, color, 0.5) + if (dy+1 < outHeight) + pixBuf[dy+1][dx].addError(pix, color, 0.25) + if (dx+1 < outWidth && dy+1 < outHeight) + pixBuf[dy+1][dx+1].addError(pix, color, 0.25) + */ + } + outRows.add(outRow) + } + return outRows + } + + def printImage(rows) + { + rows.each { row -> + print " " + row.each { pix -> print pix } + println "" + } + } + + + def writeMap(stream, rows, names) + { + def width = rows[0].size() + def height = rows.size() + + // Header: one-char code followed by two-byte length + // (length should not include the 5-byte header) + // + def len = width*height + stream.write((int)'M') // for "Map" + stream.write(width) + stream.write(height) + stream.write(len & 0xFF) + stream.write((len>>8) & 0xFF) + + // After the header comes the raw data + rows.each { row -> + row.each { tile -> + stream.write(names.findIndexOf { it == tile?.@name } + 1) + } + } + } + + // The renderer wants bits of the two pixels interleaved + def combine(pix1, pix2) { + return ((pix1 & 1) << 0) | ((pix2 & 1) << 1) | + ((pix1 & 2) << 1) | ((pix2 & 2) << 2) | + ((pix1 & 4) << 2) | ((pix2 & 4) << 3); + } + + def writeImage(stream, image) + { + // First, accumulate pixel data for all 5 mip levels plus the orig image + def buf = ByteBuffer.allocate(50000) + for (def mipLevel in 0..5) + { + // Process double rows + for (x in 0..>8) & 0xFF) + + // And copy the data to the output + def tmp = new byte[buf.position()] + buf.position(0) + buf.get(tmp) + stream.write(tmp) + } + + def pack(xmlPath, binPath) + { + // Open the XML data file produced by Outlaw Editor + def dataIn = new XmlParser().parse(xmlPath) + + // Locate the map named 'main', or failing that, the first map + def map = dataIn.map.find { it.@name == 'main' } + if (!map) + map = dataIn.map[0] + assert map : "Can't find map 'main' nor any map at all"; + + // Identify all the tiles and make a list of rows + def rows = parseMap(dataIn.tile, map); + + // Determine the unique names of all the 'obstruction' tiles. Those + // are the ones that turn into texture images. + // + def names = rows.flatten().grep{it?.@obstruction == 'true'}. + collect{it.@name}.sort().unique() + + println "Parsing images." + def images = names.collect { name -> + parseImage(dataIn.image.find { it.@name == name }) + } + + // Ready to start writing the output file. + new File(binPath).withOutputStream { stream -> + println "Writing map." + writeMap(stream, rows, names) + images.eachWithIndex { image, idx -> + println "Writing image #${idx+1}." + writeImage(stream, image) + } + stream.write(0) // properly terminate the file + } + + println "Done." + } + + static void main(String[] args) + { + if (args.size() != 2) { + println "Usage: convert yourMapFile.xml out.bin" + System.exit(1); + } + new PackMap().pack(args[0], args[1]) + } +} +