From ffebbcd2dee04b8c06e90432618e0e013ac5b7dc Mon Sep 17 00:00:00 2001 From: Alex Xu Date: Sun, 30 Mar 2014 13:42:48 -0400 Subject: unrpyc -> ast2json --- ast2json/.gitignore | 2 + ast2json/Makefile | 294 ++++++++ ast2json/ast2json.py | 62 ++ ast2json/decompiler.py | 500 +++++++++++++ ast2json/renpy/LICENSE.txt | 1024 ++++++++++++++++++++++++++ ast2json/renpy/__init__.py | 271 +++++++ ast2json/renpy/ast.py | 1757 ++++++++++++++++++++++++++++++++++++++++++++ ast2json/renpy/atl.py | 1544 ++++++++++++++++++++++++++++++++++++++ ast2json/renpy/game.py | 437 +++++++++++ ast2json/renpy/object.py | 60 ++ ast2json/rpyc2json.py | 98 +++ ast2json/script2json.py | 147 ++++ 12 files changed, 6196 insertions(+) create mode 100644 ast2json/.gitignore create mode 100644 ast2json/Makefile create mode 100644 ast2json/ast2json.py create mode 100644 ast2json/decompiler.py create mode 100644 ast2json/renpy/LICENSE.txt create mode 100644 ast2json/renpy/__init__.py create mode 100644 ast2json/renpy/ast.py create mode 100644 ast2json/renpy/atl.py create mode 100644 ast2json/renpy/game.py create mode 100644 ast2json/renpy/object.py create mode 100755 ast2json/rpyc2json.py create mode 100755 ast2json/script2json.py (limited to 'ast2json') diff --git a/ast2json/.gitignore b/ast2json/.gitignore new file mode 100644 index 0000000..466d742 --- /dev/null +++ b/ast2json/.gitignore @@ -0,0 +1,2 @@ +/*.json* +/*.rpy* diff --git a/ast2json/Makefile b/ast2json/Makefile new file mode 100644 index 0000000..48d7503 --- /dev/null +++ b/ast2json/Makefile @@ -0,0 +1,294 @@ +null := +SPACE := $(null) $(null) + +FFMPEG ?= ffmpeg +FFMPEG := $(FFMPEG) -v warning -y $(FFMPEGFLAGS) +CWEBP ?= cwebp +CWEBP := $(CWEBP) -quiet -alpha_cleanup -m 6 $(CWEBPFLAGS) +WEBPMUX ?= webpmux +WEBPMUX := $(WEBPMUX) $(WEBPMUXFLAGS) +CONVERT ?= convert +CONVERT := $(CONVERT) $(CONVERTFLAGS) +APNGASM ?= apngasm +APNGASM := $(APNGASM) $(APNGASMFLAGS) +NPM ?= npm +NPM := $(NPM) --quiet $(NPMFLAGS) +JS_COMPRESSOR ?= uglifyjs +LOCAL_UGLIFYJS := node_modules/.bin/uglifyjs +UGLIFYJS ?= $(LOCAL_UGLIFYJS) +UGLIFYJS := $(UGLIFYJS) $(UGLIFYJSFLAGS) +PACKR ?= packr +CLOSURE_COMPILER ?= java -jar compiler.jar +ifndef MINIMAL +ZOPFLIPNG ?= zopflipng +ZOPFLIPNG := $(ZOPFLIPNG) $(ZOPFLIPNGFLAGS) +#DEFLOPT ?= wine DeflOpt +#DEFLOPT := $(DEFLOPT) $(DEFLOPTFLAGS) +DEFLUFF ?= defluff +DEFLUFF := $(DEFLUFF) $(DEFLUFFFLAGS) +PNGQUANT ?= pngquant +PNGQUANT := $(PNGQUANT) $(PNGQUANTFLAGS) +endif +GZIP := $(shell unrpyc/find-gzip.sh) + +all: js json video audio images + +# === JS === + +MYJS := www/js/html5ks.js www/js/menu.js www/js/api.js \ + www/js/characters.js www/js/imachine.js www/js/i18n.js +JSLIBS := www/js/lib/fastclick.js www/js/lib/modernizr-build.js \ + www/js/lib/when.js www/js/lib/spin.js +JSDATA := www/js/play.js www/js/images.js +JS := $(JSLIBS) $(MYJS) $(JSDATA) +JSOUT := www/js/all.min.js + +js: $(JSOUT) + +ifeq ($(JS_COMPRESSOR), uglifyjs) +$(JSOUT): $(JS) $(UGLIFYJS) + $(UGLIFYJS) $(JS) -o "$@" --source-map "$@".map --source-map-url ./all.min.js.map --screw-ie8 -p 2 -m -c unsafe=true,drop_debugger=false +else +$(JSOUT): $(JS) +# note that packr doesn't actually work +ifeq ($(JS_COMPRESSOR), packr) + $(PACKR) $(JS) -o "$@" +else +ifeq ($(JS_COMPRESSOR), closure_compiler) + $(CLOSURE_COMPILER) --compilation_level SIMPLE_OPTIMIZATIONS --create_source_map "$@".map --js $(subst $(SPACE), --js ,$(JS)) --js_output_file "$@" +endif # ($(JS_COMPRESSOR), closure_compiler) +endif # ($(JS_COMPRESSOR), packr) +endif # ($(JS_COMPRESSOR), uglifyjs) + +$(LOCAL_UGLIFYJS): package.json + $(NPM) update + touch "$@" + +www/js/lib/modernizr-build.js: Modernizr config-all.json + ln -fs ../../config-all.json "$<"/lib/config-all.json + cd "$<" && $(NPM) update && node_modules/.bin/grunt build + +www/js/lib/when.js: when + export PYTHON=python2; cd when && $(NPM) update && $(NPM) run browserify-debug + +# === JSON === +JSON := www/json/script.json www/json/script.json.gz www/json/imachine.json \ + www/json/imachine.json.gz www/json/ui-strings.json \ + www/json/ui-strings.json.gz www/json/ui-strings_FR.json \ + www/json/ui-strings_FR.json.gz + +RPYC := unrpyc/script-a1-friday_FR.rpyc unrpyc/script-a1-friday.rpyc \ + unrpyc/script-a1-monday_FR.rpyc unrpyc/script-a1-monday.rpyc \ + unrpyc/script-a1-saturday_FR.rpyc unrpyc/script-a1-saturday.rpyc \ + unrpyc/script-a1-sunday_FR.rpyc unrpyc/script-a1-sunday.rpyc \ + unrpyc/script-a1-thursday_FR.rpyc unrpyc/script-a1-thursday.rpyc \ + unrpyc/script-a1-tuesday_FR.rpyc unrpyc/script-a1-tuesday.rpyc \ + unrpyc/script-a1-wednesday_FR.rpyc unrpyc/script-a1-wednesday.rpyc \ + unrpyc/script-a2-emi_FR.rpyc unrpyc/script-a2-emi.rpyc \ + unrpyc/script-a2-hanako_FR.rpyc unrpyc/script-a2-hanako.rpyc \ + unrpyc/script-a2-lilly_FR.rpyc unrpyc/script-a2-lilly.rpyc \ + unrpyc/script-a2-rin_FR.rpyc unrpyc/script-a2-rin.rpyc \ + unrpyc/script-a2-shizune_FR.rpyc unrpyc/script-a2-shizune.rpyc \ + unrpyc/script-a3-emi_FR.rpyc unrpyc/script-a3-emi.rpyc \ + unrpyc/script-a3-hanako_FR.rpyc unrpyc/script-a3-hanako.rpyc \ + unrpyc/script-a3-lilly_FR.rpyc unrpyc/script-a3-lilly.rpyc \ + unrpyc/script-a3-rin_FR.rpyc unrpyc/script-a3-rin.rpyc \ + unrpyc/script-a3-shizune_FR.rpyc unrpyc/script-a3-shizune.rpyc \ + unrpyc/script-a4-emi_FR.rpyc unrpyc/script-a4-emi.rpyc \ + unrpyc/script-a4-hanako_FR.rpyc unrpyc/script-a4-hanako.rpyc \ + unrpyc/script-a4-lilly_FR.rpyc unrpyc/script-a4-lilly.rpyc \ + unrpyc/script-a4-rin_FR.rpyc unrpyc/script-a4-rin.rpyc \ + unrpyc/script-a4-shizune_FR.rpyc unrpyc/script-a4-shizune.rpyc +SJSON := $(patsubst unrpyc/%.rpyc,www/json/%.json,$(RPYC)) +JSONO := $(patsubst unrpyc/%.rpyc,unrpyc/%.json.o,%(RPYC)) + +# FIXME +# json: $(JSON) +json: + +%.json.gz: %.json + $(GZIP) -c $< > $@ + touch $< $@ + +www/json/script.json: $(SJSON) + cat $^ > "$@" + sed -i -e 's/^/{/;s/,$$/}/' "$@" + +www/json/%.json: unrpyc/%.json.o + uglifyjs "$<" --expr > "$@" + +define unrpyc = + python3 unrpyc/unrpyc.py --clobber "$<" "$@" +endef + +unrpyc/ui-strings.json.o unrpyc/ui-strings_FR.json.o: unrpyc/ui-strings%.json.o: unrpyc/ui-strings%.rpyc unrpyc/*.py + $(unrpyc) + sed -i -e 's/ \["init_language", "[a-z]*", \],//;s/^\]}$$/}/' "$@" + +unrpyc/ui_settings.json.o: ui_settings.rpyc *.py + python3 unrpyc.py --clobber "$<" --ignore-python "$@" + sed -i -e 's/,,/,/g;/: *,$$/d' "$@" + +unrpyc/%.json.o: unrpyc/%.rpyc unrpyc/*.py + $(unrpyc) + +www/json/script-%.json: unrpyc/script-%.json.o + uglifyjs "$<" --expr > "$@" + sed -i -e 's/^{//;s/}$$/,/' "$@" + +# === VIDEO === + +VIDEO := $(wildcard www/dump/video/*.mkv) +Y4M := $(patsubst %.mkv,%.y4m,$(VIDEO)) +MP4 := $(patsubst %.mkv,%.mp4,$(VIDEO)) +WEBM := $(patsubst %.mkv,%.webm,$(VIDEO)) +VP9 := $(patsubst %.mkv,%.vp9.webm,$(VIDEO)) +OGV := $(patsubst %.mkv,%.ogv,$(VIDEO)) +CVIDEO := $(MP4) $(WEBM) $(VP9) $(OGV) + +video: $(CVIDEO) + +%.y4m: %.mkv + $(FFMPEG) -i "$<" -c:a copy "$@" + +$(if $(NOTEMP),%.mp4: %.mkv,%.mp4: %.y4m) + $(FFMPEG) -i "$<" -c:v libx264 -preset slower -tune animation -movflags empty_moov -profile:v baseline -c:a libfdk_aac -b:a 60k "$@" + +$(if $(NOTEMP),%.webm: %.mkv,%.webm: %.y4m) + $(FFMPEG) -i "$<" -crf 10 -b:v 1M -c:a copy "$@" + +$(if $(NOTEMP),%.vp9.webm: %.mkv,%.vp9.webm: %.y4m) + $(FFMPEG) -i "$<" -strict -2 -c:v libvpx-vp9 -crf 8 -b:v 1M -c:a libopus -vbr on -b:a 64k "$@" + +$(if $(NOTEMP),%.ogv: %.mkv,%.ogv: %.y4m) + $(FFMPEG) -i "$<" -c:v libtheora -qscale:v 10 -c:a copy "$@" + +# === AUDIO === + +AUDIO := $(wildcard www/dump/bgm/*.ogg) $(wildcard www/dump/sfx/*.ogg) +OPUS := $(patsubst %.ogg,%.opus,$(AUDIO)) +M4A := $(patsubst %.ogg,%.m4a,$(AUDIO)) +WAV := $(patsubst %.ogg,%.wav,$(AUDIO)) +CAUDIO := $(OPUS) $(M4A) + +audio: $(CAUDIO) + +%.wav: %.ogg + $(FFMPEG) -i "$<" -c:a pcm_s16le "$@" + +%.opus: %.wav + $(FFMPEG) -i "$<" -c:a libopus -vbr on -b:a 64k "$@" + +%.m4a: %.wav + $(FFMPEG) -i "$<" -b:a 60k "$@" + +# === IMAGES === + +PNG := $(shell find www/dump -name '*.png' ! -name 'ctc_strip.png') +JPG := $(shell find www/dump -name '*.jpg') +WEBP := $(patsubst %.png,%.webp,$(PNG)) \ + $(patsubst %.jpg,%.webp,$(JPG)) +CTC_ANIM_SRC := www/dump/ui/ctc_strip.png +CTC_ANIM_MORE_TMP := www/dump/ui/ctc_strip-1.png \ + www/dump/ui/ctc_strip-2.png www/dump/ui/ctc_strip-3.png \ + www/dump/ui/ctc_strip-4.png www/dump/ui/ctc_strip-5.png \ + www/dump/ui/ctc_strip-6.png www/dump/ui/ctc_strip-7.png \ + www/dump/ui/ctc_strip-8.png www/dump/ui/ctc_strip-9.png \ + www/dump/ui/ctc_strip-10.png www/dump/ui/ctc_strip-11.png \ + www/dump/ui/ctc_strip-12.png www/dump/ui/ctc_strip-13.png \ + www/dump/ui/ctc_strip-14.png www/dump/ui/ctc_strip-15.png \ + www/dump/ui/ctc_strip-16.png www/dump/ui/ctc_strip-17.png \ + www/dump/ui/ctc_strip-18.png www/dump/ui/ctc_strip-19.png \ + www/dump/ui/ctc_strip-20.png www/dump/ui/ctc_strip-21.png \ + www/dump/ui/ctc_strip-22.png www/dump/ui/ctc_strip-23.png \ + www/dump/ui/ctc_strip-24.png www/dump/ui/ctc_strip-25.png \ + www/dump/ui/ctc_strip-26.png www/dump/ui/ctc_strip-27.png \ + www/dump/ui/ctc_strip-28.png www/dump/ui/ctc_strip-29.png \ + www/dump/ui/ctc_strip-30.png www/dump/ui/ctc_strip-31.png \ + www/dump/ui/ctc_strip-32.png www/dump/ui/ctc_strip-33.png \ + www/dump/ui/ctc_strip-34.png www/dump/ui/ctc_strip-35.png \ + www/dump/ui/ctc_strip-36.png www/dump/ui/ctc_strip-37.png \ + www/dump/ui/ctc_strip-38.png www/dump/ui/ctc_strip-39.png \ + www/dump/ui/ctc_strip-40.png www/dump/ui/ctc_strip-41.png \ + www/dump/ui/ctc_strip-42.png www/dump/ui/ctc_strip-43.png \ + www/dump/ui/ctc_strip-44.png www/dump/ui/ctc_strip-45.png \ + www/dump/ui/ctc_strip-46.png www/dump/ui/ctc_strip-47.png \ + www/dump/ui/ctc_strip-48.png www/dump/ui/ctc_strip-49.png \ + www/dump/ui/ctc_strip-50.png www/dump/ui/ctc_strip-51.png \ + www/dump/ui/ctc_strip-52.png www/dump/ui/ctc_strip-53.png \ + www/dump/ui/ctc_strip-54.png www/dump/ui/ctc_strip-55.png \ + www/dump/ui/ctc_strip-56.png www/dump/ui/ctc_strip-57.png \ + www/dump/ui/ctc_strip-58.png www/dump/ui/ctc_strip-59.png \ + www/dump/ui/ctc_strip-60.png www/dump/ui/ctc_strip-61.png \ + www/dump/ui/ctc_strip-62.png www/dump/ui/ctc_strip-63.png +CTC_ANIM_TMP := www/dump/ui/ctc_strip-0.png $(CTC_ANIM_MORE_TMP) +CTC_ANIM_TMP_WEBP := $(patsubst %.png,%.webp,$(CTC_ANIM_TMP)) +CTC_ANIM_TMP_ALL := $(CTC_ANIM_TMP) $(CTC_ANIM_TMP_WEBP) +CTC_ANIM := www/dump/ui/ctc_anim.png www/dump/ui/ctc_anim.webp + +CIMAGE := $(WEBP) $(CTC_ANIM) www/favicon.ico + +images: $(CIMAGE) + +define png2webp = + $(CWEBP) -q 99 "$<" -o "$@" + $(if $(PNGQUANT), $(PNGQUANT) --force --speed 1 --ext .png "$<") + $(if $(ZOPFLIPNG), $(ZOPFLIPNG) -m -y "$<" "$<") + $(if $(DEFLOPT), $(DEFLOPT) "$<") + $(if $(DEFLUFF), $(DEFLUFF) < "$<" > "$<".tmp + mv -f "$<".tmp "$<") +endef + +%.webp: %.png + $(png2webp) + +%.webp: %.jpg + $(CWEBP) -q 90 "$<" -o "$@" + +www/dump/ui/ctc_strip.webp: www/dump/ui/ctc_strip.png + @ + +www/favicon.ico: www/dump/ui/icon.png + $(CONVERT) "$<" -resize 256x256 -transparent white "$@" + +www/dump/ui/bt-cf-unchecked.webp www/dump/ui/bt-cf-checked.webp: %.webp: %.png + $(CONVERT) -trim "$<" "$<" + $(png2webp) + +www/dump/ui/ctc_strip-0.png: $(CTC_ANIM_SRC) + $(CONVERT) "$<" -crop 16x16 www/dump/ui/ctc_strip-%d.png + +$(CTC_ANIM_MORE_TMP): $(CTC_ANIM_SRC) www/dump/ui/ctc_strip-0.png + @ + +# depend on webp to wait for recompression +www/dump/ui/ctc_anim.png: $(CTC_ANIM_TMP_WEBP) + $(APNGASM) "$@" $(CTC_ANIM_TMP) 3 100 + +www/dump/ui/ctc_anim.webp: $(CTC_ANIM_TMP_WEBP) + $(WEBPMUX) -frame $(subst $(SPACE), +30 -frame ,$^) +30 -loop 0 -o "$@" + +# === MISC === + +test: $(MYJS) + jshint --show-non-errors $^ + +space: + $(RM) -r www/dump/font + $(RM) $(WAV) $(VIDEO) $(CTC_ANIM_TMP) $(CTC_ANIM_TMP_WEBP) $(JSOUT) $(JSOUT).map + +dev: + $(MAKE) + ./nginx.sh + while inotifywait -r -e modify,delete,move --exclude="^\./\.git" --exclude="\.swp.?$$" .; do \ + $(MAKE); \ + done + +# disable implicit rules, increases `make` speed by 3 seconds +# also check symlink targets (for js) +MAKEFLAGS=-LRr +.SUFFIXES: + +.PRECIOUS: $(WAV) +.INTERMEDIATE: $(WAV) $(JSONO) $(Y4M) $(CTC_ANIM_TMP_ALL) $(CTC_ANIM_TMP_WEBP) +.PHONY: video audio images js jshint clean space watch diff --git a/ast2json/ast2json.py b/ast2json/ast2json.py new file mode 100644 index 0000000..7f52687 --- /dev/null +++ b/ast2json/ast2json.py @@ -0,0 +1,62 @@ +# Copyright (c) 2013, Laurent Peuch +# +# All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from _ast import AST +from ast import parse + + +def ast2json(node): + assert isinstance(node, AST) + to_return = {} + to_return['_type'] = node.__class__.__name__ + for attr in dir(node): + if attr.startswith("_"): + continue + to_return[attr] = get_value(getattr(node, attr)) + + return to_return + + +def str2json(string): + return ast2json(parse(string)) + + +def get_value(attr_value): + if attr_value is None: + return attr_value + if isinstance(attr_value, (int, str, float, complex, bool)): + return attr_value + if isinstance(attr_value, list): + return [get_value(x) for x in attr_value] + if isinstance(attr_value, AST): + return ast2json(attr_value) + else: + raise Exception("unknow case for '%s' of type '%s'" % (attr_value, type(attr_value))) + + +if __name__ == '__main__': + import json + print(json.dumps(ast2json(parse(open(__file__, "r").read())), indent=4)) diff --git a/ast2json/decompiler.py b/ast2json/decompiler.py new file mode 100644 index 0000000..0a0b916 --- /dev/null +++ b/ast2json/decompiler.py @@ -0,0 +1,500 @@ +# Copyright (c) 2012 Yuri K. Schlesner +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import renpy.ast as ast +import renpy.atl as atl +import codegen +import screendecompiler +import code + +DECOMPILE_SCREENS = True + +def pretty_print_ast(out_file, ast): + code.interact(local=locals()) + for stmt in ast: + print_statement(out_file, stmt, 0) + +def indent(f, level): + # Print indentation + f.write(u' ' * level) + +def print_statement(f, statement, indent_level=0): + indent(f, indent_level) + + func = statement_printer_dict.get(type(statement), print_Unknown) + func(f, statement, indent_level) + +def escape_string(s): + s = s.replace('"', '\\"') + s = s.replace('\n', '\\n') + s = s.replace('\t', '\\t') + return s + +# TODO "choice" and "parallel" blocks are greedily combined +# so we need a "pass" statement to separate them if +# multiple of the same block are immediately after +# each other. +def print_atl(f, atl_block, indent_level): + if not atl_block.statements: + indent(f, indent_level) + f.write(u'pass\n') + for stmt in atl_block.statements: + indent(f, indent_level) + + if type(stmt) is atl.RawMultipurpose: + # warper + if stmt.warp_function: + f.write(u"warp %s" % (stmt.warp_function.strip(), )) + f.write(u" %s " % (stmt.duration.strip(), )) + elif stmt.warper: + f.write(stmt.warper) + f.write(u" %s " % (stmt.duration.strip(), )) + elif stmt.duration.strip() != '0': + f.write(u'pause') + f.write(u" %s" % (stmt.duration.strip(), )) + + # revolution + if stmt.revolution: + f.write(u"%s " % (stmt.revolution, )) + + # circles + if stmt.circles != "0": + f.write(u"circles %s " % (stmt.circles.strip(), )) + + # splines + for (name, exprs) in stmt.splines: + f.write(u"%s " % (name, )) + for expr in exprs: + f.write(u"knot %s " % (expr.strip(), )) + + # properties + for (k, v) in stmt.properties: + f.write(u"%s %s " % (k, v.strip())) + + # with + for (expr, with_expr) in stmt.expressions: + f.write(u"%s " % (expr.strip(), )) + if with_expr: + f.write(u"with %s " % (with_expr, )) + + f.write(u"\n") + + elif type(stmt) is atl.RawBlock: + # what does stmt.animation do? + f.write(u"block:\n") + print_atl(f, stmt, indent_level + 1) + + elif type(stmt) is atl.RawChoice: + first = True + for (chance, block) in stmt.choices: + if first: + first = False + else: + indent(f, indent_level) + + f.write(u"choice") + if chance != "1.0": + f.write(u" %s" % (chance, )) + f.write(u":\n") + print_atl(f, block, indent_level + 1) + + elif type(stmt) is atl.RawContainsExpr: + f.write(u"contains %s\n" % (stmt.expression, )) + + elif type(stmt) is atl.RawEvent: + f.write(u"event %s\n" % (stmt.name, )) + + elif type(stmt) is atl.RawFunction: + f.write(u"function %s\n" % (stmt.expr, )) + + elif type(stmt) is atl.RawOn: + first = True + for name, block in stmt.handlers.iteritems(): + if first: + first = False + else: + indent(f, indent_level) + + f.write(u"on %s:\n" % (name, )) + print_atl(f, block, indent_level + 1) + + elif type(stmt) is atl.RawParallel: + first = True + for block in stmt.blocks: + if first: + first = False + else: + indent(f, indent_level) + + f.write(u"parallel:\n") + print_atl(f, block, indent_level + 1) + + elif type(stmt) is atl.RawRepeat: + f.write(u"repeat") + if stmt.repeats: + f.write(u" %s" % (stmt.repeats, )) # not sure if this is even a string + f.write(u"\n") + + elif type(stmt) is atl.RawTime: + f.write(u"time %s\n" % (stmt.time, )) + + else: + f.write(u"TODO atl.%s\n" % type(stmt).__name__) + +def print_imspec(f, imspec): + if imspec[1] is not None: # Expression + f.write(u'expression ') + f.write(imspec[1]) + else: # Image name + f.write(' '.join(imspec[0])) + + # at + if len(imspec[3]) > 0: + f.write(u" at %s" % (', '.join(imspec[3]))) + + # as + if imspec[2] is not None: + f.write(u" as %s" % (imspec[2], )) + + # behind + if len(imspec[6]) > 0: + f.write(u" behind %s" % (', '.join(imspec[6]))) + + # onlayer + if imspec[4] != 'master': + f.write(u" onlayer %s" % (imspec[4], )) + + # zorder + # This isn't in the docs, but it's in the parser + if imspec[5] is not None: + f.write(u" zorder %s" % (imspec[5], )) + +def print_Label(f, stmt, indent_level): + f.write(u"label %s" % (stmt.name, )) + if stmt.parameters is not None: + print_params(f, stmt.parameters) + f.write(u':\n') + + for sub_stmt in stmt.block: + print_statement(f, sub_stmt, indent_level + 1) + +def print_Say(f, stmt, indent_level): + if stmt.who is not None: + f.write(u"%s " % (stmt.who, )) + f.write(u"\"%s\"" % (escape_string(stmt.what), )) + if stmt.with_ is not None: + f.write(u" with %s" % (stmt.with_, )) + f.write(u'\n') + +def print_Jump(f, stmt, indent_level): + f.write(u"jump ") + if stmt.expression: + # TODO expression + f.write(u"expression TODO") + else: + f.write(stmt.target) + f.write(u'\n') + +def print_Scene(f, stmt, indent_level): + f.write(u"scene") + if stmt.imspec is None: + if stmt.layer != 'master': + f.write(u" onlayer %s" % (stmt.layer, )) + else: + f.write(u' ') + print_imspec(f, stmt.imspec) + + # with isn't handled here, but split in several statements + + if stmt.atl is not None: + f.write(u':\n') + print_atl(f, stmt.atl, indent_level+1) + else: + f.write('\n') + +def print_With(f, stmt, indent_level): + f.write(u"with %s\n" % (stmt.expr, )) + +def print_Show(f, stmt, indent_level): + f.write(u"show ") + print_imspec(f, stmt.imspec) + + # with isn't handled here, but split in several statements + + if stmt.atl is not None: + f.write(u':\n') + print_atl(f, stmt.atl, indent_level+1) + else: + f.write('\n') + +def print_Hide(f, stmt, indent_level): + f.write(u"hide ") + print_imspec(f, stmt.imspec) + + # with isn't handled here, but split in several statements + + f.write('\n') + +def print_Python(f, stmt, indent_level, early=False): + code_src = stmt.code.source + + stripped_code = code_src.strip() + + if stripped_code.count('\n') == 0: + f.write(u"$ %s\n" % (stripped_code, )) + else: + f.write(u"python") + if early: + f.write(u" early") + if stmt.hide: + f.write(u" hide") + f.write(u":\n") + + for line in code_src.splitlines(True): + indent(f, indent_level + 1) + f.write(line) + +def print_Return(f, stmt, indent_level): + f.write(u"return") + + if stmt.expression is not None: + f.write(u" %s" % (stmt.expression, )) + + f.write(u'\n') + +def print_UserStatement(f, stmt, indent_level): + f.write(u"%s\n" % (stmt.line, )) + +def print_Init(f, stmt, indent_level): + if len(stmt.block) == 1 and (hasattr(ast, 'Screen') and isinstance(stmt.block[0], ast.Screen)) and stmt.priority == -500: + print_screen(f, stmt.block[0], indent_level) + else: + f.write(u"init") + if stmt.priority != 0: + f.write(u" %d" % (stmt.priority, )) + f.write(u":\n") + for s in stmt.block: + print_statement(f, s, indent_level + 1) + +def print_Image(f, stmt, indent_level): + f.write(u"image %s" % (' '.join(stmt. imgname), )) + if stmt.code is not None: + f.write(u" = %s\n" % (stmt.code.source, )) + else: + f.write(u":\n") + print_atl(f, stmt.atl, indent_level + 1) + +def print_Transform(f, stmt, indent_level): + f.write(u"transform %s" % (stmt.varname, )) + if stmt.parameters is not None: + print_params(f, stmt.parameters) + + f.write(":\n") + print_atl(f, stmt.atl, indent_level + 1) + +def print_Menu(f, stmt, indent_level): + f.write(u"menu:\n") + + if stmt.with_ is not None: + indent(f, indent_level + 1) + f.write(u"with %s\n" % (stmt.with_, )) + + if stmt.set is not None: + indent(f, indent_level + 1) + f.write(u"set %s\n" % (stmt.with_, )) + + for item in stmt.items: + indent(f, indent_level + 1) + + # caption + f.write(u"\"%s\"" % (escape_string(item[0]), )) + + if item[2] is not None: + # condition + if item[1] != 'True': + f.write(u" if %s" % (item[1], )) + + f.write(u':\n') + + for inner_stmt in item[2]: + print_statement(f, inner_stmt, indent_level + 2) + else: + f.write(u'\n') + +def print_Pass(f, stmt, indent_level): + f.write(u"pass\n") + +def print_Call(f, stmt, indent_level): + f.write(u"call ") + if stmt.expression: + f.write(u"expression %s" % (stmt.label, )) + else: + f.write(stmt.label) + + if stmt.arguments is not None: + if stmt.expression: + f.write(u"pass ") + print_args(f, stmt.arguments) + + f.write(u'\n') + +def print_If(f, stmt, indent_level): + f.write(u"if %s:\n" % (stmt.entries[0][0], )) + for inner_stmt in stmt.entries[0][1]: + print_statement(f, inner_stmt, indent_level + 1) + + if len(stmt.entries) >= 2: + if stmt.entries[-1][0].strip() == 'True': + else_entry = stmt.entries[-1] + elif_entries = stmt.entries[1:-1] + else: + else_entry = None + elif_entries = stmt.entries + + for case in elif_entries: + indent(f, indent_level) + f.write(u"elif %s:\n" % (case[0], )) + for inner_stmt in case[1]: + print_statement(f, inner_stmt, indent_level + 1) + + if else_entry is not None: + indent(f, indent_level) + f.write(u"else:\n") + for inner_stmt in else_entry[1]: + print_statement(f, inner_stmt, indent_level + 1) + +def print_EarlyPython(f, stmt, indent_level): + print_Python(f, stmt, indent_level, early=True) + +# TODO extrapos, extrakw? +def print_args(f, arginfo): + if arginfo is None: + return + + f.write(u"(") + + first = True + for (name, val) in arginfo.arguments: + if first: + first = False + else: + f.write(u", ") + + if name is not None: + f.write(u"%s = " % (name, )) + f.write(val) + + f.write(u")") + +# TODO positional? +def print_params(f, paraminfo): + f.write(u"(") + + first = True + for param in paraminfo.parameters: + if first: + first = False + else: + f.write(u", ") + + f.write(param[0]) + + if (param[1] is not None) and ('None' not in param[1]): + f.write(u" = %s" % param[1]) + if paraminfo.extrapos: + f.write(u", ") + f.write(u"*%s" % paraminfo.extrapos) + if paraminfo.extrakw: + f.write(u", ") + f.write(u"**%s" % paraminfo.extrakw) + + f.write(u")") + +# Print while command, from http://forum.cheatengine.org/viewtopic.php?p=5377683 +def print_While(f, stmt, indent_level): + f.write(u"while %s:\n" % (stmt.condition, )) + for inner_stmt in stmt.block: + print_statement(f, inner_stmt, indent_level + 1) + +# Print define command, by iAmGhost +def print_Define(f, stmt, indent_level): + f.write(u"define %s = %s\n" % (stmt.varname, stmt.code.source,)) + +# Print Screen code (or at least code which does exactly the same. can't be picky, don't have source) +# We'll read the ast out here, but leave the decompilation of the resulting python code to screendecompiler.py +# It'd just bloat up this file +def print_screen(f, stmt, indent_level): + screen = stmt.screen + # The creator of the python ast module also created a script to revert it. + sourcecode = codegen.to_source(screen.code.source, u" "*4) + # why suddenly ast in the source code field + f.write(u"screen %s" % screen.name) + if hasattr(screen, 'parameters') and screen.parameters: + print_params(f, screen.parameters) + f.write(u":\n") + if screen.tag: + indent(f, indent_level+1) + f.write(u"tag %s\n" % screen.tag) + if screen.zorder and screen.zorder != '0': + indent(f, indent_level+1) + f.write(u"zorder %s\n" % screen.zorder) + if screen.modal: + indent(f, indent_level+1) + f.write(u"modal %s\n" % screen.modal) + if screen.variant != "None": + indent(f, indent_level+1) + f.write(u"variant %s\n" % screen.variant) + # actual screen decompilation is HARD + if DECOMPILE_SCREENS: + screendecompiler.print_screen(f, sourcecode, indent_level+1) + else: + indent(f, indent_level+1) + f.write('python:\n') + f.write('\n'.join([" "*(indent_level+2)+line for line in sourcecode.splitlines()])) + f.write(u"\n") + +statement_printer_dict = { + ast.Label: print_Label, + ast.Say: print_Say, + ast.Jump: print_Jump, + ast.Scene: print_Scene, + ast.With: print_With, + ast.Show: print_Show, + ast.Hide: print_Hide, + ast.Python: print_Python, + ast.Return: print_Return, + ast.UserStatement: print_UserStatement, + ast.Init: print_Init, + ast.Image: print_Image, + ast.Transform: print_Transform, + ast.Menu: print_Menu, + ast.Pass: print_Pass, + ast.Call: print_Call, + ast.If: print_If, + ast.While: print_While, + ast.Define: print_Define, + ast.EarlyPython: print_EarlyPython, + } +if hasattr(ast, 'Screen'): #backwards compatability + statement_printer_dict.update({ast.Screen: print_screen}) + +def print_Unknown(f, stmt, indent_level): + print "Unknown AST node: %s" % (type(stmt).__name__, ) + f.write(u"<<>>\n" % (type(stmt).__name__, )) diff --git a/ast2json/renpy/LICENSE.txt b/ast2json/renpy/LICENSE.txt new file mode 100644 index 0000000..383a780 --- /dev/null +++ b/ast2json/renpy/LICENSE.txt @@ -0,0 +1,1024 @@ +.. highlight:: none + +======= +License +======= + +Most of Ren'Py is covered by the terms of the following (MIT) license: + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Portions of Ren'Py are derived from code that is copyright using the +Lesser GNU Public License, so Ren'Py games must be distributed in a +manner that satisfies the LGPL. + +Please see each individual source file for a list of copyright +holders. The artwork in the demo is released by various copyright +holders, under the same terms. + +Ren'Py binaries include code from the following projects: + +* Python (Python License) +* Pygame (LGPL) +* SDL (LGPL) +* SDL_image (LGPL) +* SDL_ttf (LGPL) +* Freetype (LGPL) +* Fribidi (LGPL) +* libav (LGPL) +* libjpeg-turbo (LGPL) +* libpng (PNG license) +* zlib (Zlib License) +* bzip2 (Bzip2 License) +* pyobjc (MIT License) +* py2exe (MIT License) +* GLEW (Modified BSD, MIT) +* zsync (Artistic License) + +For the purpose of LGPL compliance, the source code to all LGPL +software we depend on is either in the Ren'Py package (available from +http://www.renpy.org/), or in the renpy-deps package +(http://www.renpy.org/dl/lgpl/). We believe compliance can be achieved +by including a copy of this license with every copy of Ren'Py you +distribute, and referring to it in your project's README file. + +Ren'Py may be distributed alongside the jEdit or Editra text +editors. Editra is licensed under the wxWindows license, while +jEdit is under the GNU General Public License. + + +GNU Lesser General Public License +================================= + +:: + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General Public + Licenses are intended to guarantee your freedom to share and change + free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some + specially designated software packages--typically libraries--of the + Free Software Foundation and other authors who decide to use it. You + can use it too, but we suggest you first think carefully about whether + this license or the ordinary General Public License is the better + strategy to use in any particular case, based on the explanations + below. + + When we speak of free software, we are referring to freedom of use, + not price. Our General Public Licenses are designed to make sure that + you have the freedom to distribute copies of free software (and charge + for this service if you wish); that you receive source code or can get + it if you want it; that you can change the software and use pieces of + it in new free programs; and that you are informed that you can do + these things. + + To protect your rights, we need to make restrictions that forbid + distributors to deny you these rights or to ask you to surrender these + rights. These restrictions translate to certain responsibilities for + you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis + or for a fee, you must give the recipients all the rights that we gave + you. You must make sure that they, too, receive or can get the source + code. If you link other code with the library, you must provide + complete object files to the recipients, so that they can relink them + with the library after making changes to the library and recompiling + it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the + library, and (2) we offer you this license, which gives you legal + permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that + there is no warranty for the free library. Also, if the library is + modified by someone else and passed on, the recipients should know + that what they have is not the original version, so that the original + author's reputation will not be affected by problems that might be + introduced by others. + + Finally, software patents pose a constant threat to the existence of + any free program. We wish to make sure that a company cannot + effectively restrict the users of a free program by obtaining a + restrictive license from a patent holder. Therefore, we insist that + any patent license obtained for a version of the library must be + consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the + ordinary GNU General Public License. This license, the GNU Lesser + General Public License, applies to certain designated libraries, and + is quite different from the ordinary General Public License. We use + this license for certain libraries in order to permit linking those + libraries into non-free programs. + + When a program is linked with a library, whether statically or using + a shared library, the combination of the two is legally speaking a + combined work, a derivative of the original library. The ordinary + General Public License therefore permits such linking only if the + entire combination fits its criteria of freedom. The Lesser General + Public License permits more lax criteria for linking other code with + the library. + + We call this license the "Lesser" General Public License because it + does Less to protect the user's freedom than the ordinary General + Public License. It also provides other free software developers Less + of an advantage over competing non-free programs. These disadvantages + are the reason we use the ordinary General Public License for many + libraries. However, the Lesser license provides advantages in certain + special circumstances. + + For example, on rare occasions, there may be a special need to + encourage the widest possible use of a certain library, so that it + becomes a de-facto standard. To achieve this, non-free programs must + be allowed to use the library. A more frequent case is that a free + library does the same job as widely used non-free libraries. In this + case, there is little to gain by limiting the free library to free + software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free + programs enables a greater number of people to use a large body of + free software. For example, permission to use the GNU C Library in + non-free programs enables many more people to use the whole GNU + operating system, as well as its variant, the GNU/Linux operating + system. + + Although the Lesser General Public License is Less protective of the + users' freedom, it does ensure that the user of a program that is + linked with the Library has the freedom and the wherewithal to run + that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and + modification follow. Pay close attention to the difference between a + "work based on the library" and a "work that uses the library". The + former contains code derived from the library, whereas the latter must + be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other + program which contains a notice placed by the copyright holder or + other authorized party saying it may be distributed under the terms of + this Lesser General Public License (also called "this License"). + Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data + prepared so as to be conveniently linked with application programs + (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work + which has been distributed under these terms. A "work based on the + Library" means either the Library or any derivative work under + copyright law: that is to say, a work containing the Library or a + portion of it, either verbatim or with modifications and/or translated + straightforwardly into another language. (Hereinafter, translation is + included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for + making modifications to it. For a library, complete source code means + all the source code for all modules it contains, plus any associated + interface definition files, plus the scripts used to control + compilation and installation of the library. + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of + running a program using the Library is not restricted, and output from + such a program is covered only if its contents constitute a work based + on the Library (independent of the use of the Library in a tool for + writing it). Whether that is true depends on what the Library does + and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's + complete source code as you receive it, in any medium, provided that + you conspicuously and appropriately publish on each copy an + appropriate copyright notice and disclaimer of warranty; keep intact + all the notices that refer to this License and to the absence of any + warranty; and distribute a copy of this License along with the + Library. + + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange for a + fee. + + 2. You may modify your copy or copies of the Library or any portion + of it, thus forming a work based on the Library, and copy and + distribute such modifications or work under the terms of Section 1 + above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Library, + and can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based + on the Library, the distribution of the whole must be on the terms of + this License, whose permissions for other licensees extend to the + entire whole, and thus to each and every part regardless of who wrote + it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library + with the Library (or with a work based on the Library) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public + License instead of this License to a given copy of the Library. To do + this, you must alter all the notices that refer to this License, so + that they refer to the ordinary GNU General Public License, version 2, + instead of to this License. (If a newer version than version 2 of the + ordinary GNU General Public License has appeared, then you can specify + that version instead if you wish.) Do not make any other change in + these notices. + + Once this change is made in a given copy, it is irreversible for + that copy, so the ordinary GNU General Public License applies to all + subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of + the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or + derivative of it, under Section 2) in object code or executable form + under the terms of Sections 1 and 2 above provided that you accompany + it with the complete corresponding machine-readable source code, which + must be distributed under the terms of Sections 1 and 2 above on a + medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy + from a designated place, then offering equivalent access to copy the + source code from the same place satisfies the requirement to + distribute the source code, even though third parties are not + compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the + Library, but is designed to work with the Library by being compiled or + linked with it, is called a "work that uses the Library". Such a + work, in isolation, is not a derivative work of the Library, and + therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library + creates an executable that is a derivative of the Library (because it + contains portions of the Library), rather than a "work that uses the + library". The executable is therefore covered by this License. + Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file + that is part of the Library, the object code for the work may be a + derivative work of the Library even though the source code is not. + Whether this is true is especially significant if the work can be + linked without the Library, or if the work is itself a library. The + threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data + structure layouts and accessors, and small macros and small inline + functions (ten lines or less in length), then the use of the object + file is unrestricted, regardless of whether it is legally a derivative + work. (Executables containing this object code plus portions of the + Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may + distribute the object code for the work under the terms of Section 6. + Any executables containing that work also fall under Section 6, + whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or + link a "work that uses the Library" with the Library to produce a + work containing portions of the Library, and distribute that work + under terms of your choice, provided that the terms permit + modification of the work for the customer's own use and reverse + engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the + Library is used in it and that the Library and its use are covered by + this License. You must supply a copy of this License. If the work + during execution displays copyright notices, you must include the + copyright notice for the Library among them, as well as a reference + directing the user to the copy of this License. Also, you must do one + of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the + Library" must include any data and utility programs needed for + reproducing the executable from it. However, as a special exception, + the materials to be distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies + the executable. + + It may happen that this requirement contradicts the license + restrictions of other proprietary libraries that do not normally + accompany the operating system. Such a contradiction means you cannot + use both them and the Library together in an executable that you + distribute. + + 7. You may place library facilities that are a work based on the + Library side-by-side in a single library together with other library + facilities not covered by this License, and distribute such a combined + library, provided that the separate distribution of the work based on + the Library and of the other library facilities is otherwise + permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute + the Library except as expressly provided under this License. Any + attempt otherwise to copy, modify, sublicense, link with, or + distribute the Library is void, and will automatically terminate your + rights under this License. However, parties who have received copies, + or rights, from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Library or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Library (or any work based on the + Library), you indicate your acceptance of this License to do so, and + all its terms and conditions for copying, distributing or modifying + the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the + Library), the recipient automatically receives a license from the + original licensor to copy, distribute, link with or modify the Library + subject to these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted herein. + You are not responsible for enforcing compliance by third parties with + this License. + + 11. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Library at all. For example, if a patent + license would not permit royalty-free redistribution of the Library by + all those who receive copies directly or indirectly through you, then + the only way you could satisfy both it and this License would be to + refrain entirely from distribution of the Library. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply, and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is willing + to distribute software through any other system and a licensee cannot + impose that choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Library under this License + may add an explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among + countries not thus excluded. In such case, this License incorporates + the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new + versions of the Lesser General Public License from time to time. + Such new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Library + specifies a version number of this License which applies to it and + "any later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Library does not specify a + license version number, you may choose any version ever published by + the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free + programs whose distribution conditions are incompatible with these, + write to the author to ask for permission. For software which is + copyrighted by the Free Software Foundation, write to the Free + Software Foundation; we sometimes make exceptions for this. Our + decision will be guided by the two goals of preserving the free status + of all derivatives of our free software and of promoting the sharing + and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE + LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME + THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU + FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE + LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING + RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A + FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF + SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest + possible use to the public, we recommend making it free software that + everyone can redistribute and change. You can do so by permitting + redistribution under these terms (or, alternatively, under the terms + of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. + It is safest to attach them to the start of each source file to most + effectively convey the exclusion of warranty; and each file should + have at least the "copyright" line and a pointer to where the full + notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Also add information on how to contact you by electronic and paper mail. + + You should also get your employer (if you work as a programmer) or + your school, if any, to sign a "copyright disclaimer" for the library, + if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + + That's all there is to it! + + +Python License +============== + +:: + + 1. This LICENSE AGREEMENT is between the Python Software Foundation + ("PSF"), and the Individual or Organization ("Licensee") accessing and + otherwise using Python 2.3 software in source or binary form and its + associated documentation. + + 2. Subject to the terms and conditions of this License Agreement, PSF + hereby grants Licensee a nonexclusive, royalty-free, world-wide + license to reproduce, analyze, test, perform and/or display publicly, + prepare derivative works, distribute, and otherwise use Python 2.3 + alone or in any derivative version, provided, however, that PSF's + License Agreement and PSF's notice of copyright, i.e., "Copyright (c) + 2001, 2002 Python Software Foundation; All Rights Reserved" are + retained in Python 2.3 alone or in any derivative version prepared by + Licensee. + + 3. In the event Licensee prepares a derivative work that is based on + or incorporates Python 2.3 or any part thereof, and wants to make + the derivative work available to others as provided herein, then + Licensee hereby agrees to include in any such work a brief summary of + the changes made to Python 2.3. + + 4. PSF is making Python 2.3 available to Licensee on an "AS IS" + basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR + IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND + DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT + INFRINGE ANY THIRD PARTY RIGHTS. + + 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON + 2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS + A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3, + OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + + 6. This License Agreement will automatically terminate upon a material + breach of its terms and conditions. + + 7. Nothing in this License Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between PSF and + Licensee. This License Agreement does not grant permission to use PSF + trademarks or trade name in a trademark sense to endorse or promote + products or services of Licensee, or any third party. + + 8. By copying, installing or otherwise using Python 2.3, Licensee + agrees to be bound by the terms and conditions of this License + Agreement. + +Jpeg License +============ + +:: + + In plain English: + + 1. We don't promise that this software works. (But if you find any bugs, + please let us know!) + 2. You can use this software for whatever you want. You don't have to pay us. + 3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + + In legalese: + + The authors make NO WARRANTY or representation, either express or implied, + with respect to this software, its quality, accuracy, merchantability, or + fitness for a particular purpose. This software is provided "AS IS", and you, + its user, assume the entire risk as to its quality and accuracy. + + This software is copyright (C) 1991-1998, Thomas G. Lane. + All Rights Reserved except as specified below. + + Permission is hereby granted to use, copy, modify, and distribute this + software (or portions thereof) for any purpose, without fee, subject to these + conditions: + (1) If any part of the source code for this software is distributed, then this + README file must be included, with this copyright and no-warranty notice + unaltered; and any additions, deletions, or changes to the original files + must be clearly indicated in accompanying documentation. + (2) If only executable code is distributed, then the accompanying + documentation must state that "this software is based in part on the work of + the Independent JPEG Group". + (3) Permission for use of this software is granted only if the user accepts + full responsibility for any undesirable consequences; the authors accept + NO LIABILITY for damages of any kind. + + These conditions apply to any software derived from or based on the IJG code, + not just to the unmodified library. If you use our work, you ought to + acknowledge us. + + Permission is NOT granted for the use of any IJG author's name or company name + in advertising or publicity relating to this software or products derived from + it. This software may be referred to only as "the Independent JPEG Group's + software". + + We specifically permit and encourage the use of this software as the basis of + commercial products, provided that all warranty or liability claims are + assumed by the product vendor. + + +PNG License +=========== + +:: + + The PNG Reference Library is supplied "AS IS". The Contributing Authors + and Group 42, Inc. disclaim all warranties, expressed or implied, + including, without limitation, the warranties of merchantability and of + fitness for any purpose. The Contributing Authors and Group 42, Inc. + assume no liability for direct, indirect, incidental, special, exemplary, + or consequential damages, which may result from the use of the PNG + Reference Library, even if advised of the possibility of such damage. + + Permission is hereby granted to use, copy, modify, and distribute this + source code, or portions hereof, for any purpose, without fee, subject + to the following restrictions: + + 1. The origin of this source code must not be misrepresented. + + 2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + + The Contributing Authors and Group 42, Inc. specifically permit, without + fee, and encourage the use of this source code as a component to + supporting the PNG file format in commercial products. If you use this + source code in a product, acknowledgment is not required but would be + appreciated. + +Zlib License +============ + +:: + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +Bzip2 License +============= + +:: + + This program, "bzip2", the associated library "libbzip2", and all + documentation, are copyright (C) 1996-2005 Julian R Seward. All + rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Modified BSD License +==================== + +:: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + +Artistic License +================ + +:: + + The Artistic License + Version 2.0beta4, October 2000 + + Copyright (C) 2000, Larry Wall. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + This copyright license states the terms under which a given free + software Package may be copied, modified and/or redistributed, while the + Originator(s) maintain some artistic control over the future development + of that Package (at least as much artistic control as can be given under + copyright law while still making the Package open source and free software). + + This license is bound by copyright law, and thus it legally applies only + to works which the copyright holder has permitted copying, distribution + or modification under the terms of the Artistic License, Version 2.0. + + You are reminded that You are always permitted to make arrangements + wholly outside of a given copyright license directly with the copyright + holder(s) of a given Package. If the terms of this license impede your + ability to make full use of the Package, You are encouraged to contact + the copyright holder(s) and seek a different licensing arrangement. + + Definitions + + "Package" refers to the collection of files distributed by the + Originator(s), and derivatives of that collection of files created + through textual modification. + + "Standard Version" refers to the Package if it has not been modified, or + has been modified only in ways suggested by the Originator(s). + + "Modified Version" refers to the Package, if it has been changed by You + via textual modification of the source code, and such changes were not + suggested by the Originator(s). + + "Originator" refers to the author(s) and/or copyright holder(s) of the + Standard Version of the Package. + + "You" and "Your" refers to any person who would like to copy, + distribute, or modify the Package. + + "Distribution Fee" is any fee that You charge for providing a copy of + this Package to another party. It does not refer to licensing fees. + + "Freely Available" means that: + + (a) no fee is charged for the right to use the item (though a + Distribution Fee may be charged). + + (b) recipients of the item may redistribute it under the same + conditions they received it. + + (c) If the item is a binary, object code, bytecode, the complete + corresponding machine-readable source code is included with the + item. + + + Permission for Use and Modification Without Redistribution + + (1) You are permitted to use the Standard Version and create and use + Modified Versions for any purpose without restriction, provided that + you do not redistribute the Modified Version to others outside of your + company or organization. + + + Permissions for Redistribution of the Standard Version + + (2) You may make available verbatim copies of the source code of the + Standard Version of this Package in any medium without restriction, + either gratis or for a Distribution Fee, provided that you duplicate + all of the original copyright notices and associated disclaimers. At + Your discretion, such verbatim copies may or may not include compiled + bytecode, object code or binary versions of the corresponding source + code in the same medium. + + (3) You may apply any bug fixes, portability changes, and other + modifications made available from any of the Originator(s). The + resulting modified Package will still be considered the Standard + Version, and may be copied, modified and redistributed under the terms + of the original license of the Standard Version as if it were the + Standard Version. + + + Permissions for Redistribution of Modified Versions of the Package as Source + + (4) You may modify your copy of the source code of this Package in any way + and distribute that Modified Version (either gratis or for a + Distribution Fee, and with or without a corresponding binary, bytecode + or object code version of the Modified Version) provided that You + clearly indicate what changes You made to the Package, and provided + that You do at least ONE of the following: + + (a) make the Modified Version available to the Originator(s) of the + Standard Version, under the exact license of the Standard + Version, so that the Originator(s) may include your modifications + into the Standard Version (at their discretion). + + (b) modify any installation scripts and procedures so that + installation of the Modified Version will never conflict with an + installation of the Standard Version, include for each program + installed by the Modified Version clear documentation describing + how it differs from the Standard Version, and rename your + Modified Version so that the name is substantially different from + the Standard Version. + + (c) permit and encourage anyone who receives a copy of the Modified + Version permission to make your modifications Freely Available in + some specific way. + + If Your Modified Version is in turn derived from a Modified Version + made by a third party, then You are still required to ensure that Your + Modified Version complies with the requirements of this license. + + + Permissions for Redistribution of Non-Source Versions of Package + + (5) You may distribute binary, object code, bytecode or other non-source + versions of the Standard Version of the Package, provided that you + include complete instructions on where to get the source code of the + Standard Version. Such instructions must be valid at the time of Your + distribution. If these instructions, at any time while You are + carrying our such distribution, become invalid, you must provide new + instructions on demand or cease further distribution. If You cease + distribution within thirty days after You become aware that the + instructions are invalid, then You do not forfeit any of Your rights + under this license. + + (6) You may distribute binary, object code, bytecode or other non-source + versions of a Modified Version provided that You do at least ONE of + the following: + + (a) include a copy of the corresponding source code for the Modified + Version under the terms indicated in (4). + + (b) ensure that the installation of Your non-source Modified Version + does not conflict in any way with an installation of the Standard + Version, include for each program installed by the Modified + Version clear documentation describing how it differs from the + Standard Version, and rename your Modified Version so that the + name is substantially different from the Standard Version. + + (c) ensure that the Modified Version includes notification of the + changes made from the Standard Version, and offer to provide + machine-readable source code (under a license that permits making + that source code Freely Available) of the Modified Version via + mail order. + + + Permissions for Inclusion of the Package in Aggregate Works + + (7) You may aggregate this Package (either the Standard Version or + Modified Version) with other packages and distribute the resulting + aggregation provided that You do not charge a licensing fee for the + Package. Distribution Fees are permitted, and licensing fees for + other packages in the aggregation are permitted. Your permission to + distribute Standard or Modified Versions of the Package is still + subject to the other terms set forth in other sections of this + license. + + (8) In addition to the permissions given elsewhere by this license, You + are also permitted to link Modified and Standard Versions of this + Package with other works and distribute the result without + restriction, provided You have produced binary program(s) that do not + overtly expose the interfaces of the Package. This includes + permission to embed the Package in a larger work of your own without + exposing a direct interface to the Package. This also includes + permission to build stand-alone binary or bytecode versions of your + scripts that require the Package, but do not otherwise give the casual + user direct access to the Package itself. + + + Items That are Never Considered Part of a Modified Version Package + + (9) Works (including, but not limited to, subroutines and scripts) that + you have linked or aggregated with the Package that merely extend or + make use of the Package, but are not intended to cause the Package to + operate differently from the Standard Version, do not, by themselves, + cause the Package to be a Modified Version. In addition, such works + are not considered parts of the Package itself, and are not bound by + the terms of the Package's license. + + + Acceptance of License and Disclaimer of Warranty + + (10) You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to copy, modify or + distribute the Standard or Modified Versions of the Package. These + actions are prohibited by copyright law if you do not accept this + License. Therefore, by copying, modifying or distributing Standard + and Modified Versions of the Package, you indicate your acceptance of + the license of the Package. + + + (11) Disclaimer of Warranty: + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT UNLESS REQUIRED BY + LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER OR CONTRIBUTOR + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + diff --git a/ast2json/renpy/__init__.py b/ast2json/renpy/__init__.py new file mode 100644 index 0000000..f2bd463 --- /dev/null +++ b/ast2json/renpy/__init__.py @@ -0,0 +1,271 @@ +# Copyright 2004-2013 Tom Rothamel +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# This file ensures that renpy packages will be imported in the right +# order. + +import sys +import os + +# Version numbers. +try: + from renpy.vc_version import vc_version; vc_version +except ImportError: + vc_version = 0 + +# The tuple giving the version. This needs to be updated when +# we bump the version. +# +# Be sure to change script_version in launcher/script_version.rpy. +# Be sure to change config.version in tutorial/game/options.rpy. +version_tuple = (6, 15, 4, vc_version) + +# A verbose string computed from that version. +version = "Ren'Py " + ".".join(str(i) for i in version_tuple) + +# Other versions. +script_version = 5003000 +savegame_suffix = "-LT1.save" +bytecode_version = 1 + +# True if this is the first time we've started - even including +# utter restarts. +first_utter_start = True + +def setup_modulefinder(modulefinder): + import _renpy + + libexec = os.path.dirname(_renpy.__file__) + + for i in [ "display", "gl", "angle", "text" ]: + + displaypath = os.path.join(libexec, "renpy", i) + + if os.path.exists(displaypath): + modulefinder.AddPackagePath('renpy.' + i, displaypath) + +def import_cython(): + """ + Never called, but necessary to ensure that modulefinder will properly + grab the various cython modules. + """ + + import renpy.arguments #@UnresolvedImport + + import renpy.display.accelerator #@UnresolvedImport + import renpy.display.render #@UnresolvedImport + + import renpy.gl.gldraw #@UnresolvedImport + import renpy.gl.glenviron_fixed #@UnresolvedImport + import renpy.gl.glenviron_limited #@UnresolvedImport + import renpy.gl.glenviron_shader #@UnresolvedImport + import renpy.gl.glrtt_copy #@UnresolvedImport + import renpy.gl.glrtt_fbo #@UnresolvedImport + import renpy.gl.gltexture #@UnresolvedImport + + import renpy.angle.gldraw #@UnresolvedImport + import renpy.angle.glenviron_shader #@UnresolvedImport + import renpy.angle.glrtt_copy #@UnresolvedImport + import renpy.angle.glrtt_fbo #@UnresolvedImport + import renpy.angle.gltexture #@UnresolvedImport + + +def import_all(): + + + import renpy.display #@UnresolvedImport + + # Adds in the Ren'Py loader. + import renpy.loader #@UnresolvedImport + + import renpy.ast #@UnresolvedImport + import renpy.atl #@UnresolvedImport + import renpy.curry #@UnresolvedImport + import renpy.easy #@UnresolvedImport + import renpy.execution #@UnresolvedImport + import renpy.loadsave #@UnresolvedImport + import renpy.parser #@UnresolvedImport + import renpy.python #@UnresolvedImport + import renpy.script #@UnresolvedImport + import renpy.statements #@UnresolvedImport + import renpy.style #@UnresolvedImport + import renpy.substitutions #@UnresolvedImport + import renpy.translation #@UnresolvedImport + + import renpy.display.presplash #@UnresolvedImport + import renpy.display.pgrender #@UnresolvedImport + import renpy.display.scale #@UnresolvedImport + import renpy.display.module #@UnresolvedImport + + def update_path(package): + """ + Update the __path__ of package, to import binary modules from a libexec + directory. + """ + + name = package.__name__.split(".") + + import _renpy #@UnresolvedImport + libexec = os.path.dirname(_renpy.__file__) + package.__path__.append(os.path.join(libexec, *name)) + + # Also find encodings, to deal with the way py2exe lays things out. + import encodings + libexec = os.path.dirname(encodings.__path__[0]) + package.__path__.append(os.path.join(libexec, *name)) + + update_path(renpy.display) + + import renpy.display.render # Most display stuff depends on this. @UnresolvedImport + import renpy.display.core # object @UnresolvedImport + + import renpy.text #@UnresolvedImport + update_path(renpy.text) + + import renpy.text.ftfont #@UnresolvedImport + import renpy.text.font #@UnresolvedImport + import renpy.text.textsupport #@UnresolvedImport + import renpy.text.texwrap #@UnresolvedImport + import renpy.text.text #@UnresolvedImport + import renpy.text.extras #@UnresolvedImport + + sys.modules['renpy.display.text'] = renpy.text.text + + import renpy.gl #@UnresolvedImport + update_path(renpy.gl) + + import renpy.angle #@UnresolvedImport + update_path(renpy.angle) + + import renpy.display.layout # core @UnresolvedImport + import renpy.display.motion # layout @UnresolvedImport + import renpy.display.behavior # layout @UnresolvedImport + import renpy.display.transition # core, layout @UnresolvedImport + import renpy.display.movetransition # core @UnresolvedImport + import renpy.display.im #@UnresolvedImport + import renpy.display.imagelike #@UnresolvedImport + import renpy.display.image # core, behavior, im, imagelike @UnresolvedImport + import renpy.display.video #@UnresolvedImport + import renpy.display.focus #@UnresolvedImport + import renpy.display.anim #@UnresolvedImport + import renpy.display.particle #@UnresolvedImport + import renpy.display.joystick #@UnresolvedImport + import renpy.display.minigame #@UnresolvedImport + import renpy.display.screen #@UnresolvedImport + import renpy.display.dragdrop #@UnresolvedImport + import renpy.display.imagemap #@UnresolvedImport + import renpy.display.predict #@UnresolvedImport + + import renpy.display.error #@UnresolvedImport + + # Note: For windows to work, renpy.audio.audio needs to be after + # renpy.display.module. + import renpy.audio.audio #@UnresolvedImport + import renpy.audio.music #@UnresolvedImport + import renpy.audio.sound #@UnresolvedImport + + import renpy.ui #@UnresolvedImport + import renpy.screenlang #@UnresolvedImport + + import renpy.lint #@UnresolvedImport + import renpy.warp #@UnresolvedImport + + import renpy.editor #@UnresolvedImport + import renpy.exports #@UnresolvedImport + import renpy.character # depends on exports. @UnresolvedImport + + import renpy.dump #@UnresolvedImport + + import renpy.config # depends on lots. @UnresolvedImport + import renpy.minstore # depends on lots. @UnresolvedImport + import renpy.defaultstore # depends on everything. @UnresolvedImport + import renpy.main #@UnresolvedImport + + # Create the store. + renpy.python.create_store("store") + + # Import the contents of renpy.defaultstore into renpy.store, and set + # up an alias as we do. + renpy.store = sys.modules['store'] + sys.modules['renpy.store'] = sys.modules['store'] + + import subprocess + sys.modules['renpy.subprocess'] = subprocess + + for k, v in renpy.defaultstore.__dict__.items(): + renpy.store.__dict__.setdefault(k, v) + + # Import everything into renpy.exports, provided it isn't + # already there. + for k, v in globals().items(): + vars(renpy.exports).setdefault(k, v) + +# Fool the analyzer. +if False: + import renpy.defaultstore as store + +# This reloads all modules. +def reload_all(): + + import renpy #@UnresolvedImport + + # Shut down the cache thread. + renpy.display.im.cache.quit() + + blacklist = [ "renpy", + "renpy.bootstrap", + "renpy.display", + "renpy.display.pgrender", + "renpy.display.scale" ] + + for i in list(sys.modules.keys()): + if i.startswith("renpy") and i not in blacklist: + del sys.modules[i] + + if i.startswith("store"): + del sys.modules[i] + + import gc + gc.collect() + + renpy.display.draw = None + + import_all() + +# Information about the platform we're running on. We break the platforms +# up into 4 groups - windows-like, mac-like, linux-like, and android-like. +windows = False +macintosh = False +linux = False +android = False + +import platform + +if platform.win32_ver()[0]: + windows = True +elif platform.mac_ver()[0]: + macintosh = True +else: + linux = True + +# The android init code in renpy.py will set linux=False and android=True. + + diff --git a/ast2json/renpy/ast.py b/ast2json/renpy/ast.py new file mode 100644 index 0000000..6fbd58f --- /dev/null +++ b/ast2json/renpy/ast.py @@ -0,0 +1,1757 @@ +# Copyright 2004-2013 Tom Rothamel +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# This file contains the AST for the Ren'Py script language. Each class +# here corresponds to a statement in the script language. + +# NOTE: +# When updating this file, consider if lint.py or warp.py also need +# updating. + +import renpy + +import re +import time +import hashlib +import collections + +def next_node(n): + """ + Indicates the next node that should be executed. When a statement + can crash, this should be set as early as possible, so that ignore + can bring us there. + """ + + renpy.game.context().next_node = n + +class ParameterInfo(object): + """ + This class is used to store information about parameters to a + label. + """ + def __init__(self, parameters, positional, extrapos, extrakw): + + # A list of parameter name, default value pairs. + self.parameters = parameters + + # A list, giving the positional parameters to this function, + # in order. + self.positional = positional + + # A variable that takes the extra positional arguments, if + # any. None if no such variable exists. + self.extrapos = extrapos + + # A variable that takes the extra keyword arguments, if + # any. None if no such variable exists. + self.extrakw = extrakw + +class ArgumentInfo(object): + + def __init__(self, arguments, extrapos, extrakw): + + # A list of (keyword, expression) pairs. If an argument doesn't + # have a keyword, it's thought of as positional. + self.arguments = arguments + + # An expression giving extra positional arguments being + # supplied to this function. + self.extrapos = extrapos + + # An expression giving extra keyword arguments that need + # to be supplied to this function. + self.extrakw = extrakw + + +def __newobj__(cls, *args): + return cls.__new__(cls, *args) + +# This represents a string containing python code. +class PyExpr(str): + + __slots__ = [ + 'filename', + 'linenumber', + ] + + def __new__(cls, s, filename, linenumber): + self = str.__new__(cls, s) + self.filename = filename + self.linenumber = linenumber + return self + + def __getnewargs__(self): + return (str(self), self.filename, self.linenumber) # E1101 + +class PyCode(object): + + __slots__ = [ + 'source', + 'location', + 'mode', + 'bytecode', + 'hash', + ] + + def __getstate__(self): + return (1, self.source, self.location, self.mode) + + def __setstate__(self, state): + (_, self.source, self.location, self.mode) = state + self.bytecode = None + + if renpy.game.script.record_pycode: + renpy.game.script.all_pycode.append(self) + + def __init__(self, source, loc=('', 1), mode='exec'): + + if isinstance(source, PyExpr): + loc = (source.filename, source.linenumber, source) + + # The source code. + self.source = source + + # The time is necessary so we can disambiguate between Python + # blocks on the same line in different script versions. + self.location = loc + ( int(time.time()), ) + self.mode = mode + + # This will be initialized later on, after we are serialized. + self.bytecode = None + + if renpy.game.script.record_pycode: + renpy.game.script.all_pycode.append(self) + + self.hash = None + + def get_hash(self): + try: + if self.hash is not None: + return self.hash + except: + pass + + code = self.source + if isinstance(code, renpy.python.ast.AST): #@UndefinedVariable + code = renpy.python.ast.dump(code) #@UndefinedVariable + + self.hash = chr(renpy.bytecode_version) + hashlib.md5(repr(self.location) + code.encode("utf-8")).digest() + return self.hash + + +def chain_block(block, next): #@ReservedAssignment + """ + This is called to chain together all of the nodes in a block. Node + n is chained with node n+1, while the last node is chained with + next. + """ + + if not block: + return + + for a, b in zip(block, block[1:]): + a.chain(b) + + block[-1].chain(next) + + +class Scry(object): + """ + This is used to store information about the future, if we know it. Unlike + predict, this tries to only get things we _know_ will happen. + """ + + # By default, all attributes are None. + def __getattr__(self, name): + return None + + def __next__(self): #@ReservedAssignment + if self._next is None: + return None + else: + return self._next.scry() + + +class Node(object): + """ + A node in the abstract syntax tree of the program. + + @ivar name: The name of this node. + + @ivar filename: The filename where this node comes from. + @ivar linenumber: The line number of the line on which this node is defined. + """ + + __slots__ = [ + 'name', + 'filename', + 'linenumber', + 'next', + ] + + # True if this node is translatable, false otherwise. (This can be set on + # the class or the instance.) + translatable = False + + # Called to set the state of a Node, when necessary. + def __setstate__(self, state): + for k, v in state[1].items(): + try: + setattr(self, k, v) + except AttributeError: + pass + + + def __init__(self, loc): + """ + Initializes this Node object. + + @param loc: A (filename, physical line number) tuple giving the + logical line on which this Node node starts. + """ + + self.filename, self.linenumber = loc + self.name = None + self.next = None + + def diff_info(self): + """ + Returns a tuple of diff info about ourself. This is used to + compare Nodes to see if they should be considered the same node. The + tuple returned must be hashable. + """ + + return ( id(self), ) + + def get_children(self): + """ + Returns a list of all of the nodes that are children of this + node. (That is, all of the nodes in any block associated with + this node.) + """ + + return [ ] + + def get_init(self): + """ + Returns a node that should be run at init time (that is, before + the normal start of the script.), or None if this node doesn't + care to suggest one. + + (The only class that needs to override this is Init.) + """ + + return None + + def chain(self, next): #@ReservedAssignment + """ + This is called with the Node node that should be followed after + executing this node, and all nodes that this node + executes. (For example, if this node is a block label, the + next is the node that should be executed after all nodes in + the block.) + """ + + self.next = next + + def execute(self): + """ + Causes this node to execute, and any action it entails to be + performed. The node should call next_node with the node to + be executed after this one. + """ + + assert False, "Node subclass forgot to define execute." + + def early_execute(self): + """ + Called when the module is loaded. + """ + + def predict(self): + """ + This is called to predictively load images from this node. It + should cause renpy.display.predict.image and + renpy.display.predict.screen to be called as necessary. + """ + + if self.__next__: + return [ self.__next__ ] + else: + return [ ] + + def scry(self): + """ + Called to return an object with some general, user-definable information + about the future. + """ + + rv = Scry() + rv._next = self.__next__ # W0201 + return rv + + def restructure(self, callback): + """ + Called to restructure the AST. + + When this method is called, callback is called once for each child + block of the node. The block, a list, can be updated by the callback + using slice assignment to the list. + """ + + # Does nothing for nodes that do not contain child blocks. + return + + def get_code(self, dialogue_filter=None): + """ + Returns the canonical form of the code corresponding to this statement. + This only needs to be defined if the statement is translatable. + + `filter` + If present, a filter that should be applied to human-readable + text in the statement. + """ + + raise Exception("Not Implemented") + +def say_menu_with(expression, callback): + """ + This handles the with clause of a say or menu statement. + """ + + if expression is not None: + what = renpy.python.py_eval(expression) + elif renpy.store.default_transition and renpy.game.preferences.transitions == 2: + what = renpy.store.default_transition + else: + return + + if not what: + return + + if renpy.game.preferences.transitions: + # renpy.game.interface.set_transition(what) + callback(what) + +class Say(Node): + + __slots__ = [ + 'who', + 'who_fast', + 'what', + 'with_', + 'interact', + 'attributes', + ] + + def diff_info(self): + return (Say, self.who, self.what) + + def __setstate__(self, state): + self.attributes = None + self.interact = True + Node.__setstate__(self, state) + + def __init__(self, loc, who, what, with_, interact=True, attributes=None): + + super(Say, self).__init__(loc) + + if who is not None: + self.who = who.strip() + + if re.match(r'[a-zA-Z_]\w*$', self.who): + self.who_fast = True + else: + self.who_fast = False + else: + self.who = None + self.who_fast = False + + self.what = what + self.with_ = with_ + self.interact = interact + + # A tuple of attributes that are applied to the character that's + # speaking, or None to disable this behavior. + self.attributes = attributes + + def get_code(self, dialogue_filter=None): + rv = [ ] + + if self.who: + rv.append(self.who) + + if self.attributes is not None: + rv.extend(self.attributes) + + what = self.what + if dialogue_filter is not None: + what = dialogue_filter(what) + + rv.append(renpy.translation.encode_say_string(what)) + + if not self.interact: + rv.append("nointeract") + + if self.with_: + rv.append("with") + rv.append(self.with_) + + return " ".join(rv) + + def execute(self): + + next_node(self.__next__) + + try: + + renpy.exports.say_attributes = self.attributes + + if self.who is not None: + if self.who_fast: + who = getattr(renpy.store, self.who, None) + if who is None: + raise Exception("Sayer '%s' is not defined." % self.who.encode("utf-8")) + else: + who = renpy.python.py_eval(self.who) + else: + who = None + + if not ( + (who is None) or + isinstance(who, collections.Callable) or + isinstance(who, str) ): + + raise Exception("Sayer %s is not a function or string." % self.who.encode("utf-8")) + + what = self.what + if renpy.config.say_menu_text_filter: + what = renpy.config.say_menu_text_filter(what) # E1102 + + if getattr(who, "record_say", True): + renpy.store._last_say_who = self.who + renpy.store._last_say_what = what + + say_menu_with(self.with_, renpy.game.interface.set_transition) + renpy.exports.say(who, what, interact=self.interact) + + finally: + renpy.exports.say_attributes = None + + + def predict(self): + + old_attributes = renpy.exports.say_attributes + + try: + + renpy.exports.say_attributes = self.attributes + + if self.who is not None: + if self.who_fast: + who = getattr(renpy.store, self.who) + else: + who = renpy.python.py_eval(self.who) + else: + who = None + + def predict_with(trans): + renpy.display.predict.displayable(trans(old_widget=None, new_widget=None)) + + say_menu_with(self.with_, predict_with) + + what = self.what + if renpy.config.say_menu_text_filter: + what = renpy.config.say_menu_text_filter(what) + + renpy.exports.predict_say(who, what) + + finally: + renpy.exports.say_attributes = old_attributes + + return [ self.__next__ ] + + def scry(self): + rv = Node.scry(self) + + if self.who is not None: + if self.who_fast: + who = getattr(renpy.store, self.who) + else: + who = renpy.python.py_eval(self.who) + else: + who = None + + if self.interact: + renpy.exports.scry_say(who, rv) + else: + rv.interacts = False + + return rv + +# Copy the descriptor. +setattr(Say, "with", Say.with_) # E1101 + +class Init(Node): + + __slots__ = [ + 'block', + 'priority', + ] + + def __init__(self, loc, block, priority): + super(Init, self).__init__(loc) + + self.block = block + self.priority = priority + + + def get_children(self): + return self.block + + def get_init(self): + return self.priority, self.block[0] + + # We handle chaining specially. We want to chain together the nodes in + # the block, but we want that chain to end in None, and we also want + # this node to just continue on to the next node in normal execution. + def chain(self, next): #@ReservedAssignment + self.next = next + + chain_block(self.block, None) + + def execute(self): + next_node(self.__next__) + + def restructure(self, callback): + callback(self.block) + +def apply_arguments(params, args, kwargs): + """ + Applies arguments to parameters to update scope. + + `scope` + A dict. + + `params` + The parameters object. + + `args`, `kwargs` + Positional and keyword arguments. + """ + + values = { } + rv = { } + + if args is None: + args = () + + if kwargs is None: + kwargs = { } + + if params is None: + if args or kwargs: + raise Exception("Arguments supplied, but parameter list not present") + else: + return rv + + for name, value in zip(params.positional, args): + if name in values: + raise Exception("Parameter %s has two values." % name) + + values[name] = value + + extrapos = tuple(args[len(params.positional):]) + + for name, value in kwargs.items(): + if name in values: + raise Exception("Parameter %s has two values." % name) + + values[name] = value + + for name, default in params.parameters: + + if name not in values: + if default is None: + raise Exception("Required parameter %s has no value." % name) + else: + rv[name] = renpy.python.py_eval(default) + + else: + rv[name] = values[name] + del values[name] + + # Now, values has the left-over keyword arguments, and extrapos + # has the left-over positional arguments. + + if params.extrapos: + rv[params.extrapos] = extrapos + elif extrapos: + raise Exception("Too many arguments in call (expected %d, got %d)." % (len(params.positional), len(args))) + + if params.extrakw: + rv[params.extrakw] = values + else: + if values: + raise Exception("Unknown keyword arguments: %s" % ( ", ".join(list(values.keys())))) + + return rv + +class Label(Node): + + __slots__ = [ + 'name', + 'parameters', + 'block', + 'hide', + ] + + def __setstate__(self, state): + self.parameters = None + self.hide = False + Node.__setstate__(self, state) + + def __init__(self, loc, name, block, parameters, hide=False): + """ + Constructs a new Label node. + + @param name: The name of this label. + @param block: A (potentially empty) list of nodes making up the + block associated with this label. + """ + + super(Label, self).__init__(loc) + + self.name = name + self.block = block + self.parameters = parameters + self.hide = hide + + def diff_info(self): + return (Label, self.name) + + def get_children(self): + return self.block + + def chain(self, next): #@ReservedAssignment + + if self.block: + self.next = self.block[0] + chain_block(self.block, next) + else: + self.next = next + + def execute(self): + next_node(self.__next__) + + renpy.game.context().mark_seen() + + values = apply_arguments(self.parameters, renpy.store._args, renpy.store._kwargs) + + for k, v in values.items(): + renpy.exports.dynamic(k) + setattr(renpy.store, k, v) + + renpy.store._args = None + renpy.store._kwargs = None + + if renpy.config.label_callback: + renpy.config.label_callback(self.name, renpy.game.context().last_abnormal) + + def restructure(self, callback): + callback(self.block) + + +class Python(Node): + + __slots__ = [ + 'hide', + 'code', + 'store', + ] + + def __setstate__(self, state): + self.store = "store" + super(Python, self).__setstate__(state) + + def __init__(self, loc, python_code, hide=False, store="store"): + """ + @param code: A PyCode object. + + @param hide: If True, the code will be executed with its + own local dictionary. + """ + + super(Python, self).__init__(loc) + + self.hide = hide + self.code = PyCode(python_code, loc=loc, mode='exec') + self.store = store + + def diff_info(self): + return (Python, self.code.source) + + def early_execute(self): + renpy.python.create_store(self.store) + + def execute(self): + next_node(self.__next__) + + try: + renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store) + finally: + + if not renpy.game.context().init_phase: + for i in renpy.config.python_callbacks: + i() + + def scry(self): + rv = Node.scry(self) + rv.interacts = True + return rv + +class EarlyPython(Node): + + __slots__ = [ + 'hide', + 'code', + 'store', + ] + + def __setstate__(self, state): + self.store = "store" + super(EarlyPython, self).__setstate__(state) + + def __init__(self, loc, python_code, hide=False, store="store"): + """ + @param code: A PyCode object. + + @param hide: If True, the code will be executed with its + own local dictionary. + """ + + super(EarlyPython, self).__init__(loc) + + self.hide = hide + self.code = PyCode(python_code, loc=loc, mode='exec') + self.store = store + + def diff_info(self): + return (EarlyPython, self.code.source) + + def execute(self): + next_node(self.__next__) + + def early_execute(self): + renpy.python.create_store(self.store) + + if self.code.bytecode: + renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store) + +class Image(Node): + + __slots__ = [ + 'imgname', + 'code', + 'atl', + ] + + def __init__(self, loc, name, expr=None, atl=None): + """ + @param name: The name of the image being defined. + + @param expr: An expression yielding a Displayable that is + assigned to the image. + """ + + super(Image, self).__init__(loc) + + self.imgname = name + + if expr: + self.code = PyCode(expr, loc=loc, mode='eval') + self.atl = None + else: + self.code = None + self.atl = atl + + def diff_info(self): + return (Image, tuple(self.imgname)) + + def execute(self): + + # Note: We should always check that self.code is None before + # accessing self.atl, as self.atl may not always exist. + + next_node(self.__next__) + + if self.code is not None: + img = renpy.python.py_eval_bytecode(self.code.bytecode) + else: + img = renpy.display.motion.ATLTransform(self.atl) + + renpy.exports.image(self.imgname, img) + + + +class Transform(Node): + + __slots__ = [ + + # The name of the transform. + 'varname', + + # The block of ATL associated with the transform. + 'atl', + + # The parameters associated with the transform, if any. + 'parameters', + ] + + default_parameters = ParameterInfo([ ], [ ], None, None) + + def __init__(self, loc, name, atl=None, parameters=default_parameters): + + super(Transform, self).__init__(loc) + + self.varname = name + self.atl = atl + self.parameters = parameters + + def diff_info(self): + return (Transform, self.varname) + + def execute(self): + + next_node(self.__next__) + + parameters = getattr(self, "parameters", None) + + if parameters is None: + parameters = Transform.default_parameters + + trans = renpy.display.motion.ATLTransform(self.atl, parameters=parameters) + renpy.dump.transforms.append((self.varname, self.filename, self.linenumber)) + setattr(renpy.store, self.varname, trans) + + +def predict_imspec(imspec, scene=False, atl=None): + """ + Call this to use the given callback to predict the image named + in imspec. + """ + + if len(imspec) == 7: + name, expression, tag, _at_list, layer, _zorder, _behind = imspec + + elif len(imspec) == 6: + name, expression, tag, _at_list, layer, _zorder = imspec + + elif len(imspec) == 3: + name, _at_list, layer = imspec + + + if expression: + try: + img = renpy.python.py_eval(expression) + img = renpy.easy.displayable(img) + except: + return + + else: + img = renpy.display.image.images.get(name, None) + if img is None: + return + + full_name = name + if tag: + full_name = (tag,) + full_name[1:] + + if scene: + renpy.game.context().images.predict_scene(layer) + + renpy.game.context().images.predict_show(tag or name, layer) + + if atl is not None: + try: + img = renpy.display.motion.ATLTransform(atl, child=img) + except: + import traceback + traceback.print_exc() + + renpy.display.predict.displayable(img) + + + +def show_imspec(imspec, atl=None): + + if len(imspec) == 7: + name, expression, tag, at_list, layer, zorder, behind = imspec + + elif len(imspec) == 6: + name, expression, tag, at_list, layer, zorder = imspec + behind = [ ] + + elif len(imspec) == 3: + name, at_list, layer = imspec + expression = None + tag = None + zorder = None + behind = [ ] + + if zorder is not None: + zorder = renpy.python.py_eval(zorder) + else: + zorder = 0 + + if expression is not None: + expression = renpy.python.py_eval(expression) + expression = renpy.easy.displayable(expression) + + at_list = [ renpy.python.py_eval(i) for i in at_list ] + + renpy.config.show(name, + at_list=at_list, + layer=layer, + what=expression, + zorder=zorder, + tag=tag, + behind=behind, + atl=atl) + +class Show(Node): + + __slots__ = [ + 'imspec', + 'atl', + ] + + def __init__(self, loc, imspec, atl=None): + """ + @param imspec: A triple consisting of an image name (itself a + tuple of strings), a list of at expressions, and a layer. + """ + + super(Show, self).__init__(loc) + + self.imspec = imspec + self.atl = atl + + def diff_info(self): + return (Show, tuple(self.imspec[0])) + + def execute(self): + next_node(self.__next__) + + show_imspec(self.imspec, atl=getattr(self, "atl", None)) + + def predict(self): + predict_imspec(self.imspec, atl=getattr(self, "atl", None)) + return [ self.__next__ ] + + +class Scene(Node): + + __slots__ = [ + 'imspec', + 'layer', + 'atl', + ] + + def __init__(self, loc, imgspec, layer, atl=None): + """ + @param imspec: A triple consisting of an image name (itself a + tuple of strings), a list of at expressions, and a layer, or + None to not have this scene statement also display an image. + """ + + super(Scene, self).__init__(loc) + + self.imspec = imgspec + self.layer = layer + self.atl = atl + + def diff_info(self): + + if self.imspec: + data = tuple(self.imspec[0]) + else: + data = None + + return (Scene, data) + + def execute(self): + + next_node(self.__next__) + + renpy.config.scene(self.layer) + + if self.imspec: + show_imspec(self.imspec, atl=getattr(self, "atl", None)) + + def predict(self): + + if self.imspec: + predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True) + + return [ self.__next__ ] + + +class Hide(Node): + + __slots__ = [ + 'imspec', + ] + + def __init__(self, loc, imgspec): + """ + @param imspec: A triple consisting of an image name (itself a + tuple of strings), a list of at expressions, and a list of + with expressions. + """ + + super(Hide, self).__init__(loc) + + self.imspec = imgspec + + def diff_info(self): + return (Hide, tuple(self.imspec[0])) + + def predict(self): + + if len(self.imspec) == 3: + name, _at_list, layer = self.imspec + tag = None + _expression = None + _zorder = None + _behind = None + elif len(self.imspec) == 6: + name, _expression, tag, _at_list, layer, _zorder = self.imspec + _behind = None + elif len(self.imspec) == 7: + name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec + + + if tag is None: + tag = name[0] + + renpy.game.context().images.predict_hide(tag, layer) + + return [ self.__next__ ] + + def execute(self): + + next_node(self.__next__) + + if len(self.imspec) == 3: + name, _at_list, layer = self.imspec + _expression = None + tag = None + _zorder = 0 + elif len(self.imspec) == 6: + name, _expression, tag, _at_list, layer, _zorder = self.imspec + elif len(self.imspec) == 7: + name, _expression, tag, _at_list, layer, _zorder, _behind = self.imspec + + renpy.config.hide(tag or name, layer) + + +class With(Node): + + __slots__ = [ + 'expr', + 'paired', + ] + + def __setstate__(self, state): + self.paired = None + Node.__setstate__(self, state) + + def __init__(self, loc, expr, paired=None): + """ + @param expr: An expression giving a transition or None. + """ + + super(With, self).__init__(loc) + self.expr = expr + self.paired = paired + + def diff_info(self): + return (With, self.expr) + + def execute(self): + + next_node(self.__next__) + + trans = renpy.python.py_eval(self.expr) + + if self.paired is not None: + paired = renpy.python.py_eval(self.paired) + else: + paired = None + + renpy.exports.with_statement(trans, paired) + + def predict(self): + + try: + trans = renpy.python.py_eval(self.expr) + + if trans: + renpy.display.predict.displayable(trans(old_widget=None, new_widget=None)) + + except: + pass + + + return [ self.__next__ ] + + +class Call(Node): + + __slots__ = [ + 'label', + 'arguments', + 'expression', + ] + + def __setstate__(self, state): + self.arguments = None + Node.__setstate__(self, state) + + def __init__(self, loc, label, expression, arguments): + + super(Call, self).__init__(loc) + self.label = label + self.expression = expression + self.arguments = arguments + + def diff_info(self): + return (Call, self.label, self.expression) + + def execute(self): + + label = self.label + if self.expression: + label = renpy.python.py_eval(label) + + rv = renpy.game.context().call(label, return_site=self.next.name) + next_node(rv) + renpy.game.context().abnormal = True + + if self.arguments: + + args = [ ] + kwargs = renpy.python.RevertableDict() + + for name, expr in self.arguments.arguments: + + value = renpy.python.py_eval(expr) + + if name is None: + args.append(value) + else: + if name in kwargs: + raise Exception("The argument named %s appears twice." % name) + + kwargs[name] = value + + if self.arguments.extrapos: + args.extend(renpy.python.py_eval(self.arguments.extrapos)) + + if self.arguments.extrakw: + for name, value in renpy.python.py_eval(self.arguments.extrakw).items(): + if name in kwargs: + raise Exception("The argument named %s appears twice." % name) + + kwargs[name] = value + + + renpy.store._args = tuple(args) + renpy.store._kwargs = kwargs + + def predict(self): + if self.expression: + return [ ] + else: + return [ renpy.game.script.lookup(self.label) ] + + def scry(self): + rv = Node.scry(self) + rv._next = None + return rv + + +class Return(Node): + + __slots__ = [ 'expression'] + + def __setstate__(self, state): + self.expression = None + Node.__setstate__(self, state) + + def __init__(self, loc, expression): + super(Return, self).__init__(loc) + self.expression = expression + + def diff_info(self): + return (Return, ) + + # We don't care what the next node is. + def chain(self, next): #@ReservedAssignment + self.next = None + return + + def execute(self): + + if self.expression: + renpy.store._return = renpy.python.py_eval(self.expression) + else: + renpy.store._return = None + + renpy.game.context().pop_dynamic() + + next_node(renpy.game.context().lookup_return(pop=True)) + + def predict(self): + site = renpy.game.context().lookup_return(pop=False) + if site: + return [ site ] + else: + return [ ] + + def scry(self): + rv = Node.scry(self) + rv._next = None + return rv + + +class Menu(Node): + + __slots__ = [ + 'items', + 'set', + 'with_', + ] + + def __init__(self, loc, items, set, with_): #@ReservedAssignment + super(Menu, self).__init__(loc) + + self.items = items + self.set = set + self.with_ = with_ + + def diff_info(self): + return (Menu,) + + def get_children(self): + rv = [ ] + + for _label, _condition, block in self.items: + if block: + rv.extend(block) + + return rv + + # Blocks of statements in a choice continue after the menu. + def chain(self, next): #@ReservedAssignment + + self.next = next + + for (_label, _condition, block) in self.items: + if block: + chain_block(block, next) + + def execute(self): + + next_node(self.__next__) + + choices = [ ] + narration = [ ] + + for i, (label, condition, block) in enumerate(self.items): + + if renpy.config.say_menu_text_filter: + label = renpy.config.say_menu_text_filter(label) + + if block is None: + if renpy.config.narrator_menu and label: + narration.append(label) + else: + choices.append((label, condition, None)) + else: + choices.append((label, condition, i)) + next_node(block[0]) + + if narration: + renpy.exports.say(None, "\n".join(narration), interact=False) + + say_menu_with(self.with_, renpy.game.interface.set_transition) + choice = renpy.exports.menu(choices, self.set) + + if choice is not None: + next_node(self.items[choice][2][0]) + else: + next_node(self.__next__) + + + def predict(self): + rv = [ ] + + def predict_with(trans): + renpy.display.predict.displayable(trans(old_widget=None, new_widget=None)) + + say_menu_with(self.with_, predict_with) + + renpy.store.predict_menu() + + for _label, _condition, block in self.items: + if block: + rv.append(block[0]) + + return rv + + def scry(self): + rv = Node.scry(self) + rv._next = None + rv.interacts = True + return rv + + def restructure(self, callback): + for _label, _condition, block in self.items: + if block is not None: + callback(block) + +setattr(Menu, "with", Menu.with_) # E1101 + + +# Goto is considered harmful. So we decided to name it "jump" +# instead. +class Jump(Node): + + __slots__ = [ + 'target', + 'expression', + ] + + def __init__(self, loc, target, expression): + super(Jump, self).__init__(loc) + + self.target = target + self.expression = expression + + def diff_info(self): + return (Jump, self.target, self.expression) + + # We don't care what our next node is. + def chain(self, next): #@ReservedAssignment + self.next = None + return + + def execute(self): + + target = self.target + if self.expression: + target = renpy.python.py_eval(target) + + rv = renpy.game.script.lookup(target) + renpy.game.context().abnormal = True + + next_node(rv) + + def predict(self): + + if self.expression: + return [ ] + else: + return [ renpy.game.script.lookup(self.target) ] + + def scry(self): + rv = Node.scry(self) + if self.expression: + rv._next = None + else: + rv._next = renpy.game.script.lookup(self.target) + + return rv + + +# GNDN +class Pass(Node): + + __slots__ = [ ] + + def diff_info(self): + return (Pass,) + + def execute(self): + next_node(self.__next__) + + +class While(Node): + + __slots__ = [ + 'condition', + 'block', + ] + + def __init__(self, loc, condition, block): + super(While, self).__init__(loc) + + self.condition = condition + self.block = block + + def diff_info(self): + return (While, self.condition) + + def get_children(self): + return self.block + + def chain(self, next): #@ReservedAssignment + self.next = next + chain_block(self.block, self) + + def execute(self): + + next_node(self.__next__) + + if renpy.python.py_eval(self.condition): + next_node(self.block[0]) + + def predict(self): + return [ self.block[0], self.__next__ ] + + def scry(self): + rv = Node.scry(self) + rv._next = None + return rv + + def restructure(self, callback): + callback(self.block) + +class If(Node): + + __slots__ = [ 'entries' ] + + def __init__(self, loc, entries): + """ + @param entries: A list of (condition, block) tuples. + """ + + super(If, self).__init__(loc) + + self.entries = entries + + def diff_info(self): + return (If,) + + def get_children(self): + rv = [ ] + + for _condition, block in self.entries: + rv.extend(block) + + return rv + + def chain(self, next): #@ReservedAssignment + self.next = next + + for _condition, block in self.entries: + chain_block(block, next) + + def execute(self): + + next_node(self.__next__) + + for condition, block in self.entries: + if renpy.python.py_eval(condition): + next_node(block[0]) + return + + def predict(self): + + return [ block[0] for _condition, block in self.entries ] + \ + [ self.__next__ ] + + def scry(self): + rv = Node.scry(self) + rv._next = None + return rv + + def restructure(self, callback): + for _condition, block in self.entries: + callback(block) + +class UserStatement(Node): + + __slots__ = [ + 'line', + 'parsed', + 'block', + 'translatable' ] + + def __setstate__(self, state): + self.block = [ ] + self.translatable = False + Node.__setstate__(self, state) + + def __init__(self, loc, line, block): + + super(UserStatement, self).__init__(loc) + self.line = line + self.block = block + self.parsed = None + + # Do not store the parse quite yet. + _parse_info = renpy.statements.parse(self, self.line, self.block) + + def diff_info(self): + return (UserStatement, self.line) + + def execute(self): + next_node(self.get_next()) + + self.call("execute") + + def predict(self): + self.call("predict") + return [ self.get_next() ] + + def call(self, method, *args, **kwargs): + + parsed = self.parsed + if parsed is None: + parsed = renpy.statements.parse(self, self.line, self.block) + self.parsed = parsed + + renpy.statements.call(method, parsed, *args, **kwargs) + + def get_next(self): + rv = self.call("next") + if rv is not None: + return renpy.game.script.lookup(rv) + else: + return self.__next__ + + def scry(self): + rv = Node.scry(self) + rv._next = self.get_next() + self.call("scry", rv) + return rv + + def get_code(self, dialogue_filter=None): + return self.line + + +class Define(Node): + + __slots__ = [ + 'varname', + 'code', + ] + + def __init__(self, loc, name, expr): + """ + @param name: The name of the image being defined. + + @param expr: An expression yielding a Displayable that is + assigned to the image. + """ + + super(Define, self).__init__(loc) + + self.varname = name + self.code = PyCode(expr, loc=loc, mode='eval') + + def diff_info(self): + return (Define, tuple(self.varname)) + + def execute(self): + + next_node(self.__next__) + + value = renpy.python.py_eval_bytecode(self.code.bytecode) + renpy.dump.definitions.append((self.varname, self.filename, self.linenumber)) + setattr(renpy.store, self.varname, value) + + +class Screen(Node): + + __slots__ = [ + 'screen', + ] + + def __init__(self, loc, screen): + """ + @param name: The name of the image being defined. + + @param expr: An expression yielding a Displayable that is + assigned to the image. + """ + + super(Screen, self).__init__(loc) + + self.screen = screen + + def diff_info(self): + return (Screen, self.screen.name) + + def execute(self): + next_node(self.__next__) + self.screen.define() + renpy.dump.screens.append((self.screen.name, self.filename, self.linenumber)) + + +################################################################################ +# Translations +################################################################################ + +class Translate(Node): + """ + A translation block, produced either by explicit translation statements + or implicit translation blocs. + + If language is None, when executed this transfers control to the translate + statement in the current language, if any, and otherwise runs the block. + If language is not None, causes an error to occur if control reaches this + statement. + + When control normally leaves a translate statement, in any language, it + goes to the end of the translate statement in the None language. + """ + + __slots__ = [ + "identifier", + "language", + "block", + ] + + def __init__(self, loc, identifier, language, block): + super(Translate, self).__init__(loc) + + self.identifier = identifier + self.language = language + self.block = block + + def diff_info(self): + return (Translate, self.identifier, self.language) + + def chain(self, next): #@ReservedAssignment + self.next = next + chain_block(self.block, next) + + def execute(self): + + if self.language is not None: + next_node(self.__next__) + raise Exception("Translation nodes cannot be run directly.") + + next_node(renpy.game.script.translator.lookup_translate(self.identifier)) + renpy.game.context().translate_identifier = self.identifier + + def predict(self): + node = renpy.game.script.translator.lookup_translate(self.identifier) + return [ node ] + + def scry(self): + rv = Scry() + rv._next = renpy.game.script.translator.lookup_translate(self.identifier) + return rv + + def get_children(self): + return self.block + + def restructure(self, callback): + return callback(self.block) + + +class EndTranslate(Node): + """ + A node added implicitly after each translate block. It's responsible for + resetting the translation identifier. + """ + + def __init__(self, loc): + super(EndTranslate, self).__init__(loc) + + def diff_info(self): + return (EndTranslate,) + + def execute(self): + next_node(self.__next__) + renpy.game.context().translate_identifier = None + + +class TranslateString(Node): + """ + A node used for translated strings. + """ + + __slots__ = [ + "language", + "old", + "new" + ] + + def __init__(self, loc, language, old, new): + super(TranslateString, self).__init__(loc) + self.language = language + self.old = old + self.new = new + + def diff_info(self): + return (TranslateString,) + + def execute(self): + next_node(self.__next__) + renpy.translation.add_string_translation(self.language, self.old, self.new) + +class TranslatePython(Node): + + __slots__ = [ + 'language', + 'code', + ] + + def __init__(self, loc, language, python_code): + """ + @param code: A PyCode object. + + @param hide: If True, the code will be executed with its + own local dictionary. + """ + + super(TranslatePython, self).__init__(loc) + + self.language = language + self.code = PyCode(python_code, loc=loc, mode='exec') + + def diff_info(self): + return (TranslatePython, self.code.source) + + def execute(self): + next_node(self.__next__) + + # def early_execute(self): + # renpy.python.create_store(self.store) + # renpy.python.py_exec_bytecode(self.code.bytecode, self.hide, store=self.store) + + diff --git a/ast2json/renpy/atl.py b/ast2json/renpy/atl.py new file mode 100644 index 0000000..a321e6b --- /dev/null +++ b/ast2json/renpy/atl.py @@ -0,0 +1,1544 @@ +# Copyright 2004-2013 Tom Rothamel +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import renpy +import random + +def compiling(loc): + file, number = loc #@ReservedAssignment + + renpy.game.exception_info = "Compiling ATL code at %s:%d" % (file, number) + +def executing(loc): + file, number = loc #@ReservedAssignment + + renpy.game.exception_info = "Executing ATL code at %s:%d" % (file, number) + + +# A map from the name of a time warp function to the function itself. +warpers = { } + +def atl_warper(f): + name = f.__name__ + warpers[name] = f + return f + +# The pause warper is used internally when no other warper is +# specified. +@atl_warper +def pause(t): + if t < 1.0: + return 0.0 + else: + return 1.0 + +position = object() + +# A dictionary giving property names and the corresponding default +# values. +PROPERTIES = { + "pos" : (position, position), + "xpos" : position, + "ypos" : position, + "anchor" : (position, position), + "xanchor" : position, + "yanchor" : position, + "xaround" : position, + "yaround" : position, + "xanchoraround" : float, + "yanchoraround" : float, + "align" : (float, float), + "xalign" : float, + "yalign" : float, + "rotate" : float, + "rotate_pad" : bool, + "transform_anchor" : bool, + "xzoom" : float, + "yzoom" : float, + "zoom" : float, + "alpha" : float, + "around" : (position, position), + "alignaround" : (float, float), + "angle" : float, + "radius" : float, + "crop" : (float, float, float, float), + "size" : (int, int), + "corner1" : (float, float), + "corner2" : (float, float), + "subpixel" : bool, + "delay" : float, + "xoffset" : float, + "yoffset" : float, + "offset" : (int, int), + "xcenter" : position, + "ycenter" : position, + } + + +def correct_type(v, b, ty): + """ + Corrects the type of v to match ty. b is used to inform the match. + """ + + if ty is position: + if v is None: + return None + else: + return type(b)(v) + else: + return ty(v) + + +def interpolate(t, a, b, type): #@ReservedAssignment + """ + Linearly interpolate the arguments. + """ + + if t >= 1.0: + return b + + # Recurse into tuples. + if isinstance(b, tuple): + if a is None: + a = [ None ] * len(b) + + return tuple(interpolate(t, i, j, ty) for i, j, ty in zip(a, b, type)) + + # Deal with booleans, nones, etc. + elif b is None or isinstance(b, bool): + if t >= 1.0: + return b + else: + return a + + # Interpolate everything else. + else: + if a is None: + a = 0 + + return correct_type(a + t * (b - a), b, type) + +# Interpolate the value of a spline. This code is based on Aenakume's code, +# from 00splines.rpy. +def interpolate_spline(t, spline): + + if isinstance(spline[-1], tuple): + return tuple(interpolate_spline(t, i) for i in zip(*spline)) + + if spline[0] is None: + return spline[-1] + + if len(spline) == 2: + t_p = 1.0 - t + + rv = t_p * spline[0] + t * spline[-1] + + elif len(spline) == 3: + t_pp = (1.0 - t)**2 + t_p = 2 * t * (1.0 - t) + t2 = t**2 + + rv = t_pp * spline[0] + t_p * spline[1] + t2 * spline[2] + + elif len(spline) == 4: + + t_ppp = (1.0 - t)**3 + t_pp = 3 * t * (1.0 - t)**2 + t_p = 3 * t**2 * (1.0 - t) + t3 = t**3 + + rv = t_ppp * spline[0] + t_pp * spline[1] + t_p * spline[2] + t3 * spline[3] + + else: + raise Exception("ATL can't interpolate splines of length %d." % len(spline)) + + return correct_type(rv, spline[-1], position) + + +# This is the context used when compiling an ATL statement. It stores the +# scopes that are used to evaluate the various expressions in the statement, +# and has a method to do the evaluation and return a result. +class Context(object): + def __init__(self, context): + self.context = context + + def eval(self, expr): #@ReservedAssignment + expr = renpy.python.escape_unicode(expr) + return eval(expr, renpy.store.__dict__, self.context) #@UndefinedVariable + +# This is intended to be subclassed by ATLTransform. It takes care of +# managing ATL execution, which allows ATLTransform itself to not care +# much about the contents of this file. +class ATLTransformBase(renpy.object.Object): + + # Compatibility with older saves. + parameters = renpy.ast.ParameterInfo([ ], [ ], None, None) + + def __init__(self, atl, context, parameters): + + # The constructor will be called by atltransform. + + if parameters is None: + parameters = ATLTransformBase.parameters + + # The parameters that we take. + self.parameters = parameters + + # The raw code that makes up this ATL statement. + self.atl = atl + + # The context in which execution occurs. + self.context = Context(context) + + # The code after it has been compiled into a block. + self.block = None + + # The properties of the block, if it contains only an + # Interpolation. + self.properties = None + + # The state of the statement we are executing. As this can be + # shared between more than one object (in the case of a hide), + # the data must not be altered. + self.atl_state = None + + # Are we done? + self.done = False + + # The transform event we are going to process. + self.transform_event = None + + # The transform event we last processed. + self.last_transform_event = None + + # The child transform event we last processed. + self.last_child_transform_event = None + + def take_execution_state(self, t): + """ + Updates self to begin executing from the same point as t. This + requires that t.atl is self.atl. + """ + + super(ATLTransformBase, self).take_execution_state(t) + + if t.atl is not self.atl: + return + + self.done = t.done + self.block = t.block + self.atl_state = t.atl_state + self.transform_event = t.transform_event + self.last_transform_event = t.last_transform_event + self.last_child_transform_event = t.last_child_transform_event + + self.st = t.st + self.at = t.at + self.st_offset = t.st_offset + self.at_offset = t.at_offset + + if self.child is renpy.display.motion.null: + self.child = t.child + + + def __call__(self, *args, **kwargs): + + context = self.context.context.copy() + + for k, v in self.parameters.parameters: + if v is not None: + context[k] = renpy.python.py_eval(v) + + positional = list(self.parameters.positional) + args = list(args) + + child = None + + if not positional and args: + child = args.pop(0) + + # Handle positional arguments. + while positional and args: + name = positional.pop(0) + value = args.pop(0) + + if name in kwargs: + raise Exception('Parameter %r is used as both a positional and keyword argument to a transition.' % name) + + context[name] = value + + if args: + raise Exception("Too many arguments passed to ATL transform.") + + # Handle keyword arguments. + for k, v in kwargs.items(): + + if k in positional: + positional.remove(k) + context[k] = v + elif k in context: + context[k] = v + elif k == 'child': + child = v + else: + raise Exception('Parameter %r is not known by ATL Transform.' % k) + + if child is None: + child = self.child + + if child is None: + child = renpy.display.motion.get_null() + + # Create a new ATL Transform. + parameters = renpy.ast.ParameterInfo({}, positional, None, None) + + rv = renpy.display.motion.ATLTransform( + atl=self.atl, + child=child, + style=self.style_arg, + context=context, + parameters=parameters) + + rv.take_state(self) + + return rv + + + def compile(self): #@ReservedAssignment + """ + Compiles the ATL code into a block. As necessary, updates the + properties. + """ + + if self.parameters.positional and self.parameters.positional[0][1] is None: + raise Exception("Cannot compile ATL Transform, as it's missing positional parameter %s." % self.parameters.positional[0]) + + old_exception_info = renpy.game.exception_info + + self.block = self.atl.compile(self.context) + + if len(self.block.statements) == 1 \ + and isinstance(self.block.statements[0], Interpolation): + + interp = self.block.statements[0] + + if interp.duration == 0 and interp.properties: + self.properties = interp.properties[:] + + renpy.game.exception_info = old_exception_info + + + def execute(self, trans, st, at): + + if self.done: + return None + + if not self.block: + self.compile() + + # Propagate transform_events from children. + if self.child: + if self.child.transform_event != self.last_child_transform_event: + self.last_child_transform_event = self.child.transform_event + self.transform_event = self.child.transform_event + + # Hide request. + if trans.hide_request: + self.transform_event = "hide" + + if trans.replaced_request: + self.transform_event = "replaced" + + # Notice transform events. + if self.transform_event != self.last_transform_event: + event = self.transform_event + self.last_transform_event = self.transform_event + else: + event = None + + old_exception_info = renpy.game.exception_info + + if self.atl.animation: + timebase = at + else: + timebase = st + + action, arg, pause = self.block.execute(trans, timebase, self.atl_state, event) + + renpy.game.exception_info = old_exception_info + + # print "Executing", self, self.state, self.xpos, self.ypos + + if action == "continue": + self.atl_state = arg + else: + self.done = True + + return pause + + def predict_one(self): + self.atl.predict(self.context) + + def visit(self): + if not self.block: + self.compile() + + return self.children + self.block.visit() + + +# The base class for raw ATL statements. +class RawStatement(renpy.object.Object): + + def __init__(self, loc): + super(RawStatement, self).__init__() + self.loc = loc + + # Compiles this RawStatement into a Statement, by using ctx to + # evaluate expressions as necessary. + def compile(self, ctx): #@ReservedAssignment + raise Exception("Compile not implemented.") + + # Predicts the images used by this statement. + def predict(self, ctx): + return + +# The base class for compiled ATL Statements. +class Statement(renpy.object.Object): + + def __init__(self, loc): + super(Statement, self).__init__() + self.loc = loc + + # trans is the transform we're working on. + # st is the time since this statement started executing. + # state is the state stored by this statement, or None if + # we've just started executing this statement. + # event is an event we're triggering. + # + # "continue", state, pause - Causes this statement to execute + # again, with the given state passed in the second time around. + # + # + # "next", timeleft, pause - Causes the next statement to execute, + # with timeleft being the amount of time left after this statement + # finished. + # + # "event", (name, timeleft), pause - Causes an event to be reported, + # and control to head up to the event handler. + # + # "repeat", (count, timeleft), pause - Causes the repeat behavior + # to occur. + # + # As the Repeat statement can only appear in a block, only Block + # needs to deal with the repeat behavior. + # + # Pause is the amount of time until execute should be called again, + # or None if there's no need to call execute ever again. + def execute(self, trans, st, state, event): + raise Exception("Not implemented.") + + # Return a list of displayable children. + def visit(self): + return [ ] + +# This represents a Raw ATL block. +class RawBlock(RawStatement): + + # Should we use the animation timebase or the showing timebase? + animation = False + + def __init__(self, loc, statements, animation): + + super(RawBlock, self).__init__(loc) + + # A list of RawStatements in this block. + self.statements = statements + + self.animation = animation + + def compile(self, ctx): #@ReservedAssignment + compiling(self.loc) + + statements = [ i.compile(ctx) for i in self.statements ] + + return Block(self.loc, statements) + + def predict(self, ctx): + for i in self.statements: + i.predict(ctx) + + +# A compiled ATL block. +class Block(Statement): + def __init__(self, loc, statements): + + super(Block, self).__init__(loc) + + # A list of statements in the block. + self.statements = statements + + # The start times of various statements. + self.times = [ ] + + for i, s in enumerate(statements): + if isinstance(s, Time): + self.times.append((s.time, i + 1)) + + self.times.sort() + + def execute(self, trans, st, state, event): + + executing(self.loc) + + # Unpack the state. + if state is not None: + index, start, loop_start, repeats, times, child_state = state + else: + index, start, loop_start, repeats, times, child_state = 0, 0, 0, 0, self.times[:], None + + # What we might be returning. + action = "continue" + arg = None + pause = None + + while action == "continue": + + # Target is the time we're willing to execute to. + # Max_pause is how long we'll wait before executing again. + + # If we have times queued up, then use them to inform target + # and time. + if times: + time, tindex = times[0] + target = min(time, st) + max_pause = time - target + + # Otherwise, take the defaults. + else: + target = st + max_pause = 15 + + while True: + + # If we've hit the last statement, it's the end of + # this block. + if index >= len(self.statements): + return "next", target - start, None + + + # Find the statement and try to run it. + stmt = self.statements[index] + action, arg, pause = stmt.execute(trans, target - start, child_state, event) + + # On continue, persist our state. + if action == "continue": + if pause is None: + pause = max_pause + + action, arg, pause = "continue", (index, start, loop_start, repeats, times, arg), min(max_pause, pause) + break + + elif action == "event": + return action, arg, pause + + # On next, advance to the next statement in the block. + elif action == "next": + index += 1 + start = target - arg + child_state = None + + # On repeat, either terminate the block, or go to + # the first statement. + elif action == "repeat": + + count, arg = arg + loop_end = target - arg + duration = loop_end - loop_start + + if duration <= 0: + raise Exception("ATL appears to be in an infinite loop.") + + # Figure how many durations can occur between the + # start of the loop and now. + new_repeats = int((target - loop_start) / duration) + + if count is not None: + if repeats + new_repeats >= count: + new_repeats = count - repeats + loop_start += new_repeats * duration + return "next", target - loop_start, None + + repeats += new_repeats + loop_start = loop_start + new_repeats * duration + start = loop_start + index = 0 + child_state = None + + if times: + time, tindex = times[0] + if time <= target: + times.pop(0) + + index = tindex + start = time + child_state = None + + continue + + return action, arg, pause + + def visit(self): + return [ j for i in self.statements for j in i.visit() ] + +# This can become one of four things: +# +# - A pause. +# - An interpolation (which optionally can also reference other +# blocks, as long as they're not time-dependent, and have the same +# arity as the interpolation). +# - A call to another block. +# - A command to change the image, perhaps with a transition. +# +# We won't decide which it is until runtime, as we need the +# values of the variables here. +class RawMultipurpose(RawStatement): + + warp_function = None + + def __init__(self, loc): + + super(RawMultipurpose, self).__init__(loc) + + self.warper = None + self.duration = None + self.properties = [ ] + self.expressions = [ ] + self.splines = [ ] + self.revolution = None + self.circles = "0" + + def add_warper(self, name, duration, warp_function): + self.warper = name + self.duration = duration + self.warp_function = warp_function + + def add_property(self, name, exprs): + self.properties.append((name, exprs)) + + def add_expression(self, expr, with_clause): + self.expressions.append((expr, with_clause)) + + def add_revolution(self, revolution): + self.revolution = revolution + + def add_circles(self, circles): + self.circles = circles + + def add_spline(self, name, exprs): + self.splines.append((name, exprs)) + + def compile(self, ctx): #@ReservedAssignment + + compiling(self.loc) + + # Figure out what kind of statement we have. If there's no + # interpolator, and no properties, than we have either a + # call, or a child statement. + if (self.warper is None and + self.warp_function is None and + not self.properties and + not self.splines and + len(self.expressions) == 1): + + expr, withexpr = self.expressions[0] + + child = ctx.eval(expr) + if withexpr: + transition = ctx.eval(withexpr) + else: + transition = None + + if isinstance(child, (int, float)): + return Interpolation(self.loc, "pause", child, [ ], None, 0, [ ]) + + if isinstance(child, ATLTransformBase): + child.compile() + return child.block + + else: + return Child(self.loc, child, transition) + + compiling(self.loc) + + # Otherwise, we probably have an interpolation statement. + + if self.warp_function: + warper = ctx.eval(self.warp_function) + else: + warper = self.warper or "pause" + + if warper not in warpers: + raise Exception("ATL Warper %s is unknown at runtime." % warper) + + properties = [ ] + + for name, expr in self.properties: + if name not in PROPERTIES: + raise Exception("ATL Property %s is unknown at runtime." % property) + + value = ctx.eval(expr) + properties.append((name, value)) + + splines = [ ] + + for name, exprs in self.splines: + if name not in PROPERTIES: + raise Exception("ATL Property %s is unknown at runtime." % property) + + values = [ ctx.eval(i) for i in exprs ] + + splines.append((name, values)) + + for expr, _with in self.expressions: + try: + value = ctx.eval(expr) + except: + raise Exception("Could not evaluate expression %r when compiling ATL." % expr) + + if not isinstance(value, ATLTransformBase): + raise Exception("Expression %r is not an ATL transform, and so cannot be included in an ATL interpolation." % expr) + + value.compile() + + if value.properties is None: + raise Exception("ATL transform %r is too complicated to be included in interpolation." % expr) + + + properties.extend(value.properties) + + duration = ctx.eval(self.duration) + circles = ctx.eval(self.circles) + + return Interpolation(self.loc, warper, duration, properties, self.revolution, circles, splines) + + def predict(self, ctx): + + for i, _j in self.expressions: + + try: + i = ctx.eval(i) + except: + continue + + if isinstance(i, ATLTransformBase): + i.atl.predict(ctx) + return + + try: + renpy.easy.predict(i) + except: + continue + +# This lets us have an ATL transform as our child. +class RawContainsExpr(RawStatement): + + def __init__(self, loc, expr): + + super(RawContainsExpr, self).__init__(loc) + + self.expression = expr + + def compile(self, ctx): #@ReservedAssignment + compiling(self.loc) + child = ctx.eval(self.expression) + return Child(self.loc, child, None) + + +# This allows us to have multiple children, inside a Fixed. +class RawChild(RawStatement): + + def __init__(self, loc, child): + + super(RawChild, self).__init__(loc) + + self.children = [ child ] + + def compile(self, ctx): #@ReservedAssignment + box = renpy.display.layout.MultiBox(layout='fixed') + + for i in self.children: + box.add(renpy.display.motion.ATLTransform(i, context=ctx.context)) + + return Child(self.loc, box, None) + + +# This changes the child of this statement, optionally with a transition. +class Child(Statement): + + def __init__(self, loc, child, transition): + + super(Child, self).__init__(loc) + + self.child = renpy.easy.displayable(child) + self.transition = transition + + def execute(self, trans, st, state, event): + + executing(self.loc) + + old_child = trans.raw_child + + if (old_child is not None) and (old_child is not renpy.display.motion.null) and (self.transition is not None): + child = self.transition(old_widget=old_child, + new_widget=self.child) + else: + child = self.child + + trans.set_child(child) + trans.raw_child = self.child + + return "next", st, None + + def visit(self): + return [ self.child ] + + +# This causes interpolation to occur. +class Interpolation(Statement): + + def __init__(self, loc, warper, duration, properties, revolution, circles, splines): + + super(Interpolation, self).__init__(loc) + + self.warper = warper + self.duration = duration + self.properties = properties + self.splines = splines + + # The direction we revolve in: cw, ccw, or None. + self.revolution = revolution + + # The number of complete circles we make. + self.circles = circles + + def execute(self, trans, st, state, event): + + executing(self.loc) + + warper = warpers.get(self.warper, self.warper) + + if self.duration: + complete = min(1.0, st / self.duration) + else: + complete = 1.0 + + complete = warper(complete) + + if state is None: + + # Create a new transform state, and apply the property + # changes to it. + newts = renpy.display.motion.TransformState() + newts.take_state(trans.state) + + for k, v in self.properties: + setattr(newts, k, v) + + # Now, the things we change linearly are in the difference + # between the new and old states. + linear = trans.state.diff(newts) + + revolution = None + splines = [ ] + + # Clockwise revolution. + if self.revolution is not None: + + # Remove various irrelevant motions. + for i in [ 'xpos', 'ypos', + 'xanchor', 'yanchor', + 'xaround', 'yaround', + 'xanchoraround', 'yanchoraround', + ]: + + linear.pop(i, None) + + if newts.xaround is not None: + + # Ensure we rotate around the new point. + trans.state.xaround = newts.xaround + trans.state.yaround = newts.yaround + trans.state.xanchoraround = newts.xanchoraround + trans.state.yanchoraround = newts.yanchoraround + + # Get the start and end angles and radii. + startangle = trans.state.angle + endangle = newts.angle + startradius = trans.state.radius + endradius = newts.radius + + # Make sure the revolution is in the appropriate direction, + # and contains an appropriate number of circles. + + if self.revolution == "clockwise": + if endangle < startangle: + startangle -= 360 + + startangle -= self.circles * 360 + + elif self.revolution == "counterclockwise": + if endangle > startangle: + startangle += 360 + + startangle += self.circles * 360 + + # Store the revolution. + revolution = (startangle, endangle, startradius, endradius) + + # Figure out the splines. + for name, values in self.splines: + splines.append((name, [ getattr(trans.state, name) ] + values)) + + state = (linear, revolution, splines) + + # Ensure that we set things, even if they don't actually + # change from the old state. + for k, v in self.properties: + if k not in linear: + setattr(trans.state, k, v) + + else: + linear, revolution, splines = state + + # Linearly interpolate between the things in linear. + for k, (old, new) in linear.items(): + value = interpolate(complete, old, new, PROPERTIES[k]) + + setattr(trans.state, k, value) + + # Handle the revolution. + if revolution is not None: + startangle, endangle, startradius, endradius = revolution + trans.state.angle = interpolate(complete, startangle, endangle, float) + trans.state.radius = interpolate(complete, startradius, endradius, float) + + + # Handle any splines we might have. + for name, values in splines: + value = interpolate_spline(complete, values) + setattr(trans.state, name, value) + + if st >= self.duration: + return "next", st - self.duration, None + else: + if not self.properties and not self.revolution and not self.splines: + return "continue", state, self.duration - st + else: + return "continue", state, 0 + + +# Implementation of the repeat statement. +class RawRepeat(RawStatement): + + def __init__(self, loc, repeats): + + super(RawRepeat, self).__init__(loc) + + self.repeats = repeats + + def compile(self, ctx): #@ReservedAssignment + + compiling(self.loc) + + repeats = self.repeats + + if repeats is not None: + repeats = ctx.eval(repeats) + + return Repeat(self.loc, repeats) + +class Repeat(Statement): + + def __init__(self, loc, repeats): + + super(Repeat, self).__init__(loc) + + self.repeats = repeats + + def execute(self, trans, st, state, event): + return "repeat", (self.repeats, st), 0 + + +# Parallel statement. + +class RawParallel(RawStatement): + + def __init__(self, loc, block): + + super(RawParallel, self).__init__(loc) + self.blocks = [ block ] + + def compile(self, ctx): #@ReservedAssignment + return Parallel(self.loc, [i.compile(ctx) for i in self.blocks]) + + def predict(self, ctx): + for i in self.blocks: + i.predict(ctx) + + +class Parallel(Statement): + + def __init__(self, loc, blocks): + super(Parallel, self).__init__(loc) + self.blocks = blocks + + def execute(self, trans, st, state, event): + + executing(self.loc) + + if state is None: + state = [ (i, None) for i in self.blocks ] + + # The amount of time left after finishing this block. + left = [ ] + + # The duration of the pause. + pauses = [ ] + + # The new state structure. + newstate = [ ] + + for i, istate in state: + + action, arg, pause = i.execute(trans, st, istate, event) + + if pause is not None: + pauses.append(pause) + + if action == "continue": + newstate.append((i, arg)) + elif action == "next": + left.append(arg) + elif action == "event": + return action, arg, pause + + if newstate: + return "continue", newstate, min(pauses) + else: + return "next", min(left), None + + def visit(self): + return [ j for i in self.blocks for j in i.visit() ] + + +# The choice statement. + +class RawChoice(RawStatement): + + def __init__(self, loc, chance, block): + super(RawChoice, self).__init__(loc) + + self.choices = [ (chance, block) ] + + def compile(self, ctx): #@ReservedAssignment + compiling(self.loc) + return Choice(self.loc, [ (ctx.eval(chance), block.compile(ctx)) for chance, block in self.choices]) + + def predict(self, ctx): + for _i, j in self.choices: + j.predict(ctx) + +class Choice(Statement): + + def __init__(self, loc, choices): + + super(Choice, self).__init__(loc) + + self.choices = choices + + def execute(self, trans, st, state, event): + + executing(self.loc) + + if state is None: + + total = 0 + for chance, choice in self.choices: + total += chance + + n = random.uniform(0, total) + + for chance, choice in self.choices: + if n < chance: + break + n -= chance + + cstate = None + + else: + choice, cstate = state + + action, arg, pause = choice.execute(trans, st, cstate, event) + + if action == "continue": + return "continue", (choice, arg), pause + else: + return action, arg, None + + def visit(self): + return [ j for i in self.choices for j in i[1].visit() ] + + +# The Time statement. + +class RawTime(RawStatement): + + def __init__(self, loc, time): + + super(RawTime, self).__init__(loc) + self.time = time + + def compile(self, ctx): #@ReservedAssignment + compiling(self.loc) + return Time(self.loc, ctx.eval(self.time)) + +class Time(Statement): + + def __init__(self, loc, time): + super(Time, self).__init__(loc) + + self.time = time + + def execute(self, trans, st, state, event): + return "continue", None, None + + +# The On statement. + +class RawOn(RawStatement): + + def __init__(self, loc, name, block): + super(RawOn, self).__init__(loc) + + self.handlers = { name : block } + + def compile(self, ctx): #@ReservedAssignment + + compiling(self.loc) + + handlers = { } + + for k, v in self.handlers.items(): + handlers[k] = v.compile(ctx) + + return On(self.loc, handlers) + + def predict(self, ctx): + for i in self.handlers.values(): + i.predict(ctx) + +class On(Statement): + + def __init__(self, loc, handlers): + super(On, self).__init__(loc) + + self.handlers = handlers + + def execute(self, trans, st, state, event): + + executing(self.loc) + + # If it's our first time through, start in the start state. + if state is None: + name, start, cstate = ("start", st, None) + else: + name, start, cstate = state + + # If we have an external event, and we have a handler for it, + # handle it. + if event in self.handlers: + + # Do not allow people to abort the hide handler with another + # event. + if name != "hide": + name = event + start = st + cstate = None + + + while True: + + # If we don't have a handler, return until we change event. + if name not in self.handlers: + return "continue", (name, start, cstate), None + + action, arg, pause = self.handlers[name].execute(trans, st - start, cstate, event) + + # If we get a continue, save our state. + if action == "continue": + + # If it comes from a hide block, indicate that. + if name == "hide" or name == "replaced": + trans.hide_response = False + trans.replaced_response = False + + return "continue", (name, start, arg), pause + + # If we get a next, then try going to the default + # event, unless we're already in default, in which case we + # go to None. + elif action == "next": + if name == "default" or name == "hide" or name == "replaced": + name = None + else: + name = "default" + + start = st - arg + cstate = None + + continue + + # If we get an event, then either handle it if we can, or + # pass it up the stack if we can't. + elif action == "event": + + name, arg = arg + + if name in self.handlers: + start = max(st - arg, st - 30) + cstate = None + continue + + return "event", (name, arg), None + + def visit(self): + return [ j for i in self.handlers.values() for j in i.visit() ] + + +# Event statement. + +class RawEvent(RawStatement): + + def __init__(self, loc, name): + super(RawEvent, self).__init__(loc) + + self.name = name + + def compile(self, ctx): #@ReservedAssignment + return Event(self.loc, self.name) + + +class Event(Statement): + + def __init__(self, loc, name): + super(Event, self).__init__(loc) + + self.name = name + + def execute(self, trans, st, state, event): + return "event", (self.name, st), None + + +class RawFunction(RawStatement): + + def __init__(self, loc, expr): + super(RawFunction, self).__init__(loc) + + self.expr = expr + + def compile(self, ctx): #@ReservedAssignment + compiling(self.loc) + return Function(self.loc, ctx.eval(self.expr)) + + +class Function(Statement): + + def __init__(self, loc, function): + super(Function, self).__init__(loc) + + self.function = function + + def execute(self, trans, st, state, event): + fr = self.function(trans, st, trans.at) + + if fr is not None: + return "continue", None, fr + else: + return "next", 0, None + + +# This parses an ATL block. +def parse_atl(l): + + l.advance() + block_loc = l.get_location() + + statements = [ ] + + animation = False + + while not l.eob: + + loc = l.get_location() + + if l.keyword('repeat'): + + repeats = l.simple_expression() + statements.append(RawRepeat(loc, repeats)) + + elif l.keyword('block'): + l.require(':') + l.expect_eol() + l.expect_block('block') + + block = parse_atl(l.subblock_lexer()) + statements.append(block) + + elif l.keyword('contains'): + + expr = l.simple_expression() + + if expr: + + l.expect_noblock('contains expression') + statements.append(RawContainsExpr(loc, expr)) + + else: + + l.require(':') + l.expect_eol() + l.expect_block('contains') + + block = parse_atl(l.subblock_lexer()) + statements.append(RawChild(loc, block)) + + elif l.keyword('parallel'): + l.require(':') + l.expect_eol() + l.expect_block('parallel') + + block = parse_atl(l.subblock_lexer()) + statements.append(RawParallel(loc, block)) + + elif l.keyword('choice'): + + chance = l.simple_expression() + if not chance: + chance = "1.0" + + l.require(':') + l.expect_eol() + l.expect_block('choice') + + block = parse_atl(l.subblock_lexer()) + statements.append(RawChoice(loc, chance, block)) + + elif l.keyword('on'): + + name = l.require(l.word) + + l.require(':') + l.expect_eol() + l.expect_block('on') + + block = parse_atl(l.subblock_lexer()) + statements.append(RawOn(loc, name, block)) + + elif l.keyword('time'): + time = l.require(l.simple_expression) + l.expect_noblock('time') + + statements.append(RawTime(loc, time)) + + elif l.keyword('function'): + expr = l.require(l.simple_expression) + l.expect_noblock('function') + + statements.append(RawFunction(loc, expr)) + + elif l.keyword('event'): + name = l.require(l.word) + l.expect_noblock('event') + + statements.append(RawEvent(loc, name)) + + elif l.keyword('pass'): + l.expect_noblock('pass') + statements.append(None) + + elif l.keyword('animation'): + l.expect_noblock('animation') + animation = True + + else: + + # If we can't assign it it a statement more specifically, + # we try to parse it into a RawMultipurpose. That will + # then be turned into another statement, as appropriate. + + # The RawMultipurpose we add things to. + rm = renpy.atl.RawMultipurpose(loc) + + # Is the last clause an expression? + last_expression = False + + # Is this clause an expression? + this_expression = False + + # First, look for a warper. + cp = l.checkpoint() + warper = l.name() + + + if warper in warpers: + duration = l.require(l.simple_expression) + warp_function = None + + elif warper == "warp": + + warper = None + warp_function = l.require(l.simple_expression) + duration = l.require(l.simple_expression) + + else: + l.revert(cp) + + warper = None + warp_function = None + duration = "0" + + rm.add_warper(warper, duration, warp_function) + + # Now, look for properties and simple_expressions. + while True: + + # Update expression status. + last_expression = this_expression + this_expression = False + + if l.keyword('pass'): + continue + + # Parse revolution keywords. + if l.keyword('clockwise'): + rm.add_revolution('clockwise') + continue + + if l.keyword('counterclockwise'): + rm.add_revolution('counterclockwise') + continue + + if l.keyword('circles'): + expr = l.require(l.simple_expression) + rm.add_circles(expr) + + # Try to parse a property. + cp = l.checkpoint() + + prop = l.name() + + if prop in PROPERTIES: + + expr = l.require(l.simple_expression) + + # We either have a property or a spline. It's the + # presence of knots that determine which one it is. + + knots = [ ] + + while l.keyword('knot'): + knots.append(l.require(l.simple_expression)) + + if knots: + knots.append(expr) + rm.add_spline(prop, knots) + else: + rm.add_property(prop, expr) + + continue + + # Otherwise, try to parse it as a simple expressoon, + # with an optional with clause. + + l.revert(cp) + + expr = l.simple_expression() + + if not expr: + break + + if last_expression: + l.error('ATL statement contains two expressions in a row; is one of them a misspelled property? If not, separate them with pass.') + + this_expression = True + + if l.keyword("with"): + with_expr = l.require(l.simple_expression) + else: + with_expr = None + + rm.add_expression(expr, with_expr) + + l.expect_noblock('ATL') + + statements.append(rm) + + + if l.eol(): + l.advance() + continue + + l.require(",", "comma or end of line") + + + # Merge together statements that need to be merged together. + + merged = [ ] + old = None + + for new in statements: + + if isinstance(old, RawParallel) and isinstance(new, RawParallel): + old.blocks.extend(new.blocks) + continue + + elif isinstance(old, RawChoice) and isinstance(new, RawChoice): + old.choices.extend(new.choices) + continue + + elif isinstance(old, RawChild) and isinstance(new, RawChild): + old.children.extend(new.children) + continue + + elif isinstance(old, RawOn) and isinstance(new, RawOn): + old.handlers.update(new.handlers) + continue + + # None is a pause statement, which gets skipped, but also + # prevents things from combining. + elif new is None: + old = new + continue + + merged.append(new) + old = new + + return RawBlock(block_loc, merged, animation) diff --git a/ast2json/renpy/game.py b/ast2json/renpy/game.py new file mode 100644 index 0000000..d46043f --- /dev/null +++ b/ast2json/renpy/game.py @@ -0,0 +1,437 @@ +# Copyright 2004-2013 Tom Rothamel +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# This module is intended to be used as a singleton object. +# It's purpose is to store in one global all of the data that would +# be to annoying to lug around otherwise. + +import renpy + +# The basepath. +basepath = None + +# A list of paths that we search to load things. This is searched for +# everything that can be loaded, before archives are used. +searchpath = [ ] + +# The options that were read off the command line. +args = None + +# The game's script. +script = None + +# A stack of execution contexts. +contexts = [ ] + +# The interface that the game uses to interact with the user. +interface = None + +# Are we inside lint? +lint = False + +# The RollbackLog that keeps track of changes to the game state +# and to the store. +log = None + +# Some useful additional information about program execution that +# can be added to the exception. +exception_info = '' + +# Used to store style information. +style = None + +# The set of statements we've seen in this session. +seen_session = { } + +# The set of statements we've ever seen. +seen_ever = { } + +# True if we're in the first interaction after a rollback or rollforward. +after_rollback = False + +# Code that's run after the init code. +post_init = [ ] + +# Should we attempt to run in a mode that uses less memory? +less_memory = False + +# Should we attempt to run in a mode that minimizes the number +# of screen updates? +less_updates = False + +# Should we never show the mouse? +less_mouse = False + +# Should we not imagedissiolve? +less_imagedissolve = False + +# The class that's used to hold the persistent data. +class Persistent(object): + + def __setstate__(self, data): + vars(self).update(data) + + def __getstate__(self): + return vars(self) + + # Undefined attributes return None. + def __getattr__(self, attr): + return None + +# The persistent data that's kept from session to session +persistent = Persistent() + +class Preferences(renpy.object.Object): + """ + Stores preferences that will one day be persisted. + """ + __version__ = 5 + + def after_upgrade(self, version): + if version < 1: + self.mute_volumes = 0 + if version < 2: + self.using_afm_enable = False + if version < 3: + self.physical_size = None + if version < 4: + self.renderer = "auto" + self.performance_test = True + if version < 5: + self.language = None + + def __init__(self): + self.fullscreen = False + self.skip_unseen = False + self.text_cps = 0 + self.afm_time = 0 + self.afm_enable = True + + # These will be going away soon. + self.sound = True + self.music = True + + # 2 - All transitions. + # 1 - Only non-default transitions. + # 0 - No transitions. + self.transitions = 2 + + self.skip_after_choices = False + + # Mixer channel info. + + # A map from channel name to the current volume (between 0 and 1). + self.volumes = { } + + # True if the channel should not play music. False + # otherwise. (Not used anymore.) + self.mute = { } + + # Joystick mappings. + self.joymap = dict( + joy_left="Axis 0.0 Negative", + joy_right="Axis 0.0 Positive", + joy_up="Axis 0.1 Negative", + joy_down="Axis 0.1 Positive", + joy_dismiss="Button 0.0") + + # The size of the window, or None if we don't know it yet. + self.physical_size = None + + # The graphics renderer we use. + self.renderer = "auto" + + # Should we do a performance test on startup? + self.performance_test = True + + # The language we use for translations. + self.language = None + + def set_volume(self, mixer, volume): + self.volumes[mixer] = volume + + def get_volume(self, mixer): + return self.volumes.get(mixer, 0) + + def set_mute(self, mixer, mute): + self.mute[mixer] = mute + + def get_mute(self, mixer): + return self.mute[mixer] + +# The current preferences. +preferences = Preferences() + +class RestartContext(Exception): + """ + Restarts the current context. If `label` is given, calls that label + in the restarted context. + """ + + def __init__(self, label): + self.label = label + +class RestartTopContext(Exception): + """ + Restarts the top context. If `label` is given, calls that label + in the restarted context. + """ + + def __init__(self, label): + self.label = label + +class FullRestartException(Exception): + """ + An exception of this type forces a hard restart, completely + destroying the store and config and so on. + """ + + def __init__(self, reason="end_game"): # W0231 + self.reason = reason + +class UtterRestartException(Exception): + """ + An exception of this type forces an even harder restart, causing + Ren'Py and the script to be reloaded. + """ + +class QuitException(Exception): + """ + An exception of this class will let us force a safe quit, from + anywhere in the program. + + `relaunch` + If given, the program will run another copy of itself, with the + same arguments. + """ + + def __init__(self, relaunch=False): + Exception.__init__(self) + self.relaunch = relaunch + +class JumpException(Exception): + """ + This should be raised with a label as the only argument. This causes + the current statement to terminate, and execution to be transferred + to the named label. + """ + +class JumpOutException(Exception): + """ + This should be raised with a label as the only argument. This exits + the current context, and then raises a JumpException. + """ + +class CallException(Exception): + """ + Raise this exception to cause the current statement to terminate, + and control to be transferred to the named label. + """ + + def __init__(self, label, args, kwargs): + Exception.__init__(self) + + self.label = label + self.args = args + self.kwargs = kwargs + +class EndReplay(Exception): + """ + Raise this exception to end the current replay (the current call to + call_replay). + """ + +class ParseErrorException(Exception): + """ + This is raised when a parse error occurs, after it has been + reported to the user. + """ + +# A tuple of exceptions that should not be caught by the +# exception reporting mechanism. +CONTROL_EXCEPTIONS = ( + RestartContext, + RestartTopContext, + FullRestartException, + UtterRestartException, + QuitException, + JumpException, + JumpOutException, + CallException, + EndReplay, + ParseErrorException, + KeyboardInterrupt, + ) + + +def context(index=-1): + """ + Return the current execution context, or the context at the + given index if one is specified. + """ + + return contexts[index] + +def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment + """ + This pushes the current context, and invokes the given python + function in a new context. When that function returns or raises an + exception, it removes the new context, and restores the current + context. + + Additional arguments and keyword arguments are passed to the + callable. + + Please note that the context so created cannot execute renpy + code. So exceptions that change the flow of renpy code (like + the one created by renpy.jump) cause this context to terminate, + and are handled by the next higher context. + + If you want to execute renpy code from the function, you can call + it with renpy.call_in_new_context. + + Use this to begin a second interaction with the user while + inside an interaction. + """ + + context = renpy.execution.Context(False, contexts[-1], clear=True) + contexts.append(context) + + if renpy.display.interface is not None: + renpy.display.interface.enter_context() + + try: + + return callable(*args, **kwargs) + + except renpy.game.JumpOutException as e: + + raise renpy.game.JumpException(e.args[0]) + + finally: + + contexts.pop() + contexts[-1].do_deferred_rollback() + + if interface.restart_interaction and contexts: + contexts[-1].scene_lists.focused = None + + + +def call_in_new_context(label, *args, **kwargs): + """ + This code creates a new context, and starts executing code from + that label in the new context. Rollback is disabled in the + new context. (Actually, it will just bring you back to the + real context.) + + Use this to begin a second interaction with the user while + inside an interaction. + """ + + context = renpy.execution.Context(False, contexts[-1], clear=True) + contexts.append(context) + + if renpy.display.interface is not None: + renpy.display.interface.enter_context() + + if args: + renpy.store._args = args + else: + renpy.store._args = None + + if kwargs: + renpy.store._kwargs = renpy.python.RevertableDict(kwargs) + else: + renpy.store._kwargs = None + + try: + + context.goto_label(label) + renpy.execution.run_context(False) + + rv = renpy.store._return #@UndefinedVariable + + return rv + + except renpy.game.JumpOutException as e: + + raise renpy.game.JumpException(e.args[0]) + + finally: + + contexts.pop() + contexts[-1].do_deferred_rollback() + + if interface.restart_interaction and contexts: + contexts[-1].scene_lists.focused = None + +def call_replay(label, scope={}): + """ + :doc: replay + + Calls a label as a memory. + + Keyword arguments are used to set the initial values of variables in the + memory context. + """ + + renpy.game.log.complete() + + old_log = renpy.game.log + renpy.game.log = renpy.python.RollbackLog() + + sb = renpy.python.StoreBackup() + renpy.python.clean_stores() + + context = renpy.execution.Context(True) + contexts.append(context) + + if renpy.display.interface is not None: + renpy.display.interface.enter_context() + + for k, v in scope.items(): + setattr(renpy.store, k, v) + + renpy.store._in_replay = label + + try: + + context.goto_label("_start_replay") + renpy.execution.run_context(False) + + except EndReplay: + pass + + finally: + contexts.pop() + renpy.game.log = old_log + sb.restore() + + if interface.restart_interaction and contexts: + contexts[-1].scene_lists.focused = None + + +# Type information. +if False: + script = renpy.script.Script() + interface = renpy.display.core.Interface() + log = renpy.python.RollbackLog() diff --git a/ast2json/renpy/object.py b/ast2json/renpy/object.py new file mode 100644 index 0000000..c595e8c --- /dev/null +++ b/ast2json/renpy/object.py @@ -0,0 +1,60 @@ +# Copyright 2004-2013 Tom Rothamel +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +class Object(object): + """ + Our own base class. Contains methods to simplify serialization. + """ + + __version__ = 0 + + nosave = [ ] + + def __getstate__(self): + rv = vars(self).copy() + + for f in self.nosave: + if f in rv: + del rv[f] + + + rv["__version__"] = self.__version__ + + return rv + + + # None, to prevent this from being called when unnecessary. + after_setstate = None + + def __setstate__(self, new_dict): + + version = new_dict.pop("__version__", 0) + + self.__dict__.update(new_dict) + + if version != self.__version__: + self.after_upgrade(version) # E1101 + + if self.after_setstate: + self.after_setstate() # E1102 + +# We don't handle slots with this mechanism, since the call to vars should +# throw an error. diff --git a/ast2json/rpyc2json.py b/ast2json/rpyc2json.py new file mode 100755 index 0000000..e57ed83 --- /dev/null +++ b/ast2json/rpyc2json.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2012 Yuri K. Schlesner +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import ast2json +import glob +import itertools +import simplejson as json +import optparse +import os.path +import pickle +import sys +import zlib + +class Dummy: + def record_pycode(self, *args, **kwargs): + return + all_pycode = [] + +# Needed for pickle to read the AST +import renpy +import renpy.object +import renpy.game +renpy.game.script = Dummy() +import renpy.ast +import renpy.atl + +def pretty_print_ast(out_file, ast): + json.dump(rast2json(ast), out_file, separators=(',', ':')) + +def node2json(node): + to_return = {} + to_return['_type'] = node.__class__.__name__ + for attr in node.__slots__: + to_return[attr] = get_value(getattr(node, attr)) + + return to_return + +def rast2json(ast): + return list(map(node2json, ast)) + +def get_value(attr_value): + if attr_value is None: + return attr_value + if isinstance(attr_value, (int, str, float, complex, bool)): + return attr_value + if isinstance(attr_value, list) or isinstance(attr_value, tuple): + return [get_value(x) for x in attr_value] + if isinstance(attr_value, renpy.ast.Node): + return node2json(attr_value) + if isinstance(attr_value, renpy.ast.PyCode): + return ast2json.str2json(attr_value.source) + if isinstance(attr_value, renpy.ast.ArgumentInfo): + return list(map(lambda x: getattr(attr_value, x), ["arguments", "extrapos", "extrakw"])) + if isinstance(attr_value, renpy.atl.RawBlock): + return 'ATL not implemented' + + raise Exception("I don't know how to decompile '%s' of type '%s'!" % (attr_value, type(attr_value))) + +def read_ast_from_file(in_file): + raw_contents = zlib.decompress(in_file.read()) + _, stmts = pickle.loads(raw_contents) + return stmts + +def decompile_rpyc(input_filename, out_filename): + + print("Decompiling %s to %s..." % (input_filename, out_filename)) + + with open(input_filename, 'rb') as in_file: + ast = read_ast_from_file(in_file) + + with open(out_filename, 'w', encoding='utf-8') as out_file: + pretty_print_ast(out_file, ast) + +if __name__ == "__main__": + + if len(sys.argv) != 3: raise Exception() + + decompile_rpyc(sys.argv[1], sys.argv[2]) + diff --git a/ast2json/script2json.py b/ast2json/script2json.py new file mode 100755 index 0000000..60be575 --- /dev/null +++ b/ast2json/script2json.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 + +import json +import sys + +def script2json(script): + ret = {} + if script[0]['_type'] != 'Label': raise TypeError('obj does not start with Label, wrong file?') + for inst in script: + inst_type = inst['_type'] + if inst_type == 'Label': + label = [] + ret[inst['name']] = label + continue + + func = statement_printer_dict.get(inst_type, print_Unknown) + label.append([inst_type] + func(inst)) + return ret + +def print_Say(stmt): + return [stmt['who'], stmt['what'], stmt['with_']] + +def print_Jump(stmt): + if stmt['expression']: + raise NotImplementedError() + return [stmt['target']] + +def print_Scene(stmt): + return [stmt['imspec'], stmt['atl']] + +def print_With(stmt): + return [stmt['expr']] + +def print_Show(stmt): + return [stmt['imspec'], stmt['atl']] + +def print_Hide(stmt): + return [stmt['imspec']] + +def print_Python(stmt): + return [stmt['code']] + +def print_Return(stmt): + return [stmt['expression']] + +def print_UserStatement(stmt): + return [stmt['line']] + +def print_Init(stmt): + raise NotImplementedError() + +def print_Image(stmt): + raise NotImplementedError() + +def print_Transform(stmt): + raise NotImplementedError() + +def print_Menu(stmt): + if stmt['with_'] != 'menueffect': + raise NotImplementedError() + + if stmt['set'] is not None: + raise NotImplementedError() + + return [] + + for item in stmt['items']: + # caption + f.write(u"\"%s\"" % (escape_string(item[0]), )) + + if item[2] is not None: + # condition + if item[1] != 'True': + f.write(u" if %s" % (item[1], )) + + f.write(u':\n') + + for inner_stmt in item[2]: + print_statement(f, inner_stmt, indent_level + 2) + else: + f.write(u'\n') + +def print_Pass(stmt): + return [] + +def print_Call(stmt): + f.write(u"call ") + if stmt['expression']: + raise NotImplementedError() + + return [stmt['label'], stmt['arguments']] + +def print_If(stmt): + f.write(u"if %s:\n" % (stmt['entries'][0][0], )) + for inner_stmt in stmt['entries'][0][1]: + print_statement(f, inner_stmt, indent_level + 1) + + if len(stmt['entries']) >= 2: + if stmt['entries'][-1][0].strip() == 'True': + else_entry = stmt['entries'][-1] + elif_entries = stmt['entries'][1:-1] + else: + else_entry = None + elif_entries = stmt['entries'] + + for case in elif_entries: + indent(f, indent_level) + f.write(u"elif %s:\n" % (case[0], )) + for inner_stmt in case[1]: + print_statement(f, inner_stmt, indent_level + 1) + + if else_entry is not None: + indent(f, indent_level) + f.write(u"else:\n") + for inner_stmt in else_entry[1]: + print_statement(f, inner_stmt, indent_level + 1) + +def print_EarlyPython(stmt): + print_Python(stmt, early=True) + +statement_printer_dict = { + "Say": print_Say, + "Jump": print_Jump, + "Scene": print_Scene, + "With": print_With, + "Show": print_Show, + "Hide": print_Hide, + "Python": print_Python, + "Return": print_Return, + "UserStatement": print_UserStatement, + "Init": print_Init, + "Image": print_Image, + "Transform": print_Transform, + "Menu": print_Menu, + "Pass": print_Pass, + "Call": print_Call, + "If": print_If, + "EarlyPython": print_EarlyPython, + } + +def print_Unknown(stmt): + raise NotImplementedError("Unknown AST node: %s" % type(stmt).__name__) + +with open(sys.argv[1], 'r') as f: + output = script2json(json.load(f)) + +json.dump(output, open(sys.argv[2], 'w'), separators=(',', ':')) -- cgit v1.2.3-70-g09d2