aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2010-07-29 19:53:02 +0000
committerEugene Sandulenko2010-10-12 21:38:20 +0000
commita683a420a9e43705c972b5e74d55e319729e1a81 (patch)
treebde6e4abd417bdfaec120aa951da9a19be36b654
parent7723d91c957d07205c51be32498d45cd0a78568f (diff)
downloadscummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.gz
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.tar.bz2
scummvm-rg350-a683a420a9e43705c972b5e74d55e319729e1a81.zip
SWORD25: Importing original sources
svn-id: r53171
-rwxr-xr-xengines/sword25/fmv/movieplayer.cpp36
-rwxr-xr-xengines/sword25/fmv/movieplayer.h138
-rwxr-xr-xengines/sword25/fmv/movieplayer_script.cpp187
-rwxr-xr-xengines/sword25/fmv/oggtheora/audiobuffer.cpp93
-rwxr-xr-xengines/sword25/fmv/oggtheora/audiobuffer.h53
-rwxr-xr-xengines/sword25/fmv/oggtheora/moviefile.cpp90
-rwxr-xr-xengines/sword25/fmv/oggtheora/moviefile.h57
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggstate.cpp59
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggstate.h63
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggstreamstate.cpp118
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggstreamstate.h74
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggtheora.cpp709
-rwxr-xr-xengines/sword25/fmv/oggtheora/oggtheora.h109
-rwxr-xr-xengines/sword25/fmv/oggtheora/theorastate.cpp79
-rwxr-xr-xengines/sword25/fmv/oggtheora/theorastate.h55
-rwxr-xr-xengines/sword25/fmv/oggtheora/vorbisstate.cpp97
-rwxr-xr-xengines/sword25/fmv/oggtheora/vorbisstate.h59
-rwxr-xr-xengines/sword25/fmv/oggtheora/yuvtorgba.cpp416
-rwxr-xr-xengines/sword25/fmv/oggtheora/yuvtorgba.h44
-rwxr-xr-xengines/sword25/gfx/animation.cpp878
-rwxr-xr-xengines/sword25/gfx/animation.h209
-rwxr-xr-xengines/sword25/gfx/animationdescription.cpp58
-rwxr-xr-xengines/sword25/gfx/animationdescription.h90
-rwxr-xr-xengines/sword25/gfx/animationresource.cpp323
-rwxr-xr-xengines/sword25/gfx/animationresource.h83
-rwxr-xr-xengines/sword25/gfx/animationtemplate.cpp293
-rwxr-xr-xengines/sword25/gfx/animationtemplate.h108
-rwxr-xr-xengines/sword25/gfx/animationtemplateregistry.cpp111
-rwxr-xr-xengines/sword25/gfx/animationtemplateregistry.h64
-rwxr-xr-xengines/sword25/gfx/bitmap.cpp214
-rwxr-xr-xengines/sword25/gfx/bitmap.h167
-rwxr-xr-xengines/sword25/gfx/bitmapresource.cpp54
-rwxr-xr-xengines/sword25/gfx/bitmapresource.h177
-rwxr-xr-xengines/sword25/gfx/dynamicbitmap.cpp191
-rwxr-xr-xengines/sword25/gfx/dynamicbitmap.h71
-rwxr-xr-xengines/sword25/gfx/fontresource.cpp243
-rwxr-xr-xengines/sword25/gfx/fontresource.h104
-rwxr-xr-xengines/sword25/gfx/framecounter.cpp53
-rwxr-xr-xengines/sword25/gfx/framecounter.h76
-rwxr-xr-xengines/sword25/gfx/graphicengine.cpp218
-rwxr-xr-xengines/sword25/gfx/graphicengine.h401
-rwxr-xr-xengines/sword25/gfx/graphicengine_script.cpp1722
-rwxr-xr-xengines/sword25/gfx/image/b25sloader.cpp101
-rwxr-xr-xengines/sword25/gfx/image/b25sloader.h52
-rwxr-xr-xengines/sword25/gfx/image/image.h208
-rwxr-xr-xengines/sword25/gfx/image/imageloader.cpp120
-rwxr-xr-xengines/sword25/gfx/image/imageloader.h358
-rwxr-xr-xengines/sword25/gfx/image/imageloader_ids.h44
-rwxr-xr-xengines/sword25/gfx/image/pngloader.cpp389
-rwxr-xr-xengines/sword25/gfx/image/pngloader.h64
-rwxr-xr-xengines/sword25/gfx/image/vectorimage.cpp571
-rwxr-xr-xengines/sword25/gfx/image/vectorimage.h167
-rwxr-xr-xengines/sword25/gfx/image/vectorimagerenderer.cpp198
-rwxr-xr-xengines/sword25/gfx/image/vectorimagerenderer.h76
-rwxr-xr-xengines/sword25/gfx/opengl/glimage.cpp208
-rwxr-xr-xengines/sword25/gfx/opengl/glimage.h85
-rwxr-xr-xengines/sword25/gfx/opengl/glvectorimageblit.cpp133
-rwxr-xr-xengines/sword25/gfx/opengl/openglgfx.cpp505
-rwxr-xr-xengines/sword25/gfx/opengl/openglgfx.h114
-rwxr-xr-xengines/sword25/gfx/opengl/swimage.cpp125
-rwxr-xr-xengines/sword25/gfx/opengl/swimage.h69
-rwxr-xr-xengines/sword25/gfx/panel.cpp123
-rwxr-xr-xengines/sword25/gfx/panel.h58
-rwxr-xr-xengines/sword25/gfx/renderobject.cpp571
-rwxr-xr-xengines/sword25/gfx/renderobject.h477
-rwxr-xr-xengines/sword25/gfx/renderobjectmanager.cpp163
-rwxr-xr-xengines/sword25/gfx/renderobjectmanager.h115
-rwxr-xr-xengines/sword25/gfx/renderobjectptr.h80
-rwxr-xr-xengines/sword25/gfx/renderobjectregistry.cpp50
-rwxr-xr-xengines/sword25/gfx/renderobjectregistry.h60
-rwxr-xr-xengines/sword25/gfx/rootrenderobject.h53
-rwxr-xr-xengines/sword25/gfx/screenshot.cpp202
-rwxr-xr-xengines/sword25/gfx/screenshot.h44
-rwxr-xr-xengines/sword25/gfx/staticbitmap.cpp218
-rwxr-xr-xengines/sword25/gfx/staticbitmap.h69
-rwxr-xr-xengines/sword25/gfx/text.cpp404
-rwxr-xr-xengines/sword25/gfx/text.h156
-rwxr-xr-xengines/sword25/gfx/timedrenderobject.cpp39
-rwxr-xr-xengines/sword25/gfx/timedrenderobject.h57
-rwxr-xr-xengines/sword25/input/inputengine.cpp36
-rwxr-xr-xengines/sword25/input/inputengine.h291
-rwxr-xr-xengines/sword25/input/inputengine_script.cpp364
-rwxr-xr-xengines/sword25/input/stdwininput.cpp401
-rwxr-xr-xengines/sword25/input/stdwininput.h88
-rwxr-xr-xengines/sword25/kernel/bs_stdint.h17
-rwxr-xr-xengines/sword25/kernel/callbackregistry.cpp123
-rwxr-xr-xengines/sword25/kernel/callbackregistry.h62
-rwxr-xr-xengines/sword25/kernel/common.h55
-rwxr-xr-xengines/sword25/kernel/cpuinfo.cpp260
-rwxr-xr-xengines/sword25/kernel/cpuinfo.h128
-rwxr-xr-xengines/sword25/kernel/debug/debugtools.cpp156
-rwxr-xr-xengines/sword25/kernel/debug/debugtools.h52
-rwxr-xr-xengines/sword25/kernel/debug/memorydumper.cpp184
-rwxr-xr-xengines/sword25/kernel/debug/memorydumper.h57
-rwxr-xr-xengines/sword25/kernel/filesystemutil.h102
-rwxr-xr-xengines/sword25/kernel/filesystemutil_win32.cpp256
-rwxr-xr-xengines/sword25/kernel/hashmap.h34
-rwxr-xr-xengines/sword25/kernel/inputpersistenceblock.cpp191
-rwxr-xr-xengines/sword25/kernel/inputpersistenceblock.h71
-rwxr-xr-xengines/sword25/kernel/kernel.cpp412
-rwxr-xr-xengines/sword25/kernel/kernel.h344
-rwxr-xr-xengines/sword25/kernel/kernel_script.cpp775
-rwxr-xr-xengines/sword25/kernel/log.cpp224
-rwxr-xr-xengines/sword25/kernel/log.h123
-rwxr-xr-xengines/sword25/kernel/md5.cpp385
-rwxr-xr-xengines/sword25/kernel/md5.h56
-rwxr-xr-xengines/sword25/kernel/memleaks.cpp130
-rwxr-xr-xengines/sword25/kernel/memleaks.h60
-rwxr-xr-xengines/sword25/kernel/memlog_off.h27
-rwxr-xr-xengines/sword25/kernel/memlog_on.h27
-rwxr-xr-xengines/sword25/kernel/objectregistry.h182
-rwxr-xr-xengines/sword25/kernel/outputpersistenceblock.cpp123
-rwxr-xr-xengines/sword25/kernel/outputpersistenceblock.h60
-rwxr-xr-xengines/sword25/kernel/persistable.h36
-rwxr-xr-xengines/sword25/kernel/persistenceblock.h112
-rwxr-xr-xengines/sword25/kernel/persistenceservice.cpp459
-rwxr-xr-xengines/sword25/kernel/persistenceservice.h71
-rwxr-xr-xengines/sword25/kernel/resmanager.cpp293
-rwxr-xr-xengines/sword25/kernel/resmanager.h185
-rwxr-xr-xengines/sword25/kernel/resource.cpp45
-rwxr-xr-xengines/sword25/kernel/resource.h97
-rwxr-xr-xengines/sword25/kernel/resservice.h110
-rwxr-xr-xengines/sword25/kernel/service.h57
-rwxr-xr-xengines/sword25/kernel/service_ids.h56
-rwxr-xr-xengines/sword25/kernel/string.h117
-rwxr-xr-xengines/sword25/kernel/timer.cpp53
-rwxr-xr-xengines/sword25/kernel/timer.h69
-rwxr-xr-xengines/sword25/kernel/win32window.cpp425
-rwxr-xr-xengines/sword25/kernel/win32window.h77
-rwxr-xr-xengines/sword25/kernel/wincodegenerator.cpp72
-rwxr-xr-xengines/sword25/kernel/wincodegenerator.h43
-rwxr-xr-xengines/sword25/kernel/wincodegenerator_win32.cpp128
-rwxr-xr-xengines/sword25/kernel/window.cpp53
-rwxr-xr-xengines/sword25/kernel/window.h176
-rwxr-xr-xengines/sword25/main.cpp192
-rwxr-xr-xengines/sword25/main_win.cpp244
-rwxr-xr-xengines/sword25/math/geometry.cpp45
-rwxr-xr-xengines/sword25/math/geometry.h46
-rwxr-xr-xengines/sword25/math/geometry_script.cpp602
-rwxr-xr-xengines/sword25/math/line.h206
-rwxr-xr-xengines/sword25/math/polygon.cpp506
-rwxr-xr-xengines/sword25/math/polygon.h252
-rwxr-xr-xengines/sword25/math/rect.h298
-rwxr-xr-xengines/sword25/math/region.cpp412
-rwxr-xr-xengines/sword25/math/region.h222
-rwxr-xr-xengines/sword25/math/regionregistry.cpp111
-rwxr-xr-xengines/sword25/math/regionregistry.h64
-rwxr-xr-xengines/sword25/math/vertex.cpp76
-rwxr-xr-xengines/sword25/math/vertex.h142
-rwxr-xr-xengines/sword25/math/walkregion.cpp432
-rwxr-xr-xengines/sword25/math/walkregion.h102
-rwxr-xr-xengines/sword25/package/packagemanager.cpp36
-rwxr-xr-xengines/sword25/package/packagemanager.h188
-rwxr-xr-xengines/sword25/package/packagemanager_script.cpp247
-rwxr-xr-xengines/sword25/package/physfspackagemanager.cpp487
-rwxr-xr-xengines/sword25/package/physfspackagemanager.h61
-rwxr-xr-xengines/sword25/script/lua_extensions.cpp68
-rwxr-xr-xengines/sword25/script/luabindhelper.cpp419
-rwxr-xr-xengines/sword25/script/luabindhelper.h107
-rwxr-xr-xengines/sword25/script/luacallback.cpp203
-rwxr-xr-xengines/sword25/script/luacallback.h64
-rwxr-xr-xengines/sword25/script/luascript.cpp607
-rwxr-xr-xengines/sword25/script/luascript.h102
-rwxr-xr-xengines/sword25/script/script.h99
-rwxr-xr-xengines/sword25/sfx/fmodexchannel.cpp299
-rwxr-xr-xengines/sword25/sfx/fmodexchannel.h69
-rwxr-xr-xengines/sword25/sfx/fmodexexception.h51
-rwxr-xr-xengines/sword25/sfx/fmodexresource.cpp170
-rwxr-xr-xengines/sword25/sfx/fmodexresource.h56
-rwxr-xr-xengines/sword25/sfx/fmodexsound.cpp889
-rwxr-xr-xengines/sword25/sfx/fmodexsound.h142
-rwxr-xr-xengines/sword25/sfx/soundengine.cpp36
-rwxr-xr-xengines/sword25/sfx/soundengine.h253
-rwxr-xr-xengines/sword25/sfx/soundengine_script.cpp397
-rwxr-xr-xengines/sword25/util/glsprites/glsprites.h108
-rwxr-xr-xengines/sword25/util/glsprites/internal/core.c212
-rwxr-xr-xengines/sword25/util/glsprites/internal/core.h40
-rwxr-xr-xengines/sword25/util/glsprites/internal/glinclude.h36
-rwxr-xr-xengines/sword25/util/glsprites/internal/glswindow.c449
-rwxr-xr-xengines/sword25/util/glsprites/internal/glswindow.h42
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite.c156
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite.h40
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_pow2.c170
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_pow2.h27
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_rectangle.c164
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_rectangle.h27
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_tiled.c370
-rwxr-xr-xengines/sword25/util/glsprites/internal/sprite_tiled.h41
-rwxr-xr-xengines/sword25/util/glsprites/internal/util.c77
-rwxr-xr-xengines/sword25/util/glsprites/internal/util.h42
-rwxr-xr-xengines/sword25/util/lua/COPYRIGHT34
-rwxr-xr-xengines/sword25/util/lua/HISTORY183
-rwxr-xr-xengines/sword25/util/lua/INSTALL99
-rwxr-xr-xengines/sword25/util/lua/Makefile120
-rwxr-xr-xengines/sword25/util/lua/README37
-rwxr-xr-xengines/sword25/util/lua/doc/amazon.gifbin0 -> 797 bytes
-rwxr-xr-xengines/sword25/util/lua/doc/contents.html499
-rwxr-xr-xengines/sword25/util/lua/doc/cover.pngbin0 -> 3305 bytes
-rwxr-xr-xengines/sword25/util/lua/doc/logo.gifbin0 -> 4232 bytes
-rwxr-xr-xengines/sword25/util/lua/doc/lua.1163
-rwxr-xr-xengines/sword25/util/lua/doc/lua.css41
-rwxr-xr-xengines/sword25/util/lua/doc/lua.html172
-rwxr-xr-xengines/sword25/util/lua/doc/luac.1136
-rwxr-xr-xengines/sword25/util/lua/doc/luac.html145
-rwxr-xr-xengines/sword25/util/lua/doc/manual.css13
-rwxr-xr-xengines/sword25/util/lua/doc/manual.html8764
-rwxr-xr-xengines/sword25/util/lua/doc/readme.html40
-rwxr-xr-xengines/sword25/util/lua/etc/Makefile44
-rwxr-xr-xengines/sword25/util/lua/etc/README37
-rwxr-xr-xengines/sword25/util/lua/etc/all.c38
-rwxr-xr-xengines/sword25/util/lua/etc/lua.hpp9
-rwxr-xr-xengines/sword25/util/lua/etc/lua.icobin0 -> 1078 bytes
-rwxr-xr-xengines/sword25/util/lua/etc/lua.pc31
-rwxr-xr-xengines/sword25/util/lua/etc/luavs.bat28
-rwxr-xr-xengines/sword25/util/lua/etc/min.c39
-rwxr-xr-xengines/sword25/util/lua/etc/noparser.c50
-rwxr-xr-xengines/sword25/util/lua/etc/strict.lua41
-rwxr-xr-xengines/sword25/util/lua/src/Makefile182
-rwxr-xr-xengines/sword25/util/lua/src/lapi.c1085
-rwxr-xr-xengines/sword25/util/lua/src/lapi.h16
-rwxr-xr-xengines/sword25/util/lua/src/lauxlib.c652
-rwxr-xr-xengines/sword25/util/lua/src/lauxlib.h174
-rwxr-xr-xengines/sword25/util/lua/src/lbaselib.c663
-rwxr-xr-xengines/sword25/util/lua/src/lcode.c839
-rwxr-xr-xengines/sword25/util/lua/src/lcode.h76
-rwxr-xr-xengines/sword25/util/lua/src/ldblib.c397
-rwxr-xr-xengines/sword25/util/lua/src/ldebug.c622
-rwxr-xr-xengines/sword25/util/lua/src/ldebug.h33
-rwxr-xr-xengines/sword25/util/lua/src/ldo.c518
-rwxr-xr-xengines/sword25/util/lua/src/ldo.h57
-rwxr-xr-xengines/sword25/util/lua/src/ldump.c164
-rwxr-xr-xengines/sword25/util/lua/src/lfunc.c174
-rwxr-xr-xengines/sword25/util/lua/src/lfunc.h34
-rwxr-xr-xengines/sword25/util/lua/src/lgc.c711
-rwxr-xr-xengines/sword25/util/lua/src/lgc.h110
-rwxr-xr-xengines/sword25/util/lua/src/linit.c38
-rwxr-xr-xengines/sword25/util/lua/src/liolib.c553
-rwxr-xr-xengines/sword25/util/lua/src/llex.c461
-rwxr-xr-xengines/sword25/util/lua/src/llex.h81
-rwxr-xr-xengines/sword25/util/lua/src/llimits.h128
-rwxr-xr-xengines/sword25/util/lua/src/lmathlib.c263
-rwxr-xr-xengines/sword25/util/lua/src/lmem.c86
-rwxr-xr-xengines/sword25/util/lua/src/lmem.h49
-rwxr-xr-xengines/sword25/util/lua/src/loadlib.c664
-rwxr-xr-xengines/sword25/util/lua/src/lobject.c214
-rwxr-xr-xengines/sword25/util/lua/src/lobject.h381
-rwxr-xr-xengines/sword25/util/lua/src/lopcodes.c102
-rwxr-xr-xengines/sword25/util/lua/src/lopcodes.h268
-rwxr-xr-xengines/sword25/util/lua/src/loslib.c243
-rwxr-xr-xengines/sword25/util/lua/src/lparser.c1339
-rwxr-xr-xengines/sword25/util/lua/src/lparser.h82
-rwxr-xr-xengines/sword25/util/lua/src/lstate.c214
-rwxr-xr-xengines/sword25/util/lua/src/lstate.h169
-rwxr-xr-xengines/sword25/util/lua/src/lstring.c111
-rwxr-xr-xengines/sword25/util/lua/src/lstring.h31
-rwxr-xr-xengines/sword25/util/lua/src/lstrlib.c868
-rwxr-xr-xengines/sword25/util/lua/src/ltable.c588
-rwxr-xr-xengines/sword25/util/lua/src/ltable.h40
-rwxr-xr-xengines/sword25/util/lua/src/ltablib.c279
-rwxr-xr-xengines/sword25/util/lua/src/ltm.c75
-rwxr-xr-xengines/sword25/util/lua/src/ltm.h54
-rwxr-xr-xengines/sword25/util/lua/src/lua.c392
-rwxr-xr-xengines/sword25/util/lua/src/lua.h388
-rwxr-xr-xengines/sword25/util/lua/src/luac.c200
-rwxr-xr-xengines/sword25/util/lua/src/luaconf.h763
-rwxr-xr-xengines/sword25/util/lua/src/lualib.h53
-rwxr-xr-xengines/sword25/util/lua/src/lundump.c225
-rwxr-xr-xengines/sword25/util/lua/src/lundump.h36
-rwxr-xr-xengines/sword25/util/lua/src/lvm.c763
-rwxr-xr-xengines/sword25/util/lua/src/lvm.h36
-rwxr-xr-xengines/sword25/util/lua/src/lzio.c82
-rwxr-xr-xengines/sword25/util/lua/src/lzio.h67
-rwxr-xr-xengines/sword25/util/lua/src/print.c227
-rwxr-xr-xengines/sword25/util/lua/test/README26
-rwxr-xr-xengines/sword25/util/lua/test/bisect.lua27
-rwxr-xr-xengines/sword25/util/lua/test/cf.lua16
-rwxr-xr-xengines/sword25/util/lua/test/echo.lua5
-rwxr-xr-xengines/sword25/util/lua/test/env.lua7
-rwxr-xr-xengines/sword25/util/lua/test/factorial.lua32
-rwxr-xr-xengines/sword25/util/lua/test/fib.lua40
-rwxr-xr-xengines/sword25/util/lua/test/fibfor.lua13
-rwxr-xr-xengines/sword25/util/lua/test/globals.lua13
-rwxr-xr-xengines/sword25/util/lua/test/hello.lua3
-rwxr-xr-xengines/sword25/util/lua/test/life.lua111
-rwxr-xr-xengines/sword25/util/lua/test/luac0
-rwxr-xr-xengines/sword25/util/lua/test/luac.lua7
-rwxr-xr-xengines/sword25/util/lua/test/printf.lua7
-rwxr-xr-xengines/sword25/util/lua/test/readonly.lua12
-rwxr-xr-xengines/sword25/util/lua/test/sieve.lua29
-rwxr-xr-xengines/sword25/util/lua/test/sort.lua66
-rwxr-xr-xengines/sword25/util/lua/test/table.lua12
-rwxr-xr-xengines/sword25/util/lua/test/trace-calls.lua32
-rwxr-xr-xengines/sword25/util/lua/test/trace-globals.lua38
-rwxr-xr-xengines/sword25/util/lua/test/xd.lua14
-rwxr-xr-xengines/sword25/util/pluto/CHANGELOG38
-rwxr-xr-xengines/sword25/util/pluto/FILEFORMAT168
-rwxr-xr-xengines/sword25/util/pluto/Makefile29
-rwxr-xr-xengines/sword25/util/pluto/README133
-rwxr-xr-xengines/sword25/util/pluto/THANKS10
-rwxr-xr-xengines/sword25/util/pluto/pdep.c112
-rwxr-xr-xengines/sword25/util/pluto/pdep/README5
-rwxr-xr-xengines/sword25/util/pluto/pdep/lauxlib.h174
-rwxr-xr-xengines/sword25/util/pluto/pdep/ldo.h57
-rwxr-xr-xengines/sword25/util/pluto/pdep/lfunc.h34
-rwxr-xr-xengines/sword25/util/pluto/pdep/lgc.h110
-rwxr-xr-xengines/sword25/util/pluto/pdep/llimits.h128
-rwxr-xr-xengines/sword25/util/pluto/pdep/lobject.h381
-rwxr-xr-xengines/sword25/util/pluto/pdep/lopcodes.h268
-rwxr-xr-xengines/sword25/util/pluto/pdep/lstate.h169
-rwxr-xr-xengines/sword25/util/pluto/pdep/lstring.h31
-rwxr-xr-xengines/sword25/util/pluto/pdep/ltm.h54
-rwxr-xr-xengines/sword25/util/pluto/pdep/lua.h388
-rwxr-xr-xengines/sword25/util/pluto/pdep/lzio.h65
-rwxr-xr-xengines/sword25/util/pluto/pdep/pdep.h41
-rwxr-xr-xengines/sword25/util/pluto/pluto.c1658
-rwxr-xr-xengines/sword25/util/pluto/pluto.h25
-rwxr-xr-xengines/sword25/util/pluto/plzio.c76
-rwxr-xr-xengines/sword25/util/pluto/pptest.c95
-rwxr-xr-xengines/sword25/util/pluto/pptest.lua168
-rwxr-xr-xengines/sword25/util/pluto/puptest.c81
-rwxr-xr-xengines/sword25/util/pluto/puptest.lua93
321 files changed, 67547 insertions, 0 deletions
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
new file mode 100755
index 0000000000..f7df544a48
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "movieplayer.h"
+
+#define BS_LOG_PREFIX "MOVIEPLAYER"
+
+// -----------------------------------------------------------------------------
+
+BS_MoviePlayer::BS_MoviePlayer(BS_Kernel * pKernel) : BS_Service(pKernel)
+{
+ if (!_RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
new file mode 100755
index 0000000000..ed9e94edad
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.h
@@ -0,0 +1,138 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_MOVIEPLAYER_H
+#define BS_MOVIEPLAYER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/service.h"
+
+#include "kernel/memlog_off.h"
+#include <string>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_MoviePlayer : public BS_Service
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_MoviePlayer(BS_Kernel * pKernel);
+ virtual ~BS_MoviePlayer() {};
+
+ // -----------------------------------------------------------------------------
+ // Abstraktes Interface, muss von jedem MoviePlayer implementiert werden
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Lädt eine Filmdatei
+
+ Diese Methode lädt eine Filmdatei und bereitet sie zur Wiedergabe vor.
+ Es kann immer nur eine Filmdatei zur Zeit geladen sein. Falls bereits eine Filmdatei geladen
+ ist, wird diese entladen und nötigenfalls die Wiedergabe gestoppt.
+
+ @param Filename der Dateiname der zu ladenden Filmdatei
+ @param Z gibt die Z Position des Films auf dem Graphik-Hauptlayer an
+ @return Gibt false zurück, wenn beim Laden ein Fehler aufgetreten ist, ansonsten true.
+ */
+ virtual bool LoadMovie(const std::string & Filename, unsigned int Z) = 0;
+
+ /**
+ @brief Entlädt die gerade geladene Filmdatei
+
+ @return Gibt false zurück, wenn beim Entladen ein Fehler aufgetreten ist, ansonsten true.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual bool UnloadMovie() = 0;
+
+ /**
+ @brief Spielt den Film ab.
+
+ Der Film wird unter Beibehaltung der Seitenverhältnisse auf Bildschirmgröße skaliert.<br>
+ Falls der Film mit einem Aufruf von Pause() pausiert wurde, fährt der Film an dieser Stelle fort.
+
+ @return Gibt false zurück, wenn ein Fehler aufgetreten ist, ansonsten true.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual bool Play() = 0;
+
+ /**
+ @brief Pausiert die Filmwiedergabe.
+
+ Bei einem späteren Aufruf von Play() fährt die Wiedergabe an der Stelle fort an der der Film Pausiert wurde.
+
+ @return Gibt false zurück, wenn ein Fehler aufgetreten ist, ansonsten true.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual bool Pause() = 0;
+
+ /**
+ @brief Diese Funktion muss ein mal pro Frame aufgerufen werden.
+ */
+ virtual void Update() = 0;
+
+ /**
+ @brief Gibt zurück, ob ein Film zur Wiedergabe geladen wurde.
+ */
+ virtual bool IsMovieLoaded() = 0;
+
+ /**
+ @brief Gibt zurück, ob die Filmwiedergabe pausiert wurde.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual bool IsPaused() = 0;
+
+ /**
+ @brief Gibt den Faktor zurück um den der geladene Film skaliert wird.
+
+ Beim Laden wird der Skalierungsfaktor automatisch so gewählt, dass der Film die maximal mögliche Bildschirmfläche einnimmt, ohne dass der
+ Film verzerrt wird.
+
+ @return Gibt den Skalierungsfaktor des Filmes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual float GetScaleFactor() = 0;
+
+ /**
+ @brief Legt den Faktor fest um den der geladene Film skaliert werden soll.
+ @param ScaleFactor der gewünschte Skalierungsfaktor.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual void SetScaleFactor(float ScaleFactor) = 0;
+
+ /**
+ @brief Gibt die aktuelle Abspielposition in Sekunden zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn IsMovieLoaded() true zurückgibt.
+ */
+ virtual double GetTime() = 0;
+
+private:
+ bool _RegisterScriptBindings();
+};
+
+#endif
diff --git a/engines/sword25/fmv/movieplayer_script.cpp b/engines/sword25/fmv/movieplayer_script.cpp
new file mode 100755
index 0000000000..c5160122d0
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer_script.cpp
@@ -0,0 +1,187 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+
+#include "movieplayer.h"
+
+namespace
+{
+ // -------------------------------------------------------------------------
+
+ int LoadMovie(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->LoadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<unsigned int>(luaL_checknumber(L, 2)) : 10));
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int UnloadMovie(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->UnloadMovie());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int Play(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->Play());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int Pause(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->Pause());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int Update(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ FMVPtr->Update();
+
+ return 0;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int IsMovieLoaded(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->IsMovieLoaded());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int IsPaused(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->IsPaused());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int GetScaleFactor(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushnumber(L, FMVPtr->GetScaleFactor());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int SetScaleFactor(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ FMVPtr->SetScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
+
+ return 0;
+ }
+
+ // -------------------------------------------------------------------------
+
+ int GetTime(lua_State * L)
+ {
+ BS_MoviePlayer * FMVPtr = BS_Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushnumber(L, FMVPtr->GetTime());
+
+ return 1;
+ }
+
+ // -------------------------------------------------------------------------
+
+ const char * LIBRARY_NAME = "Movieplayer";
+
+ const luaL_reg LIBRARY_FUNCTIONS[] =
+ {
+ "LoadMovie", LoadMovie,
+ "UnloadMovie", UnloadMovie,
+ "Play", Play,
+ "Pause", Pause,
+ "Update", Update,
+ "IsMovieLoaded", IsMovieLoaded,
+ "IsPaused", IsPaused,
+ "GetScaleFactor", GetScaleFactor,
+ "SetScaleFactor", SetScaleFactor,
+ "GetTime", GetTime,
+ 0, 0,
+ };
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_MoviePlayer::_RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, LIBRARY_NAME, LIBRARY_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/fmv/oggtheora/audiobuffer.cpp b/engines/sword25/fmv/oggtheora/audiobuffer.cpp
new file mode 100755
index 0000000000..9161c18fa8
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/audiobuffer.cpp
@@ -0,0 +1,93 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "audiobuffer.h"
+#include <windows.h>
+#include <queue>
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+
+struct BS_AudioBuffer::Impl
+{
+ CRITICAL_SECTION CS;
+ queue<signed short> Buffer;
+};
+
+// -----------------------------------------------------------------------------
+
+BS_AudioBuffer::BS_AudioBuffer() : t(new Impl())
+{
+ InitializeCriticalSection(&t->CS);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AudioBuffer::~BS_AudioBuffer()
+{
+ DeleteCriticalSection(&t->CS);
+
+ delete t;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_AudioBuffer::Push(signed short * SamplePtr, unsigned int SampleCount)
+{
+ EnterCriticalSection(&t->CS);
+
+ signed short * SampleEndPtr = SamplePtr + SampleCount;
+ while (SamplePtr != SampleEndPtr) t->Buffer.push(*SamplePtr++);
+
+ LeaveCriticalSection(&t->CS);
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_AudioBuffer::Pop(signed short * SamplePtr, unsigned int SampleCount)
+{
+ EnterCriticalSection(&t->CS);
+
+ unsigned int i = 0;
+ for (; i < SampleCount && !t->Buffer.empty(); ++i)
+ {
+ SamplePtr[i] = t->Buffer.front();
+ t->Buffer.pop();
+ }
+
+ LeaveCriticalSection(&t->CS);
+
+ return i;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_AudioBuffer::Size() const
+{
+ EnterCriticalSection(&t->CS);
+ unsigned int result = t->Buffer.size();
+ LeaveCriticalSection(&t->CS);
+
+ return result;
+}
diff --git a/engines/sword25/fmv/oggtheora/audiobuffer.h b/engines/sword25/fmv/oggtheora/audiobuffer.h
new file mode 100755
index 0000000000..c0c19811be
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/audiobuffer.h
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_AUDIOBUFFER_H
+#define BS_AUDIOBUFFER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_AudioBuffer
+{
+public:
+ BS_AudioBuffer();
+ virtual ~BS_AudioBuffer();
+
+ void Push(signed short * SamplePtr, unsigned int SampleCount);
+ unsigned int Pop(signed short * SamplePtr, unsigned int SampleCount);
+ unsigned int Size() const;
+
+private:
+ // PIMPL Pattern
+ struct Impl;
+ Impl * t;
+
+ // Kopie verbieten
+ BS_AudioBuffer(const BS_AudioBuffer &);
+ const BS_AudioBuffer & operator=(const BS_AudioBuffer &);
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/moviefile.cpp b/engines/sword25/fmv/oggtheora/moviefile.cpp
new file mode 100755
index 0000000000..92cbdad844
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/moviefile.cpp
@@ -0,0 +1,90 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "MOVIEFILE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "package/packagemanager.h"
+#include "oggstate.h"
+#include "moviefile.h"
+
+// -----------------------------------------------------------------------------
+
+BS_MovieFile::BS_MovieFile(const std::string & Filename, unsigned int ReadBlockSize, bool & Success) :
+ m_Data(0), m_Size(0), m_ReadPos(0), m_ReadBlockSize(ReadBlockSize)
+{
+ m_Data = reinterpret_cast<char *>(BS_Kernel::GetInstance()->GetPackage()->GetFile(Filename, &m_Size));
+ if (!m_Data)
+ {
+ BS_LOG_ERRORLN("Could not load movie file \"%s\".", Filename.c_str());
+ Success = false;
+ }
+ else
+ Success = true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_MovieFile::~BS_MovieFile()
+{
+ if (m_Data) delete [] m_Data;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_MovieFile::BufferData(BS_OggState & OggState)
+{
+ if (!m_Data || !m_Size || m_ReadPos >= m_Size)
+ {
+ BS_LOG_ERRORLN("Tried to read past the movie buffer's end.");
+ return 0;
+ }
+
+ // just grab some more compressed bitstream and sync it for page extraction
+ char * Buffer = OggState.SyncBuffer(m_ReadBlockSize);
+ if (!Buffer)
+ {
+ BS_LOG_ERRORLN("ogg_sync_buffer() failed.");
+ return 0;
+ }
+
+ // Feststellen wie viele Bytes kopiert werden sollen, maximal READ_BLOCK_SIZE, weniger falls das Ende der Daten erreicht ist.
+ int Bytes = (m_Size - m_ReadPos) > m_ReadBlockSize ? m_ReadBlockSize : m_Size - m_ReadPos;
+ memcpy(Buffer, &m_Data[m_ReadPos], Bytes);
+ m_ReadPos += Bytes;
+
+ OggState.SyncWrote(Bytes);
+
+ return Bytes;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_MovieFile::IsEOF() const
+{
+ return m_ReadPos >= m_Size;
+}
diff --git a/engines/sword25/fmv/oggtheora/moviefile.h b/engines/sword25/fmv/oggtheora/moviefile.h
new file mode 100755
index 0000000000..17935789b7
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/moviefile.h
@@ -0,0 +1,57 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_MOVIEFILE_H
+#define BS_MOVIEFILE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+#include "kernel/memlog_off.h"
+#include <string>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+
+class BS_OggState;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_MovieFile
+{
+public:
+ BS_MovieFile(const std::string & Filename, unsigned int ReadBlockSize, bool & Success);
+ virtual ~BS_MovieFile();
+
+ int BufferData(BS_OggState & OggState);
+ bool IsEOF() const;
+
+private:
+ char * m_Data;
+ unsigned int m_Size;
+ unsigned int m_ReadPos;
+ unsigned int m_ReadBlockSize;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/oggstate.cpp b/engines/sword25/fmv/oggtheora/oggstate.cpp
new file mode 100755
index 0000000000..d8300693c9
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggstate.cpp
@@ -0,0 +1,59 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "oggstate.h"
+
+// -----------------------------------------------------------------------------
+
+BS_OggState::BS_OggState()
+{
+ ogg_sync_init(&m_SyncState);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_OggState::~BS_OggState()
+{
+ ogg_sync_clear(&m_SyncState);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_OggState::SyncPageout(ogg_page * OggPage)
+{
+ return ogg_sync_pageout(&m_SyncState, OggPage);
+}
+
+// -----------------------------------------------------------------------------
+
+char * BS_OggState::SyncBuffer(long Size)
+{
+ return ogg_sync_buffer(&m_SyncState, Size);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_OggState::SyncWrote(long Bytes)
+{
+ return ogg_sync_wrote(&m_SyncState, Bytes);
+}
diff --git a/engines/sword25/fmv/oggtheora/oggstate.h b/engines/sword25/fmv/oggtheora/oggstate.h
new file mode 100755
index 0000000000..18b44e375f
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggstate.h
@@ -0,0 +1,63 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OGGSTATE_H
+#define BS_OGGSTATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "ogg/ogg.h"
+// XXX
+#include <iostream>
+// XXX
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_OggState
+{
+public:
+ BS_OggState();
+ virtual ~BS_OggState();
+
+ int SyncPageout(ogg_page * OggPage);
+ char * SyncBuffer(long Size);
+ int SyncWrote(long Bytes);
+
+ // XXX
+ void DumpInternals()
+ {
+ std::cout << "bodybytes: " << m_SyncState.bodybytes << std::endl;
+ std::cout << "fill: " << m_SyncState.fill << std::endl;
+ std::cout << "headerbytes: " << m_SyncState.headerbytes << std::endl;
+ std::cout << "returned: " << m_SyncState.returned << std::endl;
+ std::cout << "storage: " << m_SyncState.storage << std::endl;
+ std::cout << "unsynched: " << m_SyncState.unsynced << std::endl;
+ std::cout << std::endl;
+ }
+ // XXX
+private:
+ ogg_sync_state m_SyncState;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/oggstreamstate.cpp b/engines/sword25/fmv/oggtheora/oggstreamstate.cpp
new file mode 100755
index 0000000000..da4dc317b5
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggstreamstate.cpp
@@ -0,0 +1,118 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "oggstreamstate.h"
+
+// -----------------------------------------------------------------------------
+
+BS_OggStreamState::BS_OggStreamState(int SerialNo)
+{
+ ogg_stream_init(&m_State, SerialNo);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_OggStreamState::~BS_OggStreamState()
+{
+ ogg_stream_clear(&m_State);
+
+ // Alle gepufferten Pages löschen.
+ while (!m_PageBuffer.empty())
+ {
+ delete [] m_PageBuffer.front().header;
+ delete [] m_PageBuffer.front().body;
+ m_PageBuffer.pop();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_OggStreamState::PageIn(ogg_page * PagePtr)
+{
+ return ogg_stream_pagein(&m_State, PagePtr);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_OggStreamState::PacketOut(ogg_packet * PacketPtr)
+{
+ return ogg_stream_packetout(&m_State, PacketPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OggStreamState::BufferPage(ogg_page * PagePtr)
+{
+ if (PageBelongsToStream(PagePtr))
+ {
+ // Pages können nicht direkt gespeichert werden, da die Pointer im Laufe der Zeit ungültig werden.
+ // Daher wird an dieser Stelle eine tiefe Kopie der Page im erzeugt und im Pagebuffer angelegt.
+ ogg_page PageCopy;
+ PageCopy.header_len = PagePtr->header_len;
+ PageCopy.header = new unsigned char[PageCopy.header_len];
+ memcpy(PageCopy.header, PagePtr->header, PageCopy.header_len);
+ PageCopy.body_len = PagePtr->body_len;
+ PageCopy.body = new unsigned char[PageCopy.body_len];
+ memcpy(PageCopy.body, PagePtr->body, PageCopy.body_len);
+
+ m_PageBuffer.push(PageCopy);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_OggStreamState::PageInBufferedPage()
+{
+ if (GetPageBufferSize() > 0)
+ {
+ // Page in den Stream einfügen, löschen und aus dem Puffer entfernen.
+ int Result = PageIn(&m_PageBuffer.front());
+ delete [] m_PageBuffer.front().header;
+ delete [] m_PageBuffer.front().body;
+ m_PageBuffer.pop();
+ return Result;
+ }
+
+ return -1;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_OggStreamState::GetPageBufferSize() const
+{
+ return m_PageBuffer.size();
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_OggStreamState::GetUnprocessedBytes() const
+{
+ return m_State.body_fill - m_State.body_returned;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggStreamState::PageBelongsToStream(ogg_page * PagePtr) const
+{
+ return m_State.serialno == ogg_page_serialno(PagePtr);
+}
diff --git a/engines/sword25/fmv/oggtheora/oggstreamstate.h b/engines/sword25/fmv/oggtheora/oggstreamstate.h
new file mode 100755
index 0000000000..9460c7286f
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggstreamstate.h
@@ -0,0 +1,74 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OGGSTREAMSTATE_H
+#define BS_OGGSTREAMSTATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "ogg/ogg.h"
+#include <queue>
+// XXX
+#include <iostream>
+// XXX
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_OggStreamState
+{
+public:
+ BS_OggStreamState(int SerialNo);
+ virtual ~BS_OggStreamState();
+
+ int PageIn(ogg_page * PagePtr);
+ int PacketOut(ogg_packet * PacketPtr);
+
+ void BufferPage(ogg_page * PagePtr);
+ int PageInBufferedPage();
+ unsigned int GetPageBufferSize() const;
+
+ unsigned int GetUnprocessedBytes() const;
+ bool PageBelongsToStream(ogg_page * PagePtr) const;
+
+ // XXX
+ void DumpInternals()
+ {
+ using namespace std;
+
+ cout << "body_storage: " << m_State.body_storage << endl;
+ cout << "body_fill: " << m_State.body_fill << endl;
+ cout << "body_returned: " << m_State.body_returned << endl;
+ cout << "lacing_storage: " << m_State.lacing_storage << endl;
+ cout << "lacing_fill: " << m_State.lacing_fill << endl;
+ cout << "lacing_returned: " << m_State.lacing_returned << endl;
+ cout << endl;
+ }
+ // XXX
+
+private:
+ ogg_stream_state m_State;
+ std::queue<ogg_page> m_PageBuffer;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/oggtheora.cpp b/engines/sword25/fmv/oggtheora/oggtheora.cpp
new file mode 100755
index 0000000000..c5436dcc8f
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggtheora.cpp
@@ -0,0 +1,709 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// The code in this file is based in part on code from the OggTheora Software
+// codec source released under the following terms:
+//
+// Copyright (C) 2002-2007 Xiph.org Foundation
+//
+// 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 Xiph.org Foundation 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 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 FOUNDATION
+// 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.
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "OGGTHEORA"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <algorithm>
+#include "kernel/memlog_on.h"
+#include <float.h>
+
+#include "package/packagemanager.h"
+#include "kernel/cpuinfo.h"
+#include "sfx/soundengine.h"
+#include "gfx/graphicengine.h"
+#include "gfx/panel.h"
+#include "oggtheora.h"
+#include "yuvtorgba.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ // --------------------------------------------------------------------------
+
+ const int MAX_FRAMES_PER_TICK = 10;
+ const int READ_BLOCK_SIZE = 1024 * 40;
+ const float MAX_AUDIO_BUFFER_LENGTH = 1.0f;
+
+ // --------------------------------------------------------------------------
+
+ template<typename T>
+ inline T Clamp(T x, T low, T high)
+ {
+ return ((x > high) ? high : (( x < low) ? low : x));
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_OggTheora::BS_OggTheora(BS_Kernel * pKernel) : BS_MoviePlayer(pKernel), m_SoundHandle(0)
+{
+ UnloadMovie();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_OggTheora::~BS_OggTheora()
+{
+ UnloadMovie();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_OggTheora_CreateObject(BS_Kernel* pKernel) { return new BS_OggTheora(pKernel); }
+
+// -----------------------------------------------------------------------------
+// BS_MoviePlayer Interface
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// LoadMovie() mit Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ inline bool VerifyRequiredServiceAvailability()
+ {
+ char * RequiredServices[] = { "gfx", "sfx", "package" };
+ for (size_t i = 0; i < sizeof(RequiredServices) / sizeof(RequiredServices[0]); ++i)
+ {
+ if (!BS_Kernel::GetInstance()->GetService(RequiredServices[i]))
+ {
+ BS_LOG_ERRORLN("Required service \"%s\" is not active.", RequiredServices[i]);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // -------------------------------------------------------------------------
+
+ bool ParseStreamHeaders(auto_ptr<BS_MovieFile> & File,
+ auto_ptr<BS_OggState> & OggState,
+ auto_ptr<BS_OggStreamState> & TheoraStreamState,
+ auto_ptr<BS_OggStreamState> & VorbisStreamState,
+ auto_ptr<BS_TheoraState> & TheoraState,
+ auto_ptr<BS_VorbisState> & VorbisState,
+ bool & TheoraPresent,
+ bool & VorbisPresent,
+ const std::string & Filename)
+ {
+ TheoraPresent = false;
+ VorbisPresent = false;
+
+ // Ogg file open; parse the headers
+ // Only interested in Vorbis/Theora streams
+ bool FinishedHeaderParsing = false;
+ while (!FinishedHeaderParsing)
+ {
+ if (File->BufferData(*OggState.get()) == 0) return false;
+
+ ogg_page Page;
+ while(OggState->SyncPageout(&Page) > 0)
+ {
+ // is this a mandated initial header? If not, stop parsing
+ if(!ogg_page_bos(&Page))
+ {
+ // don't leak the page; get it into the appropriate stream
+ if (TheoraPresent) TheoraStreamState->PageIn(&Page);
+ if (VorbisPresent) VorbisStreamState->PageIn(&Page);
+
+ FinishedHeaderParsing = true;
+ break;
+ }
+
+ auto_ptr<BS_OggStreamState> streamState(new BS_OggStreamState(ogg_page_serialno(&Page)));
+
+ streamState->PageIn(&Page);
+
+ ogg_packet Packet;
+ streamState->PacketOut(&Packet);
+
+ // identify the codec: try theora
+ if(!TheoraPresent && TheoraState->DecodeHeader(&Packet) >= 0)
+ {
+ // it is theora
+ TheoraStreamState = streamState;
+ TheoraPresent = true;
+ }
+ else if(!VorbisPresent && VorbisState->SynthesisHeaderIn(&Packet) >=0)
+ {
+ // it is vorbis
+ VorbisStreamState = streamState;
+ VorbisPresent = true;
+ }
+ }
+ // fall through to non-bos page parsing
+ }
+
+ // we're expecting more header packets.
+ unsigned int TheoraPacketsRead = TheoraPresent ? 1 : 0;
+ unsigned int VorbisPacketsRead = VorbisPresent ? 1 : 0;
+ while((TheoraPresent && TheoraPacketsRead < 3) || (VorbisPresent && VorbisPacketsRead < 3))
+ {
+ int ret;
+ ogg_packet Packet;
+
+ // look for further theora headers
+ while(TheoraPresent && (TheoraPacketsRead < 3) && (ret = TheoraStreamState->PacketOut(&Packet)))
+ {
+ if(ret < 0 || TheoraState->DecodeHeader(&Packet))
+ {
+ BS_LOG_ERRORLN("Error parsing Theora stream headers. Stream is possibly corrupt. (%s)", Filename.c_str());
+ return false;
+ }
+
+ ++TheoraPacketsRead;
+ if (TheoraPacketsRead == 3) break;
+ }
+
+ // look for more vorbis header packets
+ while(VorbisPresent && (VorbisPacketsRead < 3) && (ret = VorbisStreamState->PacketOut(&Packet)))
+ {
+ if(ret < 0 || VorbisState->SynthesisHeaderIn(&Packet))
+ {
+ BS_LOG_ERRORLN("Error parsing Vorbis stream headers. Stream is possibly corrupt. (%s)", Filename.c_str());
+ return false;
+ }
+
+ ++VorbisPacketsRead;
+ if (VorbisPacketsRead == 3) break;
+ }
+
+ // The header pages/packets will arrive before anything else we care about, or the stream is not obeying spec
+ ogg_page Page;
+ if(OggState->SyncPageout(&Page) > 0)
+ {
+ // demux into the appropriate stream
+ if(TheoraPresent) TheoraStreamState->PageIn(&Page);
+ if(VorbisPresent) VorbisStreamState->PageIn(&Page);
+ }
+ else
+ {
+ if(File->BufferData(*OggState.get()) == 0)
+ {
+ BS_LOG_ERRORLN("End of file while searching for codec headers. (%s)", Filename.c_str());
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::LoadMovie(const std::string & Filename, unsigned int Z)
+{
+ if (!VerifyRequiredServiceAvailability()) return false;
+
+ UnloadMovie();
+
+ // Alle Objekte die in dieser Funktion erzeugt werden, werden in zunächst lokalen Auto-Pointern gehalten.
+ // Bei erfolgreicher Beendigung dieser Funktion werden sie den objektlokalen Auto-Pointern zugewiesen.
+ // So wird sichergestellt, dass sie korrekt deinitialisiert werden, wenn in dieser Funktion ein Fehler auftritt, denn beim Zerstören der
+ // lokalen Auto-Pointer werden die entsprechenden Destruktoren aufgerufen.
+
+ // Film laden
+ // Für Filmdateien wird das Cachingsystem nicht benutzt, da Filme in der Regel nur ein Mal abgespielt werden.
+ bool Success;
+ auto_ptr<BS_MovieFile> File(new BS_MovieFile(Filename, READ_BLOCK_SIZE, Success));
+ if (!Success) return false;
+
+ // States erzeugen für Ogg, Vorbis und Theora, sowie die Ogg und Theora Streams
+ auto_ptr<BS_OggState> OggState(new BS_OggState());
+ auto_ptr<BS_VorbisState> VorbisState(new BS_VorbisState());
+ auto_ptr<BS_TheoraState> TheoraState(new BS_TheoraState());
+
+ auto_ptr<BS_OggStreamState> TheoraStreamState;
+ auto_ptr<BS_OggStreamState> VorbisStreamState;
+
+ if (!ParseStreamHeaders(File, OggState, TheoraStreamState, VorbisStreamState, TheoraState, VorbisState, m_TheoraPresent, m_VorbisPresent, Filename)) return false;
+
+ // Theora-Decoder Initialisieren
+ if(m_TheoraPresent)
+ {
+ TheoraState->DecodeInit();
+
+ const theora_info & TheoraInfo = TheoraState->GetInfo();
+
+ if (TheoraInfo.pixelformat != OC_PF_444 &&
+ TheoraInfo.pixelformat != OC_PF_422 &&
+ TheoraInfo.pixelformat != OC_PF_420)
+ {
+ BS_LOG_ERRORLN("Unknown chroma sampling. (%s)", Filename.c_str());
+ return false;
+ }
+
+ // Ausgabebitmap erstellen
+ BS_GraphicEngine * pGfx = BS_Kernel::GetInstance()->GetGfx();
+ m_OutputBitmap = pGfx->GetMainPanel()->AddDynamicBitmap(TheoraInfo.frame_width, TheoraInfo.frame_height);
+ if (!m_OutputBitmap.IsValid())
+ {
+ BS_LOG_ERRORLN("Output bitmap for movie playback could not be created.");
+ return false;
+ }
+
+ // Skalierung des Ausgabebitmaps berechnen, so dass es möglichst viel Bildschirmfläche einnimmt.
+ float ScreenToVideoWidth = (float) pGfx->GetDisplayWidth() / (float) m_OutputBitmap->GetWidth();
+ float ScreenToVideoHeight = (float) pGfx->GetDisplayHeight() / (float) m_OutputBitmap->GetHeight();
+ float ScaleFactor = std::min(ScreenToVideoWidth, ScreenToVideoHeight);
+ if (abs(ScaleFactor - 1.0f) < FLT_EPSILON) ScaleFactor = 1.0f;
+ m_OutputBitmap->SetScaleFactor(ScaleFactor);
+
+ // Z-Wert setzen
+ m_OutputBitmap->SetZ(Z);
+
+ // Ausgabebitmap auf dem Bildschirm zentrieren
+ m_OutputBitmap->SetX((pGfx->GetDisplayWidth() - m_OutputBitmap->GetWidth()) / 2);
+ m_OutputBitmap->SetY((pGfx->GetDisplayHeight() - m_OutputBitmap->GetHeight()) / 2);
+
+ // Buffer für die Pixeldaten erstellen
+ m_Pixels.resize(TheoraInfo.width * TheoraInfo.height * 4);
+
+ m_VideoEnded = false;
+ }
+
+ // Vorbis-Decoder initialisieren
+ if(m_VorbisPresent)
+ {
+ VorbisState->SynthesisInit();
+ VorbisState->BlockInit();
+ m_AudioBuffer.reset(new BS_AudioBuffer());
+
+ m_AudioEnded = false;
+ }
+
+ // Keine Kopie, überträgt Besitz der erzeugten Objekte von der Funktion auf das Objekt.
+ m_File = File;
+ m_OggState = OggState;
+ m_TheoraState = TheoraState;
+ m_TheoraStreamState = TheoraStreamState;
+ m_VorbisState = VorbisState;
+ m_VorbisStreamState = VorbisStreamState;
+ m_MovieLoaded = true;
+ m_Timer = 0;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::UnloadMovie()
+{
+ m_MovieLoaded = false;
+ m_Paused = true;
+
+ m_VorbisStreamState.reset();
+ m_VorbisPresent = false;
+ m_VorbisState.reset();
+ if (m_SoundHandle)
+ {
+ BS_Kernel::GetInstance()->GetSfx()->StopSound(m_SoundHandle);
+ m_SoundHandle = 0;
+ }
+ m_AudioEnded = true;
+ m_AudioBuffer.reset();
+
+ m_TheoraStreamState.reset();
+ m_TheoraPresent = false;
+ m_TheoraState.reset();
+ m_VideoEnded = true;
+
+ m_OggState.reset();
+
+ m_File.reset();
+
+ m_StartTime = 0;
+ m_LastFrameTime = 0;
+ m_Timer = 0.0f;
+
+ vector<unsigned char>().swap(m_Pixels);
+ m_OutputBitmap.Erase();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::Play()
+{
+ if (m_MovieLoaded)
+ {
+ if (m_Paused)
+ {
+ if (m_SoundHandle)
+ {
+ BS_SoundEngine * SfxPtr = BS_Kernel::GetInstance()->GetSfx();
+ SfxPtr->ResumeSound(m_SoundHandle);
+ }
+ m_Paused = false;
+ }
+
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Cannot play movie, when no movie is loaded.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::Pause()
+{
+ if (m_MovieLoaded)
+ {
+ if (m_SoundHandle)
+ {
+ BS_SoundEngine * SfxPtr = BS_Kernel::GetInstance()->GetSfx();
+ SfxPtr->PauseSound(m_SoundHandle);
+ }
+
+ m_Paused = true;
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Cannot pause movie, when no movie is loaded.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OggTheora::ReadData()
+{
+ if (!m_File->IsEOF()) m_File->BufferData(*m_OggState.get());
+
+ ogg_page Page;
+ while(m_OggState->SyncPageout(&Page) > 0)
+ {
+ if(m_TheoraPresent) m_TheoraStreamState->BufferPage(&Page);
+ if(m_VorbisPresent) m_VorbisStreamState->BufferPage(&Page);
+ }
+}
+
+void BS_OggTheora::Update()
+{
+ if (m_AudioEnded && m_VideoEnded)
+ {
+ m_Paused = true;
+
+ // Falls der Sound noch läuft, muss er jetzt beendet werden.
+ if (m_SoundHandle)
+ {
+ BS_SoundEngine * SfxPtr = BS_Kernel::GetInstance()->GetSfx();
+ SfxPtr->StopSound(m_SoundHandle);
+ m_SoundHandle = 0;
+ }
+ }
+
+ if (m_Paused) return;
+
+ // Timer aktualisieren.
+ // Wird nur genutzt, wenn keine Audiodaten vorhanden sind.
+ m_Timer += BS_Kernel::GetInstance()->GetGfx()->GetSecondaryFrameDuration();
+
+ // Audiodaten dekodieren
+ if (m_VorbisPresent && !m_AudioEnded) DecodeVorbis();
+
+
+ // Videodaten dekodieren
+ if (m_TheoraPresent && !m_VideoEnded)
+ {
+ bool Result = DecodeTheora();
+
+ if (Result)
+ {
+ // YUV Framebuffer holen
+ yuv_buffer YUVBuffer;
+ m_TheoraState->DecodeYUVOut(&YUVBuffer);
+
+ // YUV Bilddaten nach RGBA konvertieren
+ BS_YUVtoRGBA::YUVtoRGBA(YUVBuffer, m_TheoraState->GetInfo(), m_Pixels);
+
+ // RGBA Bilddaten auf das Ausgabebild kopieren, dabei die Postion des Theoraframes innerhalb des dekodierten Frames beachten.
+ const theora_info & TheoraInfo = m_TheoraState->GetInfo();
+ m_OutputBitmap->SetContent(m_Pixels, (TheoraInfo.offset_x + TheoraInfo.width * TheoraInfo.offset_y) * 4, (TheoraInfo.width - TheoraInfo.frame_width) * 4);
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::DecodeTheora()
+{
+ double MovieTime = GetTime();
+
+ // Check if this frame time has not passed yet. If the frame is late we need
+ // to decode additonal ones and keep looping, since theora at this stage
+ // needs to decode all frames (due to keyframing)
+ // Getting the current time once at the beginning of the function rather than
+ // every time at the beginning of the loop produces the smoothest framerate
+
+ unsigned int FramesDecoded = 0;
+ bool FrameReady = false;
+ while (m_TheoraState->GranuleTime() < MovieTime)
+ {
+ // theora is one in, one out...
+ ogg_packet Packet;
+ if(m_TheoraStreamState->PacketOut(&Packet) > 0)
+ {
+ if (FramesDecoded < MAX_FRAMES_PER_TICK || Packet.granulepos >= MovieTime)
+ {
+ m_TheoraState->DecodePacketIn(&Packet);
+ FrameReady = true;
+ ++FramesDecoded;
+ }
+ }
+ else
+ {
+ if (m_TheoraStreamState->GetPageBufferSize() > 0)
+ m_TheoraStreamState->PageInBufferedPage();
+ else
+ {
+ if(m_File->IsEOF())
+ {
+ m_VideoEnded = true;
+ break;
+ }
+ else
+ ReadData();
+ }
+ }
+ }
+ m_LastFrameTime = MovieTime;
+
+ return FrameReady;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OggTheora::DynamicSoundCallBack(void * UserData, void * Data, unsigned int DataLength)
+{
+ BS_OggTheora & t = *reinterpret_cast<BS_OggTheora *>(UserData);
+
+ signed short * Buffer = reinterpret_cast<signed short *>(Data);
+
+ // Audiodaten in den Soundbuffer schreiben.
+ DataLength -= t.m_AudioBuffer->Pop(Buffer, DataLength / 2) * 2;
+
+ // Falls nicht genug Audiodaten vorhanden waren, wird der Rest mit Stille überschrieben.
+ if (DataLength)
+ {
+ char * ByteBuffer = reinterpret_cast<char *>(Buffer);
+ while (DataLength--)
+ {
+ *ByteBuffer++ = 0;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OggTheora::DecodeVorbis()
+{
+ // Vorbis-Stream Infos holen.
+ const vorbis_info & Info = m_VorbisState->GetInfo();
+
+ // Maximalgröße des Audiobuffers berechnen.
+ size_t MaxAudioBufferSamples = static_cast<size_t>(Info.channels * Info.rate * MAX_AUDIO_BUFFER_LENGTH);
+
+ // Zwischenspeicher für die Samples.
+ vector<signed short> Samples;
+
+ // Audiobuffer bis zur Maximalgröße füllen, wenn möglich.
+ while (m_AudioBuffer->Size() < MaxAudioBufferSamples)
+ {
+ // Vorhandene Audiodaten auslesen
+ float ** PCM;
+ int SampleCount = m_VorbisState->SynthesisPCMout(&PCM);
+
+ // Wenn Audiodaten gelesen werden konnten, werden diese in 16-Bit Sample umgewandelt und in den Audiobuffer geschrieben.
+ if(SampleCount > 0)
+ {
+ // Im Samplezwischenspeicher leeren und genügend Platz für die kommenden Samples reservieren.
+ Samples.reserve(SampleCount);
+ Samples.clear();
+
+ // Samples konvertieren und in die Samplezwischenspeicher schreiben.
+ for (int i = 0; i < SampleCount; ++i)
+ {
+ for(int j = 0; j < Info.channels; ++j)
+ {
+ int SampleValue = static_cast<int>(PCM[j][i] * 32767.0f);
+ Samples.push_back(Clamp(SampleValue, -32700, 32700));
+ }
+ }
+
+ // Daten aus dem Samplezwischenspeicher in den Audiobuffer schreiben.
+ m_AudioBuffer->Push(&Samples[0], Samples.size());
+
+ // Vorbis mitteilen, dass wir alle Samples gelesen haben.
+ m_VorbisState->SynthesisRead(SampleCount);
+ }
+ else
+ {
+ // Wir konnten, keine Audiodaten auslesen, versuchen ein neues Paket zu dekodieren.
+ ogg_packet Packet;
+ if(m_VorbisStreamState->PacketOut(&Packet) > 0)
+ {
+ if (m_VorbisState->Synthesis(&Packet) == 0) m_VorbisState->SynthesisBlockIn();
+ }
+ else
+ {
+ // Gepufferte Daten in den Stream einfügen.
+ if (m_VorbisStreamState->GetPageBufferSize() > 0)
+ m_VorbisStreamState->PageInBufferedPage();
+ else
+ {
+ // Nicht genug Daten vorhanden. Wenn die Datei leer ist und bereits alle Audiodaten gelesen wurden, ist der Audiostream am Ende.
+ // Ansonsten Daten nachladen.
+ if(m_File->IsEOF())
+ {
+ if (m_AudioBuffer->Size() == 0)
+ {
+ m_AudioEnded = true;
+ }
+
+ break;
+ }
+ else
+ ReadData();
+ }
+ }
+ }
+ }
+
+ // Soundkanal abspielen, wenn er noch nicht spielt und Audiodaten vorhanden sind.
+ if (m_SoundHandle == 0 && m_AudioBuffer->Size())
+ {
+ BS_SoundEngine * SfxPtr = BS_Kernel::GetInstance()->GetSfx();
+ m_SoundHandle = SfxPtr->PlayDynamicSoundEx(&DynamicSoundCallBack, this, BS_SoundEngine::SFX, Info.rate, 16, Info.channels);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+double BS_OggTheora::GetTime()
+{
+ if(m_VorbisPresent)
+ {
+ if (m_SoundHandle)
+ {
+ float time = BS_Kernel::GetInstance()->GetSfx()->GetSoundTime(m_SoundHandle);
+ return time;
+ }
+ else
+ return 0.0f;
+ }
+ else
+ return m_Timer;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::IsMovieLoaded()
+{
+ return m_MovieLoaded;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OggTheora::IsPaused()
+{
+ return m_Paused;
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_OggTheora::GetScaleFactor()
+{
+ if (m_MovieLoaded)
+ return m_OutputBitmap->GetScaleFactorX();
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OggTheora::SetScaleFactor(float ScaleFactor)
+{
+ if (m_MovieLoaded)
+ {
+ m_OutputBitmap->SetScaleFactor(ScaleFactor);
+
+ // Ausgabebitmap auf dem Bildschirm zentrieren
+ BS_GraphicEngine * GfxPtr = BS_Kernel::GetInstance()->GetGfx();
+ m_OutputBitmap->SetX((GfxPtr->GetDisplayWidth() - m_OutputBitmap->GetWidth()) / 2);
+ m_OutputBitmap->SetY((GfxPtr->GetDisplayHeight() - m_OutputBitmap->GetHeight()) / 2);
+ }
+}
diff --git a/engines/sword25/fmv/oggtheora/oggtheora.h b/engines/sword25/fmv/oggtheora/oggtheora.h
new file mode 100755
index 0000000000..0b43ba698d
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/oggtheora.h
@@ -0,0 +1,109 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OGGTHEORA_H
+#define BS_OGGTHEORA_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include <memory>
+#include <queue>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "kernel/bs_stdint.h"
+#include "gfx/bitmap.h"
+#include "gfx/renderobjectptr.h"
+#include "fmv/movieplayer.h"
+#include "vorbisstate.h"
+#include "theorastate.h"
+#include "oggstate.h"
+#include "oggstreamstate.h"
+#include "moviefile.h"
+#include "audiobuffer.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_OggTheora : public BS_MoviePlayer
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_OggTheora(BS_Kernel * pKernel);
+ virtual ~BS_OggTheora();
+
+ // -----------------------------------------------------------------------------
+ // BS_MoviePlayer Interface
+ // -----------------------------------------------------------------------------
+
+ virtual bool LoadMovie(const std::string & Filename, unsigned int Z);
+ virtual bool UnloadMovie();
+ virtual bool Play();
+ virtual bool Pause();
+ virtual void Update();
+ virtual bool IsMovieLoaded();
+ virtual bool IsPaused();
+ virtual float GetScaleFactor();
+ virtual void SetScaleFactor(float ScaleFactor);
+ virtual double GetTime();
+
+private:
+ bool DecodeTheora();
+ void DecodeVorbis();
+ void RefillOggBuffer();
+ void ReadData();
+ static void DynamicSoundCallBack(void * UserData, void * Data, unsigned int DataLength);
+
+ bool m_MovieLoaded;
+ bool m_Paused;
+
+ std::auto_ptr<BS_OggStreamState> m_VorbisStreamState;
+ bool m_VorbisPresent;
+ std::auto_ptr<BS_VorbisState> m_VorbisState;
+ unsigned int m_SoundHandle;
+ bool m_AudioEnded;
+ std::auto_ptr<BS_AudioBuffer> m_AudioBuffer;
+
+ std::auto_ptr<BS_OggStreamState> m_TheoraStreamState;
+ bool m_TheoraPresent;
+ std::auto_ptr<BS_TheoraState> m_TheoraState;
+ bool m_VideoEnded;
+
+ std::auto_ptr<BS_OggState> m_OggState;
+
+ std::auto_ptr<BS_MovieFile> m_File;
+
+ uint64_t m_StartTime;
+ double m_LastFrameTime;
+
+ float m_Timer;
+
+ std::vector<unsigned char> m_Pixels;
+ BS_RenderObjectPtr<BS_Bitmap> m_OutputBitmap;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/theorastate.cpp b/engines/sword25/fmv/oggtheora/theorastate.cpp
new file mode 100755
index 0000000000..853ef911ba
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/theorastate.cpp
@@ -0,0 +1,79 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "theorastate.h"
+
+// -----------------------------------------------------------------------------
+
+BS_TheoraState::BS_TheoraState() :
+ m_StateInitialized(false)
+{
+ theora_comment_init(&m_Comment);
+ theora_info_init(&m_Info);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_TheoraState::~BS_TheoraState()
+{
+ if (m_StateInitialized) theora_clear(&m_State);
+ theora_info_clear(&m_Info);
+ theora_comment_clear(&m_Comment);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_TheoraState::DecodeHeader(ogg_packet * OggPacketPtr)
+{
+ return theora_decode_header(&m_Info, &m_Comment, OggPacketPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_TheoraState::DecodeInit()
+{
+ int Result = theora_decode_init(&m_State, &m_Info);
+ m_StateInitialized = (Result == 0);
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+double BS_TheoraState::GranuleTime()
+{
+ return theora_granule_time(&m_State, m_State.granulepos);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_TheoraState::DecodePacketIn(ogg_packet * OggPacketPtr)
+{
+ return theora_decode_packetin(&m_State, OggPacketPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_TheoraState::DecodeYUVOut(yuv_buffer * YUV)
+{
+ return theora_decode_YUVout(&m_State, YUV);
+}
diff --git a/engines/sword25/fmv/oggtheora/theorastate.h b/engines/sword25/fmv/oggtheora/theorastate.h
new file mode 100755
index 0000000000..582215bad9
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/theorastate.h
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_THEORASTATE_H
+#define BS_THEORASTATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "theora/theora.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_TheoraState
+{
+public:
+ BS_TheoraState();
+ virtual ~BS_TheoraState();
+
+ int DecodeHeader(ogg_packet * OggPacketPtr);
+ int DecodeInit();
+ int DecodePacketIn(ogg_packet * OggPacketPtr);
+ int DecodeYUVOut(yuv_buffer * YUV);
+ double GranuleTime();
+
+ const theora_info & GetInfo() const { return m_Info; }
+
+private:
+ theora_info m_Info;
+ theora_comment m_Comment;
+ bool m_StateInitialized;
+ theora_state m_State;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/vorbisstate.cpp b/engines/sword25/fmv/oggtheora/vorbisstate.cpp
new file mode 100755
index 0000000000..683b6fd0d7
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/vorbisstate.cpp
@@ -0,0 +1,97 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "vorbisstate.h"
+
+// -----------------------------------------------------------------------------
+
+BS_VorbisState::BS_VorbisState() :
+ m_DSPStateInitialized(false),
+ m_BlockInitialized(false)
+{
+ vorbis_info_init(&m_Info);
+ vorbis_comment_init(&m_Comment);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_VorbisState::~BS_VorbisState()
+{
+ if (m_BlockInitialized) vorbis_block_clear(&m_Block);
+ if (m_DSPStateInitialized) vorbis_dsp_clear(&m_DSPState);
+ vorbis_comment_clear(&m_Comment);
+ vorbis_info_clear(&m_Info);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::SynthesisHeaderIn(ogg_packet * OggPacketPtr)
+{
+ return vorbis_synthesis_headerin(&m_Info, &m_Comment, OggPacketPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::SynthesisInit()
+{
+ int Result = vorbis_synthesis_init(&m_DSPState, &m_Info);
+ m_DSPStateInitialized = (Result == 0);
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::BlockInit()
+{
+ int Result = vorbis_block_init(&m_DSPState, &m_Block);
+ m_BlockInitialized = (Result == 0);
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::SynthesisPCMout(float *** PCM)
+{
+ return vorbis_synthesis_pcmout(&m_DSPState, PCM);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::SynthesisRead(int Samples)
+{
+ return vorbis_synthesis_read(&m_DSPState, Samples);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::Synthesis(ogg_packet * OggPacketPtr)
+{
+ return vorbis_synthesis(&m_Block, OggPacketPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_VorbisState::SynthesisBlockIn()
+{
+ return vorbis_synthesis_blockin(&m_DSPState, &m_Block);
+}
diff --git a/engines/sword25/fmv/oggtheora/vorbisstate.h b/engines/sword25/fmv/oggtheora/vorbisstate.h
new file mode 100755
index 0000000000..193e115637
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/vorbisstate.h
@@ -0,0 +1,59 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_VORBISSTATE_H
+#define BS_VORBISSTATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "vorbis/codec.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_VorbisState
+{
+public:
+ BS_VorbisState();
+ virtual ~BS_VorbisState();
+
+ int SynthesisHeaderIn(ogg_packet * OggPacketPtr);
+ int SynthesisInit();
+ int BlockInit();
+ int SynthesisPCMout(float *** PCM);
+ int SynthesisRead(int Samples);
+ int Synthesis(ogg_packet * OggPacketPtr);
+ int SynthesisBlockIn();
+
+ const vorbis_info & GetInfo() const { return m_Info; }
+
+private:
+ vorbis_info m_Info;
+ bool m_DSPStateInitialized;
+ vorbis_dsp_state m_DSPState;
+ bool m_BlockInitialized;
+ vorbis_block m_Block;
+ vorbis_comment m_Comment;
+};
+
+#endif
diff --git a/engines/sword25/fmv/oggtheora/yuvtorgba.cpp b/engines/sword25/fmv/oggtheora/yuvtorgba.cpp
new file mode 100755
index 0000000000..0e709b12e0
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/yuvtorgba.cpp
@@ -0,0 +1,416 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/cpuinfo.h"
+#include "yuvtorgba.h"
+
+#include <mmintrin.h>
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+
+ static const int PRECISION = 32768;
+ static const int COEFFS_Y[256] = {
+ -593888, -555746, -517604, -479462, -441320, -403178, -365036, -326894, -288752, -250610, -212468, -174326, -136184, -98042, -59900, -21758,
+ 16384, 54526, 92668, 130810, 168952, 207094, 245236, 283378, 321520, 359662, 397804, 435946, 474088, 512230, 550372, 588514,
+ 626656, 664798, 702940, 741082, 779224, 817366, 855508, 893650, 931792, 969934, 1008076, 1046218, 1084360, 1122502, 1160644, 1198786,
+ 1236928, 1275070, 1313212, 1351354, 1389496, 1427638, 1465780, 1503922, 1542064, 1580206, 1618348, 1656490, 1694632, 1732774, 1770916, 1809058,
+ 1847200, 1885342, 1923484, 1961626, 1999768, 2037910, 2076052, 2114194, 2152336, 2190478, 2228620, 2266762, 2304904, 2343046, 2381188, 2419330,
+ 2457472, 2495614, 2533756, 2571898, 2610040, 2648182, 2686324, 2724466, 2762608, 2800750, 2838892, 2877034, 2915176, 2953318, 2991460, 3029602,
+ 3067744, 3105886, 3144028, 3182170, 3220312, 3258454, 3296596, 3334738, 3372880, 3411022, 3449164, 3487306, 3525448, 3563590, 3601732, 3639874,
+ 3678016, 3716158, 3754300, 3792442, 3830584, 3868726, 3906868, 3945010, 3983152, 4021294, 4059436, 4097578, 4135720, 4173862, 4212004, 4250146,
+ 4288288, 4326430, 4364572, 4402714, 4440856, 4478998, 4517140, 4555282, 4593424, 4631566, 4669708, 4707850, 4745992, 4784134, 4822276, 4860418,
+ 4898560, 4936702, 4974844, 5012986, 5051128, 5089270, 5127412, 5165554, 5203696, 5241838, 5279980, 5318122, 5356264, 5394406, 5432548, 5470690,
+ 5508832, 5546974, 5585116, 5623258, 5661400, 5699542, 5737684, 5775826, 5813968, 5852110, 5890252, 5928394, 5966536, 6004678, 6042820, 6080962,
+ 6119104, 6157246, 6195388, 6233530, 6271672, 6309814, 6347956, 6386098, 6424240, 6462382, 6500524, 6538666, 6576808, 6614950, 6653092, 6691234,
+ 6729376, 6767518, 6805660, 6843802, 6881944, 6920086, 6958228, 6996370, 7034512, 7072654, 7110796, 7148938, 7187080, 7225222, 7263364, 7301506,
+ 7339648, 7377790, 7415932, 7454074, 7492216, 7530358, 7568500, 7606642, 7644784, 7682926, 7721068, 7759210, 7797352, 7835494, 7873636, 7911778,
+ 7949920, 7988062, 8026204, 8064346, 8102488, 8140630, 8178772, 8216914, 8255056, 8293198, 8331340, 8369482, 8407624, 8445766, 8483908, 8522050,
+ 8560192, 8598334, 8636476, 8674618, 8712760, 8750902, 8789044, 8827186, 8865328, 8903470, 8941612, 8979754, 9017896, 9056038, 9094180, 9132322,
+ };
+ static const int COEFFS_RV[256] = {
+ -6694144, -6641846, -6589548, -6537250, -6484952, -6432654, -6380356, -6328058, -6275760, -6223462, -6171164, -6118866, -6066568, -6014270, -5961972, -5909674,
+ -5857376, -5805078, -5752780, -5700482, -5648184, -5595886, -5543588, -5491290, -5438992, -5386694, -5334396, -5282098, -5229800, -5177502, -5125204, -5072906,
+ -5020608, -4968310, -4916012, -4863714, -4811416, -4759118, -4706820, -4654522, -4602224, -4549926, -4497628, -4445330, -4393032, -4340734, -4288436, -4236138,
+ -4183840, -4131542, -4079244, -4026946, -3974648, -3922350, -3870052, -3817754, -3765456, -3713158, -3660860, -3608562, -3556264, -3503966, -3451668, -3399370,
+ -3347072, -3294774, -3242476, -3190178, -3137880, -3085582, -3033284, -2980986, -2928688, -2876390, -2824092, -2771794, -2719496, -2667198, -2614900, -2562602,
+ -2510304, -2458006, -2405708, -2353410, -2301112, -2248814, -2196516, -2144218, -2091920, -2039622, -1987324, -1935026, -1882728, -1830430, -1778132, -1725834,
+ -1673536, -1621238, -1568940, -1516642, -1464344, -1412046, -1359748, -1307450, -1255152, -1202854, -1150556, -1098258, -1045960, -993662, -941364, -889066,
+ -836768, -784470, -732172, -679874, -627576, -575278, -522980, -470682, -418384, -366086, -313788, -261490, -209192, -156894, -104596, -52298,
+ 0, 52298, 104596, 156894, 209192, 261490, 313788, 366086, 418384, 470682, 522980, 575278, 627576, 679874, 732172, 784470,
+ 836768, 889066, 941364, 993662, 1045960, 1098258, 1150556, 1202854, 1255152, 1307450, 1359748, 1412046, 1464344, 1516642, 1568940, 1621238,
+ 1673536, 1725834, 1778132, 1830430, 1882728, 1935026, 1987324, 2039622, 2091920, 2144218, 2196516, 2248814, 2301112, 2353410, 2405708, 2458006,
+ 2510304, 2562602, 2614900, 2667198, 2719496, 2771794, 2824092, 2876390, 2928688, 2980986, 3033284, 3085582, 3137880, 3190178, 3242476, 3294774,
+ 3347072, 3399370, 3451668, 3503966, 3556264, 3608562, 3660860, 3713158, 3765456, 3817754, 3870052, 3922350, 3974648, 4026946, 4079244, 4131542,
+ 4183840, 4236138, 4288436, 4340734, 4393032, 4445330, 4497628, 4549926, 4602224, 4654522, 4706820, 4759118, 4811416, 4863714, 4916012, 4968310,
+ 5020608, 5072906, 5125204, 5177502, 5229800, 5282098, 5334396, 5386694, 5438992, 5491290, 5543588, 5595886, 5648184, 5700482, 5752780, 5805078,
+ 5857376, 5909674, 5961972, 6014270, 6066568, 6118866, 6171164, 6223462, 6275760, 6328058, 6380356, 6432654, 6484952, 6537250, 6589548, 6641846,
+ };
+ static const int COEFFS_GU[256] = {
+ 1639936, 1627124, 1614312, 1601500, 1588688, 1575876, 1563064, 1550252, 1537440, 1524628, 1511816, 1499004, 1486192, 1473380, 1460568, 1447756,
+ 1434944, 1422132, 1409320, 1396508, 1383696, 1370884, 1358072, 1345260, 1332448, 1319636, 1306824, 1294012, 1281200, 1268388, 1255576, 1242764,
+ 1229952, 1217140, 1204328, 1191516, 1178704, 1165892, 1153080, 1140268, 1127456, 1114644, 1101832, 1089020, 1076208, 1063396, 1050584, 1037772,
+ 1024960, 1012148, 999336, 986524, 973712, 960900, 948088, 935276, 922464, 909652, 896840, 884028, 871216, 858404, 845592, 832780,
+ 819968, 807156, 794344, 781532, 768720, 755908, 743096, 730284, 717472, 704660, 691848, 679036, 666224, 653412, 640600, 627788,
+ 614976, 602164, 589352, 576540, 563728, 550916, 538104, 525292, 512480, 499668, 486856, 474044, 461232, 448420, 435608, 422796,
+ 409984, 397172, 384360, 371548, 358736, 345924, 333112, 320300, 307488, 294676, 281864, 269052, 256240, 243428, 230616, 217804,
+ 204992, 192180, 179368, 166556, 153744, 140932, 128120, 115308, 102496, 89684, 76872, 64060, 51248, 38436, 25624, 12812,
+ 0, -12812, -25624, -38436, -51248, -64060, -76872, -89684, -102496, -115308, -128120, -140932, -153744, -166556, -179368, -192180,
+ -204992, -217804, -230616, -243428, -256240, -269052, -281864, -294676, -307488, -320300, -333112, -345924, -358736, -371548, -384360, -397172,
+ -409984, -422796, -435608, -448420, -461232, -474044, -486856, -499668, -512480, -525292, -538104, -550916, -563728, -576540, -589352, -602164,
+ -614976, -627788, -640600, -653412, -666224, -679036, -691848, -704660, -717472, -730284, -743096, -755908, -768720, -781532, -794344, -807156,
+ -819968, -832780, -845592, -858404, -871216, -884028, -896840, -909652, -922464, -935276, -948088, -960900, -973712, -986524, -999336, -1012148,
+ -1024960, -1037772, -1050584, -1063396, -1076208, -1089020, -1101832, -1114644, -1127456, -1140268, -1153080, -1165892, -1178704, -1191516, -1204328, -1217140,
+ -1229952, -1242764, -1255576, -1268388, -1281200, -1294012, -1306824, -1319636, -1332448, -1345260, -1358072, -1370884, -1383696, -1396508, -1409320, -1422132,
+ -1434944, -1447756, -1460568, -1473380, -1486192, -1499004, -1511816, -1524628, -1537440, -1550252, -1563064, -1575876, -1588688, -1601500, -1614312, -1627124,
+ };
+ static const int COEFFS_GV[256] = {
+ 3409920, 3383280, 3356640, 3330000, 3303360, 3276720, 3250080, 3223440, 3196800, 3170160, 3143520, 3116880, 3090240, 3063600, 3036960, 3010320,
+ 2983680, 2957040, 2930400, 2903760, 2877120, 2850480, 2823840, 2797200, 2770560, 2743920, 2717280, 2690640, 2664000, 2637360, 2610720, 2584080,
+ 2557440, 2530800, 2504160, 2477520, 2450880, 2424240, 2397600, 2370960, 2344320, 2317680, 2291040, 2264400, 2237760, 2211120, 2184480, 2157840,
+ 2131200, 2104560, 2077920, 2051280, 2024640, 1998000, 1971360, 1944720, 1918080, 1891440, 1864800, 1838160, 1811520, 1784880, 1758240, 1731600,
+ 1704960, 1678320, 1651680, 1625040, 1598400, 1571760, 1545120, 1518480, 1491840, 1465200, 1438560, 1411920, 1385280, 1358640, 1332000, 1305360,
+ 1278720, 1252080, 1225440, 1198800, 1172160, 1145520, 1118880, 1092240, 1065600, 1038960, 1012320, 985680, 959040, 932400, 905760, 879120,
+ 852480, 825840, 799200, 772560, 745920, 719280, 692640, 666000, 639360, 612720, 586080, 559440, 532800, 506160, 479520, 452880,
+ 426240, 399600, 372960, 346320, 319680, 293040, 266400, 239760, 213120, 186480, 159840, 133200, 106560, 79920, 53280, 26640,
+ 0, -26640, -53280, -79920, -106560, -133200, -159840, -186480, -213120, -239760, -266400, -293040, -319680, -346320, -372960, -399600,
+ -426240, -452880, -479520, -506160, -532800, -559440, -586080, -612720, -639360, -666000, -692640, -719280, -745920, -772560, -799200, -825840,
+ -852480, -879120, -905760, -932400, -959040, -985680, -1012320, -1038960, -1065600, -1092240, -1118880, -1145520, -1172160, -1198800, -1225440, -1252080,
+ -1278720, -1305360, -1332000, -1358640, -1385280, -1411920, -1438560, -1465200, -1491840, -1518480, -1545120, -1571760, -1598400, -1625040, -1651680, -1678320,
+ -1704960, -1731600, -1758240, -1784880, -1811520, -1838160, -1864800, -1891440, -1918080, -1944720, -1971360, -1998000, -2024640, -2051280, -2077920, -2104560,
+ -2131200, -2157840, -2184480, -2211120, -2237760, -2264400, -2291040, -2317680, -2344320, -2370960, -2397600, -2424240, -2450880, -2477520, -2504160, -2530800,
+ -2557440, -2584080, -2610720, -2637360, -2664000, -2690640, -2717280, -2743920, -2770560, -2797200, -2823840, -2850480, -2877120, -2903760, -2930400, -2957040,
+ -2983680, -3010320, -3036960, -3063600, -3090240, -3116880, -3143520, -3170160, -3196800, -3223440, -3250080, -3276720, -3303360, -3330000, -3356640, -3383280,
+ };
+ static const int COEFFS_BU[256] = {
+ -8464128, -8398002, -8331876, -8265750, -8199624, -8133498, -8067372, -8001246, -7935120, -7868994, -7802868, -7736742, -7670616, -7604490, -7538364, -7472238,
+ -7406112, -7339986, -7273860, -7207734, -7141608, -7075482, -7009356, -6943230, -6877104, -6810978, -6744852, -6678726, -6612600, -6546474, -6480348, -6414222,
+ -6348096, -6281970, -6215844, -6149718, -6083592, -6017466, -5951340, -5885214, -5819088, -5752962, -5686836, -5620710, -5554584, -5488458, -5422332, -5356206,
+ -5290080, -5223954, -5157828, -5091702, -5025576, -4959450, -4893324, -4827198, -4761072, -4694946, -4628820, -4562694, -4496568, -4430442, -4364316, -4298190,
+ -4232064, -4165938, -4099812, -4033686, -3967560, -3901434, -3835308, -3769182, -3703056, -3636930, -3570804, -3504678, -3438552, -3372426, -3306300, -3240174,
+ -3174048, -3107922, -3041796, -2975670, -2909544, -2843418, -2777292, -2711166, -2645040, -2578914, -2512788, -2446662, -2380536, -2314410, -2248284, -2182158,
+ -2116032, -2049906, -1983780, -1917654, -1851528, -1785402, -1719276, -1653150, -1587024, -1520898, -1454772, -1388646, -1322520, -1256394, -1190268, -1124142,
+ -1058016, -991890, -925764, -859638, -793512, -727386, -661260, -595134, -529008, -462882, -396756, -330630, -264504, -198378, -132252, -66126,
+ 0, 66126, 132252, 198378, 264504, 330630, 396756, 462882, 529008, 595134, 661260, 727386, 793512, 859638, 925764, 991890,
+ 1058016, 1124142, 1190268, 1256394, 1322520, 1388646, 1454772, 1520898, 1587024, 1653150, 1719276, 1785402, 1851528, 1917654, 1983780, 2049906,
+ 2116032, 2182158, 2248284, 2314410, 2380536, 2446662, 2512788, 2578914, 2645040, 2711166, 2777292, 2843418, 2909544, 2975670, 3041796, 3107922,
+ 3174048, 3240174, 3306300, 3372426, 3438552, 3504678, 3570804, 3636930, 3703056, 3769182, 3835308, 3901434, 3967560, 4033686, 4099812, 4165938,
+ 4232064, 4298190, 4364316, 4430442, 4496568, 4562694, 4628820, 4694946, 4761072, 4827198, 4893324, 4959450, 5025576, 5091702, 5157828, 5223954,
+ 5290080, 5356206, 5422332, 5488458, 5554584, 5620710, 5686836, 5752962, 5819088, 5885214, 5951340, 6017466, 6083592, 6149718, 6215844, 6281970,
+ 6348096, 6414222, 6480348, 6546474, 6612600, 6678726, 6744852, 6810978, 6877104, 6943230, 7009356, 7075482, 7141608, 7207734, 7273860, 7339986,
+ 7406112, 7472238, 7538364, 7604490, 7670616, 7736742, 7802868, 7868994, 7935120, 8001246, 8067372, 8133498, 8199624, 8265750, 8331876, 8398002,
+ };
+ static const int CLAMP_TAB[1024] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ };
+
+ void YUVtoRGBA_c(const yuv_buffer & YUVBuffer, std::vector<unsigned char> & PixelData)
+ {
+ // Width and height of all buffers have to be divisible by 2.
+ BS_ASSERT((YUVBuffer.y_width & 1) == 0);
+ BS_ASSERT((YUVBuffer.y_height & 1) == 0);
+ BS_ASSERT((YUVBuffer.uv_width & 1) == 0);
+ BS_ASSERT((YUVBuffer.uv_height & 1) == 0);
+ // UV images have to have a quarter of the Y image resolution
+ BS_ASSERT(YUVBuffer.uv_width == YUVBuffer.y_width >> 1);
+ BS_ASSERT(YUVBuffer.uv_height == YUVBuffer.y_height >> 1);
+
+ const int *cl = &CLAMP_TAB[320];
+
+ const unsigned char * ySrc0 = YUVBuffer.y;
+ const unsigned char * ySrc1 = YUVBuffer.y + YUVBuffer.y_stride;
+ const unsigned char * uSrc = YUVBuffer.u;
+ const unsigned char * vSrc = YUVBuffer.v;
+ unsigned char * dst0 = &PixelData[0];
+ unsigned char * dst1 = &PixelData[0] + YUVBuffer.y_width * 4;
+
+ for (int h = 0; h < YUVBuffer.y_height / 2; ++h)
+ {
+ for (int w = 0; w < YUVBuffer.y_width / 2; ++w)
+ {
+ int u = *uSrc++;
+ int v = *vSrc++;
+
+ int rUV = COEFFS_RV[v];
+ int gUV = COEFFS_GU[u] + COEFFS_GV[v];
+ int bUV = COEFFS_BU[u];
+
+ int y = *ySrc0++;
+ int r = COEFFS_Y[y] + rUV;
+ int g = COEFFS_Y[y] + gUV;
+ int b = COEFFS_Y[y] + bUV;
+ *dst0++ = cl[r / PRECISION];
+ *dst0++ = cl[g / PRECISION];
+ *dst0++ = cl[b / PRECISION];
+ *dst0++ = 255;
+
+ y = *ySrc1++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst1++ = cl[r / PRECISION];
+ *dst1++ = cl[g / PRECISION];
+ *dst1++ = cl[b / PRECISION];
+ *dst1++ = 255;
+
+ y = *ySrc0++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst0++ = cl[r / PRECISION];
+ *dst0++ = cl[g / PRECISION];
+ *dst0++ = cl[b / PRECISION];
+ *dst0++ = 255;
+
+ y = *ySrc1++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst1++ = cl[r / PRECISION];
+ *dst1++ = cl[g / PRECISION];
+ *dst1++ = cl[b / PRECISION];
+ *dst1++ = 255;
+ }
+
+ dst0 += YUVBuffer.y_width * 4;
+ dst1 += YUVBuffer.y_width * 4;
+ ySrc0 += YUVBuffer.y_stride * 2 - YUVBuffer.y_width;
+ ySrc1 += YUVBuffer.y_stride * 2 - YUVBuffer.y_width;
+ uSrc += YUVBuffer.uv_stride - YUVBuffer.uv_width;
+ vSrc += YUVBuffer.uv_stride - YUVBuffer.uv_width;
+ }
+ }
+
+ // -----------------------------------------------------------------------------
+
+ static const __m64 COEFF_Y_MMX[256] = {
+ 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000,
+ 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000, 0x1fff000000000000,
+ 0x1fff000000000000, 0x1fff004a004a004a, 0x1fff009500950095, 0x1fff00df00df00df, 0x1fff012a012a012a, 0x1fff017401740174, 0x1fff01bf01bf01bf, 0x1fff020902090209,
+ 0x1fff025402540254, 0x1fff029e029e029e, 0x1fff02e902e902e9, 0x1fff033303330333, 0x1fff037e037e037e, 0x1fff03c803c803c8, 0x1fff041304130413, 0x1fff045d045d045d,
+ 0x1fff04a804a804a8, 0x1fff04f204f204f2, 0x1fff053d053d053d, 0x1fff058705870587, 0x1fff05d205d205d2, 0x1fff061c061c061c, 0x1fff066706670667, 0x1fff06b106b106b1,
+ 0x1fff06fc06fc06fc, 0x1fff074607460746, 0x1fff079107910791, 0x1fff07db07db07db, 0x1fff082608260826, 0x1fff087008700870, 0x1fff08bb08bb08bb, 0x1fff090509050905,
+ 0x1fff095009500950, 0x1fff099a099a099a, 0x1fff09e509e509e5, 0x1fff0a2f0a2f0a2f, 0x1fff0a7a0a7a0a7a, 0x1fff0ac40ac40ac4, 0x1fff0b0f0b0f0b0f, 0x1fff0b590b590b59,
+ 0x1fff0ba40ba40ba4, 0x1fff0bee0bee0bee, 0x1fff0c390c390c39, 0x1fff0c830c830c83, 0x1fff0cce0cce0cce, 0x1fff0d180d180d18, 0x1fff0d630d630d63, 0x1fff0dad0dad0dad,
+ 0x1fff0df80df80df8, 0x1fff0e420e420e42, 0x1fff0e8d0e8d0e8d, 0x1fff0ed70ed70ed7, 0x1fff0f220f220f22, 0x1fff0f6c0f6c0f6c, 0x1fff0fb70fb70fb7, 0x1fff100110011001,
+ 0x1fff104c104c104c, 0x1fff109610961096, 0x1fff10e110e110e1, 0x1fff112b112b112b, 0x1fff117611761176, 0x1fff11c011c011c0, 0x1fff120b120b120b, 0x1fff125512551255,
+ 0x1fff12a012a012a0, 0x1fff12ea12ea12ea, 0x1fff133513351335, 0x1fff137f137f137f, 0x1fff13ca13ca13ca, 0x1fff141414141414, 0x1fff145f145f145f, 0x1fff14a914a914a9,
+ 0x1fff14f414f414f4, 0x1fff153e153e153e, 0x1fff158915891589, 0x1fff15d315d315d3, 0x1fff161e161e161e, 0x1fff166816681668, 0x1fff16b316b316b3, 0x1fff16fd16fd16fd,
+ 0x1fff174817481748, 0x1fff179217921792, 0x1fff17dd17dd17dd, 0x1fff182718271827, 0x1fff187218721872, 0x1fff18bc18bc18bc, 0x1fff190719071907, 0x1fff195119511951,
+ 0x1fff199c199c199c, 0x1fff19e619e619e6, 0x1fff1a311a311a31, 0x1fff1a7b1a7b1a7b, 0x1fff1ac61ac61ac6, 0x1fff1b101b101b10, 0x1fff1b5b1b5b1b5b, 0x1fff1ba51ba51ba5,
+ 0x1fff1bf01bf01bf0, 0x1fff1c3a1c3a1c3a, 0x1fff1c851c851c85, 0x1fff1ccf1ccf1ccf, 0x1fff1d1a1d1a1d1a, 0x1fff1d641d641d64, 0x1fff1daf1daf1daf, 0x1fff1df91df91df9,
+ 0x1fff1e441e441e44, 0x1fff1e8e1e8e1e8e, 0x1fff1ed91ed91ed9, 0x1fff1f231f231f23, 0x1fff1f6e1f6e1f6e, 0x1fff1fb81fb81fb8, 0x1fff200320032003, 0x1fff204d204d204d,
+ 0x1fff209820982098, 0x1fff20e220e220e2, 0x1fff212d212d212d, 0x1fff217721772177, 0x1fff21c221c221c2, 0x1fff220c220c220c, 0x1fff225722572257, 0x1fff22a122a122a1,
+ 0x1fff22ec22ec22ec, 0x1fff233623362336, 0x1fff238123812381, 0x1fff23cb23cb23cb, 0x1fff241624162416, 0x1fff246024602460, 0x1fff24aa24aa24aa, 0x1fff24f524f524f5,
+ 0x1fff253f253f253f, 0x1fff258a258a258a, 0x1fff25d425d425d4, 0x1fff261f261f261f, 0x1fff266926692669, 0x1fff26b426b426b4, 0x1fff26fe26fe26fe, 0x1fff274927492749,
+ 0x1fff279327932793, 0x1fff27de27de27de, 0x1fff282828282828, 0x1fff287328732873, 0x1fff28bd28bd28bd, 0x1fff290829082908, 0x1fff295229522952, 0x1fff299d299d299d,
+ 0x1fff29e729e729e7, 0x1fff2a322a322a32, 0x1fff2a7c2a7c2a7c, 0x1fff2ac72ac72ac7, 0x1fff2b112b112b11, 0x1fff2b5c2b5c2b5c, 0x1fff2ba62ba62ba6, 0x1fff2bf12bf12bf1,
+ 0x1fff2c3b2c3b2c3b, 0x1fff2c862c862c86, 0x1fff2cd02cd02cd0, 0x1fff2d1b2d1b2d1b, 0x1fff2d652d652d65, 0x1fff2db02db02db0, 0x1fff2dfa2dfa2dfa, 0x1fff2e452e452e45,
+ 0x1fff2e8f2e8f2e8f, 0x1fff2eda2eda2eda, 0x1fff2f242f242f24, 0x1fff2f6f2f6f2f6f, 0x1fff2fb92fb92fb9, 0x1fff300430043004, 0x1fff304e304e304e, 0x1fff309930993099,
+ 0x1fff30e330e330e3, 0x1fff312e312e312e, 0x1fff317831783178, 0x1fff31c331c331c3, 0x1fff320d320d320d, 0x1fff325832583258, 0x1fff32a232a232a2, 0x1fff32ed32ed32ed,
+ 0x1fff333733373337, 0x1fff338233823382, 0x1fff33cc33cc33cc, 0x1fff341734173417, 0x1fff346134613461, 0x1fff34ac34ac34ac, 0x1fff34f634f634f6, 0x1fff354135413541,
+ 0x1fff358b358b358b, 0x1fff35d635d635d6, 0x1fff362036203620, 0x1fff366b366b366b, 0x1fff36b536b536b5, 0x1fff370037003700, 0x1fff374a374a374a, 0x1fff379537953795,
+ 0x1fff37df37df37df, 0x1fff382a382a382a, 0x1fff387438743874, 0x1fff38bf38bf38bf, 0x1fff390939093909, 0x1fff395439543954, 0x1fff399e399e399e, 0x1fff39e939e939e9,
+ 0x1fff3a333a333a33, 0x1fff3a7e3a7e3a7e, 0x1fff3ac83ac83ac8, 0x1fff3b133b133b13, 0x1fff3b5d3b5d3b5d, 0x1fff3ba83ba83ba8, 0x1fff3bf23bf23bf2, 0x1fff3c3d3c3d3c3d,
+ 0x1fff3c873c873c87, 0x1fff3cd23cd23cd2, 0x1fff3d1c3d1c3d1c, 0x1fff3d673d673d67, 0x1fff3db13db13db1, 0x1fff3dfc3dfc3dfc, 0x1fff3e463e463e46, 0x1fff3e913e913e91,
+ 0x1fff3edb3edb3edb, 0x1fff3f263f263f26, 0x1fff3f703f703f70, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb,
+ 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb,
+ 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb, 0x1fff3fbb3fbb3fbb,
+ };
+ static const __m64 COEFF_U_MMX[256] = {
+ 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000,
+ 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000, 0x1fffc7800af30000,
+ 0x1fffc7800af30000, 0x1fffc8010ada0000, 0x1fffc8820ac10000, 0x1fffc9030aa80000, 0x1fffc9850a8f0000, 0x1fffca060a760000, 0x1fffca870a5d0000, 0x1fffcb080a440000,
+ 0x1fffcb890a2a0000, 0x1fffcc0a0a110000, 0x1fffcc8b09f80000, 0x1fffcd0d09df0000, 0x1fffcd8e09c60000, 0x1fffce0f09ad0000, 0x1fffce9009940000, 0x1fffcf11097b0000,
+ 0x1fffcf9209620000, 0x1fffd01409490000, 0x1fffd09509300000, 0x1fffd11609170000, 0x1fffd19708fe0000, 0x1fffd21808e50000, 0x1fffd29908cc0000, 0x1fffd31a08b30000,
+ 0x1fffd39c089a0000, 0x1fffd41d08810000, 0x1fffd49e08680000, 0x1fffd51f084f0000, 0x1fffd5a008360000, 0x1fffd621081d0000, 0x1fffd6a308040000, 0x1fffd72407eb0000,
+ 0x1fffd7a507d20000, 0x1fffd82607b90000, 0x1fffd8a707a00000, 0x1fffd92807870000, 0x1fffd9a9076e0000, 0x1fffda2b07550000, 0x1fffdaac073c0000, 0x1fffdb2d07230000,
+ 0x1fffdbae070a0000, 0x1fffdc2f06f10000, 0x1fffdcb006d80000, 0x1fffdd3206bf0000, 0x1fffddb306a60000, 0x1fffde34068d0000, 0x1fffdeb506740000, 0x1fffdf36065b0000,
+ 0x1fffdfb706420000, 0x1fffe03806290000, 0x1fffe0ba060f0000, 0x1fffe13b05f60000, 0x1fffe1bc05dd0000, 0x1fffe23d05c40000, 0x1fffe2be05ab0000, 0x1fffe33f05920000,
+ 0x1fffe3c005790000, 0x1fffe44205600000, 0x1fffe4c305470000, 0x1fffe544052e0000, 0x1fffe5c505150000, 0x1fffe64604fc0000, 0x1fffe6c704e30000, 0x1fffe74904ca0000,
+ 0x1fffe7ca04b10000, 0x1fffe84b04980000, 0x1fffe8cc047f0000, 0x1fffe94d04660000, 0x1fffe9ce044d0000, 0x1fffea4f04340000, 0x1fffead1041b0000, 0x1fffeb5204020000,
+ 0x1fffebd303e90000, 0x1fffec5403d00000, 0x1fffecd503b70000, 0x1fffed56039e0000, 0x1fffedd803850000, 0x1fffee59036c0000, 0x1fffeeda03530000, 0x1fffef5b033a0000,
+ 0x1fffefdc03210000, 0x1ffff05d03080000, 0x1ffff0de02ef0000, 0x1ffff16002d60000, 0x1ffff1e102bd0000, 0x1ffff26202a40000, 0x1ffff2e3028b0000, 0x1ffff36402720000,
+ 0x1ffff3e502590000, 0x1ffff46702400000, 0x1ffff4e802270000, 0x1ffff569020e0000, 0x1ffff5ea01f40000, 0x1ffff66b01db0000, 0x1ffff6ec01c20000, 0x1ffff76d01a90000,
+ 0x1ffff7ef01900000, 0x1ffff87001770000, 0x1ffff8f1015e0000, 0x1ffff97201450000, 0x1ffff9f3012c0000, 0x1ffffa7401130000, 0x1ffffaf500fa0000, 0x1ffffb7700e10000,
+ 0x1ffffbf800c80000, 0x1ffffc7900af0000, 0x1ffffcfa00960000, 0x1ffffd7b007d0000, 0x1ffffdfc00640000, 0x1ffffe7e004b0000, 0x1ffffeff00320000, 0x1fffff8000190000,
+ 0x1fff000000000000, 0x1fff0081ffe80000, 0x1fff0102ffcf0000, 0x1fff0183ffb60000, 0x1fff0205ff9d0000, 0x1fff0286ff840000, 0x1fff0307ff6b0000, 0x1fff0388ff520000,
+ 0x1fff0409ff390000, 0x1fff048aff200000, 0x1fff050cff070000, 0x1fff058dfeee0000, 0x1fff060efed50000, 0x1fff068ffebc0000, 0x1fff0710fea30000, 0x1fff0791fe8a0000,
+ 0x1fff0812fe710000, 0x1fff0894fe580000, 0x1fff0915fe3f0000, 0x1fff0996fe260000, 0x1fff0a17fe0d0000, 0x1fff0a98fdf30000, 0x1fff0b19fdda0000, 0x1fff0b9afdc10000,
+ 0x1fff0c1cfda80000, 0x1fff0c9dfd8f0000, 0x1fff0d1efd760000, 0x1fff0d9ffd5d0000, 0x1fff0e20fd440000, 0x1fff0ea1fd2b0000, 0x1fff0f23fd120000, 0x1fff0fa4fcf90000,
+ 0x1fff1025fce00000, 0x1fff10a6fcc70000, 0x1fff1127fcae0000, 0x1fff11a8fc950000, 0x1fff1229fc7c0000, 0x1fff12abfc630000, 0x1fff132cfc4a0000, 0x1fff13adfc310000,
+ 0x1fff142efc180000, 0x1fff14affbff0000, 0x1fff1530fbe60000, 0x1fff15b2fbcd0000, 0x1fff1633fbb40000, 0x1fff16b4fb9b0000, 0x1fff1735fb820000, 0x1fff17b6fb690000,
+ 0x1fff1837fb500000, 0x1fff18b8fb370000, 0x1fff193afb1e0000, 0x1fff19bbfb050000, 0x1fff1a3cfaec0000, 0x1fff1abdfad30000, 0x1fff1b3efaba0000, 0x1fff1bbffaa10000,
+ 0x1fff1c41fa880000, 0x1fff1cc2fa6f0000, 0x1fff1d43fa560000, 0x1fff1dc4fa3d0000, 0x1fff1e45fa240000, 0x1fff1ec6fa0b0000, 0x1fff1f47f9f20000, 0x1fff1fc9f9d80000,
+ 0x1fff204af9bf0000, 0x1fff20cbf9a60000, 0x1fff214cf98d0000, 0x1fff21cdf9740000, 0x1fff224ef95b0000, 0x1fff22cff9420000, 0x1fff2351f9290000, 0x1fff23d2f9100000,
+ 0x1fff2453f8f70000, 0x1fff24d4f8de0000, 0x1fff2555f8c50000, 0x1fff25d6f8ac0000, 0x1fff2658f8930000, 0x1fff26d9f87a0000, 0x1fff275af8610000, 0x1fff27dbf8480000,
+ 0x1fff285cf82f0000, 0x1fff28ddf8160000, 0x1fff295ef7fd0000, 0x1fff29e0f7e40000, 0x1fff2a61f7cb0000, 0x1fff2ae2f7b20000, 0x1fff2b63f7990000, 0x1fff2be4f7800000,
+ 0x1fff2c65f7670000, 0x1fff2ce7f74e0000, 0x1fff2d68f7350000, 0x1fff2de9f71c0000, 0x1fff2e6af7030000, 0x1fff2eebf6ea0000, 0x1fff2f6cf6d10000, 0x1fff2fedf6b80000,
+ 0x1fff306ff69f0000, 0x1fff30f0f6860000, 0x1fff3171f66d0000, 0x1fff31f2f6540000, 0x1fff3273f63b0000, 0x1fff32f4f6220000, 0x1fff3376f6090000, 0x1fff33f7f5f00000,
+ 0x1fff3478f5d70000, 0x1fff34f9f5bd0000, 0x1fff357af5a40000, 0x1fff35fbf58b0000, 0x1fff367cf5720000, 0x1fff36fef5590000, 0x1fff377ff5400000, 0x1fff3800f5270000,
+ 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000,
+ 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000, 0x1fff3881f50e0000,
+ };
+ static const __m64 COEFF_V_MMX[256] = {
+ 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351,
+ 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351, 0x1fff000016c4d351,
+ 0x1fff000016c4d351, 0x1fff00001690d3b7, 0x1fff0000165cd41d, 0x1fff00001627d483, 0x1fff000015f3d4e9, 0x1fff000015bfd550, 0x1fff0000158bd5b6, 0x1fff00001557d61c,
+ 0x1fff00001523d682, 0x1fff000014efd6e8, 0x1fff000014bbd74e, 0x1fff00001487d7b4, 0x1fff00001453d81b, 0x1fff0000141fd881, 0x1fff000013ebd8e7, 0x1fff000013b7d94d,
+ 0x1fff00001383d9b3, 0x1fff0000134fda19, 0x1fff0000131bda7f, 0x1fff000012e7dae6, 0x1fff000012b3db4c, 0x1fff0000127fdbb2, 0x1fff0000124bdc18, 0x1fff00001217dc7e,
+ 0x1fff000011e3dce4, 0x1fff000011afdd4a, 0x1fff0000117bddb1, 0x1fff00001147de17, 0x1fff00001113de7d, 0x1fff000010dfdee3, 0x1fff000010abdf49, 0x1fff00001077dfaf,
+ 0x1fff00001043e015, 0x1fff0000100fe07c, 0x1fff00000fdae0e2, 0x1fff00000fa6e148, 0x1fff00000f72e1ae, 0x1fff00000f3ee214, 0x1fff00000f0ae27a, 0x1fff00000ed6e2e0,
+ 0x1fff00000ea2e347, 0x1fff00000e6ee3ad, 0x1fff00000e3ae413, 0x1fff00000e06e479, 0x1fff00000dd2e4df, 0x1fff00000d9ee545, 0x1fff00000d6ae5ab, 0x1fff00000d36e612,
+ 0x1fff00000d02e678, 0x1fff00000ccee6de, 0x1fff00000c9ae744, 0x1fff00000c66e7aa, 0x1fff00000c32e810, 0x1fff00000bfee877, 0x1fff00000bcae8dd, 0x1fff00000b96e943,
+ 0x1fff00000b62e9a9, 0x1fff00000b2eea0f, 0x1fff00000afaea75, 0x1fff00000ac6eadb, 0x1fff00000a92eb42, 0x1fff00000a5eeba8, 0x1fff00000a2aec0e, 0x1fff000009f6ec74,
+ 0x1fff000009c2ecda, 0x1fff0000098eed40, 0x1fff00000959eda6, 0x1fff00000925ee0d, 0x1fff000008f1ee73, 0x1fff000008bdeed9, 0x1fff00000889ef3f, 0x1fff00000855efa5,
+ 0x1fff00000821f00b, 0x1fff000007edf071, 0x1fff000007b9f0d8, 0x1fff00000785f13e, 0x1fff00000751f1a4, 0x1fff0000071df20a, 0x1fff000006e9f270, 0x1fff000006b5f2d6,
+ 0x1fff00000681f33c, 0x1fff0000064df3a3, 0x1fff00000619f409, 0x1fff000005e5f46f, 0x1fff000005b1f4d5, 0x1fff0000057df53b, 0x1fff00000549f5a1, 0x1fff00000515f607,
+ 0x1fff000004e1f66e, 0x1fff000004adf6d4, 0x1fff00000479f73a, 0x1fff00000445f7a0, 0x1fff00000411f806, 0x1fff000003ddf86c, 0x1fff000003a9f8d2, 0x1fff00000375f939,
+ 0x1fff00000341f99f, 0x1fff0000030cfa05, 0x1fff000002d8fa6b, 0x1fff000002a4fad1, 0x1fff00000270fb37, 0x1fff0000023cfb9d, 0x1fff00000208fc04, 0x1fff000001d4fc6a,
+ 0x1fff000001a0fcd0, 0x1fff0000016cfd36, 0x1fff00000138fd9c, 0x1fff00000104fe02, 0x1fff000000d0fe68, 0x1fff0000009cfecf, 0x1fff00000068ff35, 0x1fff00000034ff9b,
+ 0x1fff000000000000, 0x1fff0000ffcd0066, 0x1fff0000ff9900cc, 0x1fff0000ff650132, 0x1fff0000ff310199, 0x1fff0000fefd01ff, 0x1fff0000fec90265, 0x1fff0000fe9502cb,
+ 0x1fff0000fe610331, 0x1fff0000fe2d0397, 0x1fff0000fdf903fd, 0x1fff0000fdc50464, 0x1fff0000fd9104ca, 0x1fff0000fd5d0530, 0x1fff0000fd290596, 0x1fff0000fcf505fc,
+ 0x1fff0000fcc00662, 0x1fff0000fc8c06c8, 0x1fff0000fc58072f, 0x1fff0000fc240795, 0x1fff0000fbf007fb, 0x1fff0000fbbc0861, 0x1fff0000fb8808c7, 0x1fff0000fb54092d,
+ 0x1fff0000fb200993, 0x1fff0000faec09fa, 0x1fff0000fab80a60, 0x1fff0000fa840ac6, 0x1fff0000fa500b2c, 0x1fff0000fa1c0b92, 0x1fff0000f9e80bf8, 0x1fff0000f9b40c5e,
+ 0x1fff0000f9800cc5, 0x1fff0000f94c0d2b, 0x1fff0000f9180d91, 0x1fff0000f8e40df7, 0x1fff0000f8b00e5d, 0x1fff0000f87c0ec3, 0x1fff0000f8480f29, 0x1fff0000f8140f90,
+ 0x1fff0000f7e00ff6, 0x1fff0000f7ac105c, 0x1fff0000f77810c2, 0x1fff0000f7441128, 0x1fff0000f710118e, 0x1fff0000f6dc11f4, 0x1fff0000f6a8125b, 0x1fff0000f67312c1,
+ 0x1fff0000f63f1327, 0x1fff0000f60b138d, 0x1fff0000f5d713f3, 0x1fff0000f5a31459, 0x1fff0000f56f14bf, 0x1fff0000f53b1526, 0x1fff0000f507158c, 0x1fff0000f4d315f2,
+ 0x1fff0000f49f1658, 0x1fff0000f46b16be, 0x1fff0000f4371724, 0x1fff0000f403178a, 0x1fff0000f3cf17f1, 0x1fff0000f39b1857, 0x1fff0000f36718bd, 0x1fff0000f3331923,
+ 0x1fff0000f2ff1989, 0x1fff0000f2cb19ef, 0x1fff0000f2971a56, 0x1fff0000f2631abc, 0x1fff0000f22f1b22, 0x1fff0000f1fb1b88, 0x1fff0000f1c71bee, 0x1fff0000f1931c54,
+ 0x1fff0000f15f1cba, 0x1fff0000f12b1d21, 0x1fff0000f0f71d87, 0x1fff0000f0c31ded, 0x1fff0000f08f1e53, 0x1fff0000f05b1eb9, 0x1fff0000f0271f1f, 0x1fff0000eff21f85,
+ 0x1fff0000efbe1fec, 0x1fff0000ef8a2052, 0x1fff0000ef5620b8, 0x1fff0000ef22211e, 0x1fff0000eeee2184, 0x1fff0000eeba21ea, 0x1fff0000ee862250, 0x1fff0000ee5222b7,
+ 0x1fff0000ee1e231d, 0x1fff0000edea2383, 0x1fff0000edb623e9, 0x1fff0000ed82244f, 0x1fff0000ed4e24b5, 0x1fff0000ed1a251b, 0x1fff0000ece62582, 0x1fff0000ecb225e8,
+ 0x1fff0000ec7e264e, 0x1fff0000ec4a26b4, 0x1fff0000ec16271a, 0x1fff0000ebe22780, 0x1fff0000ebae27e6, 0x1fff0000eb7a284d, 0x1fff0000eb4628b3, 0x1fff0000eb122919,
+ 0x1fff0000eade297f, 0x1fff0000eaaa29e5, 0x1fff0000ea762a4b, 0x1fff0000ea422ab1, 0x1fff0000ea0e2b18, 0x1fff0000e9da2b7e, 0x1fff0000e9a52be4, 0x1fff0000e9712c4a,
+ 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0,
+ 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0, 0x1fff0000e93d2cb0,
+ };
+
+ // -----------------------------------------------------------------------------
+
+ void YUVtoRGBA_MMX(yuv_buffer & YUVBuffer, std::vector<unsigned char> & PixelData)
+ {
+ // Width and height of all buffers have to be divisible by 4.
+ BS_ASSERT((YUVBuffer.y_width & 3) == 0);
+ BS_ASSERT((YUVBuffer.y_height & 3) == 0);
+ BS_ASSERT((YUVBuffer.uv_width & 3) == 0);
+ BS_ASSERT((YUVBuffer.uv_height & 3) == 0);
+ // UV images have to have a quarter of the Y image resolution
+ BS_ASSERT(YUVBuffer.uv_width == YUVBuffer.y_width >> 1);
+ BS_ASSERT(YUVBuffer.uv_height == YUVBuffer.y_height >> 1);
+
+ const unsigned char * ySrc = YUVBuffer.y;
+ const unsigned char * uSrc = YUVBuffer.u;
+ const unsigned char * vSrc = YUVBuffer.v;
+ unsigned char * dst = &PixelData[0];
+
+ const int yPadding = YUVBuffer.y_stride - YUVBuffer.y_width;
+ int uvStride = YUVBuffer.uv_stride;
+
+ for (int h = 0; h < YUVBuffer.y_height; ++h)
+ {
+ uvStride ^= YUVBuffer.uv_stride;
+ for (int w = 0; w < YUVBuffer.y_width / 4; ++w)
+ {
+ __m64 uvCoeff0 = _m_paddw(COEFF_U_MMX[*uSrc++], COEFF_V_MMX[*vSrc++]);
+ __m64 uvCoeff1 = _m_paddw(COEFF_U_MMX[*uSrc++], COEFF_V_MMX[*vSrc++]);
+
+ __m64 px01 = COEFF_Y_MMX[*ySrc++];
+ __m64 px23 = COEFF_Y_MMX[*ySrc++];
+ __m64 px45 = COEFF_Y_MMX[*ySrc++];
+ __m64 px67 = COEFF_Y_MMX[*ySrc++];
+
+ px01 = _m_paddw(px01, uvCoeff0);
+ px23 = _m_paddw(px23, uvCoeff0);
+ px45 = _m_paddw(px45, uvCoeff1);
+ px67 = _m_paddw(px67, uvCoeff1);
+
+ px01 = _m_psrawi(px01, 6);
+ px23 = _m_psrawi(px23, 6);
+ px45 = _m_psrawi(px45, 6);
+ px67 = _m_psrawi(px67, 6);
+
+ ((__m64*)dst)[0] = _m_packuswb(px01, px23);
+ ((__m64*)dst)[1] = _m_packuswb(px45, px67);
+ dst += 16;
+ }
+
+ ySrc += yPadding;
+ uSrc += uvStride - YUVBuffer.uv_width;
+ vSrc += uvStride - YUVBuffer.uv_width;
+ }
+
+ _m_empty();
+ }
+
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_YUVtoRGBA::YUVtoRGBA(yuv_buffer & YUVBuffer, const theora_info & TheoraInfo, vector<unsigned char> & Pixels)
+{
+ BS_ASSERT(TheoraInfo.pixelformat == OC_PF_420);
+ BS_ASSERT(int(Pixels.size()) >= YUVBuffer.y_width * YUVBuffer.y_height);
+
+ return YUVtoRGBA_c(YUVBuffer, Pixels);
+
+ if (BS_CPUInfo::GetInstance().IsMMXSupported())
+ return YUVtoRGBA_MMX(YUVBuffer, Pixels);
+ else
+ return YUVtoRGBA_c(YUVBuffer, Pixels);
+}
diff --git a/engines/sword25/fmv/oggtheora/yuvtorgba.h b/engines/sword25/fmv/oggtheora/yuvtorgba.h
new file mode 100755
index 0000000000..0bd74e5a18
--- /dev/null
+++ b/engines/sword25/fmv/oggtheora/yuvtorgba.h
@@ -0,0 +1,44 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_YUVTORGBA_H
+#define BS_YUVTORGBA_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "theora/theora.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_YUVtoRGBA
+{
+public:
+ static void YUVtoRGBA(yuv_buffer & YUVBuffer, const theora_info & TheoraInfo, std::vector<unsigned char> & Pixels);
+};
+
+#endif
diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp
new file mode 100755
index 0000000000..a4511454ed
--- /dev/null
+++ b/engines/sword25/gfx/animation.cpp
@@ -0,0 +1,878 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include <memory>
+
+#include "animation.h"
+
+#include "kernel/kernel.h"
+#include "kernel/resmanager.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/callbackregistry.h"
+#include "package/packagemanager.h"
+#include "util/tinyxml/tinyxml.h"
+#include "image/image.h"
+#include "animationtemplate.h"
+#include "animationtemplateregistry.h"
+#include "animationresource.h"
+#include "bitmapresource.h"
+#include "graphicengine.h"
+
+#define BS_LOG_PREFIX "ANIMATION"
+
+// Konstruktion / Destruktion
+// --------------------------
+
+BS_Animation::BS_Animation(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const std::string & FileName) :
+ BS_TimedRenderObject(ParentPtr, BS_RenderObject::TYPE_ANIMATION)
+{
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!m_InitSuccess) return;
+
+ InitMembers();
+
+ // Vom negativen Fall ausgehen.
+ m_InitSuccess = false;
+
+ InitializeAnimationResource(FileName);
+
+ // Erfolg signalisieren.
+ m_InitSuccess = true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Animation::BS_Animation(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const BS_AnimationTemplate & Template) :
+ BS_TimedRenderObject(ParentPtr, BS_RenderObject::TYPE_ANIMATION)
+{
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!m_InitSuccess) return;
+
+ InitMembers();
+
+ // Vom negativen Fall ausgehen.
+ m_InitSuccess = false;
+
+ m_AnimationTemplateHandle = BS_AnimationTemplate::Create(Template);
+
+ // Erfolg signalisieren.
+ m_InitSuccess = true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Animation::BS_Animation(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle) :
+ BS_TimedRenderObject(ParentPtr, BS_RenderObject::TYPE_ANIMATION, Handle)
+{
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!m_InitSuccess) return;
+
+ InitMembers();
+
+ // Objekt vom Stream laden.
+ m_InitSuccess = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::InitializeAnimationResource(const std::string &FileName)
+{
+ // Die Resource wird für die gesamte Lebensdauer des Animations-Objektes gelockt.
+ BS_Resource * ResourcePtr = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(FileName);
+ if (ResourcePtr && ResourcePtr->GetType() == BS_Resource::TYPE_ANIMATION)
+ m_AnimationResourcePtr = static_cast<BS_AnimationResource *>(ResourcePtr);
+ else
+ {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested. The Animation can't be created.", FileName.c_str());
+ return;
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen.
+ ComputeCurrentCharacteristics();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::InitMembers()
+{
+ m_CurrentFrame = 0;
+ m_CurrentFrameTime = 0;
+ m_Direction = FORWARD;
+ m_Running = false;
+ m_Finished = false;
+ m_RelX = 0;
+ m_RelY = 0;
+ m_ScaleFactorX = 1.0f;
+ m_ScaleFactorY = 1.0f;
+ m_ModulationColor = 0xffffffff;
+ m_AnimationResourcePtr = 0;
+ m_AnimationTemplateHandle = 0;
+ m_FramesLocked = false;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Animation::~BS_Animation()
+{
+ if (GetAnimationDescription())
+ {
+ Stop();
+ GetAnimationDescription()->Unlock();
+ }
+
+ // Delete Callbacks
+ std::vector<ANIMATION_CALLBACK_DATA>::iterator it = m_DeleteCallbacks.begin();
+ for (; it != m_DeleteCallbacks.end(); it++) ((*it).Callback)((*it).Data);
+
+}
+
+// -----------------------------------------------------------------------------
+// Steuermethoden
+// -----------------------------------------------------------------------------
+
+void BS_Animation::Play()
+{
+ // Wenn die Animation zuvor komplett durchgelaufen ist, wird sie wieder von Anfang abgespielt
+ if (m_Finished) Stop();
+
+ m_Running = true;
+ LockAllFrames();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::Pause()
+{
+ m_Running = false;
+ UnlockAllFrames();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::Stop()
+{
+ m_CurrentFrame = 0;
+ m_CurrentFrameTime = 0;
+ m_Direction = FORWARD;
+ Pause();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetFrame(unsigned int Nr)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+
+ if (Nr >= animationDescriptionPtr->GetFrameCount())
+ {
+ BS_LOG_ERRORLN("Tried to set animation to illegal frame (%d). Value must be between 0 and %d.",
+ Nr, animationDescriptionPtr->GetFrameCount());
+ return;
+ }
+
+ m_CurrentFrame = Nr;
+ m_CurrentFrameTime = 0;
+ ComputeCurrentCharacteristics();
+ ForceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+// Rendern
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::DoRender()
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(m_CurrentFrame < animationDescriptionPtr->GetFrameCount());
+
+ // Bitmap des aktuellen Frames holen
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->GetFrame(m_CurrentFrame).FileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource * pBitmapResource = static_cast<BS_BitmapResource*>(pResource);
+
+ // Framebufferobjekt holen
+ BS_GraphicEngine * pGfx = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool Result;
+ if (IsScalingAllowed() && (m_Width != pBitmapResource->GetWidth() || m_Height != pBitmapResource->GetHeight()))
+ {
+ Result = pBitmapResource->Blit(m_AbsoluteX, m_AbsoluteY,
+ (animationDescriptionPtr->GetFrame(m_CurrentFrame).FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->GetFrame(m_CurrentFrame).FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, m_Width, m_Height);
+ }
+ else
+ {
+ Result = pBitmapResource->Blit(m_AbsoluteX, m_AbsoluteY,
+ (animationDescriptionPtr->GetFrame(m_CurrentFrame).FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->GetFrame(m_CurrentFrame).FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, -1, -1);
+ }
+
+ // Resource freigeben
+ pBitmapResource->Release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+// Frame Notifikation
+// -----------------------------------------------------------------------------
+
+void BS_Animation::FrameNotification(int TimeElapsed)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(TimeElapsed >= 0);
+
+ // Nur wenn die Animation läuft wird sie auch weiterbewegt
+ if (m_Running)
+ {
+ int OldFrame = m_CurrentFrame;
+
+ // Gesamte vergangene Zeit bestimmen (inkl. Restzeit des aktuellen Frames)
+ m_CurrentFrameTime += TimeElapsed;
+
+ // Anzahl an zu überpringenden Frames bestimmen
+ int SkipFrames = animationDescriptionPtr->GetMillisPerFrame() == 0 ? 0 : m_CurrentFrameTime / animationDescriptionPtr->GetMillisPerFrame();
+
+ // Neue Frame-Restzeit bestimmen
+ m_CurrentFrameTime -= animationDescriptionPtr->GetMillisPerFrame() * SkipFrames;
+
+ // Neuen Frame bestimmen (je nach aktuellener Abspielrichtung wird addiert oder subtrahiert)
+ int TmpCurFrame = m_CurrentFrame;
+ switch (m_Direction)
+ {
+ case FORWARD:
+ TmpCurFrame += SkipFrames;
+ break;
+
+ case BACKWARD:
+ TmpCurFrame -= SkipFrames;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+
+ // Überläufe behandeln
+ if (TmpCurFrame < 0)
+ {
+ // Loop-Point Callbacks
+ std::vector<ANIMATION_CALLBACK_DATA>::iterator it = m_LoopPointCallbacks.begin();
+ while (it != m_LoopPointCallbacks.end())
+ {
+ if (((*it).Callback)((*it).Data) == false)
+ {
+ it = m_LoopPointCallbacks.erase(it);
+ }
+ else
+ it++;
+ }
+
+ // Ein Unterlauf darf nur auftreten, wenn der Animationstyp JOJO ist.
+ BS_ASSERT(animationDescriptionPtr->GetAnimationType() == AT_JOJO);
+ TmpCurFrame = - TmpCurFrame;
+ m_Direction = FORWARD;
+ }
+ else if (static_cast<unsigned int>(TmpCurFrame) >= animationDescriptionPtr->GetFrameCount())
+ {
+ // Loop-Point Callbacks
+ std::vector<ANIMATION_CALLBACK_DATA>::iterator it = m_LoopPointCallbacks.begin();
+ while (it != m_LoopPointCallbacks.end())
+ {
+ if (((*it).Callback)((*it).Data) == false)
+ it = m_LoopPointCallbacks.erase(it);
+ else
+ it++;
+ }
+
+ switch (animationDescriptionPtr->GetAnimationType())
+ {
+ case AT_ONESHOT:
+ TmpCurFrame = animationDescriptionPtr->GetFrameCount() - 1;
+ m_Finished = true;
+ Pause();
+ break;
+
+ case AT_LOOP:
+ TmpCurFrame = TmpCurFrame % animationDescriptionPtr->GetFrameCount();
+ break;
+
+ case AT_JOJO:
+ TmpCurFrame = animationDescriptionPtr->GetFrameCount() - (TmpCurFrame % animationDescriptionPtr->GetFrameCount()) - 1;
+ m_Direction = BACKWARD;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+ }
+
+ if (m_CurrentFrame != TmpCurFrame)
+ {
+ ForceRefresh();
+
+ if (animationDescriptionPtr->GetFrame(m_CurrentFrame).Action != "")
+ {
+ // Action Callbacks
+ std::vector<ANIMATION_CALLBACK_DATA>::iterator it = m_ActionCallbacks.begin();
+ while (it != m_ActionCallbacks.end())
+ {
+ if (((*it).Callback)((*it).Data) == false)
+ it = m_ActionCallbacks.erase(it);
+ else
+ it++;
+ }
+ }
+ }
+
+ m_CurrentFrame = static_cast<unsigned int>(TmpCurFrame);
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen
+ ComputeCurrentCharacteristics();
+
+ BS_ASSERT(m_CurrentFrame < animationDescriptionPtr->GetFrameCount());
+ BS_ASSERT(m_CurrentFrameTime >= 0);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::ComputeCurrentCharacteristics()
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const BS_AnimationResource::Frame & CurFrame = animationDescriptionPtr->GetFrame(m_CurrentFrame);
+
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(CurFrame.FileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource* pBitmap = static_cast<BS_BitmapResource *>(pResource);
+
+ // Größe des Bitmaps auf die Animation übertragen
+ m_Width = static_cast<int>(pBitmap->GetWidth() * m_ScaleFactorX);
+ m_Height = static_cast<int>(pBitmap->GetHeight() * m_ScaleFactorY);
+
+ // Position anhand des Hotspots berechnen und setzen
+ int PosX = m_RelX + ComputeXModifier();
+ int PosY = m_RelY + ComputeYModifier();
+
+ BS_RenderObject::SetPos(PosX, PosY);
+
+ pBitmap->Release();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::LockAllFrames()
+{
+ if (!m_FramesLocked)
+ {
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (unsigned int i = 0; i < animationDescriptionPtr->GetFrameCount(); ++i)
+ {
+ if (!BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->GetFrame(i).FileName))
+ {
+ BS_LOG_ERRORLN("Could not lock all animation frames.");
+ return false;
+ }
+ }
+
+ m_FramesLocked = true;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::UnlockAllFrames()
+{
+ if (m_FramesLocked)
+ {
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (unsigned int i = 0; i < animationDescriptionPtr->GetFrameCount(); ++i)
+ {
+ BS_Resource* pResource;
+ if (!(pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->GetFrame(i).FileName)))
+ {
+ BS_LOG_ERRORLN("Could not unlock all animation frames.");
+ return false;
+ }
+
+ // Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
+ pResource->Release();
+ if (pResource->GetLockCount()) pResource->Release();
+ }
+
+ m_FramesLocked = false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Getter
+// -----------------------------------------------------------------------------
+
+BS_Animation::ANIMATION_TYPES BS_Animation::GetAnimationType() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->GetAnimationType();
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetFPS() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->GetFPS();
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetFrameCount() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->GetFrameCount();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::IsScalingAllowed() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->IsScalingAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::IsAlphaAllowed() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->IsAlphaAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::IsColorModulationAllowed() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->IsColorModulationAllowed();
+}
+
+// -----------------------------------------------------------------------------
+// Positionieren
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetPos(int RelX, int RelY)
+{
+ m_RelX = RelX;
+ m_RelY = RelY;
+
+ ComputeCurrentCharacteristics();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetX(int RelX)
+{
+ m_RelX = RelX;
+
+ ComputeCurrentCharacteristics();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetY(int RelY)
+{
+ m_RelY = RelY;
+
+ ComputeCurrentCharacteristics();
+}
+
+// -----------------------------------------------------------------------------
+// Darstellungsart festlegen
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetAlpha(int Alpha)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->IsAlphaAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set alpha value on an animation that does not support alpha. Call was ignored.");
+ return;
+ }
+
+ unsigned int NewModulationColor = (m_ModulationColor & 0x00ffffff) | Alpha << 24;
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetModulationColor(unsigned int ModulationColor)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->IsColorModulationAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set modulation color on an animation that does not support color modulation. Call was ignored");
+ return;
+ }
+
+ unsigned int NewModulationColor = (ModulationColor & 0x00ffffff) | (m_ModulationColor & 0xff000000);
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetScaleFactor(float ScaleFactor)
+{
+ SetScaleFactorX(ScaleFactor);
+ SetScaleFactorY(ScaleFactor);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetScaleFactorX(float ScaleFactorX)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->IsScalingAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set x scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (ScaleFactorX != m_ScaleFactorX)
+ {
+ m_ScaleFactorX = ScaleFactorX;
+ if (m_ScaleFactorX <= 0.0f) m_ScaleFactorX = 0.001f;
+ ForceRefresh();
+ ComputeCurrentCharacteristics();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::SetScaleFactorY(float ScaleFactorY)
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->IsScalingAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set y scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (ScaleFactorY != m_ScaleFactorY)
+ {
+ m_ScaleFactorY = ScaleFactorY;
+ if (m_ScaleFactorY <= 0.0f) m_ScaleFactorY = 0.001f;
+ ForceRefresh();
+ ComputeCurrentCharacteristics();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+const std::string & BS_Animation::GetCurrentAction() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->GetFrame(m_CurrentFrame).Action;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetX() const
+{
+ return m_RelX;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetY() const
+{
+ return m_RelY;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetAbsoluteX() const
+{
+ return m_AbsoluteX + (m_RelX - m_X);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::GetAbsoluteY() const
+{
+ return m_AbsoluteY + (m_RelY - m_Y);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::ComputeXModifier() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const BS_AnimationResource::Frame & CurFrame = animationDescriptionPtr->GetFrame(m_CurrentFrame);
+
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(CurFrame.FileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource* pBitmap = static_cast<BS_BitmapResource *>(pResource);
+
+ int Result = CurFrame.FlipV ? - static_cast<int>((pBitmap->GetWidth() - 1 - CurFrame.HotspotX) * m_ScaleFactorX) :
+ - static_cast<int>(CurFrame.HotspotX * m_ScaleFactorX);
+
+ pBitmap->Release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Animation::ComputeYModifier() const
+{
+ BS_AnimationDescription * animationDescriptionPtr = GetAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const BS_AnimationResource::Frame & CurFrame = animationDescriptionPtr->GetFrame(m_CurrentFrame);
+
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(CurFrame.FileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource* pBitmap = static_cast<BS_BitmapResource *>(pResource);
+
+ int Result = CurFrame.FlipH ? - static_cast<int>((pBitmap->GetHeight() - 1 - CurFrame.HotspotY) * m_ScaleFactorY) :
+ - static_cast<int>(CurFrame.HotspotY * m_ScaleFactorY);
+
+ pBitmap->Release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::RegisterActionCallback(ANIMATION_CALLBACK Callback, unsigned int Data)
+{
+ ANIMATION_CALLBACK_DATA CD;
+ CD.Callback = Callback;
+ CD.Data = Data;
+ m_ActionCallbacks.push_back(CD);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::RegisterLoopPointCallback(ANIMATION_CALLBACK Callback, unsigned int Data)
+{
+ ANIMATION_CALLBACK_DATA CD;
+ CD.Callback = Callback;
+ CD.Data = Data;
+ m_LoopPointCallbacks.push_back(CD);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::RegisterDeleteCallback(ANIMATION_CALLBACK Callback, unsigned int Data)
+{
+ ANIMATION_CALLBACK_DATA CD;
+ CD.Callback = Callback;
+ CD.Data = Data;
+ m_DeleteCallbacks.push_back(CD);
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+void BS_Animation::PersistCallbackVector(BS_OutputPersistenceBlock & Writer, const std::vector<ANIMATION_CALLBACK_DATA> & Vector)
+{
+ // Anzahl an Callbacks persistieren.
+ Writer.Write(Vector.size());
+
+ // Alle Callbacks einzeln persistieren.
+ std::vector<ANIMATION_CALLBACK_DATA>::const_iterator It = Vector.begin();
+ while (It != Vector.end())
+ {
+ Writer.Write(BS_CallbackRegistry::GetInstance().ResolveCallbackPointer(It->Callback));
+ Writer.Write(It->Data);
+
+ ++It;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Animation::UnpersistCallbackVector(BS_InputPersistenceBlock & Reader, std::vector<ANIMATION_CALLBACK_DATA> & Vector)
+{
+ // Callbackvector leeren.
+ Vector.resize(0);
+
+ // Anzahl an Callbacks einlesen.
+ unsigned int CallbackCount;
+ Reader.Read(CallbackCount);
+
+ // Alle Callbacks einzeln wieder herstellen.
+ for (unsigned int i = 0; i < CallbackCount; ++i)
+ {
+ ANIMATION_CALLBACK_DATA CallbackData;
+
+ std::string CallbackFunctionName;
+ Reader.Read(CallbackFunctionName);
+ CallbackData.Callback = reinterpret_cast<ANIMATION_CALLBACK>(BS_CallbackRegistry::GetInstance().ResolveCallbackFunction(CallbackFunctionName));
+
+ Reader.Read(CallbackData.Data);
+
+ Vector.push_back(CallbackData);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Persist(Writer);
+
+ Writer.Write(m_RelX);
+ Writer.Write(m_RelY);
+ Writer.Write(m_ScaleFactorX);
+ Writer.Write(m_ScaleFactorY);
+ Writer.Write(m_ModulationColor);
+ Writer.Write(m_CurrentFrame);
+ Writer.Write(m_CurrentFrameTime);
+ Writer.Write(m_Running);
+ Writer.Write(m_Finished);
+ Writer.Write(static_cast<unsigned int>(m_Direction));
+
+ // Je nach Animationstyp entweder das Template oder die Ressource speichern.
+ if (m_AnimationResourcePtr)
+ {
+ unsigned int Marker = 0;
+ Writer.Write(Marker);
+ Writer.Write(m_AnimationResourcePtr->GetFileName());
+ }
+ else if (m_AnimationTemplateHandle)
+ {
+ unsigned int Marker = 1;
+ Writer.Write(Marker);
+ Writer.Write(m_AnimationTemplateHandle);
+ }
+ else
+ {
+ BS_ASSERT(false);
+ }
+
+ //Writer.Write(m_AnimationDescriptionPtr);
+
+ Writer.Write(m_FramesLocked);
+ PersistCallbackVector(Writer, m_LoopPointCallbacks);
+ PersistCallbackVector(Writer, m_ActionCallbacks);
+ PersistCallbackVector(Writer, m_DeleteCallbacks);
+
+ Result &= BS_RenderObject::PersistChildren(Writer);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Animation::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Unpersist(Reader);
+
+ Reader.Read(m_RelX);
+ Reader.Read(m_RelY);
+ Reader.Read(m_ScaleFactorX);
+ Reader.Read(m_ScaleFactorY);
+ Reader.Read(m_ModulationColor);
+ Reader.Read(m_CurrentFrame);
+ Reader.Read(m_CurrentFrameTime);
+ Reader.Read(m_Running);
+ Reader.Read(m_Finished);
+ unsigned int Direction;
+ Reader.Read(Direction);
+ m_Direction = static_cast<DIRECTION>(Direction);
+
+ // Animationstyp einlesen.
+ unsigned int Marker;
+ Reader.Read(Marker);
+ if (Marker == 0)
+ {
+ std::string ResourceFilename;
+ Reader.Read(ResourceFilename);
+ InitializeAnimationResource(ResourceFilename);
+ }
+ else if (Marker == 1)
+ {
+ Reader.Read(m_AnimationTemplateHandle);
+ }
+ else
+ {
+ BS_ASSERT(false);
+ }
+
+ Reader.Read(m_FramesLocked);
+ if (m_FramesLocked) LockAllFrames();
+
+ UnpersistCallbackVector(Reader, m_LoopPointCallbacks);
+ UnpersistCallbackVector(Reader, m_ActionCallbacks);
+ UnpersistCallbackVector(Reader, m_DeleteCallbacks);
+
+ Result &= BS_RenderObject::UnpersistChildren(Reader);
+
+ return Reader.IsGood() && Result;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationDescription * BS_Animation::GetAnimationDescription() const
+{
+ if (m_AnimationResourcePtr) return m_AnimationResourcePtr;
+ else return BS_AnimationTemplateRegistry::GetInstance().ResolveHandle(m_AnimationTemplateHandle);
+}
diff --git a/engines/sword25/gfx/animation.h b/engines/sword25/gfx/animation.h
new file mode 100755
index 0000000000..37380f27a9
--- /dev/null
+++ b/engines/sword25/gfx/animation.h
@@ -0,0 +1,209 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ANIMATION_H
+#define BS_ANIMATION_H
+
+// Includes
+#include "kernel/common.h"
+#include "timedrenderobject.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// Forward declarations
+class BS_Kernel;
+class BS_PackageManager;
+class BS_AnimationResource;
+class BS_AnimationTemplate;
+class BS_AnimationDescription;
+class BS_InputPersistenceBlock;
+
+class BS_Animation : public BS_TimedRenderObject
+{
+friend BS_RenderObject;
+
+private:
+ BS_Animation(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const std::string & FileName);
+ BS_Animation(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const BS_AnimationTemplate & Template);
+ BS_Animation(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle);
+
+public:
+ enum ANIMATION_TYPES
+ {
+ AT_ONESHOT,
+ AT_LOOP,
+ AT_JOJO,
+ };
+
+ virtual ~BS_Animation();
+
+ void Play();
+ void Pause();
+ void Stop();
+ void SetFrame(unsigned int Nr);
+
+ virtual void SetPos(int X, int Y);
+ virtual void SetX(int X);
+ virtual void SetY(int Y);
+
+ virtual int GetX() const;
+ virtual int GetY() const;
+ virtual int GetAbsoluteX() const;
+ virtual int GetAbsoluteY() const;
+
+ /**
+ @brief Setzt den Alphawert der Animation.
+ @param Alpha der neue Alphawert der Animation (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void SetAlpha(int Alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Animation.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe der Animation festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void SetModulationColor(unsigned int ModulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation.
+ @param ScaleFactor der Faktor um den die Animation in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactor(float ScaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der X-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactorX(float ScaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der Y-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactorY(float ScaleFactorY);
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float GetScaleFactorX() const { return m_ScaleFactorX; }
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float GetScaleFactorY() const { return m_ScaleFactorY; }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+ virtual void FrameNotification(int TimeElapsed);
+
+ ANIMATION_TYPES GetAnimationType() const;
+ int GetFPS() const;
+ int GetFrameCount() const;
+ bool IsScalingAllowed() const;
+ bool IsAlphaAllowed() const;
+ bool IsColorModulationAllowed() const;
+ unsigned int GetCurrentFrame() const { return m_CurrentFrame; }
+ const std::string & GetCurrentAction() const ;
+ bool IsRunning() const { return m_Running; }
+
+ typedef bool (*ANIMATION_CALLBACK)(unsigned int);
+
+ void RegisterLoopPointCallback(ANIMATION_CALLBACK Callback, unsigned int Data = 0);
+ void RegisterActionCallback(ANIMATION_CALLBACK Callback, unsigned int Data = 0);
+ void RegisterDeleteCallback(ANIMATION_CALLBACK Callback, unsigned int Data = 0);
+
+protected:
+ virtual bool DoRender();
+
+private:
+ enum DIRECTION
+ {
+ FORWARD,
+ BACKWARD
+ };
+
+ int m_RelX;
+ int m_RelY;
+ float m_ScaleFactorX;
+ float m_ScaleFactorY;
+ unsigned int m_ModulationColor;
+ unsigned int m_CurrentFrame;
+ int m_CurrentFrameTime;
+ bool m_Running;
+ bool m_Finished;
+ DIRECTION m_Direction;
+ BS_AnimationResource * m_AnimationResourcePtr;
+ unsigned int m_AnimationTemplateHandle;
+ bool m_FramesLocked;
+
+ struct ANIMATION_CALLBACK_DATA
+ {
+ ANIMATION_CALLBACK Callback;
+ unsigned int Data;
+ };
+ std::vector<ANIMATION_CALLBACK_DATA> m_LoopPointCallbacks;
+ std::vector<ANIMATION_CALLBACK_DATA> m_ActionCallbacks;
+ std::vector<ANIMATION_CALLBACK_DATA> m_DeleteCallbacks;
+
+ /**
+ @brief Lockt alle Frames.
+ @return Gibt false zurück, falls nicht alle Frames gelockt werden konnten.
+ */
+ bool LockAllFrames();
+
+ /**
+ @brief Unlockt alle Frames.
+ @return Gibt false zurück, falls nicht alles Frames freigegeben werden konnten.
+ */
+ bool UnlockAllFrames();
+
+ /**
+ @brief Diese Methode aktualisiert die Parameter (Größe, Position) der Animation anhand des aktuellen Frames.
+
+ Diese Methode muss bei jedem Framewechsel aufgerufen werden damit der RenderObject-Manager immer aktuelle Daten hat.
+ */
+ void ComputeCurrentCharacteristics();
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int ComputeXModifier() const;
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int ComputeYModifier() const;
+
+ void InitMembers();
+ void PersistCallbackVector(BS_OutputPersistenceBlock & Writer, const std::vector<ANIMATION_CALLBACK_DATA> & Vector);
+ void UnpersistCallbackVector(BS_InputPersistenceBlock & Reader, std::vector<ANIMATION_CALLBACK_DATA> & Vector);
+ BS_AnimationDescription * GetAnimationDescription() const;
+ void InitializeAnimationResource(const std::string &FileName);
+};
+
+#endif
diff --git a/engines/sword25/gfx/animationdescription.cpp b/engines/sword25/gfx/animationdescription.cpp
new file mode 100755
index 0000000000..3bd5c05c0b
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.cpp
@@ -0,0 +1,58 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+#include "animationdescription.h"
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationDescription::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ Writer.Write(static_cast<unsigned int>(m_AnimationType));
+ Writer.Write(m_FPS);
+ Writer.Write(m_MillisPerFrame);
+ Writer.Write(m_ScalingAllowed);
+ Writer.Write(m_AlphaAllowed);
+ Writer.Write(m_ColorModulationAllowed);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationDescription::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ unsigned int AnimationType;
+ Reader.Read(AnimationType);
+ m_AnimationType = static_cast<BS_Animation::ANIMATION_TYPES>(AnimationType);
+ Reader.Read(m_FPS);
+ Reader.Read(m_MillisPerFrame);
+ Reader.Read(m_ScalingAllowed);
+ Reader.Read(m_AlphaAllowed);
+ Reader.Read(m_ColorModulationAllowed);
+
+ return Reader.IsGood();
+}
diff --git a/engines/sword25/gfx/animationdescription.h b/engines/sword25/gfx/animationdescription.h
new file mode 100755
index 0000000000..16bde6d101
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.h
@@ -0,0 +1,90 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ANIMATIONDESCRIPTION_H
+#define BS_ANIMATIONDESCRIPTION_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "animation.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_AnimationDescription : public BS_Persistable
+{
+protected:
+ BS_AnimationDescription() :
+ m_AnimationType(BS_Animation::AT_LOOP),
+ m_FPS(10),
+ m_MillisPerFrame(0),
+ m_ScalingAllowed(true),
+ m_AlphaAllowed(true),
+ m_ColorModulationAllowed(true)
+ {};
+
+public:
+ struct Frame
+ {
+ // Die Hotspot-Angabe bezieht sich auf das ungeflippte Bild!!
+ int HotspotX;
+ int HotspotY;
+ bool FlipV;
+ bool FlipH;
+ std::string FileName;
+ std::string Action;
+ };
+
+ // -----------------------------------------------------------------------------
+ // Abstrakte Methoden
+ // -----------------------------------------------------------------------------
+
+ virtual const Frame & GetFrame(unsigned int Index) const = 0;
+ virtual unsigned int GetFrameCount() const = 0;
+ virtual void Unlock() = 0;
+
+ // -----------------------------------------------------------------------------
+ // Getter Methoden
+ // -----------------------------------------------------------------------------
+
+ BS_Animation::ANIMATION_TYPES GetAnimationType() const { return m_AnimationType; }
+ int GetFPS() const { return m_FPS; }
+ int GetMillisPerFrame() const { return m_MillisPerFrame; }
+ bool IsScalingAllowed() const { return m_ScalingAllowed; }
+ bool IsAlphaAllowed() const { return m_AlphaAllowed; }
+ bool IsColorModulationAllowed() const { return m_ColorModulationAllowed; }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ BS_Animation::ANIMATION_TYPES m_AnimationType;
+ int m_FPS;
+ int m_MillisPerFrame;
+ bool m_ScalingAllowed;
+ bool m_AlphaAllowed;
+ bool m_ColorModulationAllowed;
+};
+
+#endif
diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp
new file mode 100755
index 0000000000..f1f88eaf2f
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -0,0 +1,323 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "animationresource.h"
+
+#include "kernel/kernel.h"
+#include "kernel/string.h"
+#include "package/packagemanager.h"
+#include "util/tinyxml/tinyxml.h"
+#include "bitmapresource.h"
+
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "ANIMATIONRESOURCE"
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const int DEFAULT_FPS = 10;
+ const int MIN_FPS = 1;
+ const int MAX_FPS = 200;
+}
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+BS_AnimationResource::BS_AnimationResource(const std::string& FileName) :
+ BS_Resource(FileName, BS_Resource::TYPE_ANIMATION),
+ m_Valid(false)
+{
+ // Pointer auf den Package-Manager bekommen
+ BS_PackageManager* PackagePtr = BS_Kernel::GetInstance()->GetPackage();
+ BS_ASSERT(PackagePtr);
+
+ // Animations-XML laden
+ TiXmlDocument Doc;
+ {
+ // Die Daten werden zunächst über den Package-Manager gelesen und dann in einen um ein Byte größeren Buffer kopiert und
+ // NULL-Terminiert, da TinyXML NULL-Terminierte Daten benötigt.
+ unsigned int FileSize;
+ char * LoadBuffer = (char *) PackagePtr->GetFile(GetFileName(), &FileSize);
+ if (!LoadBuffer)
+ {
+ BS_LOG_ERRORLN("Could not read \"%s\".", GetFileName().c_str());
+ return;
+ }
+ std::vector<char> WorkBuffer(FileSize + 1);
+ memcpy(&WorkBuffer[0], LoadBuffer, FileSize);
+ delete LoadBuffer;
+ WorkBuffer[FileSize] = '\0';
+
+ // Datei parsen
+ Doc.Parse(&WorkBuffer[0]);
+ if (Doc.Error())
+ {
+ BS_LOG_ERRORLN("The following TinyXML-Error occured while parsing \"%s\": %s", GetFileName().c_str(), Doc.ErrorDesc());
+ return;
+ }
+ }
+
+ // Wurzelknoten des Animations-Tags finden, prüfen und Attribute auslesen.
+ TiXmlElement* pElement;
+ {
+ TiXmlNode* pNode = Doc.FirstChild("animation");
+ if (!pNode || pNode->Type() != TiXmlNode::ELEMENT)
+ {
+ BS_LOG_ERRORLN("No <animation> tag found in \"%s\".", GetFileName().c_str());
+ return;
+ }
+ pElement = pNode->ToElement();
+
+ // Animation-Tag parsen
+ if (!ParseAnimationTag(*pElement, m_FPS, m_AnimationType))
+ {
+ BS_LOG_ERRORLN("An error occurred while parsing <animation> tag in \"%s\".", GetFileName().c_str());
+ return;
+ }
+ }
+
+ // Zeit (in Millisekunden) bestimmen für die ein einzelner Frame angezeigt wird
+ m_MillisPerFrame = 1000000 / m_FPS;
+
+ // In das Verzeichnis der Eingabedatei wechseln, da die Dateiverweise innerhalb der XML-Datei relativ zu diesem Verzeichnis sind.
+ std::string OldDirectory = PackagePtr->GetCurrentDirectory();
+ int LastSlash = GetFileName().rfind('/');
+ if (LastSlash != std::string::npos)
+ {
+ std::string Dir = GetFileName().substr(0, LastSlash);
+ PackagePtr->ChangeDirectory(Dir);
+ }
+
+ // Nacheinander alle Frames-Informationen erstellen.
+ TiXmlElement* pFrameElement = pElement->FirstChild("frame")->ToElement();
+ while (pFrameElement)
+ {
+ Frame CurFrame;
+
+ if (!ParseFrameTag(*pFrameElement, CurFrame, *PackagePtr))
+ {
+ BS_LOG_ERRORLN("An error occurred in \"%s\" while parsing <frame> tag.", GetFileName().c_str());
+ return;
+ }
+
+ m_Frames.push_back(CurFrame);
+ pFrameElement = pFrameElement->NextSiblingElement("frame");
+ }
+
+ // Ursprungsverzeichnis wieder herstellen
+ PackagePtr->ChangeDirectory(OldDirectory);
+
+ // Sicherstellen, dass die Animation mindestens einen Frame besitzt
+ if (m_Frames.empty())
+ {
+ BS_LOG_ERRORLN("\"%s\" does not have any frames.", GetFileName().c_str());
+ return;
+ }
+
+ // Alle Frame-Dateien werden vorgecached
+ if (!PrecacheAllFrames())
+ {
+ BS_LOG_ERRORLN("Could not precache all frames of \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ // Feststellen, ob die Animation skalierbar ist
+ if (!ComputeFeatures())
+ {
+ BS_LOG_ERRORLN("Could not determine the features of \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ m_Valid = true;
+}
+
+// -----------------------------------------------------------------------------
+// Dokument-Parsermethoden
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationResource::ParseAnimationTag(TiXmlElement& AnimationTag, int& FPS, BS_Animation::ANIMATION_TYPES & AnimationType)
+{
+ // FPS einlesen
+ const char* FPSString;
+ if (FPSString = AnimationTag.Attribute("fps"))
+ {
+ int TempFPS;
+ if (!BS_String::ToInt(std::string(FPSString), TempFPS) || TempFPS < MIN_FPS || TempFPS > MAX_FPS)
+ {
+ BS_LOG_WARNINGLN("Illegal fps value (\"%s\") in <animation> tag in \"%s\". Assuming default (\"%d\"). "
+ "The fps value has to be between %d and %d.",
+ FPSString, GetFileName().c_str(), DEFAULT_FPS, MIN_FPS, MAX_FPS);
+ }
+ else
+ FPS = TempFPS;
+ }
+
+ // Loop-Typ einlesen
+ const char* LoopTypeString;
+ if (LoopTypeString = AnimationTag.Attribute("type"))
+ {
+ if (strcmp(LoopTypeString, "oneshot") == 0)
+ AnimationType = BS_Animation::AT_ONESHOT;
+ else if (strcmp(LoopTypeString, "loop") == 0)
+ AnimationType = BS_Animation::AT_LOOP;
+ else if (strcmp(LoopTypeString, "jojo") == 0)
+ AnimationType = BS_Animation::AT_JOJO;
+ else
+ BS_LOG_WARNINGLN("Illegal type value (\"%s\") in <animation> tag in \"%s\". Assuming default (\"loop\").",
+ LoopTypeString, GetFileName().c_str());
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationResource::ParseFrameTag(TiXmlElement& FrameTag, Frame& Frame, BS_PackageManager& PackageManager)
+{
+ const char* FileString = FrameTag.Attribute("file");
+ if (!FileString)
+ {
+ BS_LOG_ERRORLN("<frame> tag without file attribute occurred in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+ Frame.FileName = PackageManager.GetAbsolutePath(FileString);
+ if (Frame.FileName == "")
+ {
+ BS_LOG_ERRORLN("Could not create absolute path for file specified in <frame> tag in \"%s\": \"%s\".", GetFileName().c_str(), FileString);
+ return false;
+ }
+
+ const char* ActionString = FrameTag.Attribute("action");
+ if (ActionString)
+ Frame.Action = ActionString;
+
+ const char* HotspotxString = FrameTag.Attribute("hotspotx");
+ const char* HotspotyString = FrameTag.Attribute("hotspoty");
+ if ((!HotspotxString && HotspotyString) ||
+ (HotspotxString && !HotspotyString))
+ BS_LOG_WARNINGLN("%s attribute occurred without %s attribute in <frame> tag in \"%s\". Assuming default (\"0\").",
+ HotspotxString ? "hotspotx" : "hotspoty",
+ !HotspotyString ? "hotspoty" : "hotspotx",
+ GetFileName().c_str());
+
+ Frame.HotspotX = 0;
+ if (HotspotxString && !BS_String::ToInt(std::string(HotspotxString), Frame.HotspotX))
+ BS_LOG_WARNINGLN("Illegal hotspotx value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ HotspotxString,GetFileName().c_str(), Frame.HotspotX);
+
+ Frame.HotspotY = 0;
+ if (HotspotyString && !BS_String::ToInt(std::string(HotspotyString), Frame.HotspotY))
+ BS_LOG_WARNINGLN("Illegal hotspoty value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ HotspotyString, GetFileName().c_str(), Frame.HotspotY);
+
+ const char* FlipVString = FrameTag.Attribute("flipv");
+ if (FlipVString)
+ {
+ if (!BS_String::ToBool(std::string(FlipVString), Frame.FlipV))
+ {
+ BS_LOG_WARNINGLN("Illegal flipv value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ FlipVString, GetFileName().c_str());
+ Frame.FlipV = false;
+ }
+ }
+ else
+ Frame.FlipV = false;
+
+ const char* FlipHString = FrameTag.Attribute("fliph");
+ if (FlipHString)
+ {
+ if (!BS_String::ToBool(FlipHString, Frame.FlipH))
+ {
+ BS_LOG_WARNINGLN("Illegal fliph value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ FlipHString, GetFileName().c_str());
+ Frame.FlipH = false;
+ }
+ }
+ else
+ Frame.FlipH = false;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationResource::~BS_AnimationResource()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationResource::PrecacheAllFrames() const
+{
+ std::vector<Frame>::const_iterator Iter = m_Frames.begin();
+ for (; Iter != m_Frames.end(); ++Iter)
+ {
+ if (!BS_Kernel::GetInstance()->GetResourceManager()->PrecacheResource((*Iter).FileName))
+ {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", (*Iter).FileName.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationResource::ComputeFeatures()
+{
+ BS_ASSERT(m_Frames.size());
+
+ // Alle Features werden als vorhanden angenommen
+ m_ScalingAllowed = true;
+ m_AlphaAllowed = true;
+ m_ColorModulationAllowed = true;
+
+ // Alle Frame durchgehen und alle Features deaktivieren, die auch nur von einem Frame nicht unterstützt werden.
+ std::vector<Frame>::const_iterator Iter = m_Frames.begin();
+ for (; Iter != m_Frames.end(); ++Iter)
+ {
+ BS_BitmapResource* pBitmap;
+ if (!(pBitmap = static_cast<BS_BitmapResource*> (BS_Kernel::GetInstance()->GetResourceManager()->RequestResource((*Iter).FileName))))
+ {
+ BS_LOG_ERRORLN("Could not request \"%s\".", (*Iter).FileName.c_str());
+ return false;
+ }
+
+ if (!pBitmap->IsScalingAllowed())
+ m_ScalingAllowed = false;
+ if (!pBitmap->IsAlphaAllowed())
+ m_AlphaAllowed = false;
+ if (!pBitmap->IsColorModulationAllowed())
+ m_ColorModulationAllowed = false;
+
+ pBitmap->Release();
+ }
+
+ return true;
+}
diff --git a/engines/sword25/gfx/animationresource.h b/engines/sword25/gfx/animationresource.h
new file mode 100755
index 0000000000..ebb374f87a
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.h
@@ -0,0 +1,83 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ANIMATIONRESOURCE_H
+#define BS_ANIMATIONRESOURCE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/resource.h"
+#include "animationdescription.h"
+#include "animation.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_PackageManager;
+class TiXmlElement;
+
+// -----------------------------------------------------------------------------
+// Class Definition
+// -----------------------------------------------------------------------------
+
+class BS_AnimationResource : public BS_Resource, public BS_AnimationDescription
+{
+public:
+ BS_AnimationResource(const std::string & FileName);
+ virtual ~BS_AnimationResource();
+
+ virtual const Frame & GetFrame(unsigned int Index) const { BS_ASSERT(Index < m_Frames.size()); return m_Frames[Index]; }
+ virtual unsigned int GetFrameCount() const { return m_Frames.size(); }
+ virtual void Unlock() { Release(); }
+
+ BS_Animation::ANIMATION_TYPES GetAnimationType() const { return m_AnimationType; }
+ int GetFPS() const { return m_FPS; }
+ int GetMillisPerFrame() const { return m_MillisPerFrame; }
+ bool IsScalingAllowed() const { return m_ScalingAllowed; }
+ bool IsAlphaAllowed() const { return m_AlphaAllowed; }
+ bool IsColorModulationAllowed() const { return m_ColorModulationAllowed; }
+ bool IsValid() const { return m_Valid; }
+
+private:
+ bool m_Valid;
+
+ std::vector<Frame> m_Frames;
+
+ //@{
+ /** @name Dokument-Parser Methoden */
+
+ bool ParseAnimationTag(TiXmlElement& AnimationTag, int& FPS, BS_Animation::ANIMATION_TYPES & AnimationType);
+ bool ParseFrameTag(TiXmlElement& FrameTag, Frame& Frame, BS_PackageManager& PackageManager);
+
+ //@}
+
+ bool ComputeFeatures();
+ bool PrecacheAllFrames() const;
+};
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplate.cpp b/engines/sword25/gfx/animationtemplate.cpp
new file mode 100755
index 0000000000..72b1e9282b
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.cpp
@@ -0,0 +1,293 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "kernel/resource.h"
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include "animationresource.h"
+#include "animationtemplate.h"
+#include "animationtemplateregistry.h"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+unsigned int BS_AnimationTemplate::Create(const std::string & SourceAnimation)
+{
+ BS_AnimationTemplate * AnimationTemplatePtr = new BS_AnimationTemplate(SourceAnimation);
+
+ if (AnimationTemplatePtr->IsValid())
+ {
+ return BS_AnimationTemplateRegistry::GetInstance().ResolvePtr(AnimationTemplatePtr);
+ }
+ else
+ {
+ delete AnimationTemplatePtr;
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_AnimationTemplate::Create(const BS_AnimationTemplate & Other)
+{
+ BS_AnimationTemplate * AnimationTemplatePtr = new BS_AnimationTemplate(Other);
+
+ if (AnimationTemplatePtr->IsValid())
+ {
+ return BS_AnimationTemplateRegistry::GetInstance().ResolvePtr(AnimationTemplatePtr);
+ }
+ else
+ {
+ delete AnimationTemplatePtr;
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_AnimationTemplate::Create(BS_InputPersistenceBlock & Reader, unsigned int Handle)
+{
+ BS_AnimationTemplate * AnimationTemplatePtr = new BS_AnimationTemplate(Reader, Handle);
+
+ if (AnimationTemplatePtr->IsValid())
+ {
+ return BS_AnimationTemplateRegistry::GetInstance().ResolvePtr(AnimationTemplatePtr);
+ }
+ else
+ {
+ delete AnimationTemplatePtr;
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationTemplate::BS_AnimationTemplate(const std::string & SourceAnimation)
+{
+ // Objekt registrieren.
+ BS_AnimationTemplateRegistry::GetInstance().RegisterObject(this);
+
+ m_Valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ m_SourceAnimationPtr = RequestSourceAnimation(SourceAnimation);
+
+ // Erfolg signalisieren
+ m_Valid = (m_SourceAnimationPtr != 0);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationTemplate::BS_AnimationTemplate(const BS_AnimationTemplate & Other)
+{
+ // Objekt registrieren.
+ BS_AnimationTemplateRegistry::GetInstance().RegisterObject(this);
+
+ m_Valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt.
+ if (!Other.m_SourceAnimationPtr) return;
+ m_SourceAnimationPtr = RequestSourceAnimation(Other.m_SourceAnimationPtr->GetFileName());
+
+ // Alle Member kopieren.
+ m_AnimationType = Other.m_AnimationType;
+ m_FPS = Other.m_FPS;
+ m_MillisPerFrame = Other.m_MillisPerFrame;
+ m_ScalingAllowed = Other.m_ScalingAllowed;
+ m_AlphaAllowed = Other.m_AlphaAllowed;
+ m_ColorModulationAllowed = Other.m_ColorModulationAllowed;
+ m_Frames = Other.m_Frames;
+ m_SourceAnimationPtr = Other.m_SourceAnimationPtr;
+ m_Valid = Other.m_Valid;
+
+ m_Valid &= (m_SourceAnimationPtr != 0);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationTemplate::BS_AnimationTemplate(BS_InputPersistenceBlock & Reader, unsigned int Handle)
+{
+ // Objekt registrieren.
+ BS_AnimationTemplateRegistry::GetInstance().RegisterObject(this, Handle);
+
+ // Objekt laden.
+ m_Valid = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationResource * BS_AnimationTemplate::RequestSourceAnimation(const std::string & SourceAnimation) const
+{
+ BS_ResourceManager * RMPtr = BS_Kernel::GetInstance()->GetResourceManager();
+ BS_Resource * ResourcePtr;
+ if (NULL == (ResourcePtr = RMPtr->RequestResource(SourceAnimation)) || ResourcePtr->GetType() != BS_Resource::TYPE_ANIMATION)
+ {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested or is has an invalid type. The animation template can't be created.", SourceAnimation.c_str());
+ return 0;
+ }
+ return static_cast<BS_AnimationResource *>(ResourcePtr);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_AnimationTemplate::~BS_AnimationTemplate()
+{
+ // Animations-Resource freigeben
+ if (m_SourceAnimationPtr)
+ {
+ m_SourceAnimationPtr->Release();
+ }
+
+ // Objekt deregistrieren
+ BS_AnimationTemplateRegistry::GetInstance().DeregisterObject(this);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_AnimationTemplate::AddFrame(int Index)
+{
+ if (ValidateSourceIndex(Index))
+ {
+ m_Frames.push_back(m_SourceAnimationPtr->GetFrame(Index));
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_AnimationTemplate::SetFrame(int DestIndex, int SrcIndex)
+{
+ if (ValidateDestIndex(DestIndex) && ValidateSourceIndex(SrcIndex))
+ {
+ m_Frames[DestIndex] = m_SourceAnimationPtr->GetFrame(SrcIndex);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplate::ValidateSourceIndex(unsigned int Index) const
+{
+ if (Index > m_SourceAnimationPtr->GetFrameCount())
+ {
+ BS_LOG_WARNINGLN("Tried to insert a frame (\"%d\") that does not exist in the source animation (\"%s\"). Ignoring call.",
+ Index, m_SourceAnimationPtr->GetFileName().c_str());
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplate::ValidateDestIndex(unsigned int Index) const
+{
+ if (Index > m_Frames.size())
+ {
+ BS_LOG_WARNINGLN("Tried to change a nonexistent frame (\"%d\") in a template animation. Ignoring call.",
+ Index);
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_AnimationTemplate::SetFPS(int FPS)
+{
+ m_FPS = FPS;
+ m_MillisPerFrame = 1000000 / m_FPS;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplate::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Parent persistieren.
+ Result &= BS_AnimationDescription::Persist(Writer);
+
+ // Frameanzahl schreiben.
+ Writer.Write(m_Frames.size());
+
+ // Frames einzeln persistieren.
+ std::vector<const Frame>::const_iterator Iter = m_Frames.begin();
+ while (Iter != m_Frames.end())
+ {
+ Writer.Write(Iter->HotspotX);
+ Writer.Write(Iter->HotspotY);
+ Writer.Write(Iter->FlipV);
+ Writer.Write(Iter->FlipH);
+ Writer.Write(Iter->FileName);
+ Writer.Write(Iter->Action);
+ ++Iter;
+ }
+
+ // Restliche Member persistieren.
+ Writer.Write(m_SourceAnimationPtr->GetFileName());
+ Writer.Write(m_Valid);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplate::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Parent wieder herstellen.
+ Result &= BS_AnimationDescription::Unpersist(Reader);
+
+ // Frameanzahl lesen.
+ unsigned int FrameCount;
+ Reader.Read(FrameCount);
+
+ // Frames einzeln wieder herstellen.
+ for (unsigned int i = 0; i < FrameCount; ++i)
+ {
+ Frame frame;
+ Reader.Read(frame.HotspotX);
+ Reader.Read(frame.HotspotY);
+ Reader.Read(frame.FlipV);
+ Reader.Read(frame.FlipH);
+ Reader.Read(frame.FileName);
+ Reader.Read(frame.Action);
+
+ m_Frames.push_back(frame);
+ }
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ std::string SourceAnimation;
+ Reader.Read(SourceAnimation);
+ m_SourceAnimationPtr = RequestSourceAnimation(SourceAnimation);
+
+ Reader.Read(m_Valid);
+
+ return m_SourceAnimationPtr && Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/animationtemplate.h b/engines/sword25/gfx/animationtemplate.h
new file mode 100755
index 0000000000..df63735b2e
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.h
@@ -0,0 +1,108 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ANIMATION_TEMPLATE_H
+#define BS_ANIMATION_TEMPLATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "animationdescription.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_AnimationResource;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_AnimationTemplate : public BS_AnimationDescription
+{
+public:
+ static unsigned int Create(const std::string & SourceAnimation);
+ static unsigned int Create(const BS_AnimationTemplate & Other);
+ static unsigned int Create(BS_InputPersistenceBlock & Reader, unsigned int Handle);
+ BS_AnimationTemplate * ResolveHandle(unsigned int Handle) const;
+
+private:
+ BS_AnimationTemplate(const std::string & SourceAnimation);
+ BS_AnimationTemplate(const BS_AnimationTemplate & Other);
+ BS_AnimationTemplate(BS_InputPersistenceBlock & Reader, unsigned int Handle);
+
+public:
+ ~BS_AnimationTemplate();
+
+ virtual const Frame & GetFrame(unsigned int Index) const { BS_ASSERT(Index < m_Frames.size()); return m_Frames[Index]; }
+ virtual unsigned int GetFrameCount() const { return m_Frames.size(); }
+ virtual void Unlock() { delete this; }
+
+ bool IsValid() const { return m_Valid; }
+
+ /**
+ @brief Fügt einen neuen Frame zur Animation hinzu.
+
+ Der Frame wird an das Ende der Animation angehängt.
+
+ @param Index der Index des Frames in der Quellanimation
+ */
+ void AddFrame(int Index);
+
+ /**
+ @brief Ändert einen bereits in der Animation vorhandenen Frame.
+ @param DestIndex der Index des Frames der überschrieben werden soll
+ @param SrcIndex der Index des einzufügenden Frames in der Quellanimation
+ */
+ void SetFrame(int DestIndex, int SrcIndex);
+
+ /**
+ @brief Setzt den Animationstyp.
+ @param Type der Typ der Animation. Muss aus den enum BS_Animation::ANIMATION_TYPES sein.
+ */
+ void SetAnimationType(BS_Animation::ANIMATION_TYPES Type) { m_AnimationType = Type; }
+
+ /**
+ @brief Setzt die Abspielgeschwindigkeit.
+ @param FPS die Abspielgeschwindigkeit in Frames pro Sekunde.
+ */
+ void SetFPS(int FPS);
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ std::vector<const Frame> m_Frames;
+ BS_AnimationResource * m_SourceAnimationPtr;
+ bool m_Valid;
+
+ BS_AnimationResource * RequestSourceAnimation(const std::string & SourceAnimation) const;
+ bool ValidateSourceIndex(unsigned int Index) const;
+ bool ValidateDestIndex(unsigned int Index) const;
+};
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplateregistry.cpp b/engines/sword25/gfx/animationtemplateregistry.cpp
new file mode 100755
index 0000000000..aabea41417
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.cpp
@@ -0,0 +1,111 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATEREGISTRY"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+#include "animationtemplateregistry.h"
+#include "animationtemplate.h"
+
+// -----------------------------------------------------------------------------
+// Implementation
+// -----------------------------------------------------------------------------
+
+std::auto_ptr<BS_AnimationTemplateRegistry> BS_AnimationTemplateRegistry::m_InstancePtr;
+
+// -----------------------------------------------------------------------------
+
+void BS_AnimationTemplateRegistry::LogErrorLn(const char * Message) const
+{
+ BS_LOG_ERRORLN(Message);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_AnimationTemplateRegistry::LogWarningLn(const char * Message) const
+{
+ BS_LOG_WARNINGLN(Message);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplateRegistry::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Das nächste zu vergebene Handle schreiben.
+ Writer.Write(m_NextHandle);
+
+ // Anzahl an BS_AnimationTemplates schreiben.
+ Writer.Write(m_Handle2PtrMap.size());
+
+ // Alle BS_AnimationTemplates persistieren.
+ HANDLE2PTR_MAP::const_iterator Iter = m_Handle2PtrMap.begin();
+ while (Iter != m_Handle2PtrMap.end())
+ {
+ // Handle persistieren.
+ Writer.Write(Iter->first);
+
+ // Objekt persistieren.
+ Result &= Iter->second->Persist(Writer);
+
+ ++Iter;
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_AnimationTemplateRegistry::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Das nächste zu vergebene Handle wieder herstellen.
+ Reader.Read(m_NextHandle);
+
+ // Alle vorhandenen BS_AnimationTemplates zerstören.
+ while (!m_Handle2PtrMap.empty()) delete m_Handle2PtrMap.begin()->second;
+
+ // Anzahl an BS_AnimationTemplates einlesen.
+ unsigned int AnimationTemplateCount;
+ Reader.Read(AnimationTemplateCount);
+
+ // Alle gespeicherten BS_AnimationTemplates wieder herstellen.
+ for (unsigned int i = 0; i < AnimationTemplateCount; ++i)
+ {
+ // Handle lesen.
+ unsigned int Handle;
+ Reader.Read(Handle);
+
+ // BS_AnimationTemplate wieder herstellen.
+ Result &= (BS_AnimationTemplate::Create(Reader, Handle) != 0);
+ }
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/animationtemplateregistry.h b/engines/sword25/gfx/animationtemplateregistry.h
new file mode 100755
index 0000000000..78c83fd2bb
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.h
@@ -0,0 +1,64 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ANIMATIONTEMPLATEREGISTRY_H
+#define BS_ANIMATIONTEMPLATEREGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "kernel/objectregistry.h"
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+class BS_AnimationTemplate;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_AnimationTemplateRegistry : public BS_ObjectRegistry<BS_AnimationTemplate>, public BS_Persistable
+{
+public:
+ static BS_AnimationTemplateRegistry & GetInstance()
+ {
+ if (!m_InstancePtr.get()) m_InstancePtr.reset(new BS_AnimationTemplateRegistry);
+ return *m_InstancePtr.get();
+ }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ virtual void LogErrorLn(const char * Message) const;
+ virtual void LogWarningLn(const char * Message) const;
+
+ static std::auto_ptr<BS_AnimationTemplateRegistry> m_InstancePtr;
+};
+
+#endif
diff --git a/engines/sword25/gfx/bitmap.cpp b/engines/sword25/gfx/bitmap.cpp
new file mode 100755
index 0000000000..85442a835a
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.cpp
@@ -0,0 +1,214 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "bitmap.h"
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "BITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_Bitmap::BS_Bitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, TYPES Type, unsigned int Handle) :
+ BS_RenderObject(ParentPtr, Type, Handle),
+ m_ModulationColor(0xffffffff),
+ m_ScaleFactorX(1.0f),
+ m_ScaleFactorY(1.0f),
+ m_FlipH(false),
+ m_FlipV(false)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Bitmap::~BS_Bitmap()
+{
+}
+
+// -----------------------------------------------------------------------------
+// Darstellungsart festlegen
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetAlpha(int Alpha)
+{
+ if (!IsAlphaAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set alpha value on a bitmap that does not support alpha blending. Call was ignored.");
+ return;
+ }
+
+ if (Alpha < 0 || Alpha > 255)
+ {
+ int OldAlpha = Alpha;
+ if (Alpha < 0) Alpha = 0;
+ if (Alpha > 255) Alpha = 255;
+ BS_LOG_WARNINGLN("Tried to set an invalid alpha value (%d) on a bitmap. Value was changed to %d.", OldAlpha, Alpha);
+
+ return;
+ }
+
+ unsigned int NewModulationColor = (m_ModulationColor & 0x00ffffff) | Alpha << 24;
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetModulationColor(unsigned int ModulationColor)
+{
+ if (!IsColorModulationAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set modulation color of a bitmap that does not support color modulation. Call was ignored.");
+ return;
+ }
+
+ unsigned int NewModulationColor = (ModulationColor & 0x00ffffff) | (m_ModulationColor & 0xff000000);
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetScaleFactor(float ScaleFactor)
+{
+ SetScaleFactorX(ScaleFactor);
+ SetScaleFactorY(ScaleFactor);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetScaleFactorX(float ScaleFactorX)
+{
+ if (!IsScalingAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (ScaleFactorX < 0)
+ {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (ScaleFactorX != m_ScaleFactorX)
+ {
+ m_ScaleFactorX = ScaleFactorX;
+ m_Width = static_cast<int>(m_OriginalWidth * m_ScaleFactorX);
+ if (m_ScaleFactorX <= 0.0f) m_ScaleFactorX = 0.001f;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetScaleFactorY(float ScaleFactorY)
+{
+ if (!IsScalingAllowed())
+ {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (ScaleFactorY < 0)
+ {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (ScaleFactorY != m_ScaleFactorY)
+ {
+ m_ScaleFactorY = ScaleFactorY;
+ m_Height = static_cast<int>(m_OriginalHeight * ScaleFactorY);
+ if (m_ScaleFactorY <= 0.0f) m_ScaleFactorY = 0.001f;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetFlipH(bool FlipH)
+{
+ m_FlipH = FlipH;
+ ForceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Bitmap::SetFlipV(bool FlipV)
+{
+ m_FlipV = FlipV;
+ ForceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_Bitmap::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Persist(Writer);
+ Writer.Write(m_FlipH);
+ Writer.Write(m_FlipV);
+ Writer.Write(m_ScaleFactorX);
+ Writer.Write(m_ScaleFactorY);
+ Writer.Write(m_ModulationColor);
+ Writer.Write(m_OriginalWidth);
+ Writer.Write(m_OriginalHeight);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Bitmap::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Unpersist(Reader);
+ Reader.Read(m_FlipH);
+ Reader.Read(m_FlipV);
+ Reader.Read(m_ScaleFactorX);
+ Reader.Read(m_ScaleFactorY);
+ Reader.Read(m_ModulationColor);
+ Reader.Read(m_OriginalWidth);
+ Reader.Read(m_OriginalHeight);
+
+ ForceRefresh();
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/bitmap.h b/engines/sword25/gfx/bitmap.h
new file mode 100755
index 0000000000..053dc8b9a1
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.h
@@ -0,0 +1,167 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_BITMAP_H
+#define BS_BITMAP_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "renderobject.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_Bitmap : public BS_RenderObject
+{
+protected:
+ BS_Bitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, TYPES Type, unsigned int Handle = 0);
+
+public:
+
+ virtual ~BS_Bitmap();
+
+ /**
+ @brief Setzt den Alphawert des Bitmaps.
+ @param Alpha der neue Alphawert der Bitmaps (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void SetAlpha(int Alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Bitmaps.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe des Bitmaps festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void SetModulationColor(unsigned int ModulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor des Bitmaps.
+ @param ScaleFactor der Faktor um den das Bitmap in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactor(float ScaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der X-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactorX(float ScaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der Y-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void SetScaleFactorY(float ScaleFactorY);
+
+ /**
+ @brief Legt fest, ob das Bild an der X-Achse gespiegelt werden soll.
+ */
+ void SetFlipH(bool FlipH);
+
+ /**
+ @brief Legt fest, ob das Bild an der Y-Achse gespiegelt werden soll.
+ */
+ void SetFlipV(bool FlipV);
+
+ /**
+ @brief Gibt den aktuellen Alphawert des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ int GetAlpha() { return m_ModulationColor >> 24; }
+
+ /**
+ @brief Gibt die aktuelle 24bit RGB Modulationsfarde des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ int GetModulationColor() { return m_ModulationColor & 0x00ffffff; }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float GetScaleFactorX() const { return m_ScaleFactorX; }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float GetScaleFactorY() const { return m_ScaleFactorY; }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der X-Achse gespiegelt angezeigt wird.
+ */
+ bool IsFlipH() { return m_FlipH; }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der Y-Achse gespiegelt angezeigt wird.
+ */
+ bool IsFlipV() { return m_FlipV; }
+
+ // -----------------------------------------------------------------------------
+ // Die folgenden Methoden müssen alle BS_Bitmap-Klassen implementieren
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual unsigned int GetPixel(int X, int Y) const = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset = 0, unsigned int Stride = 0) = 0;
+
+ virtual bool IsScalingAllowed() const = 0;
+ virtual bool IsAlphaAllowed() const = 0;
+ virtual bool IsColorModulationAllowed() const = 0;
+ virtual bool IsSetContentAllowed() const = 0;
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ bool m_FlipH;
+ bool m_FlipV;
+ float m_ScaleFactorX;
+ float m_ScaleFactorY;
+ unsigned int m_ModulationColor;
+ int m_OriginalWidth;
+ int m_OriginalHeight;
+};
+
+#endif
diff --git a/engines/sword25/gfx/bitmapresource.cpp b/engines/sword25/gfx/bitmapresource.cpp
new file mode 100755
index 0000000000..af1638333e
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.cpp
@@ -0,0 +1,54 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include <memory>
+
+#include "bitmapresource.h"
+#include "kernel/kernel.h"
+#include "graphicengine.h"
+#include "image/imageloader.h"
+#include "package/packagemanager.h"
+
+#define BS_LOG_PREFIX "BITMAP"
+
+// Konstruktion / Destruktion
+// --------------------------
+
+BS_BitmapResource::BS_BitmapResource(const std::string & Filename, BS_Image * pImage) :
+ m_Valid(false),
+ m_pImage(pImage),
+ BS_Resource(Filename, BS_Resource::TYPE_BITMAP)
+{
+ m_Valid = m_pImage != 0;
+}
+
+BS_BitmapResource::~BS_BitmapResource()
+{
+ delete m_pImage;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_BitmapResource::GetPixel(int X, int Y) const
+{
+ BS_ASSERT(X >= 0 && X < m_pImage->GetWidth());
+ BS_ASSERT(Y >= 0 && Y < m_pImage->GetHeight());
+
+ return m_pImage->GetPixel(X, Y);
+}
diff --git a/engines/sword25/gfx/bitmapresource.h b/engines/sword25/gfx/bitmapresource.h
new file mode 100755
index 0000000000..0b0ea6db99
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.h
@@ -0,0 +1,177 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_BITMAP_RESOURCE_H
+#define BS_BITMAP_RESOURCE_H
+
+// Includes
+#include "kernel/common.h"
+#include "kernel/resource.h"
+#include "image/image.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Rect;
+
+class BS_BitmapResource : public BS_Resource
+{
+public:
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS
+ {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ BS_BitmapResource(const std::string & Filename, BS_Image * pImage);
+ virtual ~BS_BitmapResource();
+
+ /**
+ @brief Gibt zurück, ob das Objekt einen gültigen Zustand hat.
+ */
+ bool IsValid() const { return m_Valid; }
+
+ /**
+ @brief Gibt die Breite des Bitmaps zurück.
+ */
+ int GetWidth() const { BS_ASSERT(m_pImage); return m_pImage->GetWidth(); }
+
+ /**
+ @brief Gibt die Höhe des Bitmaps zurück.
+ */
+ int GetHeight() const { BS_ASSERT(m_pImage); return m_pImage->GetHeight(); }
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein BS_Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ */
+ bool Blit(int PosX = 0, int PosY = 0,
+ int Flipping = FLIP_NONE,
+ BS_Rect* pSrcPartRect = NULL,
+ unsigned int Color = BS_ARGB(255, 255, 255, 255),
+ int Width = -1, int Height = -1)
+ {
+ BS_ASSERT(m_pImage);
+ return m_pImage->Blit(PosX, PosY, Flipping, pSrcPartRect, Color, Width, Height);
+ }
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein BS_Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Ein Aufruf dieser Methode ist nur gestattet, wenn IsFillingAllowed() true zurückgibt.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ bool Fill(const BS_Rect* pFillRect = 0, unsigned int Color = BS_RGB(0, 0, 0)) { BS_ASSERT(m_pImage); return m_pImage->Fill(pFillRect, Color); }
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ unsigned int GetPixel(int X, int Y) const;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ bool IsBlitTarget() { BS_ASSERT(m_pImage); return m_pImage->IsBlitTarget(); }
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ bool IsScalingAllowed() { BS_ASSERT(m_pImage); return m_pImage->IsScalingAllowed(); }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ bool IsFillingAllowed() { BS_ASSERT(m_pImage); return m_pImage->IsFillingAllowed(); }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ bool IsAlphaAllowed() { BS_ASSERT(m_pImage); return m_pImage->IsAlphaAllowed(); }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ bool IsColorModulationAllowed() { BS_ASSERT(m_pImage); return m_pImage->IsColorModulationAllowed(); }
+
+private:
+ BS_Image * m_pImage;
+ bool m_Valid;
+};
+
+#endif
diff --git a/engines/sword25/gfx/dynamicbitmap.cpp b/engines/sword25/gfx/dynamicbitmap.cpp
new file mode 100755
index 0000000000..6e2c258641
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.cpp
@@ -0,0 +1,191 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "dynamicbitmap.h"
+#include "bitmapresource.h"
+#include "package/packagemanager.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include <vector>
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "DYNAMICBITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_DynamicBitmap::BS_DynamicBitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Width, unsigned int Height) :
+ BS_Bitmap(ParentPtr, TYPE_DYNAMICBITMAP)
+{
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!m_InitSuccess) return;
+
+ m_InitSuccess = CreateGLImage(Width, Height);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_DynamicBitmap::BS_DynamicBitmap(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle) :
+ BS_Bitmap(ParentPtr, TYPE_DYNAMICBITMAP, Handle)
+{
+ m_InitSuccess = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::CreateGLImage(unsigned int Width, unsigned int Height)
+{
+ // GLImage mit den gewünschten Maßen erstellen
+ bool Result;
+ m_Image.reset(new BS_GLImage(Width, Height, Result));
+
+ m_OriginalWidth = m_Width = Width;
+ m_OriginalHeight = m_Height = Height;
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_DynamicBitmap::~BS_DynamicBitmap()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_DynamicBitmap::GetPixel(int X, int Y) const
+{
+ BS_ASSERT(X >= 0 && X < m_Width);
+ BS_ASSERT(Y >= 0 && Y < m_Height);
+
+ return m_Image->GetPixel(X, Y);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::DoRender()
+{
+ // Framebufferobjekt holen
+ BS_GraphicEngine * pGfx = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool Result;
+ if (m_ScaleFactorX == 1.0f && m_ScaleFactorY == 1.0f)
+ {
+ Result = m_Image->Blit(m_AbsoluteX, m_AbsoluteY,
+ (m_FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (m_FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, -1, -1);
+ }
+ else
+ {
+ Result = m_Image->Blit(m_AbsoluteX, m_AbsoluteY,
+ (m_FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (m_FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, m_Width, m_Height);
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride)
+{
+ return m_Image->SetContent(Pixeldata, Offset, Stride);
+}
+
+// -----------------------------------------------------------------------------
+// Auskunftsmethoden
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::IsScalingAllowed() const
+{
+ return m_Image->IsScalingAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::IsAlphaAllowed() const
+{
+ return m_Image->IsAlphaAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::IsColorModulationAllowed() const
+{
+ return m_Image->IsColorModulationAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::IsSetContentAllowed() const
+{
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_DynamicBitmap::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_Bitmap::Persist(Writer);
+
+ // Bilddaten werden nicht gespeichert. Dies ist auch nicht weiter von bedeutung, da BS_DynamicBitmap nur vom Videoplayer benutzt wird.
+ // Während ein Video abläuft kann niemals gespeichert werden. BS_DynamicBitmap kann nur der Vollständigkeit halber persistiert werden.
+ BS_LOG_WARNINGLN("Persisting a BS_DynamicBitmap. Bitmap content is not persisted.");
+
+ Result &= BS_RenderObject::PersistChildren(Writer);
+
+ return Result;
+}
+
+bool BS_DynamicBitmap::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_Bitmap::Unpersist(Reader);
+
+ // Ein BS_GLImage mit den gespeicherten Maßen erstellen.
+ Result &= CreateGLImage(m_Width, m_Height);
+
+ // Bilddaten werden nicht gespeichert (s.o.).
+ BS_LOG_WARNINGLN("Unpersisting a BS_DynamicBitmap. Bitmap contents are missing.");
+
+ // Bild mit durchsichtigen Bilddaten initialisieren.
+ std::vector<unsigned char> TransparentImageData(m_Width * m_Height * 4);
+ m_Image->SetContent(TransparentImageData);
+
+ Result &= BS_RenderObject::UnpersistChildren(Reader);
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/dynamicbitmap.h b/engines/sword25/gfx/dynamicbitmap.h
new file mode 100755
index 0000000000..0eb856ce81
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.h
@@ -0,0 +1,71 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_DYNAMIC_BITMAP_H
+#define BS_DYNAMIC_BITMAP_H
+
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "bitmap.h"
+#include "opengl/glimage.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_DynamicBitmap : public BS_Bitmap
+{
+friend BS_RenderObject;
+
+public:
+ virtual ~BS_DynamicBitmap();
+
+ virtual unsigned int GetPixel(int X, int Y) const;
+
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride);
+
+ virtual bool IsScalingAllowed() const;
+ virtual bool IsAlphaAllowed() const;
+ virtual bool IsColorModulationAllowed() const;
+ virtual bool IsSetContentAllowed() const;
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ virtual bool DoRender();
+
+private:
+ BS_DynamicBitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Width, unsigned int Height);
+ BS_DynamicBitmap(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle);
+
+ bool CreateGLImage(unsigned int Width, unsigned int Height);
+
+ std::auto_ptr<BS_GLImage> m_Image;
+};
+
+#endif
diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp
new file mode 100755
index 0000000000..3d2d12e992
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -0,0 +1,243 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "FONTRESOURCE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <memory>
+
+#include "kernel/kernel.h"
+#include "kernel/string.h"
+#include "package/packagemanager.h"
+#include "util/tinyxml/tinyxml.h"
+
+#include "fontresource.h"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+static const unsigned int DEFAULT_LINEHEIGHT = 20;
+static const unsigned int DEFAULT_GAPWIDTH = 1;
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_FontResource::BS_FontResource(BS_Kernel* pKernel, const std::string& FileName) :
+ _pKernel(pKernel),
+ _Valid(false),
+ BS_Resource(FileName, BS_Resource::TYPE_FONT)
+{
+ // XML Fontdatei parsen
+ TiXmlDocument Doc;
+ if (!_ParseXMLDocument(FileName, Doc))
+ {
+ BS_LOG_ERRORLN("The following TinyXML-Error occured while parsing \"%s\": %s", GetFileName().c_str(), Doc.ErrorDesc());
+ return;
+ }
+
+ // Font-Tag finden
+ TiXmlElement* pElement = Doc.FirstChildElement("font");
+ if (!pElement)
+ {
+ BS_LOG_ERRORLN("No <font> tag found in \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ // Font-Tag parsen
+ std::string BitmapFileName;
+ if (!_ParseFontTag(*pElement, BitmapFileName, _LineHeight, _GapWidth))
+ {
+ BS_LOG_ERRORLN("An error occurred while parsing <font> tag in \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ // Absoluten, eindeutigen Pfad zur Bitmapdatei bestimmen und dabei auf vorhandensein prüfen
+ {
+ // Pointer auf den Package-Manager bekommen
+ BS_ASSERT(_pKernel);
+ BS_PackageManager* pPackage = static_cast<BS_PackageManager *>(_pKernel->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Absoluten, eindeutigen Pfad bestimmen
+ _BitmapFileName = pPackage->GetAbsolutePath(BitmapFileName);
+ if (_BitmapFileName == "")
+ {
+ BS_LOG_ERRORLN("Image file \"%s\" was specified in <font> tag of \"%s\" but could not be found.",
+ _BitmapFileName.c_str(), GetFileName().c_str());
+ return;
+ }
+
+ // Bitmapdatei cachen
+ if (!_pKernel->GetResourceManager()->PrecacheResource(_BitmapFileName))
+ {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", _BitmapFileName.c_str());
+ return;
+ }
+ }
+
+ // Das Erste Character-Tag finden
+ pElement = pElement->FirstChildElement("character");
+ if (!pElement)
+ {
+ BS_LOG_ERRORLN("No <character> tag found in \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ // Alle Character-Tags parsen
+ while (pElement)
+ {
+ int CharCode;
+ BS_Rect CharRect;
+
+ // Aktuelles Character-Tag parsen
+ if (!_ParseCharacterTag(*pElement, CharCode, CharRect))
+ {
+ BS_LOG_ERRORLN("An error occured while parsing a <character> tag in \"%s\".", GetFileName().c_str());
+ return;
+ }
+
+ // Ausgelesene Daten in das _CharacterRects-Array eintragen
+ BS_ASSERT(CharCode < 256);
+ _CharacterRects[CharCode] = CharRect;
+
+ // Zum nächsten Character-Tag iterieren
+ pElement = pElement->NextSiblingElement("character");
+ }
+
+ // Erfolg signalisieren
+ _Valid = true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FontResource::_ParseXMLDocument(const std::string & FileName, TiXmlDocument & Doc) const
+{
+ // Pointer auf den Package-Manager bekommen
+ BS_ASSERT(_pKernel);
+ BS_PackageManager* pPackage = static_cast<BS_PackageManager *>(_pKernel->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Die Daten werden zunächst über den Package-Manager gelesen und dann in einen um ein Byte größeren Buffer kopiert
+ // und NULL-Terminiert, da TinyXML NULL-Terminierte Daten benötigt.
+ unsigned int FileSize;
+ char * LoadBuffer = (char*) pPackage->GetFile(GetFileName(), &FileSize);
+ if (!LoadBuffer)
+ {
+ BS_LOG_ERRORLN("Could not read \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ // Daten kopieren und NULL-terminieren
+ std::vector<char> WorkBuffer(FileSize + 1);
+ memcpy(&WorkBuffer[0], LoadBuffer, FileSize);
+ delete LoadBuffer;
+ WorkBuffer[FileSize] = '\0';
+
+ // Daten parsen
+ Doc.Parse(&WorkBuffer[0]);
+
+ return !Doc.Error();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FontResource::_ParseFontTag(TiXmlElement & Tag, std::string & BitmapFileName, int & Lineheight, int & GapWidth) const
+{
+ // Bitmap Attribut auslesen
+ const char * BitmapString = Tag.Attribute("bitmap");
+ if (!BitmapString)
+ {
+ BS_LOG_ERRORLN("<font> tag without bitmap attribute occurred in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+ BitmapFileName = BitmapString;
+
+ // Lineheight Attribut auslesen
+ const char * LineheightString = Tag.Attribute("lineheight");
+ if (!LineheightString || !BS_String::ToInt(std::string(LineheightString), Lineheight) || Lineheight < 0)
+ {
+ BS_LOG_WARNINGLN("Illegal or missing lineheight attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ GetFileName().c_str(), DEFAULT_LINEHEIGHT);
+ Lineheight = DEFAULT_LINEHEIGHT;
+ }
+
+
+ // Gap Attribut auslesen
+ const char * GapString = Tag.Attribute("gap");
+ if (!GapString || !BS_String::ToInt(std::string(GapString), GapWidth) || GapWidth < 0)
+ {
+ BS_LOG_WARNINGLN("Illegal or missing gap attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ GetFileName().c_str(), DEFAULT_GAPWIDTH);
+ GapWidth = DEFAULT_GAPWIDTH;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FontResource::_ParseCharacterTag(TiXmlElement & Tag, int & Code, BS_Rect & Rect) const
+{
+ // Code Attribut auslesen
+ const char * CodeString = Tag.Attribute("code");
+ if (!CodeString || !BS_String::ToInt(std::string(CodeString), Code) || Code < 0 || Code >= 256)
+ {
+ BS_LOG_ERRORLN("Illegal or missing code attribute in <character> tag in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ // Left Attribut auslesen
+ const char * LeftString = Tag.Attribute("left");
+ if (!LeftString || !BS_String::ToInt(std::string(LeftString), Rect.left) || Rect.left < 0)
+ {
+ BS_LOG_ERRORLN("Illegal or missing left attribute in <character> tag in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ // Right Attribut auslesen
+ const char * RightString = Tag.Attribute("right");
+ if (!RightString || !BS_String::ToInt(RightString, Rect.right) || Rect.right < 0)
+ {
+ BS_LOG_ERRORLN("Illegal or missing right attribute in <character> tag in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ // Top Attribut auslesen
+ const char * TopString = Tag.Attribute("top");
+ if (!TopString || !BS_String::ToInt(TopString, Rect.top) || Rect.top < 0)
+ {
+ BS_LOG_ERRORLN("Illegal or missing top attribute in <character> tag in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ // Bottom Attribut auslesen
+ const char * BottomString = Tag.Attribute("bottom");
+ if (!BottomString || !BS_String::ToInt(BottomString, Rect.bottom) || Rect.bottom < 0)
+ {
+ BS_LOG_ERRORLN("Illegal or missing bottom attribute in <character> tag in \"%s\".", GetFileName().c_str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/engines/sword25/gfx/fontresource.h b/engines/sword25/gfx/fontresource.h
new file mode 100755
index 0000000000..adb790be6b
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.h
@@ -0,0 +1,104 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_FONTRESOURCE_H
+#define BS_FONTRESOURCE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/resource.h"
+#include "math/rect.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class TiXmlDocument;
+class TiXmlElement;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FontResource : public BS_Resource
+{
+public:
+ /**
+ @brief Erzeugt eine neues Exemplar von BS_FontResource
+ @param pKernel ein Pointer auf den Kernel
+ @param FileName der Dateiname der zu ladenen Resource
+ @remark Wenn der Konstruktor erfolgreich ausgeführt werden konnte gibt die Methode IsValid true zurück.
+ */
+ BS_FontResource(BS_Kernel * pKernel, const std::string & FileName);
+
+ /**
+ @brief Gibt true zurück, wenn das Objekt korrekt initialisiert wurde.
+
+ Diese Methode kann dazu benutzt werden um festzustellen, ob der Konstruktor erfolgreich ausgeführt wurde.
+ */
+ bool IsValid() const { return _Valid; }
+
+ /**
+ @brief Gibt die Zeilenhöhe des Fonts in Pixeln zurück.
+
+ Die Zeilenhöhe ist der Wert, der zur Y-Koordinate addiert wird, wenn ein Zeilenumbruch auftritt.
+ */
+ int GetLineHeight() const { return _LineHeight; }
+
+ /**
+ @brief Gibt den Buchstabenabstand der Fonts in Pixeln zurück.
+
+ Der Buchstabenabstand ist der Wert, der zwischen zwei Buchstaben freigelassen wird.
+ */
+ int GetGapWidth() const { return _GapWidth; }
+
+ /**
+ @brief Gibt das Bounding-Rect eines Zeichens auf der Charactermap zurück.
+ @param Character der ASCII-Code des Zeichens
+ @return Das Bounding-Rect des übergebenen Zeichens auf der Charactermap.
+ */
+ const BS_Rect & GetCharacterRect(int Character) const { BS_ASSERT(Character >= 0 && Character < 256); return _CharacterRects[Character]; }
+
+ /**
+ @brief Gibt den Dateinamen der Charactermap zurück.
+ */
+ const std::string & GetCharactermapFileName() const { return _BitmapFileName; }
+
+private:
+ BS_Kernel * _pKernel;
+ bool _Valid;
+ std::string _BitmapFileName;
+ int _LineHeight;
+ int _GapWidth;
+ BS_Rect _CharacterRects[256];
+
+ // -----------------------------------------------------------------------------
+ // Hilfsmethoden
+ // -----------------------------------------------------------------------------
+
+ bool _ParseXMLDocument(const std::string & FileName, TiXmlDocument & Doc) const;
+ bool _ParseFontTag(TiXmlElement & Tag, std::string & BitmapFileName, int & LineHeight, int & GapWidth) const;
+ bool _ParseCharacterTag(TiXmlElement & Tag, int & Code, BS_Rect & Rect) const;
+};
+
+#endif
diff --git a/engines/sword25/gfx/framecounter.cpp b/engines/sword25/gfx/framecounter.cpp
new file mode 100755
index 0000000000..54c844967d
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.cpp
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "framecounter.h"
+#include "kernel/timer.h"
+
+BS_Framecounter::BS_Framecounter(int UpdateFrequency) :
+ m_FPS(0),
+ m_FPSCount(0),
+ m_LastUpdateTime(-1)
+{
+ SetUpdateFrequency(UpdateFrequency);
+}
+
+void BS_Framecounter::Update()
+{
+ // Aktuellen Systemtimerstand auslesen
+ uint64_t Timer = BS_Timer::GetMicroTicks();
+
+ // Falls m_LastUpdateTime == -1 ist, wird der Frame-Counter zum ersten Mal aufgerufen und der aktuelle Systemtimer als erster
+ // Messzeitpunkt genommen.
+ if (m_LastUpdateTime == -1)
+ m_LastUpdateTime = Timer;
+ else
+ {
+ // Die Anzahl der Frames im aktuellen Messzeitraum wird erhöht.
+ m_FPSCount++;
+
+ // Falls der Messzeitraum verstrichen ist, wird die durchschnittliche Framerate berechnet und ein neuer Messzeitraum begonnen.
+ if (Timer - m_LastUpdateTime >= m_UpdateDelay)
+ {
+ m_FPS = static_cast<int>((1000000 * (uint64_t)m_FPSCount) / (Timer - m_LastUpdateTime));
+ m_LastUpdateTime = Timer;
+ m_FPSCount = 0;
+ }
+ }
+}
diff --git a/engines/sword25/gfx/framecounter.h b/engines/sword25/gfx/framecounter.h
new file mode 100755
index 0000000000..79a2b04df6
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.h
@@ -0,0 +1,76 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef _BS_FRAMECOUNTER_H
+#define _BS_FRAMECOUNTER_H
+
+// Includes
+#include "kernel/common.h"
+#include "kernel/bs_stdint.h"
+
+/**
+ @brief Eine einfache Klasse die einen Framecounter implementiert.
+*/
+class BS_Framecounter
+{
+private:
+ enum
+ {
+ DEFAULT_UPDATE_FREQUENCY = 10
+ };
+
+public:
+ /**
+ @brief Erzeugt ein neues BS_Framecounter Objekt.
+ @param UpdateFrequency gibt an wie oft der Framecounter in einer Sekunde aktualisiert werden soll.<br>
+ Der Standardwert ist 10.
+ */
+ BS_Framecounter(int UpdateFrequency = DEFAULT_UPDATE_FREQUENCY);
+
+ /**
+ @brief Bestimmt wie oft der Framecounter in einer Sekunde aktualisiert werden soll.
+ @param UpdateFrequency gibt an wie oft der Framecounter in einer Sekunde aktualisiert werden soll.
+ */
+ inline void SetUpdateFrequency(int UpdateFrequency);
+
+ /**
+ @brief Diese Methode muss einmal pro Frame aufgerufen werden.
+ */
+ void Update();
+
+ /**
+ @brief Gibt den aktuellen FPS-Wert zurück.
+ */
+ int GetFPS() const { return m_FPS; }
+
+private:
+ int m_FPS;
+ int m_FPSCount;
+ uint64_t m_LastUpdateTime;
+ uint64_t m_UpdateDelay;
+};
+
+// Inlines
+void BS_Framecounter::SetUpdateFrequency(int UpdateFrequency)
+{
+ // Frequenz in Laufzeit (in Microsekunden) umrechnen.
+ m_UpdateDelay = 1000000 / UpdateFrequency;
+}
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp
new file mode 100755
index 0000000000..b8ad3a04bd
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.cpp
@@ -0,0 +1,218 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+#include "image/image.h"
+#include "screenshot.h"
+#include "kernel/memlog_off.h"
+#include <memory>
+#include <vector>
+#include "kernel/memlog_on.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+
+extern "C"
+{
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+static const unsigned int FRAMETIME_SAMPLE_COUNT = 5; // Anzahl der Framezeiten über die, die Framezeit gemittelt wird
+
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "graphicengine.h"
+
+// -----------------------------------------------------------------------------
+
+BS_GraphicEngine::BS_GraphicEngine(BS_Kernel * pKernel) :
+ m_Width(0),
+ m_Height(0),
+ m_BitDepth(0),
+ m_Windowed(0),
+ m_LastTimeStamp(9223372036854775807), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
+ m_LastFrameDuration(0),
+ m_TimerActive(true),
+ m_FrameTimeSamples(FRAMETIME_SAMPLE_COUNT, 0),
+ m_FrameTimeSampleSlot(0),
+ m_RepaintedPixels(0),
+ BS_ResourceService(pKernel)
+{
+ if (!RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_GraphicEngine::UpdateLastFrameDuration()
+{
+ // Aktuelle Zeit holen
+ uint64_t CurrentTime = BS_Kernel::GetInstance()->GetMicroTicks();
+
+ // Verstrichene Zeit seit letztem Frame berechnen und zu große Zeitsprünge ( > 250 msek.) unterbinden
+ // (kann vorkommen bei geladenen Spielständen, während des Debuggings oder Hardwareungenauigkeiten)
+ m_FrameTimeSamples[m_FrameTimeSampleSlot] = static_cast<unsigned int>(CurrentTime - m_LastTimeStamp);
+ if (m_FrameTimeSamples[m_FrameTimeSampleSlot] > 250000) m_FrameTimeSamples[m_FrameTimeSampleSlot] = 250000;
+ m_FrameTimeSampleSlot = (m_FrameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
+
+ // Die Framezeit wird über mehrere Frames gemittelt um Ausreisser zu eliminieren
+ std::vector<unsigned int>::const_iterator it = m_FrameTimeSamples.begin();
+ unsigned int Sum = *it;
+ for (it++; it != m_FrameTimeSamples.end(); it++) Sum += *it;
+ m_LastFrameDuration = Sum / FRAMETIME_SAMPLE_COUNT;
+
+ // _LastTimeStamp auf die Zeit des aktuellen Frames setzen
+ m_LastTimeStamp = CurrentTime;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ bool DoSaveScreenshot(BS_GraphicEngine & GraphicEngine, const std::string & Filename, bool Thumbnail)
+ {
+ unsigned int Width;
+ unsigned int Height;
+ vector<unsigned int> Data;
+ if (!GraphicEngine.GetScreenshot(Width, Height, Data))
+ {
+ BS_LOG_ERRORLN("Call to GetScreenshot() failed. Cannot save screenshot.");
+ return false;
+ }
+
+ unsigned int test = Data.size();
+
+ if (Thumbnail)
+ return BS_Screenshot::SaveThumbnailToFile(Width, Height, Data, Filename);
+ else
+ return BS_Screenshot::SaveToFile(Width, Height, Data, Filename);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GraphicEngine::SaveScreenshot(const std::string & Filename)
+{
+ return DoSaveScreenshot(*this, Filename, false);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GraphicEngine::SaveThumbnailScreenshot( const std::string & Filename )
+{
+ return DoSaveScreenshot(*this, Filename, true);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_GraphicEngine::ARGBColorToLuaColor(lua_State * L, unsigned int Color)
+{
+ lua_Number Components[4] =
+ {
+ (Color >> 16) & 0xff, // Rot
+ (Color >> 8) & 0xff, // Grün
+ Color & 0xff, // Blau
+ Color >> 24, // Alpha
+ };
+
+ lua_newtable(L);
+
+ for (unsigned int i = 1; i <= 4; i++)
+ {
+ lua_pushnumber(L, i);
+ lua_pushnumber(L, Components[i - 1]);
+ lua_settable(L, -3);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_GraphicEngine::LuaColorToARGBColor(lua_State * L, int StackIndex)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass wir wirklich eine Tabelle betrachten
+ luaL_checktype(L, StackIndex, LUA_TTABLE);
+ // Größe der Tabelle auslesen
+ unsigned int n = luaL_getn(L, StackIndex);
+ // RGB oder RGBA Farben werden unterstützt und sonst keine
+ if (n != 3 && n != 4) luaL_argcheck(L, 0, StackIndex, "at least 3 of the 4 color components have to be specified");
+
+ // Rote Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 1);
+ unsigned int Red = static_cast<unsigned int>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Red >= 256) luaL_argcheck(L, 0, StackIndex, "red color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Grüne Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 2);
+ unsigned int Green = static_cast<unsigned int>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Green >= 256) luaL_argcheck(L, 0, StackIndex, "green color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Blaue Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 3);
+ unsigned int Blue = static_cast<unsigned int>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Blue >= 256) luaL_argcheck(L, 0, StackIndex, "blue color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Alpha Farbkomponente auslesen
+ unsigned int Alpha = 0xff;
+ if (n == 4)
+ {
+ lua_rawgeti(L, StackIndex, 4);
+ Alpha = static_cast<unsigned int>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Alpha >= 256) luaL_argcheck(L, 0, StackIndex, "alpha color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return (Alpha << 24) | (Red << 16) | (Green << 8) | Blue;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GraphicEngine::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ Writer.Write(m_TimerActive);
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GraphicEngine::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ Reader.Read(m_TimerActive);
+ return Reader.IsGood();
+}
diff --git a/engines/sword25/gfx/graphicengine.h b/engines/sword25/gfx/graphicengine.h
new file mode 100755
index 0000000000..9fe8fd93fd
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.h
@@ -0,0 +1,401 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_GraphicEngine
+ ----------------
+ Dies ist das Graphik-Engine Interface, dass alle Methoden und Klassen enthält, die eine
+ Graphik-Engine implementieren muss.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_GRAPHICENGINE_H
+#define _BS_GRAPHICENGINE_H
+
+// Includes
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "kernel/bs_stdint.h"
+#include "kernel/resservice.h"
+#include "kernel/persistable.h"
+#include "math/rect.h"
+#include "framecounter.h"
+#include "renderobjectptr.h"
+
+class BS_Kernel;
+class BS_Image;
+class BS_Panel;
+class BS_Screenshot;
+
+// Typen
+typedef unsigned int BS_COLOR;
+
+// Makros
+#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
+#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
+
+/**
+ @brief Dies ist das Graphik-Engine Interface, dass alle Methoden und Klassen enthält, die eine Graphik-Engine implementieren muss.
+
+ Hier sind nur wenige Rumpffunktionen realisiert, wie z.B. das Abfragen der Parameter des Ausgabepuffers.
+ Die Hauptfunktionen muss eine Implementation dieses Inferfaces stellen.<br>
+ Die bisher einzige Implementation ist BS_DDrawGfx.
+*/
+
+class BS_GraphicEngine : public BS_ResourceService, public BS_Persistable
+{
+public:
+ // Enums
+ // -----
+
+ // Farbformate
+ /**
+ @brief Die von der Engine benutzten Farbformate
+ */
+ enum COLOR_FORMATS
+ {
+ /// Undefiniertes/unbekanntes Farbformat
+ CF_UNKNOWN = 0,
+ /// 16 Bit Farbformat (5 Bit Rot, 5 Bit Grün, 5 Bit Blau)
+ CF_RGB15,
+ /// 16 Bit Farbformat (5 Bit Rot, 6 Bit Grün, 5 Bit Blau)
+ CF_RGB16,
+ /**
+ Spezielles Alpha-Farbformat der Engine, welches besonders schnelles Darstellen unter Benutzung von MMX-Befehlen unterstützt.<br>
+ Die Pixel sind 16 Bit breit und haben das selbe Format wie #CF_RGB15. Zusätzlich besitzt jeder Pixel noch einen 8 Bit Alphawert.<br>
+ Es werden jeweils 4 Pixel und 4 Alphawerte zu einem 12 Byte großen Datenblock zusammengefasst.<br>
+ Dabei werden die Daten in folgender Reihenfolge abgelegt:
+ Alpha0 Alpha1 Alpha2 Alpha3 Pixel0 Pixel1 Pixel2 Pixel3
+ Falls die Pixelanzahl einer Zeile nicht durch 4 teilbar ist, wird der letzte Pixelblock trotzdem komplett abgespeichert, die
+ nicht verwendeten Pixel- und Alphawerte können beliebige Werte haben.
+ */
+ CF_RGB15_INTERLEAVED,
+ /**
+ Spezielles Alpha-Farbformat der Engine, welches besonders schnelles Darstellen unter Benutzung von MMX-Befehlen unterstützt.<br>
+ Die Pixel sind 16 Bit breit und haben das selbe Format wie #CF_RGB16. Zusätzlich besitzt jeder Pixel noch einen 8 Bit Alphawert.<br>
+ Es werden jeweils 4 Pixel und 4 Alphawerte zu einem 12 Byte großen Datenblock zusammengefasst.<br>
+ Dabei werden die Daten in folgender Reihenfolge abgelegt:
+ Alpha0 Alpha1 Alpha2 Alpha3 Pixel0 Pixel1 Pixel2 Pixel3
+ Falls die Pixelanzahl einer Zeile nicht durch 4 teilbar ist, wird der letzte Pixelblock trotzdem komplett abgespeichert, die
+ nicht verwendeten Pixel- und Alphawerte können beliebige Werte haben.
+ */
+ CF_RGB16_INTERLEAVED,
+ /**
+ 24 Bit Farbformat (8 Bit Rot, 8 Bit Grün, 8 Bit Blau)
+ */
+ CF_RGB24,
+ /**
+ 32 Bit Farbformat (8 Bit Alpha, 8 Bit Rot, 8 Bit Grün, 8 Bit Blau) (little endian)
+ */
+ CF_ARGB32,
+ /**
+ 32 Bit Farbformat (8 Bit Alpha, 8 Bit Blau, 8 Bit Grün, 8 Bit Rot) (little endian)
+ */
+ CF_ABGR32,
+ };
+
+ // Interface
+ // ---------
+
+ /**
+ @brief Initialisiert die Graphikengine und setzt den Bildschirmmodus.
+ @param Width die Breite des Ausgabepuffers in Pixeln.<br>Der Standardwert ist 800.
+ @param Height die Höhe des Ausgabepuffers in Pixeln.<br>Der Standardwert ist 600.
+ @param BitDepth die Bittiefe des gewünschten Ausgabepuffers in Bit.<br>Der Standardwert ist 16.
+ @param BackbufferCount die Anzahl an Backbuffern die erzeugt werden soll.<br>Der Standardwert ist 2.
+ @param Windowed gibt an, ob die Engine im Fenstermodus laufen soll. Falls true angegeben wird, wird ein Fenster geöffnet in das
+ die Ausgaben gerendert werden. Ansonsten wird ein Vollbildmodus gesetzt der den Parametern das Ausgabepuffers
+ entspricht.
+ @return Gibt false zurück, falls die Initialisierung fehlgeschlagen ist.
+ @remark Der Fenstermodus sollte nur zu Debuggingzwecken benutzt werden und ist nicht dafür gedacht im endgültigen Produkt benutzt
+ zu werden.
+ @remark Diese Methode sollte direkt nach der Initialisierung aller Services aufgerufen werden.
+ */
+ virtual bool Init(int Width = 800, int Height = 600, int BitDepth = 16, int BackbufferCount = 2, bool Windowed = false) = 0;
+
+ //@{
+ /** @name Frame-Methoden */
+ /**
+ @brief Beginnt das Rendern eines neuen Frames.
+ @param UpdateAll gibt an, ob der Renderer im nächsten Frame alles neu zeichnen soll.<br>
+ Diese Funktion kann nützlich sein, wenn der Renderer mit Dirty-Rectangles arbeitet, der Benutzer aber gelegentlich
+ darauf angewiesen ist, dass der gesamte Bildschirminhalt neu gezeichnet werden soll.<br>
+ Der Standardwert ist false.
+ Diese Methode muss am Anfang das Main-Loops aufgerufen werden und vor dem Aufruf jeglicher Rendermethoden.
+ @return Gibt false zurück, falls ein Fehler aufgetreten ist.
+ @remark Implementationen dieser Methode müssen _UpdateLastFrameDuration() aufrufen.
+ */
+ virtual bool StartFrame(bool UpdateAll = false) = 0;
+
+ /**
+ @brief Beendet das Rendern des Frames und stellt diesen auf dem Bildschirm dar.
+
+ Diese Methode muss am Ende des Main-Loops aufgerufen werden. Nach dem Aufruf dürfen keine weiteren Rendermethoden mehr aufgerufen
+ werden. Dafür muss erst wieder ein Aufruf von #StartFrame erfolgen.
+ @return Gibt false zurück, falls ein Fehler aufgetreten ist:
+ */
+ virtual bool EndFrame() = 0;
+
+ //@}
+
+ //@{
+ /** @name Debug-Methoden */
+
+ /**
+ @brief Zeichnet eine Line in den Framebuffer.
+
+ Diese Methode muss zwischen StartFrame() und EndFrame() aufgerufen werden und ist nur für Debugzwecke gedacht.
+ Die Linie erscheint nur für einen Frame. Wenn die Linie dauerhaft zu sehen sein soll, muss sie jeden Frame neu
+ gezeichnet werden.
+
+ @param Start der Startpunkt der Linie
+ @param End der Endpunkt der Linie
+ @param Color die Farbe der Linie. Der Standardwert ist BS_RGB(255, 255, 255) (Weiß).
+ */
+ virtual void DrawDebugLine(const BS_Vertex & Start, const BS_Vertex & End, unsigned int Color = BS_RGB(255, 255, 255)) = 0;
+
+ /**
+ @brief Erstellt einen Screenshot.
+
+ Erstellt einen Screenshot vom aktuellen Framebuffer und schreibt ihn in eine Grafikdatei.<br>
+ Das verwendete Dateiformat ist PNG.
+
+ @param Der Dateiname des Screenshots.
+ @return Gibt true zurück, wenn der Screenshot gespeichert werden konnte, ansonsten false.
+ @remark Diese Methode darf erst nach einem Aufruf von EndFrame() und vor dem nächsten Aufruf von StartFrame() aufgerufen werden.
+ */
+ bool SaveScreenshot(const std::string & Filename);
+
+ /**
+ @Brief Erstellt einen kleinen Screenshot.
+
+ Erstellt einen Screenshot mit den Maßen 200x125. Hierfür werden am oberen und unteren Bildschirmrand die Interfaceleisten abgeschnitten und
+ das Bild auf ein 16tel seiner Ursprungsgröße verkleinert.
+
+ @param Der Dateiname des Screenshots.
+ @return Gibt true zurück, wenn der Screenshot gespeichert werden konnte, ansonsten false.
+ @remark Diese Methode darf erst nach einem Aufruf von EndFrame() und vor dem nächsten Aufruf von StartFrame() aufgerufen werden.
+ @remark Der Framebuffer muss eine Auflösung von 800x600 haben.
+ */
+ bool SaveThumbnailScreenshot(const std::string & Filename);
+
+ /**
+ @brief Liest den aktuellen Inhalt des Framebuffer aus.
+ @param Width enthält nach einem erfolgreichen Aufruf die Breite des Framebuffers.
+ @param Height enthält nach einem erfolgreichen Aufruf die Höhe des Framebuffers.
+ @param Data enthält nach einem erfolgreichen Aufruf den Inhalt des Framebuffers als 32-Bit Farbwerte.
+ @return Gibt true zurück, wenn der Aufruf erfolgreich war, ansonsten false.
+ @remark Diese Methode ist für das Erstellen von Screenshots gedacht. Sie muss also nicht sehr effizient sein.
+ @remark Diese Methode darf erst nach einem Aufruf von EndFrame() und vor dem nächsten Aufruf von StartFrame() aufgerufen werden.
+ */
+ virtual bool GetScreenshot(unsigned int & Width, unsigned int & Height, std::vector<unsigned int> & Data) = 0;
+
+ //@}
+
+ virtual BS_RenderObjectPtr<BS_Panel> GetMainPanel() = 0;
+
+ /**
+ @brief Gibt die Zeit (in Microsekunden) zurück die seit dem letzten Frame vergangen ist.
+ */
+ int GetLastFrameDurationMicro() { if (m_TimerActive) return m_LastFrameDuration; else return 0; }
+
+ /**
+ @brief Gibt die Zeit (in Sekunden) zurück die seit dem letzten Frame vergangen ist.
+ */
+ float GetLastFrameDuration() { if (m_TimerActive) return static_cast<float>(m_LastFrameDuration) / 1000000.0f; else return 0; }
+
+ void StopMainTimer() { m_TimerActive = false; }
+ void ResumeMainTimer() { m_TimerActive = true; }
+ float GetSecondaryFrameDuration() { return static_cast<float>(m_LastFrameDuration) / 1000000.0f; }
+
+ //@{
+ /** @name Accessor-Methoden */
+
+ /**
+ @brief Gibt die Breite des Ausgabepuffers in Pixeln zurück.
+ */
+ int GetDisplayWidth() { return m_Width; }
+
+ /**
+ @brief Gibt die Höhe des Ausgabepuffers in Pixeln zurück.
+ */
+ int GetDisplayHeight() { return m_Height; }
+
+ /**
+ @brief Gibt die Bounding-Box des Ausgabepuffers zurück. (0, 0, Width, Height)
+ */
+ BS_Rect& GetDisplayRect() { return m_ScreenRect; }
+
+ /**
+ @brief Gibt die Bittiefe des Ausgabepuffers zurück.
+ */
+ int GetBitDepth() { return m_BitDepth; }
+
+ /**
+ @brief Legt fest ob der Framebufferwechsel mit dem vertikalen Strahlenrücklauf synchronisiert werden soll.<br>
+ Vsync ist standardmäßig eingeschaltet.
+ @param Vsync gibt an, ob der Framebufferwechsel mit dem vertikalen Strahlenrücklauf synchronisiert werden soll.
+ @remark Im Fenstermodus hat diese Einstellung keine Auswirkung.
+ */
+ virtual void SetVsync(bool Vsync) = 0;
+
+ /**
+ @brief Gibt true zurück, wenn V-Sync an ist.
+ @remark Im Fenstermodus hat diese Einstellung keine Auswirkung.
+ */
+ virtual bool GetVsync() const = 0;
+
+ /**
+ @brief Gibt true zurück, falls die Engine im Fenstermodus läuft.
+ */
+ bool IsWindowed() { return m_Windowed; }
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Framebuffers mit einer Farbe.
+ @param FillRectPtr Pointer auf ein BS_Rect, welches den Ausschnitt des Framebuffers spezifiziert, der gefüllt
+ werden soll oder 0, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist 0.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.<br>
+ Der Standardwert ist BS_RGB(0, 0, 0) (Schwarz).
+ @return Gibt true zurück, wenn der Aufruf erfolgreich war, ansonsten false.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem
+ Alphawert ungleich 255 angibt.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ virtual bool Fill(const BS_Rect * FillRectPtr = 0, unsigned int Color = BS_RGB(0, 0, 0)) = 0;
+
+ //@}
+
+ //@{
+ /** @name Debugging-Methoden */
+
+ int GetFPSCount() const { return m_FPSCounter.GetFPS(); }
+ int GetRepaintedPixels() const { return m_RepaintedPixels; }
+
+ //@}
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Gibt die Größe eines Pixeleintrages in Byte für ein bestimmtes Farbformat zurück
+ @param ColorFormat das gewünschte Farbformat. Der Parameter muss vom Typ COLOR_FORMATS sein.
+ @return Gibt die Größe eines Pixeleintrages in Byte des Farbsformates ColorFormat zurück.<br>
+ Falls das Farbformat unbekannt ist, wird -1 zurückgegeben.
+ */
+ static int GetPixelSize(BS_GraphicEngine::COLOR_FORMATS ColorFormat)
+ {
+ switch (ColorFormat)
+ {
+ case BS_GraphicEngine::CF_RGB16:
+ case BS_GraphicEngine::CF_RGB15:
+ return 2;
+
+ case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
+ case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
+ return 3;
+
+ case BS_GraphicEngine::CF_ARGB32:
+ return 4;
+ }
+
+ return -1;
+ }
+
+ /**
+ @brief Berechnet die Länge einer Bildzeile eines Bilder in Byte, abhängig vom Farbformat.
+ @param ColorFormat das Farbformat des Bildes.
+ @param Width die Länge einer Bildzeile in Pixel.
+ @return Gibt die Länge einer Bildzeile in Byte wieder.<br>
+ Falls das Farbformat unbekannt ist, wird -1 zurückgegeben.
+ */
+ static int CalcPitch(BS_GraphicEngine::COLOR_FORMATS ColorFormat, int Width)
+ {
+ switch (ColorFormat)
+ {
+ case BS_GraphicEngine::CF_RGB16:
+ case BS_GraphicEngine::CF_RGB15:
+ return Width * 2;
+
+ case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
+ case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
+ return (Width + 3) / 4 * 12;
+
+ case BS_GraphicEngine::CF_ARGB32:
+ case BS_GraphicEngine::CF_ABGR32:
+ return Width * 4;
+
+ default:
+ BS_ASSERT(false);
+ }
+
+ return -1;
+ }
+
+ //@}
+
+ // Persistenz Methoden
+ // -------------------
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+ static void ARGBColorToLuaColor(lua_State * L, unsigned int Color);
+ static unsigned int LuaColorToARGBColor(lua_State * L, int StackIndex);
+
+protected:
+ // Konstruktor
+ // -----------
+ BS_GraphicEngine(BS_Kernel* pKernel);
+
+ // Display Variablen
+ // -----------------
+ int m_Width;
+ int m_Height;
+ BS_Rect m_ScreenRect;
+ int m_BitDepth;
+ bool m_Windowed;
+
+ // Debugging-Variablen
+ // -------------------
+ BS_Framecounter m_FPSCounter;
+
+ unsigned int m_RepaintedPixels;
+
+ /**
+ @brief Berechnet die Zeit die seit dem letzten Framebeginn vergangen ist.
+ */
+ void UpdateLastFrameDuration();
+
+private:
+ bool RegisterScriptBindings();
+
+ // LastFrameDuration-Variablen
+ // ---------------------------
+ uint64_t m_LastTimeStamp;
+ unsigned int m_LastFrameDuration;
+ bool m_TimerActive;
+ std::vector<unsigned int> m_FrameTimeSamples;
+ unsigned int m_FrameTimeSampleSlot;
+};
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine_script.cpp b/engines/sword25/gfx/graphicengine_script.cpp
new file mode 100755
index 0000000000..5419f09f2c
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine_script.cpp
@@ -0,0 +1,1722 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <algorithm>
+#include <string>
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "kernel/callbackregistry.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+#include "script/luacallback.h"
+#include "math/vertex.h"
+
+#include "graphicengine.h"
+#include "renderobject.h"
+#include "bitmap.h"
+#include "animation.h"
+#include "panel.h"
+#include "text.h"
+#include "animationtemplate.h"
+#include "animationtemplateregistry.h"
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+// -----------------------------------------------------------------------------
+// Callback-Objekte
+// -----------------------------------------------------------------------------
+
+static bool AnimationDeleteCallback(unsigned int Data);
+static bool AnimationActionCallback(unsigned int Data);
+static bool AnimationLoopPointCallback(unsigned int Data);
+
+namespace
+{
+ // -------------------------------------------------------------------------
+
+ class ActionCallback : public BS_LuaCallback
+ {
+ public:
+ ActionCallback(lua_State * L) : BS_LuaCallback(L) {};
+
+ std::string Action;
+
+ protected:
+ virtual int PreFunctionInvokation(lua_State * L)
+ {
+ lua_pushstring(L, Action.c_str());
+ return 1;
+ }
+ };
+
+ std::auto_ptr<BS_LuaCallback> LoopPointCallbackPtr;
+ std::auto_ptr<ActionCallback> ActionCallbackPtr;
+
+ // -------------------------------------------------------------------------
+
+ struct CallbackfunctionRegisterer
+ {
+ CallbackfunctionRegisterer()
+ {
+ BS_CallbackRegistry::GetInstance().RegisterCallbackFunction("LuaLoopPointCB", AnimationLoopPointCallback);
+ BS_CallbackRegistry::GetInstance().RegisterCallbackFunction("LuaActionCB", AnimationActionCallback);
+ BS_CallbackRegistry::GetInstance().RegisterCallbackFunction("LuaDeleteCB", AnimationDeleteCallback);
+ }
+ };
+ static CallbackfunctionRegisterer Instance;
+}
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+// Die Strings werden als #defines definiert um Stringkomposition zur Compilezeit zu ermöglichen.
+#define RENDEROBJECT_CLASS_NAME "Gfx.RenderObject"
+#define BITMAP_CLASS_NAME "Gfx.Bitmap"
+#define PANEL_CLASS_NAME "Gfx.Panel"
+#define TEXT_CLASS_NAME "Gfx.Text"
+#define ANIMATION_CLASS_NAME "Gfx.Animation"
+#define ANIMATION_TEMPLATE_CLASS_NAME "Gfx.AnimationTemplate"
+static const char * GFX_LIBRARY_NAME = "Gfx";
+
+// -----------------------------------------------------------------------------
+
+// Wie luaL_checkudata, nur ohne dass kein Fehler erzeugt wird.
+static void * my_checkudata (lua_State *L, int ud, const char *tname)
+{
+ int top = lua_gettop(L);
+
+ void * p = lua_touserdata(L, ud);
+ if (p != NULL) /* value is a userdata? */
+ {
+ if (lua_getmetatable(L, ud)) /* does it have a metatable? */
+ {
+ // lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ BS_LuaBindhelper::GetMetatable(L, tname);
+ if (lua_rawequal(L, -1, -2)) /* does it have the correct mt? */
+ {
+ lua_settop(L, top);
+ return p;
+ }
+ }
+ }
+
+ lua_settop(L, top);
+ return NULL;
+}
+
+// -----------------------------------------------------------------------------
+
+static void NewUintUserData(lua_State * L, unsigned int Value)
+{
+ void * UserData = lua_newuserdata(L, sizeof(Value));
+ memcpy(UserData, &Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+static BS_AnimationTemplate * CheckAnimationTemplate(lua_State * L, int idx = 1)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.AnimationTemplate
+ unsigned int AnimationTemplateHandle;
+ if ((AnimationTemplateHandle = *reinterpret_cast<unsigned int *>(my_checkudata(L, idx, ANIMATION_TEMPLATE_CLASS_NAME))) != 0)
+ {
+ BS_AnimationTemplate * AnimationTemplatePtr = BS_AnimationTemplateRegistry::GetInstance().ResolveHandle(AnimationTemplateHandle);
+ if (!AnimationTemplatePtr)
+ luaL_error(L, "The animation template with the handle %d does no longer exist.", AnimationTemplateHandle);
+ return AnimationTemplatePtr;
+ }
+ else
+ {
+ luaL_argcheck(L, 0, idx, "'" ANIMATION_TEMPLATE_CLASS_NAME "' expected");
+ return 0;
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+static int NewAnimationTemplate(lua_State * L)
+{
+ unsigned int AnimationTemplateHandle = BS_AnimationTemplate::Create(luaL_checkstring(L, 1));
+ BS_AnimationTemplate * AnimationTemplatePtr = BS_AnimationTemplateRegistry::GetInstance().ResolveHandle(AnimationTemplateHandle);
+ if (AnimationTemplatePtr && AnimationTemplatePtr->IsValid())
+ {
+ NewUintUserData(L, AnimationTemplateHandle);
+ //luaL_getmetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ }
+ else
+ {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_AddFrame(lua_State * L)
+{
+ BS_AnimationTemplate * pAT = CheckAnimationTemplate(L);
+ pAT->AddFrame(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetFrame(lua_State * L)
+{
+ BS_AnimationTemplate * pAT = CheckAnimationTemplate(L);
+ pAT->SetFrame(static_cast<int>(luaL_checknumber(L, 2)), static_cast<int>(luaL_checknumber(L, 3)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationTypeStringToNumber(const char * TypeString, BS_Animation::ANIMATION_TYPES & Result)
+{
+ if (strcmp(TypeString, "jojo") == 0)
+ {
+ Result = BS_Animation::AT_JOJO;
+ return true;
+ }
+ else if (strcmp(TypeString, "loop") == 0)
+ {
+ Result = BS_Animation::AT_LOOP;
+ return true;
+ }
+ else if (strcmp(TypeString, "oneshot") == 0)
+ {
+ Result = BS_Animation::AT_ONESHOT;
+ return true;
+ }
+ else
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetAnimationType(lua_State * L)
+{
+ BS_AnimationTemplate * pAT = CheckAnimationTemplate(L);
+ BS_Animation::ANIMATION_TYPES AnimationType;
+ if (AnimationTypeStringToNumber(luaL_checkstring(L, 2), AnimationType))
+ {
+ pAT->SetAnimationType(AnimationType);
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 2, "Invalid animation type");
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetFPS(lua_State * L)
+{
+ BS_AnimationTemplate * pAT = CheckAnimationTemplate(L);
+ pAT->SetFPS(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_Finalize(lua_State * L)
+{
+ BS_AnimationTemplate * pAT = CheckAnimationTemplate(L);
+ delete pAT;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg ANIMATION_TEMPLATE_METHODS[] =
+{
+ "AddFrame", AT_AddFrame,
+ "SetFrame", AT_SetFrame,
+ "SetAnimationType", AT_SetAnimationType,
+ "SetFPS", AT_SetFPS,
+ "__gc", AT_Finalize,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_GraphicEngine * GetGE()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_GraphicEngine * pGE = static_cast<BS_GraphicEngine *>(pKernel->GetService("gfx"));
+ BS_ASSERT(pGE);
+ return pGE;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Init(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ switch (lua_gettop(L))
+ {
+ case 0:
+ lua_pushbooleancpp(L, pGE->Init());
+ break;
+ case 1:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1))));
+ break;
+ case 2:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L,1)), static_cast<int>(luaL_checknumber(L, 2))));
+ break;
+ case 3:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L,1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3))));
+ break;
+ case 4:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L,1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4))));
+ break;
+ default:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L,1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4)),
+ lua_tobooleancpp(L, 5)));
+ }
+
+
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Main-Panel zum Gfx-Modul hinzufügen
+ BS_RenderObjectPtr<BS_Panel> MainPanelPtr(GetGE()->GetMainPanel());
+ BS_ASSERT(MainPanelPtr.IsValid());
+
+ lua_pushstring(L, GFX_LIBRARY_NAME);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ BS_ASSERT(!lua_isnil(L, -1));
+
+ NewUintUserData(L, MainPanelPtr->GetHandle());
+ BS_ASSERT(!lua_isnil(L, -1));
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, "MainPanel");
+ lua_insert(L, -2);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StartFrame(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ if (lua_gettop(L) == 0)
+ lua_pushbooleancpp(L, pGE->StartFrame());
+ else
+ lua_pushbooleancpp(L, pGE->StartFrame(lua_tobooleancpp(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int EndFrame(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->EndFrame());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int DrawDebugLine(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ BS_Vertex Start;
+ BS_Vertex End;
+ BS_Vertex::LuaVertexToVertex(L, 1, Start);
+ BS_Vertex::LuaVertexToVertex(L, 2, End);
+ pGE->DrawDebugLine(Start, End, BS_GraphicEngine::LuaColorToARGBColor(L, 3));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetDisplayWidth(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetDisplayWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetDisplayHeight(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetDisplayHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetBitDepth(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetBitDepth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetVsync(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ pGE->SetVsync(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsVsync(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->GetVsync());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsWindowed(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->IsWindowed());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetFPSCount(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetFPSCount());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetLastFrameDuration(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetLastFrameDuration());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StopMainTimer(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+ pGE->StopMainTimer();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ResumeMainTimer(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+ pGE->ResumeMainTimer();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSecondaryFrameDuration(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetSecondaryFrameDuration());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveScreenshot(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+ lua_pushbooleancpp(L, pGE->SaveScreenshot(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveThumbnailScreenshot(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+ lua_pushbooleancpp(L, pGE->SaveThumbnailScreenshot(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetRepaintedPixels(lua_State * L)
+{
+ BS_GraphicEngine * pGE = GetGE();
+ lua_pushnumber(L, static_cast<lua_Number>(pGE->GetRepaintedPixels()));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg GFX_FUNCTIONS[] =
+{
+ "Init", Init,
+ "StartFrame", StartFrame,
+ "EndFrame", EndFrame,
+ "DrawDebugLine", DrawDebugLine,
+ "SetVsync", SetVsync,
+ "GetDisplayWidth", GetDisplayWidth,
+ "GetDisplayHeight", GetDisplayHeight,
+ "GetBitDepth", GetBitDepth,
+ "IsVsync", IsVsync,
+ "IsWindowed", IsWindowed,
+ "GetFPSCount", GetFPSCount,
+ "GetLastFrameDuration", GetLastFrameDuration,
+ "StopMainTimer", StopMainTimer,
+ "ResumeMainTimer", ResumeMainTimer,
+ "GetSecondaryFrameDuration", GetSecondaryFrameDuration,
+ "SaveScreenshot", SaveScreenshot,
+ "NewAnimationTemplate", NewAnimationTemplate,
+ "GetRepaintedPixels", GetRepaintedPixels,
+ "SaveThumbnailScreenshot", SaveThumbnailScreenshot,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_RenderObjectPtr<BS_RenderObject> CheckRenderObject(lua_State * L, bool ErrorIfRemoved = true)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable einer Klasse haben, die von Gfx.RenderObject "erbt".
+ unsigned int * UserDataPtr;
+ if ((UserDataPtr = (unsigned int *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (unsigned int *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (unsigned int *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (unsigned int *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0)
+ {
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr(* UserDataPtr);
+ if (ROPtr.IsValid())
+ return ROPtr;
+ else
+ {
+ if (ErrorIfRemoved)
+ luaL_error(L, "The renderobject with the handle %d does no longer exist.", * UserDataPtr);
+ }
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" RENDEROBJECT_CLASS_NAME "' expected");
+ }
+
+ return BS_RenderObjectPtr<BS_RenderObject>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetPos(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ BS_Vertex Pos;
+ BS_Vertex::LuaVertexToVertex(L, 2, Pos);
+ ROPtr->SetPos(Pos.X, Pos.Y);
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr->SetX(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr->SetY(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetZ(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr->SetZ(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetVisible(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr->SetVisible(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetZ(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetZ());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetAbsoluteX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetAbsoluteX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetAbsoluteY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetAbsoluteY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetWidth(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetHeight(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushnumber(L, ROPtr->GetHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_IsVisible(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ lua_pushbooleancpp(L, ROPtr->IsVisible());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddPanel(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ BS_RenderObjectPtr<BS_Panel> PanelPtr = ROPtr->AddPanel(static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)),
+ BS_GraphicEngine::LuaColorToARGBColor(L, 4));
+ if (PanelPtr.IsValid())
+ {
+ NewUintUserData(L, PanelPtr->GetHandle());
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddBitmap(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ BS_RenderObjectPtr<BS_Bitmap> BitmaPtr = ROPtr->AddBitmap(luaL_checkstring(L, 2));
+ if (BitmaPtr.IsValid())
+ {
+ NewUintUserData(L, BitmaPtr->GetHandle());
+ // luaL_getmetatable(L, BITMAP_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, BITMAP_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddText(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+
+ BS_RenderObjectPtr<BS_Text> TextPtr;
+ if (lua_gettop(L) >= 3) TextPtr = ROPtr->AddText(luaL_checkstring(L, 2), luaL_checkstring(L, 3));
+ else TextPtr = ROPtr->AddText(luaL_checkstring(L, 2));
+
+ if (TextPtr.IsValid())
+ {
+ NewUintUserData(L, TextPtr->GetHandle());
+ // luaL_getmetatable(L, TEXT_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, TEXT_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddAnimation(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr;
+ if (lua_type(L, 2) == LUA_TUSERDATA)
+ AnimationPtr = ROPtr->AddAnimation(*CheckAnimationTemplate(L, 2));
+ else
+ AnimationPtr = ROPtr->AddAnimation(luaL_checkstring(L, 2));
+
+ if (AnimationPtr.IsValid())
+ {
+ NewUintUserData(L, AnimationPtr->GetHandle());
+ // luaL_getmetatable(L, ANIMATION_CLASS_NAME);
+ BS_LuaBindhelper::GetMetatable(L, ANIMATION_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ // Alle Animationscallbacks registrieren.
+ AnimationPtr->RegisterDeleteCallback(AnimationDeleteCallback, AnimationPtr->GetHandle());
+ AnimationPtr->RegisterLoopPointCallback(AnimationLoopPointCallback, AnimationPtr->GetHandle());
+ AnimationPtr->RegisterActionCallback(AnimationActionCallback, AnimationPtr->GetHandle());
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_Remove(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ ROPtr.Erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg RENDEROBJECT_METHODS[] =
+{
+ "AddAnimation", RO_AddAnimation,
+ "AddText", RO_AddText,
+ "AddBitmap", RO_AddBitmap,
+ "AddPanel", RO_AddPanel,
+ "SetPos", RO_SetPos,
+ "SetX", RO_SetX,
+ "SetY", RO_SetY,
+ "SetZ", RO_SetZ,
+ "SetVisible", RO_SetVisible,
+ "GetX", RO_GetX,
+ "GetY", RO_GetY,
+ "GetZ", RO_GetZ,
+ "GetAbsoluteX", RO_GetAbsoluteX,
+ "GetAbsoluteY", RO_GetAbsoluteY,
+ "GetWidth", RO_GetWidth,
+ "GetHeight", RO_GetHeight,
+ "IsVisible", RO_IsVisible,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_RenderObjectPtr<BS_Panel> CheckPanel(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Panel
+ unsigned int * UserDataPtr;
+ if ((UserDataPtr = (unsigned int *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0)
+ {
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.IsValid())
+ {
+ return ROPtr->ToPanel();
+ }
+ else
+ luaL_error(L, "The panel with the handle %d does no longer exist.", *UserDataPtr);
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" PANEL_CLASS_NAME "' expected");
+ }
+
+ return BS_RenderObjectPtr<BS_Panel>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_GetColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Panel> PanelPtr = CheckPanel(L);
+ BS_ASSERT(PanelPtr.IsValid());
+ BS_GraphicEngine::ARGBColorToLuaColor(L, PanelPtr->GetColor());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_SetColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Panel> PanelPtr = CheckPanel(L);
+ BS_ASSERT(PanelPtr.IsValid());
+ PanelPtr->SetColor(BS_GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_Remove(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr.Erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg PANEL_METHODS[] =
+{
+ "GetColor", P_GetColor,
+ "SetColor", P_SetColor,
+ "Remove", P_Remove,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_RenderObjectPtr<BS_Bitmap> CheckBitmap(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Bitmap
+ unsigned int * UserDataPtr;
+ if ((UserDataPtr = (unsigned int *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0)
+ {
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.IsValid())
+ {
+ return ROPtr->ToBitmap();
+ }
+ else
+ luaL_error(L, "The bitmap with the handle %d does no longer exist.", *UserDataPtr);
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" BITMAP_CLASS_NAME "' expected");
+ }
+
+ return BS_RenderObjectPtr<BS_Bitmap>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetAlpha(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetAlpha(static_cast<unsigned int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetTintColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetModulationColor(BS_GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactorX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactorY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetFlipH(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetFlipH(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetFlipV(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BitmapPtr->SetFlipV(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetAlpha(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushnumber(L, BitmapPtr->GetAlpha());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetTintColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BS_GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->GetModulationColor());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetScaleFactorX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushnumber(L, BitmapPtr->GetScaleFactorX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetScaleFactorY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushnumber(L, BitmapPtr->GetScaleFactorY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsFlipH(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushbooleancpp(L, BitmapPtr->IsFlipH());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsFlipV(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushbooleancpp(L, BitmapPtr->IsFlipV());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetPixel(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ BS_Vertex Pos;
+ BS_Vertex::LuaVertexToVertex(L, 2, Pos);
+ BS_GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->GetPixel(Pos.X, Pos.Y));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsScalingAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushbooleancpp(L, BitmapPtr->IsScalingAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsAlphaAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushbooleancpp(L, BitmapPtr->IsAlphaAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsTintingAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.IsValid());
+ lua_pushbooleancpp(L, BitmapPtr->IsColorModulationAllowed());
+ return 1;
+}
+// -----------------------------------------------------------------------------
+
+static int B_Remove(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.IsValid());
+ ROPtr.Erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg BITMAP_METHODS[] =
+{
+ "SetAlpha", B_SetAlpha,
+ "SetTintColor", B_SetTintColor,
+ "SetScaleFactor", B_SetScaleFactor,
+ "SetScaleFactorX", B_SetScaleFactorX,
+ "SetScaleFactorY", B_SetScaleFactorY,
+ "SetFlipH", B_SetFlipH,
+ "SetFlipV", B_SetFlipV,
+ "GetAlpha", B_GetAlpha,
+ "GetTintColor", B_GetTintColor,
+ "GetScaleFactorX", B_GetScaleFactorX,
+ "GetScaleFactorY", B_GetScaleFactorY,
+ "IsFlipH", B_IsFlipH,
+ "IsFlipV", B_IsFlipV,
+ "GetPixel", B_GetPixel,
+ "IsScalingAllowed", B_IsScalingAllowed,
+ "IsAlphaAllowed", B_IsAlphaAllowed,
+ "IsTintingAllowed", B_IsTintingAllowed,
+ "Remove", B_Remove,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_RenderObjectPtr<BS_Animation> CheckAnimation(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Animation
+ unsigned int * UserDataPtr;
+ if ((UserDataPtr = (unsigned int *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0)
+ {
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.IsValid())
+ return ROPtr->ToAnimation();
+ else
+ {
+ luaL_error(L, "The animation with the handle %d does no longer exist.", *UserDataPtr);
+ }
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" ANIMATION_CLASS_NAME "' expected");
+ }
+
+ return BS_RenderObjectPtr<BS_Animation>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Play(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->Play();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Pause(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->Pause();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Stop(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->Stop();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetFrame(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetFrame(static_cast<unsigned int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetAlpha(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+// -----------------------------------------------------------------------------
+
+static int A_SetTintColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetModulationColor(BS_GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactorX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactorY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr->SetScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetScaleFactorX(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushnumber(L, AnimationPtr->GetScaleFactorX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetScaleFactorY(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushnumber(L, AnimationPtr->GetScaleFactorY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetAnimationType(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ switch (AnimationPtr->GetAnimationType())
+ {
+ case BS_Animation::AT_JOJO:
+ lua_pushstring(L, "jojo");
+ break;
+ case BS_Animation::AT_LOOP:
+ lua_pushstring(L, "loop");
+ break;
+ case BS_Animation::AT_ONESHOT:
+ lua_pushstring(L, "oneshot");
+ break;
+ default:
+ BS_ASSERT(false);
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetFPS(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushnumber(L, AnimationPtr->GetFPS());
+ return 1;
+}
+
+
+// -----------------------------------------------------------------------------
+
+static int A_GetFrameCount(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushnumber(L, AnimationPtr->GetFrameCount());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsScalingAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushbooleancpp(L, AnimationPtr->IsScalingAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsAlphaAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushbooleancpp(L, AnimationPtr->IsAlphaAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsTintingAllowed(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushbooleancpp(L, AnimationPtr->IsColorModulationAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetCurrentFrame(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushnumber(L, AnimationPtr->GetCurrentFrame());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetCurrentAction(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushstring(L, AnimationPtr->GetCurrentAction().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsPlaying(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ lua_pushbooleancpp(L, AnimationPtr->IsRunning());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationLoopPointCallback(unsigned int Handle)
+{
+ lua_State * L = static_cast<lua_State *>(BS_Kernel::GetInstance()->GetScript()->GetScriptObject());
+ LoopPointCallbackPtr->InvokeCallbackFunctions(L, Handle);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_RegisterLoopPointCallback(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ LoopPointCallbackPtr->RegisterCallbackFunction(L, AnimationPtr->GetHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_UnregisterLoopPointCallback(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ LoopPointCallbackPtr->UnregisterCallbackFunction(L, AnimationPtr->GetHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationActionCallback(unsigned int Handle)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr(Handle);
+ if (AnimationPtr.IsValid())
+ {
+ ActionCallbackPtr->Action = AnimationPtr->GetCurrentAction();
+ lua_State * L = static_cast<lua_State *>(BS_Kernel::GetInstance()->GetScript()->GetScriptObject());
+ ActionCallbackPtr->InvokeCallbackFunctions(L, AnimationPtr->GetHandle());
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_RegisterActionCallback(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ ActionCallbackPtr->RegisterCallbackFunction(L, AnimationPtr->GetHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_UnregisterActionCallback(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ ActionCallbackPtr->UnregisterCallbackFunction(L, AnimationPtr->GetHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationDeleteCallback(unsigned int Handle)
+{
+ lua_State * L = static_cast<lua_State *>(BS_Kernel::GetInstance()->GetScript()->GetScriptObject());
+ LoopPointCallbackPtr->RemoveAllObjectCallbacks(L, Handle);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Remove(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.IsValid());
+ AnimationPtr.Erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg ANIMATION_METHODS[] =
+{
+ "Play", A_Play,
+ "Pause", A_Pause,
+ "Stop", A_Stop,
+ "SetFrame", A_SetFrame,
+ "SetAlpha", A_SetAlpha,
+ "SetTintColor", A_SetTintColor,
+ "SetScaleFactor", A_SetScaleFactor,
+ "SetScaleFactorX", A_SetScaleFactorX,
+ "SetScaleFactorY", A_SetScaleFactorY,
+ "GetScaleFactorX", A_GetScaleFactorX,
+ "GetScaleFactorY", A_GetScaleFactorY,
+ "GetAnimationType", A_GetAnimationType,
+ "GetFPS", A_GetFPS,
+ "GetFrameCount", A_GetFrameCount,
+ "IsScalingAllowed", A_IsScalingAllowed,
+ "IsAlphaAllowed", A_IsAlphaAllowed,
+ "IsTintingAllowed", A_IsTintingAllowed,
+ "GetCurrentFrame", A_GetCurrentFrame,
+ "GetCurrentAction", A_GetCurrentAction,
+ "IsPlaying", A_IsPlaying,
+ "RegisterLoopPointCallback", A_RegisterLoopPointCallback,
+ "UnregisterLoopPointCallback", A_UnregisterLoopPointCallback,
+ "RegisterActionCallback", A_RegisterActionCallback,
+ "UnregisterActionCallback", A_UnregisterActionCallback,
+ "Remove", A_Remove,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_RenderObjectPtr<BS_Text> CheckText(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Text
+ unsigned int * UserDataPtr;
+ if ((UserDataPtr = (unsigned int *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0)
+ {
+ BS_RenderObjectPtr<BS_RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.IsValid())
+ return ROPtr->ToText();
+ else
+ luaL_error(L, "The text with the handle %d does no longer exist.", *UserDataPtr);
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" TEXT_CLASS_NAME "' expected");
+ }
+
+ return BS_RenderObjectPtr<BS_Text>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetFont(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetFont(luaL_checkstring(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetText(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetText(luaL_checkstring(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAlpha(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetColor(BS_GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAutoWrap(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetAutoWrap(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAutoWrapThreshold(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr->SetAutoWrapThreshold(static_cast<unsigned int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetText(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushstring(L, TextPtr->GetText().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetFont(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushstring(L, TextPtr->GetFont().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetAlpha(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushnumber(L, TextPtr->GetAlpha());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetColor(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushnumber(L, TextPtr->GetColor());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_IsAutoWrap(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushbooleancpp(L, TextPtr->IsAutoWrapActive());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetAutoWrapThreshold(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ lua_pushnumber(L, TextPtr->GetAutoWrapThreshold());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_Remove(lua_State * L)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.IsValid());
+ TextPtr.Erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg TEXT_METHODS[] =
+{
+ "SetFont", T_SetFont,
+ "SetText", T_SetText,
+ "SetAlpha", T_SetAlpha,
+ "SetColor", T_SetColor,
+ "SetAutoWrap", T_SetAutoWrap,
+ "SetAutoWrapThreshold", T_SetAutoWrapThreshold,
+ "GetText", T_GetText,
+ "GetFont", T_GetFont,
+ "GetAlpha", T_GetAlpha,
+ "GetColor", T_GetColor,
+ "IsAutoWrap", T_IsAutoWrap,
+ "GetAutoWrapThreshold", T_GetAutoWrapThreshold,
+ "Remove", T_Remove,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_GraphicEngine::RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, BITMAP_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, ANIMATION_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, PANEL_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, TEXT_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, PANEL_CLASS_NAME, PANEL_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, BITMAP_CLASS_NAME, BITMAP_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, TEXT_CLASS_NAME, TEXT_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, ANIMATION_CLASS_NAME, ANIMATION_METHODS)) return false;
+
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, ANIMATION_TEMPLATE_CLASS_NAME, ANIMATION_TEMPLATE_METHODS)) return false;
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, GFX_LIBRARY_NAME, GFX_FUNCTIONS)) return false;
+
+ LoopPointCallbackPtr.reset(new BS_LuaCallback(L));
+ ActionCallbackPtr.reset(new ActionCallback(L));
+
+ return true;
+}
diff --git a/engines/sword25/gfx/image/b25sloader.cpp b/engines/sword25/gfx/image/b25sloader.cpp
new file mode 100755
index 0000000000..55aaf7a2c5
--- /dev/null
+++ b/engines/sword25/gfx/image/b25sloader.cpp
@@ -0,0 +1,101 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <sstream>
+#include <algorithm>
+using namespace std;
+
+#include "b25sloader.h"
+#include "pngloader.h"
+
+#define BS_LOG_PREFIX "B25SLOADER"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ unsigned int FindEmbeddedPNG(const char * FileDataPtr, unsigned int FileSize)
+ {
+ // Einen Stringstream mit dem Anfang der Datei intialisieren. 512 Byte sollten hierfür genügen.
+ istringstream StringStream(string(FileDataPtr, FileDataPtr + min(static_cast<unsigned int>(512), FileSize)));
+
+ // Headerinformationen der Spielstandes einlesen.
+ string Marker, VersionID;
+ unsigned int CompressedGamedataSize, UncompressedGamedataSize;
+ StringStream >> Marker >> VersionID >> CompressedGamedataSize >> UncompressedGamedataSize;
+ if (!StringStream.good()) return 0;
+
+ // Testen, ob wir tatsächlich einen Spielstand haben.
+ if (Marker == "BS25SAVEGAME")
+ {
+ // Offset zum PNG innerhalb des Spielstandes berechnen und zurückgeben.
+ return static_cast<unsigned int>(StringStream.tellg()) + CompressedGamedataSize + 1;
+ }
+
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_B25SLoader::IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize)
+{
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0)
+ {
+ return BS_PNGLoader::DoIsCorrectImageFormat(FileDataPtr + PNGOffset, FileSize - PNGOffset);
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_B25SLoader::DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr,
+ int & Width, int & Height, int & Pitch)
+{
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0)
+ {
+ return BS_PNGLoader::DoDecodeImage(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_B25SLoader::ImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height)
+{
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ unsigned int PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0)
+ {
+ return BS_PNGLoader::DoImageProperties(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, Width, Height);
+ }
+
+ return false;
+}
diff --git a/engines/sword25/gfx/image/b25sloader.h b/engines/sword25/gfx/image/b25sloader.h
new file mode 100755
index 0000000000..bafea87365
--- /dev/null
+++ b/engines/sword25/gfx/image/b25sloader.h
@@ -0,0 +1,52 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_B25SLOADER_H
+#define BS_B25SLOADER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "imageloader.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_B25SLoader : public BS_ImageLoader
+{
+public:
+ static BS_ImageLoader * CreateInstance()
+ {
+ #include "kernel/memlog_off.h"
+ return static_cast<BS_ImageLoader *>(new BS_B25SLoader());
+ #include "kernel/memlog_on.h"
+ }
+
+protected:
+ virtual bool IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize);
+ virtual bool DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr,
+ int & Width, int & Height, int & Pitch);
+ virtual bool ImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height);
+
+};
+
+#endif
diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h
new file mode 100755
index 0000000000..8f6fe9cab9
--- /dev/null
+++ b/engines/sword25/gfx/image/image.h
@@ -0,0 +1,208 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Image
+ --------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_IMAGE_H
+#define BS_IMAGE_H
+
+// Includes
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+#include "kernel/common.h"
+#include "math/rect.h"
+#include "gfx/graphicengine.h"
+
+class BS_Image
+{
+public:
+ virtual ~BS_Image() {};
+
+ // Enums
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS
+ {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ //@{
+ /** @name Accessor-Methoden */
+
+ /**
+ @brief Gibt die Breite des Bildes in Pixeln zurück
+ */
+ virtual int GetWidth() const = 0;
+
+ /**
+ @brief Gibt die Höhe des Bildes in Pixeln zurück
+ */
+ virtual int GetHeight() const = 0;
+
+ /**
+ @brief Gibt das Farbformat des Bildes zurück
+ */
+ virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const = 0;
+
+ //@}
+
+ //@{
+ /** @name Render-Methoden */
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param pDest ein Pointer auf das Zielbild. In den meisten Fällen ist dies der Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein BS_Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ - IsSetContentAllowed()
+ */
+ virtual bool Blit(int PosX = 0, int PosY = 0,
+ int Flipping = FLIP_NONE,
+ BS_Rect* pPartRect = NULL,
+ unsigned int Color = BS_ARGB(255, 255, 255, 255),
+ int Width = -1, int Height = -1) = 0;
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein BS_Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ virtual bool Fill(const BS_Rect* pFillRect = 0, unsigned int Color = BS_RGB(0, 0, 0)) = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride) = 0;
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual unsigned int GetPixel(int X, int Y) = 0;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob an dem BS_Image Blit() aufgerufen werden darf.
+ @return Gibt false zurück, falls ein Blit()-Aufruf an diesem Objekt nicht gestattet ist.
+ */
+ virtual bool IsBlitSource() const = 0;
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ virtual bool IsBlitTarget() const = 0;
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ virtual bool IsScalingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ virtual bool IsFillingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ virtual bool IsAlphaAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ virtual bool IsColorModulationAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn der Inhalt des BS_Image durch eine Aufruf von SetContent() ausgetauscht werden kann.
+ */
+ virtual bool IsSetContentAllowed() const = 0;
+
+ //@}
+};
+
+#endif
diff --git a/engines/sword25/gfx/image/imageloader.cpp b/engines/sword25/gfx/image/imageloader.cpp
new file mode 100755
index 0000000000..18d94a4e21
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader.cpp
@@ -0,0 +1,120 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "imageloader.h"
+#include "imageloader_ids.h"
+
+#define BS_LOG_PREFIX "IMAGELOADER"
+
+// Statische Elemente der Klasse BS_ImageLoader intialisieren.
+std::list<BS_ImageLoader*> BS_ImageLoader::_ImageLoaderList;
+bool BS_ImageLoader::_ImageLoaderListInitialized = false;
+
+// Lade Methode
+// ------------
+
+bool BS_ImageLoader::LoadImage(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat,
+ char*& pUncompressedData,
+ int& Width, int& Height,
+ int& Pitch)
+{
+ // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
+ if (!_ImageLoaderListInitialized)
+ _InitializeLoaderList();
+
+ // Passenden BS_ImageLoader finden und Bild dekodieren
+ BS_ImageLoader* pLoader = _FindSuitableImageLoader(pFileData, FileSize);
+ if (pLoader)
+ {
+ return pLoader->DecodeImage(pFileData, FileSize,
+ ColorFormat,
+ pUncompressedData,
+ Width, Height,
+ Pitch);
+ }
+
+ return false;
+}
+
+// Info Methode
+// ------------
+
+bool BS_ImageLoader::ExtractImageProperties(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS& ColorFormat,
+ int& Width, int& Height)
+{
+ // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
+ if (!_ImageLoaderListInitialized)
+ _InitializeLoaderList();
+
+ // Passenden BS_ImageLoader finden und Bildeigenschaften auslesen.
+ BS_ImageLoader* pLoader = _FindSuitableImageLoader(pFileData, FileSize);
+ if (pLoader)
+ {
+ return pLoader->ImageProperties(pFileData, FileSize,
+ ColorFormat,
+ Width, Height);
+ }
+
+ return false;
+}
+
+// Verwaltungs Methoden
+// --------------------
+
+void BS_ImageLoader::_InitializeLoaderList()
+{
+ // Von jedem BS_ImageLoader wird eine Instanz erzeugt, diese fügen sich selbständig in die BS_ImageLoader-Liste ein.
+ for (int i = 0; i < BS_IMAGELOADER_COUNT; i++)
+ BS_IMAGELOADER_IDS[i]();
+
+ // Die Liste als gefüllt markieren.
+ _ImageLoaderListInitialized = true;
+
+ // Sicherstellen, dass beim Beenden alle BS_ImageLoader Instanzen zerstört werden.
+ atexit(BS_ImageLoader::_DeinitializeLoaderList);
+}
+
+void BS_ImageLoader::_DeinitializeLoaderList()
+{
+ while (!_ImageLoaderList.empty())
+ {
+ delete _ImageLoaderList.back();
+ _ImageLoaderList.pop_back();
+ }
+}
+
+BS_ImageLoader* BS_ImageLoader::_FindSuitableImageLoader(const char* pFileData, unsigned int FileSize)
+{
+ // Alle BS_ImageLoader-Objekte durchgehen, bis eins gefunden wurde, dass das Bild laden kann
+ std::list<BS_ImageLoader*>::iterator Iter = _ImageLoaderList.begin();
+ for (; Iter != _ImageLoaderList.end(); ++Iter)
+ {
+ // Falls ein geeigneter BS-ImageLoader gefunden wurde, wird er zurückgegeben.
+ if ((*Iter)->IsCorrectImageFormat(pFileData, FileSize))
+ {
+ return (*Iter);
+ }
+ }
+
+ // Es konnte kein passender BS_ImageLoader gefunden werden.
+ BS_LOG_ERRORLN("Could not find suitable image loader for image data.");
+ return NULL;
+}
diff --git a/engines/sword25/gfx/image/imageloader.h b/engines/sword25/gfx/image/imageloader.h
new file mode 100755
index 0000000000..952c36b093
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader.h
@@ -0,0 +1,358 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_ImageLoader
+ --------------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_IMAGELOADER_H
+#define BS_IMAGELOADER_H
+
+// Includes
+#include "kernel/bs_stdint.h"
+#include "kernel/common.h"
+#include "../graphicengine.h"
+
+// Die folgenden Header vertragen sich nicht mit der Memoryleak-Detection, daher wird sie kurzzeitig deaktiviert
+#include "kernel/memlog_off.h"
+#include <list>
+#include "kernel/memlog_on.h"
+
+/**
+ @brief Über die statischen Methoden dieser Klasse werden alle unterstützten Bildformate geladen.
+
+ Zum Laden von Bildern wird die #LoadImage-Methode benutzt.
+
+ Außerdem stellt diese Klasse das Interface da, das alle Klassen implementieren müssen, die Bildformate einlesen.<br>
+ Zur Unterstützung eines neuen Bildformates muss folgendermaßen vorgegangen werden:
+ - Erzeugen einer neuen von #BS_ImageLoader abgeleiteten Klasse, die die Methoden #IsCorrectImageFormat und #DecodeImage impelementiert.
+ - Die Klasse muss eine statische Methode haben, die eine Instanz von ihr erzeugt und einen Pointer darauf zurückgibt.
+ - Diese Methode muss in der Liste in der Datei imageloader_ids.h eingetragen werden.
+ - Die Klasse muss JEDES Eingabebild seines Bildformates in die folgenden Farbformate konvertieren können:
+ - BS_GraphicEngine::CF_RGB16
+ - BS_GraphicEngine::CF_RGB15
+ - BS_GraphicEngine::CF_RGB16_INTERLEAVED
+ - BS_GraphicEngine::CF_RGB15_INTERLEAVED
+ - BS_GraphicEngine::CF_ARGB32
+ - BS_GraphicEngine::CF_BGRA32
+ - Zum Konvertieren der Bilddaten können die Hilfsmethoden dieser Klasse benutzt werden, die ARGB Bilddaten in alle benötigten
+ Farbformate konvertieren.
+*/
+class BS_ImageLoader
+{
+public:
+
+ //@{
+ /** @name Lade Methoden */
+
+ /**
+ @brief Lädt eine Bilddatei.
+
+ Diese Methode kann sämtliche unterstütztem Bildformate lesen. Die Methode erkennt selbstständing um welches Dateiformat es sich
+ bei der vorliegenden Datei handelt.<br>
+ Bisher wird nur PNG unterstützt.
+
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe der Bilddaten in Byte.
+ @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
+ Folgende Farbformate werden unterstützt:
+ - BS_GraphicEngine::CF_RGB16
+ - BS_GraphicEngine::CF_RGB15
+ - BS_GraphicEngine::CF_RGB16_INTERLEAVED
+ - BS_GraphicEngine::CF_RGB15_INTERLEAVED
+ - BS_GraphicEngine::CF_ARGB32
+ @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
+ @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
+ @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
+ @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
+ @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
+ @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ */
+ static bool LoadImage(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat,
+ char*& pUncompressedData,
+ int& Width, int& Height,
+ int& Pitch);
+
+ /**
+ @brief Liest die Bildeigenschaften eines Bildes aus.
+
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe des Bilddaten in Byte.
+ @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
+ @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
+ @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
+ @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ */
+ static bool ExtractImageProperties(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS& ColorFormat,
+ int& Width, int& Height);
+ //@}
+
+protected:
+
+ // Protected Konstruktor, damit Instanzen dieser Klasse nur von BS_ImageLoader-Objekten erstellt werden können
+ /**
+ @brief Der Standardkonstruktor.
+
+ Dieser Konstruktor registriert alle Instanzen von #BS_ImageLoader-Klassen in einer Liste.<br>
+ Diese Liste enthält jeweils eine Instanz jedes #BS_ImageLoader und wird benutzt um beliebige Bilddateien einem Loader zuzuordnen.
+ @remark Dieser Konstruktor ist protected damit nur #BS_ImageLoader-Objekte diese Klasse instanziieren können.
+ */
+ BS_ImageLoader()
+ {
+ // Klasse registrieren
+ _ImageLoaderList.push_front(this);
+ }
+
+ //@{
+ /** @name Abstrakte Methoden */
+
+ /**
+ @brief Gibt an, ob der #BS_ImageLoader ein Bild lesen kann.
+ @param pFileData ein Pointer auf die kompletten Daten des Bildes.
+ @param FileSize die Größe der Daten in Byte.
+ @return Gibt true zurück, wenn der #BS_ImageLoader das Bild lesen kann, ansonsten false.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool IsCorrectImageFormat(const char* pFileData, unsigned int FileSize) = 0;
+
+ /**
+ @brief Lädt eine Bilddatei.
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe der Bilddaten in Byte.
+ @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
+ Folgende Farbformate werden unterstützt:
+ - BS_GraphicEngine::CF_RGB16
+ - BS_GraphicEngine::CF_RGB15
+ - BS_GraphicEngine::CF_RGB16_INTERLEAVED
+ - BS_GraphicEngine::CF_RGB15_INTERLEAVED
+ - BS_GraphicEngine::CF_ARGB32
+ @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
+ @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
+ @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
+ @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
+ @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
+ @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool DecodeImage(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat,
+ char*& pUncompressedData,
+ int& Width, int& Height,
+ int& Pitch) = 0;
+
+ /**
+ @brief Liest die Bildeigenschaften aus.
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe des Bilddaten in Byte.
+ @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
+ @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
+ @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
+ @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool ImageProperties(const char* pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS& ColorFormat,
+ int& Width, int& Height) = 0;
+
+ //@}
+
+ //@{
+ /** @name Konvertierungsmethoden */
+
+ /**
+ @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB16 Farbformat.
+ @param pSrcData ein Pointer auf die Quelldaten.
+ @param pDestData ein Pointer auf den Zielpuffern.
+ @param Width die Anzahl der Pixel in der Bildzeile.
+ @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br>
+ Es sind mindestens Width * 2 Byte notwendig.
+ */
+ static void RowARGB32ToRGB16(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width)
+ {
+ for (unsigned int i = 0; i < Width; i++)
+ {
+ ((uint16_t*)pDestData)[i] = ((pSrcData[2] >> 3) << 11) | ((pSrcData[1] >> 2) << 5) | (pSrcData[0] >> 3);
+ pSrcData += 4;
+ }
+ }
+
+ /**
+ @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB15 Farbformat.
+ @param pSrcData ein Pointer auf die Quelldaten.
+ @param pDestData ein Pointer auf den Zielpuffern.
+ @param Width die Anzahl der Pixel in der Bildzeile.
+ @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br>
+ Es sind mindestens Width * 2 Byte notwendig.
+ */
+ static void RowARGB32ToRGB15(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width)
+ {
+ for (unsigned int i = 0; i < Width; i++)
+ {
+ ((uint16_t*)pDestData)[i] = ((pSrcData[2] >> 3) << 10) | ((pSrcData[1] >> 3) << 5) | (pSrcData[0] >> 3);
+ pSrcData += 4;
+ }
+ }
+
+ /**
+ @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB16_INTERLEAVED Farbformat.
+ @param pSrcData ein Pointer auf die Quelldaten.
+ @param pDestData ein Pointer auf den Zielpuffern.
+ @param Width die Anzahl der Pixel in der Bildzeile.
+ @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß sein muss.<br>
+ Es sind mindestens ((Width + 3) / 4) * 12 Byte notwendig.
+ */
+ static void RowARGB32ToRGB16_INTERLEAVED(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width)
+ {
+ // Die Pixelblöcke erstellen, dabei werden immer jeweils 4 Pixel zu einem Block zusammengefasst
+ unsigned int BlockFillCount = 0;
+ unsigned int AlphaBlock = 0;
+ for (unsigned int i = 0; i < Width; i++)
+ {
+ // Alphawert in den Alphablock schreiben
+ AlphaBlock = (AlphaBlock >> 8) | (pSrcData[BlockFillCount * 4 + 3] << 24);
+
+ // Füllstand der Pixelblockes aktualisieren
+ BlockFillCount++;
+
+ // Sobald 4 Alphawerte gesammelt wurden, oder die Zeile zu Ende ist wird der Pixelblock in den Zielpuffer geschrieben
+ if (BlockFillCount == 4 || i == (Width - 1))
+ {
+ // Falls der AlphaBlock nicht ganz gefüllt ist muss geshiftet werden um sicherzustellen, dass die Alphawerte
+ // "left aligned" sind.
+ AlphaBlock >>= (4 - BlockFillCount) * 8;
+
+ // Alphablock schreiben
+ *((unsigned int*)pDestData) = AlphaBlock;
+ pDestData += 4;
+
+ // Pixel konvertieren und schreiben
+ RowARGB32ToRGB16(pSrcData, pDestData, BlockFillCount);
+
+ // Pointer auf den nächsten Platz im Zielpuffer setzen
+ pDestData += 8;
+
+ // Pointer auf die nächsten 4 Pixel im Quellpuffer setzen
+ pSrcData += 16;
+
+ // Neuen Pixelblock beginnen
+ BlockFillCount = 0;
+ }
+ }
+ }
+
+ /**
+ @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_RGB15_INTERLEAVED Farbformat.
+ @param pSrcData ein Pointer auf die Quelldaten.
+ @param pDestData ein Pointer auf den Zielpuffern.
+ @param Width die Anzahl der Pixel in der Bildzeile.
+ @remark Es gilt zu beachten, dass der Zielpuffer ausreichend groß ist.<br>
+ Es sind mindestens (Width / 4 + Width % 4) * 3 Byte notwendig.
+ */
+ static void RowARGB32ToRGB15_INTERLEAVED(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width)
+ {
+ // Die Pixelblöcke erstellen, dabei werden immer jeweils 4 Pixel zu einem Block zusammengefasst
+ unsigned int BlockFillCount = 0;
+ unsigned int AlphaBlock = 0;
+ for (unsigned int i = 0; i < Width; i++)
+ {
+ // Alphawert in den Alphablock schreiben
+ AlphaBlock = (AlphaBlock >> 8) | (pSrcData[BlockFillCount * 4 + 3] << 24);
+
+ // Füllstand der Pixelblockes aktualisieren
+ BlockFillCount++;
+
+ // Sobald 4 Alphawerte gesammelt wurden, oder die Zeile zu Ende ist wird der Pixelblock in den Zielpuffer geschrieben
+ if (BlockFillCount == 4 || i == (Width - 1))
+ {
+ // Falls der AlphaBlock nicht ganz gefüllt ist muss geshiftet werden um sicherzustellen, dass die Alphawerte
+ // "left aligned" sind.
+ AlphaBlock >>= (4 - BlockFillCount) * 8;
+
+ // Alphablock schreiben
+ *((unsigned int*)pDestData) = AlphaBlock;
+ pDestData += 4;
+
+ // Pixel konvertieren und schreiben
+ RowARGB32ToRGB15(pSrcData, pDestData, BlockFillCount);
+
+ // Pointer auf den nächsten Platz im Zielpuffer setzen
+ pDestData += 8;
+
+ // Pointer auf die nächsten 4 Pixel im Quellpuffer setzen
+ pSrcData += 16;
+
+ // Neuen Pixelblock beginnen
+ BlockFillCount = 0;
+ }
+ }
+ }
+
+ /**
+ @brief Konvertiert eine Bildzeile mit ARGB Pixeldaten in das BS_GraphicEngine::CF_BGRA32 Farbformat.
+ @param pSrcData ein Pointer auf die Quelldaten.
+ @param pDestData ein Pointer auf den Zielpuffern.
+ @param Width die Anzahl der Pixel in der Bildzeile.
+ */
+ static void RowARGB32ToABGR32(unsigned char* pSrcData, unsigned char* pDestData, unsigned int Width)
+ {
+ for (unsigned int i = 0; i < Width; ++i)
+ {
+ *pDestData++ = pSrcData[2];
+ *pDestData++ = pSrcData[1];
+ *pDestData++ = pSrcData[0];
+ *pDestData++ = pSrcData[3];
+
+ pSrcData += 4;
+ }
+ }
+
+private:
+
+ /**
+ @brief Erzeugt je eine Instanz aller BS_ImageLoader Klassen und fügt diese in eine interne Liste ein. Diese werden dann beim
+ Laden von Bildern benutzt.
+ @remark Die Klassen müssen in der Datei imageloader_ids.h eingetragen sein, damit sie an dieser Stelle berücksichtigt werden.
+ */
+ static void _InitializeLoaderList();
+
+ /**
+ @brief Zerstört alle Instanzen von BS_ImageLoader Klassen, die in dieser Klasse registriert sind.
+ */
+ static void _DeinitializeLoaderList();
+
+ /**
+ @brief Sucht zu Bilddaten ein BS_ImageLoader Objekt, dass die Bilddaten dekodieren kann.
+ @return Gibt einen Pointer auf ein passendes BS_ImageLoader Objekt zurück, oder NULL, wenn kein passendes Objekt gefunden wurde.
+ */
+ static BS_ImageLoader* BS_ImageLoader::_FindSuitableImageLoader(const char* pFileData, unsigned int FileSize);
+
+ static std::list<BS_ImageLoader*> _ImageLoaderList; // Die Liste aller BS_ImageLoader-Objekte
+ static bool _ImageLoaderListInitialized; // Gibt an, ob die Liste schon intialisiert wurde
+};
+
+#endif
diff --git a/engines/sword25/gfx/image/imageloader_ids.h b/engines/sword25/gfx/image/imageloader_ids.h
new file mode 100755
index 0000000000..4564996a0f
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader_ids.h
@@ -0,0 +1,44 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ imageloader_ids.h
+ -----------------
+ In dieser Datei sind alle ImageLoader verzeichnet.
+ JEDER neuer ImageLoader muss hier eingetragen werden, ansonsten wird er beim Laden eines Bildes nicht berücksichtigt.
+
+ Autor: Malte Thiesen
+*/
+
+#include "imageloader.h"
+
+// Die Headerdateien der ImageLoader müssen hier eingebunden werden
+#include "pngloader.h"
+#include "b25sloader.h"
+
+// Die Tabelle enthält Pointer auf statische Member-Funktionen innerhalb der Klassen, die eine Instanz der Klasse
+// erzeugen
+typedef BS_ImageLoader* (*BS_IMAGELOADER_NEW)();
+const BS_IMAGELOADER_NEW BS_IMAGELOADER_IDS[] =
+{
+ BS_PNGLoader::CreateInstance,
+ BS_B25SLoader::CreateInstance,
+};
+const int BS_IMAGELOADER_COUNT = sizeof(BS_IMAGELOADER_IDS) / sizeof(BS_IMAGELOADER_NEW);
+
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp
new file mode 100755
index 0000000000..3815f4e4ff
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.cpp
@@ -0,0 +1,389 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "image.h"
+#include "pngloader.h"
+#include "util/libpng/png.h"
+
+#define BS_LOG_PREFIX "PNGLOADER"
+
+// -----------------------------------------------------------------------------
+// Konstruktor / Destruktor
+// -----------------------------------------------------------------------------
+
+BS_PNGLoader::BS_PNGLoader()
+{
+}
+
+// -----------------------------------------------------------------------------
+// Laden
+// -----------------------------------------------------------------------------
+
+static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ memcpy(data, (char*)png_ptr->io_ptr, length);
+ png_ptr->io_ptr = (void*)((png_size_t)png_ptr->io_ptr + length);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::DoDecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr,
+ int & Width, int & Height, int & Pitch)
+{
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ png_bytep RawDataBuffer = NULL;
+ png_bytep* pRowPtr = NULL;
+
+ int BitDepth;
+ int ColorType;
+ int InterlaceType;
+ int i;
+
+ // Zielfarbformat überprüfen
+ if (ColorFormat != BS_GraphicEngine::CF_RGB16 &&
+ ColorFormat != BS_GraphicEngine::CF_RGB15 &&
+ ColorFormat != BS_GraphicEngine::CF_RGB16_INTERLEAVED &&
+ ColorFormat != BS_GraphicEngine::CF_RGB15_INTERLEAVED &&
+ ColorFormat != BS_GraphicEngine::CF_ARGB32 &&
+ ColorFormat != BS_GraphicEngine::CF_ABGR32)
+ {
+ BS_LOG_ERRORLN("Illegal or unsupported color format.");
+ return false;
+ }
+
+ try
+ {
+ // PNG Signatur überprüfen
+ if (!png_check_sig(reinterpret_cast<png_bytep>(const_cast<char *>(FileDataPtr)), 8))
+ {
+ throw(0);
+ }
+
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create libpng read struct.");
+ throw(0);
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create libpng info struct.");
+ throw(0);
+ }
+
+ // Rücksprungpunkt setzen. Wird im Falle eines Fehlers angesprungen.
+ if (setjmp(png_jmpbuf(png_ptr))) throw(0);
+
+ // Alternative Lesefunktion benutzen
+ png_set_read_fn(png_ptr, (void*)FileDataPtr, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL);
+
+ // Pitch des Ausgabebildes berechnen
+ Pitch = BS_GraphicEngine::CalcPitch(ColorFormat, Width);
+
+ // Speicher für die endgültigen Bilddaten reservieren
+ // Dieses geschieht vor dem reservieren von Speicher für temporäre Bilddaten um die Fragmentierung des Speichers gering zu halten
+ UncompressedDataPtr = new char[Pitch * Height];
+ if (!UncompressedDataPtr)
+ {
+ BS_LOG_ERRORLN("Could not allocate memory for output image.");
+ throw(0);
+ }
+
+ // Bilder jeglicher Farbformate werden zunächst in ARGB Bilder umgewandelt
+ if (BitDepth == 16)
+ png_set_strip_16(png_ptr);
+ if (ColorType == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(png_ptr);
+ if (BitDepth < 8)
+ png_set_expand(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+ if (ColorType == PNG_COLOR_TYPE_GRAY ||
+ ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ png_set_bgr(png_ptr);
+
+ if (ColorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ // Nachdem die Transformationen registriert wurden, werden die Bilddaten erneut eingelesen
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
+
+ // PNGs ohne Interlacing werden Zeilenweise eingelesen
+ if (InterlaceType == PNG_INTERLACE_NONE)
+ {
+ // Speicher für eine Bildzeile reservieren
+ RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
+ if (!RawDataBuffer)
+ {
+ BS_LOG_ERRORLN("Could not allocate memory for row buffer.");
+ throw(0);
+ }
+
+ // Bilddaten zeilenweise einlesen und in das gewünschte Zielformat konvertieren
+ for (i = 0; i < Height; i++)
+ {
+ // Zeile einlesen
+ png_read_row(png_ptr, RawDataBuffer, NULL);
+
+ // Zeile konvertieren
+ switch (ColorFormat)
+ {
+ case BS_GraphicEngine::CF_RGB16:
+ RowARGB32ToRGB16((unsigned char*)RawDataBuffer,
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB15:
+ RowARGB32ToRGB15((unsigned char*)RawDataBuffer,
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
+ RowARGB32ToRGB16_INTERLEAVED((unsigned char*)RawDataBuffer,
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
+ RowARGB32ToRGB15_INTERLEAVED((unsigned char*)RawDataBuffer,
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_ARGB32:
+ memcpy(&UncompressedDataPtr[i * Pitch],
+ RawDataBuffer,
+ Pitch);
+ break;
+
+ case BS_GraphicEngine::CF_ABGR32:
+ RowARGB32ToABGR32((unsigned char*)RawDataBuffer,
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ default:
+ BS_ASSERT(false);
+ }
+ }
+ }
+ // PNGs mit Interlacing werden an einem Stück eingelesen
+ else
+ {
+ // Speicher für das komplette Bild reservieren
+ RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr) * Height];
+ if (!RawDataBuffer)
+ {
+ BS_LOG_ERRORLN("Could not allocate memory for raw image buffer.");
+ throw(0);
+ }
+
+ // Speicher für die Rowpointer reservieren
+ pRowPtr = new png_bytep[Height];
+ if (!pRowPtr)
+ {
+ BS_LOG_ERRORLN("Could not allocate memory for row pointers.");
+ throw(0);
+ }
+
+ // Alle Rowpointer mit den richtigen Offsets initialisieren
+ for (i = 0; i < Height; i++)
+ pRowPtr[i] = RawDataBuffer + i * png_get_rowbytes(png_ptr, info_ptr);
+
+ // Bild einlesen
+ png_read_image(png_ptr, pRowPtr);
+
+ // Bilddaten zeilenweise in das gewünschte Ausgabeformat konvertieren
+ switch (ColorFormat)
+ {
+ case BS_GraphicEngine::CF_RGB16:
+ for (i = 0; i < Height; i++)
+ RowARGB32ToRGB16((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]),
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB15:
+ for (i = 0; i < Height; i++)
+ RowARGB32ToRGB15((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]),
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
+ for (i = 0; i < Height; i++)
+ RowARGB32ToRGB16_INTERLEAVED((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]),
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
+ for (i = 0; i < Height; i++)
+ RowARGB32ToRGB15_INTERLEAVED((unsigned char*)(&RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)]),
+ (unsigned char*)&UncompressedDataPtr[i * Pitch],
+ Width);
+ break;
+
+ case BS_GraphicEngine::CF_ARGB32:
+ for (i = 0; i < Height; i++)
+ memcpy(&UncompressedDataPtr[i * Pitch],
+ &RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)],
+ Pitch);
+ break;
+ }
+ }
+
+ // Die zusätzlichen Daten am Ende des Bildes lesen
+ png_read_end(png_ptr, NULL);
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ // Temporäre Buffer freigeben
+ delete[] pRowPtr;
+ delete[] RawDataBuffer;
+ }
+
+ catch(int)
+ {
+ delete[] pRowPtr;
+ delete[] RawDataBuffer;
+ if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL);
+ if (info_ptr) png_destroy_read_struct(NULL, &info_ptr, NULL);
+
+ // Der Funktionsaufruf war nicht erfolgreich
+ return false;
+ }
+
+ // Der Funktionsaufruf war erfolgreich
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::DecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr,
+ int & Width, int & Height, int & Pitch)
+{
+ return DoDecodeImage(FileDataPtr, FileSize, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::DoImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height)
+{
+ // PNG Signatur überprüfen
+ if (!DoIsCorrectImageFormat(FileDataPtr, FileSize)) return false;
+
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ try
+ {
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create libpng read struct.");
+ throw(0);
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create libpng info struct.");
+ throw(0);
+ }
+
+ // Alternative Lesefunktion benutzen
+ png_set_read_fn(png_ptr, (void*)FileDataPtr, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ int BitDepth;
+ int ColorType;
+ png_get_IHDR(png_ptr, info_ptr, (unsigned long*)&Width, (unsigned long*)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
+
+ // PNG-ColorType in BS ColorFormat konvertieren.
+ if (ColorType & PNG_COLOR_MASK_ALPHA || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ ColorFormat = BS_GraphicEngine::CF_ARGB32;
+ else
+ ColorFormat = BS_GraphicEngine::CF_RGB24;
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ }
+
+ catch (int)
+ {
+ if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL);
+ if (info_ptr) png_destroy_read_struct(NULL, &info_ptr, NULL);
+
+ // Der Funktionsaufruf war nicht erfolgreich
+ return false;
+ }
+
+ return true;
+
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::ImageProperties(const char* FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height)
+{
+ return DoImageProperties(FileDataPtr, FileSize, ColorFormat, Width, Height);
+}
+
+// -----------------------------------------------------------------------------
+// Header überprüfen
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::DoIsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize)
+{
+ if (FileSize > 8)
+ return png_check_sig((unsigned char*)FileDataPtr, 8) ? true : false;
+ else
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PNGLoader::IsCorrectImageFormat(const char* FileDataPtr, unsigned int FileSize)
+{
+ return DoIsCorrectImageFormat(FileDataPtr, FileSize);
+}
diff --git a/engines/sword25/gfx/image/pngloader.h b/engines/sword25/gfx/image/pngloader.h
new file mode 100755
index 0000000000..5fdedf49fc
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.h
@@ -0,0 +1,64 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_PNGLoader
+ ------------
+ BS_ImageLoader-Klasse zum Laden von PNG-Dateien
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_PNGLOADER2_H
+#define BS_PNGLOADER2_H
+
+// Includes
+#include "kernel/common.h"
+#include "imageloader.h"
+
+// Klassendefinition
+class BS_PNGLoader : public BS_ImageLoader
+{
+public:
+ static BS_ImageLoader* CreateInstance()
+ {
+ #include "kernel/memlog_off.h"
+ return (BS_ImageLoader*) new BS_PNGLoader();
+ #include "kernel/memlog_on.h"
+ }
+
+ // Alle virtuellen Methoden von BS_ImageLoader sind hier als static-Methode implementiert, damit sie von BS_B25SLoader aufgerufen werden können.
+ // Die virtuellen Methoden rufen diese Methoden auf.
+ static bool DoIsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize);
+ static bool DoDecodeImage(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS ColorFormat, char * & UncompressedDataPtr,
+ int & Width, int & Height, int & Pitch);
+ static bool DoImageProperties(const char * FileDataPtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height);
+
+protected:
+ BS_PNGLoader();
+ bool DecodeImage(const char * pFileData, unsigned int FileSize,
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat,
+ char * & pUncompressedData,
+ int & Width, int & Height,
+ int & Pitch);
+ bool IsCorrectImageFormat(const char * FileDataPtr, unsigned int FileSize);
+ bool ImageProperties(const char * FileDatePtr, unsigned int FileSize, BS_GraphicEngine::COLOR_FORMATS & ColorFormat, int & Width, int & Height);
+};
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp
new file mode 100755
index 0000000000..035d572149
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.cpp
@@ -0,0 +1,571 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/bs_stdint.h"
+#include "vectorimage.h"
+#include <vector>
+#include <stdexcept>
+
+#include "agg_bounding_rect.h"
+
+using namespace std;
+
+#define BS_LOG_PREFIX "VECTORIMAGE"
+
+
+// -----------------------------------------------------------------------------
+// SWF Datentypen
+// -----------------------------------------------------------------------------
+
+typedef uint8_t u8;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef int16_t s32;
+
+
+// -----------------------------------------------------------------------------
+// Bitstream Hilfsklasse
+// -----------------------------------------------------------------------------
+// Das Parsen von SWF-Dateien erfordert sowohl bitweises Auslesen als auch an
+// Bytegrenzen ausgerichtetes Lesen.
+// Diese Klasse ist speziell dafür ausgestattet.
+// -----------------------------------------------------------------------------
+
+class BS_VectorImage::SWFBitStream
+{
+public:
+ SWFBitStream(const unsigned char * pData, unsigned int DataSize) :
+ m_Pos(pData), m_End(pData + DataSize), m_WordMask(0)
+ {}
+
+ inline u32 GetBits(unsigned int BitCount)
+ {
+ if (BitCount == 0 || BitCount > 32)
+ {
+ throw(runtime_error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time"));
+ }
+
+ u32 value = 0;
+ while (BitCount)
+ {
+ if (m_WordMask == 0) FlushByte();
+
+ value <<= 1;
+ value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
+ m_WordMask >>= 1;
+
+ --BitCount;
+ }
+
+ return value;
+ }
+
+ inline s32 GetSignedBits(unsigned int BitCount)
+ {
+ // Bits einlesen
+ u32 Temp = GetBits(BitCount);
+
+ // Falls das Sign-Bit gesetzt ist, den Rest des Rückgabewertes mit 1-Bits auffüllen (Sign Extension)
+ if (Temp & 1 << (BitCount - 1))
+ return (0xffffffff << BitCount) | Temp;
+ else
+ return Temp;
+ }
+
+ inline u32 GetU32()
+ {
+ u32 Byte1 = GetU8();
+ u32 Byte2 = GetU8();
+ u32 Byte3 = GetU8();
+ u32 Byte4 = GetU8();
+
+ return Byte1 | (Byte2 << 8) | (Byte3 << 16) | (Byte4 << 24);
+ }
+
+ inline u16 GetU16()
+ {
+ u32 Byte1 = GetU8();
+ u32 Byte2 = GetU8();
+
+ return Byte1 | (Byte2 << 8);
+ }
+
+ inline u8 GetU8()
+ {
+ FlushByte();
+ u8 Value = m_Word;
+ m_WordMask = 0;
+ FlushByte();
+
+ return Value;
+ }
+
+ inline void FlushByte()
+ {
+ if (m_WordMask != 128)
+ {
+ if (m_Pos >= m_End)
+ {
+ throw(runtime_error("Attempted to read past end of file"));
+ }
+ else
+ {
+ m_Word = *m_Pos++;
+ m_WordMask = 128;
+ }
+ }
+ }
+
+ inline void SkipBytes(unsigned int SkipLength)
+ {
+ FlushByte();
+ if (m_Pos + SkipLength >= m_End)
+ {
+ throw(runtime_error("Attempted to read past end of file"));
+ }
+ else
+ {
+ m_Pos += SkipLength;
+ m_Word = *(m_Pos - 1);
+ }
+ }
+
+private:
+ const unsigned char * m_Pos;
+ const unsigned char * m_End;
+
+ u8 m_Word;
+ unsigned int m_WordMask;
+};
+
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ // -----------------------------------------------------------------------------
+ // Konstanten
+ // -----------------------------------------------------------------------------
+
+ const u32 MAX_ACCEPTED_FLASH_VERSION = 3; // Die höchste Flash-Dateiversion, die vom Lader akzeptiert wird
+
+
+ // -----------------------------------------------------------------------------
+ // Konvertiert SWF-Rechteckdaten in einem Bitstrom in BS_Rect-Objekte
+ // -----------------------------------------------------------------------------
+
+ BS_Rect FlashRectToBSRect(BS_VectorImage::SWFBitStream & bs)
+ {
+ bs.FlushByte();
+
+ // Feststellen mit wie vielen Bits die einzelnen Komponenten kodiert sind
+ u32 BitsPerValue = bs.GetBits(5);
+
+ // Die einzelnen Komponenten einlesen
+ s32 XMin = bs.GetSignedBits(BitsPerValue);
+ s32 XMax = bs.GetSignedBits(BitsPerValue);
+ s32 YMin = bs.GetSignedBits(BitsPerValue);
+ s32 YMax = bs.GetSignedBits(BitsPerValue);
+
+ return BS_Rect(XMin, YMin, XMax + 1, YMax + 1);
+ }
+
+
+ // -----------------------------------------------------------------------------
+ // Konvertiert SWF-Farben in AntiGrain Farben
+ // -----------------------------------------------------------------------------
+
+ agg::rgba8 FlashColorToAGGRGBA8(unsigned int FlashColor)
+ {
+ agg::rgba8 ResultColor((FlashColor >> 16) & 0xff, (FlashColor >> 8) & 0xff, FlashColor & 0xff, FlashColor >> 24);
+ ResultColor.premultiply();
+ return ResultColor;
+ }
+
+
+ // -----------------------------------------------------------------------------
+ // Berechnet die Bounding-Box eines BS_VectorImageElement
+ // -----------------------------------------------------------------------------
+
+ struct CBBGetId
+ {
+ CBBGetId(const BS_VectorImageElement & VectorImageElement_) : VectorImageElement(VectorImageElement_) {}
+ unsigned operator [] (unsigned i) const { return VectorImageElement.GetPathInfo(i).GetID(); }
+ const BS_VectorImageElement & VectorImageElement;
+ };
+
+ BS_Rect CalculateBoundingBox(const BS_VectorImageElement & VectorImageElement)
+ {
+ agg::path_storage Path = VectorImageElement.GetPaths();
+ CBBGetId IdSource(VectorImageElement);
+
+ double x1, x2, y1, y2;
+ agg::bounding_rect(Path, IdSource, 0, VectorImageElement.GetPathCount(), &x1, &y1, &x2, &y2);
+
+ return BS_Rect(static_cast<int>(x1), static_cast<int>(y1), static_cast<int>(x2) + 1, static_cast<int>(y2) + 1);
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+BS_VectorImage::BS_VectorImage(const unsigned char * pFileData, unsigned int FileSize, bool & Success)
+{
+ Success = false;
+
+ // Bitstream-Objekt erzeugen
+ // Im Folgenden werden die Dateidaten aus diesem ausgelesen.
+ SWFBitStream bs(pFileData, FileSize);
+
+ try
+ {
+ // SWF-Signatur überprüfen
+ u32 Signature[3];
+ Signature[0] = bs.GetU8();
+ Signature[1] = bs.GetU8();
+ Signature[2] = bs.GetU8();
+ if (Signature[0] != 'F' ||
+ Signature[1] != 'W' ||
+ Signature[2] != 'S')
+ {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // Versionsangabe überprüfen
+ u32 Version = bs.GetU8();
+ if (Version > MAX_ACCEPTED_FLASH_VERSION)
+ {
+ BS_LOG_ERRORLN("File is of version %d. Highest accepted version is %d.", Version, MAX_ACCEPTED_FLASH_VERSION);
+ return;
+ }
+
+ // Dateigröße auslesen und mit der tatsächlichen Größe vergleichen
+ u32 StoredFileSize = bs.GetU32();
+ if (StoredFileSize != FileSize)
+ {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // SWF-Maße auslesen
+ BS_Rect MovieRect = FlashRectToBSRect(bs);
+
+ // Framerate und Frameanzahl auslesen
+ u32 FrameRate = bs.GetU16();
+ u32 FrameCount = bs.GetU16();
+
+ // Tags parsen
+ // Da wir uns nur für das erste DefineShape-Tag interessieren
+ bool KeepParsing = true;
+ while (KeepParsing)
+ {
+ // Tags beginnen immer an Bytegrenzen
+ bs.FlushByte();
+
+ // Tagtyp und Länge auslesen
+ u16 TagTypeAndLength = bs.GetU16();
+ u32 TagType = TagTypeAndLength >> 6;
+ u32 TagLength = TagTypeAndLength & 0x3f;
+ if (TagLength == 0x3f) TagLength = bs.GetU32();
+
+ switch (TagType)
+ {
+ case 2:
+ // DefineShape
+ Success = ParseDefineShape(2, bs);
+ return;
+ case 22:
+ // DefineShape2
+ Success = ParseDefineShape(2, bs);
+ return;
+ case 32:
+ Success = ParseDefineShape(3, bs);
+ return;
+ default:
+ // Unbekannte Tags ignorieren
+ bs.SkipBytes(TagLength);
+ }
+ }
+ }
+
+ catch (runtime_error & e)
+ {
+ // Fehler loggen und Funktion verlassen
+ // Success ist somit "false" und signalisiert dem Programmierer, dass die Konstruktion fehlgeschlagen ist.
+ BS_LOG_ERRORLN("The following exception occured while parsing a SWF-file: %s", e.what());
+ return;
+ }
+
+ // Die Ausführung darf nicht an dieser Stelle ankommen: Entweder es wird ein Shape gefunden, dann wird die Funktion mit vorher verlassen, oder
+ // es wird keines gefunden, dann tritt eine Exception auf sobald über das Ende der Datei hinaus gelesen wird.
+ BS_ASSERT(false);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImage::ParseDefineShape(unsigned int ShapeType, SWFBitStream & bs)
+{
+ u32 ShapeID = bs.GetU16();
+
+ // Bounding Box auslesen
+ m_BoundingBox = FlashRectToBSRect(bs);
+
+ // Erstes Image-Element erzeugen
+ m_Elements.resize(1);
+
+ // Styles einlesen
+ unsigned int NumFillBits;
+ unsigned int NumLineBits;
+ if (!ParseStyles(ShapeType, bs, NumFillBits, NumLineBits)) return false;
+
+ unsigned int LineStyle = 0;
+ unsigned int FillStyle0 = 0;
+ unsigned int FillStyle1 = 0;
+
+ // Shaperecord parsen
+ // ------------------
+
+ bool EndOfShapeDiscovered = false;
+ while (!EndOfShapeDiscovered)
+ {
+ u32 TypeFlag = bs.GetBits(1);
+
+ // Non-Edge Record
+ if (TypeFlag == 0)
+ {
+ // Feststellen welche Parameter gesetzt werden
+ u32 StateNewStyles = bs.GetBits(1);
+ u32 StateLineStyle = bs.GetBits(1);
+ u32 StateFillStyle1 = bs.GetBits(1);
+ u32 StateFillStyle0 = bs.GetBits(1);
+ u32 StateMoveTo = bs.GetBits(1);
+
+ // End der Shape-Definition erreicht?
+ if (!StateNewStyles && !StateLineStyle && !StateFillStyle0 && !StateFillStyle1 && !StateMoveTo)
+ EndOfShapeDiscovered = true;
+ // Parameter dekodieren
+ else
+ {
+ s32 MoveDeltaX = 0;
+ s32 MoveDeltaY = 0;
+ if (StateMoveTo)
+ {
+ u32 MoveToBits = bs.GetBits(5);
+ MoveDeltaX = bs.GetSignedBits(MoveToBits);
+ MoveDeltaY = bs.GetSignedBits(MoveToBits);
+ }
+
+ if (StateFillStyle0)
+ {
+ if (NumFillBits > 0)
+ FillStyle0 = bs.GetBits(NumFillBits);
+ else
+ FillStyle0 = 0;
+ }
+
+ if (StateFillStyle1)
+ {
+ if (NumFillBits > 0)
+ FillStyle1 = bs.GetBits(NumFillBits);
+ else
+ FillStyle1 = 0;
+ }
+
+ if (StateLineStyle)
+ {
+ if (NumLineBits)
+ LineStyle = bs.GetBits(NumLineBits);
+ else
+ NumLineBits = 0;
+ }
+
+ if (StateNewStyles)
+ {
+ // An dieser Stelle werden in Flash die alten Style-Definitionen verworfen und mit den neuen überschrieben.
+ // Es wird ein neues Element begonnen.
+ m_Elements.resize(m_Elements.size() + 1);
+ if (!ParseStyles(ShapeType, bs, NumFillBits, NumLineBits)) return false;
+ }
+
+ // Ein neuen Pfad erzeugen, es sei denn, es wurden nur neue Styles definiert
+ if (StateLineStyle || StateFillStyle0 || StateFillStyle1 || StateMoveTo)
+ {
+ // Letzte Zeichenposition merken, beim Aufruf von start_new_path() wird die Zeichenpostionen auf 0, 0 zurückgesetzt
+ double LastX = m_Elements.back().m_Paths.last_x();
+ double LastY = m_Elements.back().m_Paths.last_y();
+
+ // Neue Pfadinformation erzeugen
+ m_Elements.back().m_PathInfos.push_back(BS_VectorPathInfo(m_Elements.back().m_Paths.start_new_path(), LineStyle, FillStyle0, FillStyle1));
+
+ // Falls eine Bewegung definiert wurde, wird die Zeichenpositionen an die entsprechende Position gesetzt.
+ // Ansonsten wird die Zeichenposition auf die letzte Zeichenposition gesetzt.
+ if (StateMoveTo)
+ m_Elements.back().m_Paths.move_to(MoveDeltaX, MoveDeltaY);
+ else
+ m_Elements.back().m_Paths.move_to(LastX, LastY);
+ }
+ }
+ }
+ // Edge Record
+ else
+ {
+ u32 EdgeFlag = bs.GetBits(1);
+ u32 NumBits = bs.GetBits(4) + 2;
+
+ // Curved edge
+ if (EdgeFlag == 0)
+ {
+ s32 ControlDeltaX = bs.GetSignedBits(NumBits);
+ s32 ControlDeltaY = bs.GetSignedBits(NumBits);
+ s32 AnchorDeltaX = bs.GetSignedBits(NumBits);
+ s32 AnchorDeltaY = bs.GetSignedBits(NumBits);
+
+ double ControlX = m_Elements.back().m_Paths.last_x() + ControlDeltaX;
+ double ControlY = m_Elements.back().m_Paths.last_y() + ControlDeltaY;
+ double AnchorX = ControlX + AnchorDeltaX;
+ double AnchorY = ControlY + AnchorDeltaY;
+ m_Elements.back().m_Paths.curve3(ControlX, ControlY, AnchorX, AnchorY);
+ }
+ // Staight edge
+ else
+ {
+ s32 DeltaX = 0;
+ s32 DeltaY = 0;
+
+ u32 GeneralLineFlag = bs.GetBits(1);
+ if (GeneralLineFlag)
+ {
+ DeltaX = bs.GetSignedBits(NumBits);
+ DeltaY = bs.GetSignedBits(NumBits);
+ }
+ else
+ {
+ u32 VertLineFlag = bs.GetBits(1);
+ if (VertLineFlag)
+ DeltaY = bs.GetSignedBits(NumBits);
+ else
+ DeltaX = bs.GetSignedBits(NumBits);
+ }
+
+ m_Elements.back().m_Paths.line_rel(DeltaX, DeltaY);
+ }
+ }
+ }
+
+ // Bounding-Boxes der einzelnen Elemente berechnen
+ vector<BS_VectorImageElement>::iterator it = m_Elements.begin();
+ for (; it != m_Elements.end(); ++it) it->m_BoundingBox = CalculateBoundingBox(*it);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImage::ParseStyles(unsigned int ShapeType, SWFBitStream & bs, unsigned int & NumFillBits, unsigned int & NumLineBits)
+{
+ bs.FlushByte();
+
+ // Fillstyles parsen
+ // -----------------
+
+ // Anzahl an Fillstyles bestimmen
+ unsigned int FillStyleCount = bs.GetU8();
+ if (FillStyleCount == 0xff) FillStyleCount = bs.GetU16();
+
+ // Alle Fillstyles einlesen, falls ein Fillstyle mit Typ != 0 gefunden wird, wird das Parsen abgebrochen.
+ // Es wird nur "solid fill" (Typ 0) unterstützt.
+ m_Elements.back().m_FillStyles.reserve(FillStyleCount);
+ for (unsigned int i = 0; i < FillStyleCount; ++i)
+ {
+ u8 Type = bs.GetU8();
+ u32 Color;
+ if (ShapeType == 3)
+ {
+ Color = (bs.GetU8() << 16) | (bs.GetU8() << 8) | bs.GetU8() | (bs.GetU8() << 24);
+ }
+ else
+ Color = bs.GetBits(24) | (0xff << 24);
+ if (Type != 0) return false;
+
+ m_Elements.back().m_FillStyles.push_back(FlashColorToAGGRGBA8(Color));
+ }
+
+ // Linestyles parsen
+ // -----------------
+
+ // Anzahl an Linestyles bestimmen
+ unsigned int LineStyleCount = bs.GetU8();
+ if (LineStyleCount == 0xff) LineStyleCount = bs.GetU16();
+
+ // Alle Linestyles einlesen
+ m_Elements.back().m_LineStyles.reserve(LineStyleCount);
+ for (unsigned int i = 0; i < LineStyleCount; ++i)
+ {
+ double Width = bs.GetU16();
+ u32 Color;
+ if (ShapeType == 3)
+ Color = (bs.GetU8() << 16) | (bs.GetU8() << 8) | bs.GetU8() | (bs.GetU8() << 24);
+ else
+ Color = bs.GetBits(24) | (0xff << 24);
+
+ m_Elements.back().m_LineStyles.push_back(BS_VectorImageElement::LineStyleType(Width, FlashColorToAGGRGBA8(Color)));
+ }
+
+ // Bitbreite für die folgenden Styleindizes auslesen
+ NumFillBits = bs.GetBits(4);
+ NumLineBits = bs.GetBits(4);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImage::Fill(const BS_Rect* pFillRect, unsigned int Color)
+{
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_VectorImage::GetPixel(int X, int Y)
+{
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImage::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride)
+{
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return 0;
+}
diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h
new file mode 100755
index 0000000000..74f6c860d9
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.h
@@ -0,0 +1,167 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_VECTORIMAGE_H
+#define BS_VECTORIMAGE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "gfx/image/image.h"
+#include "math/rect.h"
+
+#include <vector>
+#include "agg_path_storage.h"
+#include "agg_color_rgba.h"
+
+
+class BS_VectorImage;
+
+/**
+ @brief Pfadinformationen zu BS_VectorImageElement Objekten
+
+ Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
+ gespeichert werden.
+*/
+
+class BS_VectorPathInfo
+{
+public:
+ BS_VectorPathInfo(unsigned int ID, unsigned int LineStyle, unsigned int FillStyle0, unsigned int FillStyle1) :
+ m_ID(ID), m_LineStyle(LineStyle), m_FillStyle0(FillStyle0), m_FillStyle1(FillStyle1) {};
+
+ unsigned int GetID() const { return m_ID; }
+ unsigned int GetLineStyle() const { return m_LineStyle; }
+ unsigned int GetFillStyle0() const { return m_FillStyle0; }
+ unsigned int GetFillStyle1() const { return m_FillStyle1; }
+
+private:
+ unsigned int m_ID;
+ unsigned int m_LineStyle;
+ unsigned int m_FillStyle0;
+ unsigned int m_FillStyle1;
+};
+
+
+/**
+ @brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
+ Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
+*/
+class BS_VectorImageElement
+{
+friend BS_VectorImage;
+public:
+ const agg::path_storage & GetPaths() const { return m_Paths; }
+ unsigned int GetPathCount() const { return m_PathInfos.size(); }
+ const BS_VectorPathInfo & GetPathInfo(unsigned int PathNr) const { BS_ASSERT(PathNr < GetPathCount()); return m_PathInfos[PathNr]; }
+
+ double GetLineStyleWidth(unsigned int LineStyle) const
+ {
+ BS_ASSERT(LineStyle < m_LineStyles.size());
+ return m_LineStyles[LineStyle].Width;
+ }
+
+ unsigned int GetLineStyleCount() const { return m_LineStyles.size(); }
+
+ const agg::rgba8 & GetLineStyleColor(unsigned int LineStyle) const
+ {
+ BS_ASSERT(LineStyle < m_LineStyles.size());
+ return m_LineStyles[LineStyle].Color;
+ }
+
+ unsigned int GetFillStyleCount() const { return m_FillStyles.size(); }
+
+ const agg::rgba8 & GetFillStyleColor(unsigned int FillStyle) const
+ {
+ BS_ASSERT(FillStyle < m_FillStyles.size());
+ return m_FillStyles[FillStyle];
+ }
+
+ const BS_Rect & GetBoundingBox() const { return m_BoundingBox; }
+
+private:
+ struct LineStyleType
+ {
+ LineStyleType(double Width_, const agg::rgba8 & Color_) : Width(Width_), Color(Color_) {};
+ double Width;
+ agg::rgba8 Color;
+ };
+
+ agg::path_storage m_Paths;
+ std::vector<BS_VectorPathInfo> m_PathInfos;
+ std::vector<LineStyleType> m_LineStyles;
+ std::vector<agg::rgba8> m_FillStyles;
+ BS_Rect m_BoundingBox;
+};
+
+
+/**
+ @brief Eine Vektorgraphik
+
+ Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
+*/
+
+class BS_VectorImage : public BS_Image
+{
+public:
+ BS_VectorImage(const unsigned char * pFileData, unsigned int FileSize, bool & Success);
+
+ unsigned int GetElementCount() const { return m_Elements.size(); }
+ const BS_VectorImageElement & GetElement(unsigned int ElementNr) const
+ {
+ BS_ASSERT(ElementNr < m_Elements.size());
+ return m_Elements[ElementNr];
+ }
+ const BS_Rect & GetBoundingBox() const { return m_BoundingBox; }
+
+ //
+ // Die abstrakten Methoden von BS_Image
+ //
+ virtual int GetWidth() const { return m_BoundingBox.GetWidth(); }
+ virtual int GetHeight() const { return m_BoundingBox.GetHeight(); }
+ virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const { return BS_GraphicEngine::CF_ARGB32; }
+ virtual bool Fill(const BS_Rect* pFillRect = 0, unsigned int Color = BS_RGB(0, 0, 0));
+ virtual unsigned int GetPixel(int X, int Y);
+ virtual bool IsBlitSource() const { return true; }
+ virtual bool IsBlitTarget() const { return false; }
+ virtual bool IsScalingAllowed() const { return true; }
+ virtual bool IsFillingAllowed() const { return false; }
+ virtual bool IsAlphaAllowed() const { return true; }
+ virtual bool IsColorModulationAllowed() const { return true; }
+ virtual bool IsSetContentAllowed() const { return false; }
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride);
+ virtual bool Blit(int PosX = 0, int PosY = 0,
+ int Flipping = FLIP_NONE,
+ BS_Rect* pPartRect = NULL,
+ unsigned int Color = BS_ARGB(255, 255, 255, 255),
+ int Width = -1, int Height = -1);
+
+ class SWFBitStream;
+
+private:
+ bool ParseDefineShape(unsigned int ShapeType, SWFBitStream & bs);
+ bool ParseStyles(unsigned int ShapeType, SWFBitStream & bs, unsigned int & NumFillBits, unsigned int & NumLineBits);
+
+ std::vector<BS_VectorImageElement> m_Elements;
+ BS_Rect m_BoundingBox;
+};
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp
new file mode 100755
index 0000000000..76f2330a65
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp
@@ -0,0 +1,198 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "vectorimagerenderer.h"
+#include "vectorimage.h"
+#include "agg_conv_curve.h"
+#include "agg_path_storage.h"
+#include "agg_conv_stroke.h"
+
+
+// -----------------------------------------------------------------------------
+// CompoundShape
+// -----------------------------------------------------------------------------
+
+class CompoundShape
+{
+public:
+ CompoundShape(const BS_VectorImageElement & VectorImageElement) :
+ m_ImageElement(VectorImageElement),
+ m_Path(VectorImageElement.GetPaths()),
+ m_Affine(),
+ m_Curve(m_Path),
+ m_Trans(m_Curve, m_Affine)
+ {}
+
+ unsigned operator [] (unsigned i) const
+ {
+ return m_ImageElement.GetPathInfo(i).GetID();
+ }
+
+ unsigned paths() const { return m_ImageElement.GetPathCount(); }
+
+ void rewind(unsigned path_id)
+ {
+ m_Trans.rewind(path_id);
+ }
+
+ unsigned vertex(double* x, double* y)
+ {
+ return m_Trans.vertex(x, y);
+ }
+
+private:
+ const BS_VectorImageElement & m_ImageElement;
+ agg::path_storage m_Path;
+ agg::trans_affine m_Affine;
+ agg::conv_curve<agg::path_storage> m_Curve;
+ agg::conv_transform< agg::conv_curve<agg::path_storage> > m_Trans;
+};
+
+
+// -----------------------------------------------------------------------------
+// StyleHandler
+// -----------------------------------------------------------------------------
+
+class StyleHandler
+{
+public:
+ StyleHandler(const BS_VectorImageElement & VectorImageElement) : m_ImageElement(VectorImageElement) {}
+
+ bool is_solid(unsigned int style) const
+ {
+ return true;
+ }
+
+ const agg::rgba8 & color(unsigned style) const
+ {
+ return m_ImageElement.GetFillStyleColor(style);
+ }
+
+ void generate_span(agg::rgba8 * span, int x, int y, unsigned len, unsigned style)
+ {
+ // Wird nicht benutzt
+ return;
+ }
+
+private:
+ const BS_VectorImageElement & m_ImageElement;
+};
+
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+BS_VectorImageRenderer::BS_VectorImageRenderer() :
+ PixelFormat(rbuf)
+{
+
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImageRenderer::Render(const BS_VectorImage & VectorImage,
+ float ScaleFactorX, float ScaleFactorY,
+ unsigned int & Width, unsigned int & Height,
+ std::vector<char> & ImageData,
+ float LineScaleFactor,
+ bool NoAlphaShapes)
+{
+ Width = static_cast<unsigned int>(VectorImage.GetWidth() * ScaleFactorX);
+ Height = static_cast<unsigned int>(VectorImage.GetHeight() * ScaleFactorY);
+
+ ImageData.resize(Width * Height * 4);
+ memset(&ImageData[0], 0, ImageData.size());
+ rbuf.attach(reinterpret_cast<agg::int8u *>(&ImageData[0]), Width, Height, Width * 4);
+
+ BaseRenderer.attach(PixelFormat);
+ ScanlineRenderer.attach(BaseRenderer);
+
+ // Die SWF-Shapes sind häufig nicht am Ursprung (0, 0) ausgerichtet, daher wird die Shape vor dem Rendern derart verschoben, dass
+ // sich die linke obere Ecke der Bounding-Box im Ursprung befindet. Danach wird die Skalierung angewandt.
+ Scale = agg::trans_affine_translation(- VectorImage.GetBoundingBox().left, - VectorImage.GetBoundingBox().top);
+ Scale *= agg::trans_affine_scaling(ScaleFactorX, ScaleFactorY);
+
+ for (unsigned int element = 0; element < VectorImage.GetElementCount(); ++element)
+ {
+ const BS_VectorImageElement & CurImageElement = VectorImage.GetElement(element);
+
+ CompoundShape ImageCompoundShape(CurImageElement);
+ StyleHandler ImageStyleHandler(CurImageElement);
+ agg::conv_transform<CompoundShape> Shape(ImageCompoundShape, Scale);
+ agg::conv_stroke<agg::conv_transform<CompoundShape> > Stroke(Shape);
+
+ // Fill shape
+ //----------------------
+ CompoundRasterizer.clip_box(0, 0, Width, Height);
+ CompoundRasterizer.reset();
+ for(unsigned int i = 0; i < CurImageElement.GetPathCount(); ++i)
+ {
+ unsigned int FillStyle0 = CurImageElement.GetPathInfo(i).GetFillStyle0();
+ unsigned int FillStyle1 = CurImageElement.GetPathInfo(i).GetFillStyle1();
+
+ if (NoAlphaShapes)
+ {
+ if (FillStyle0 != 0 && CurImageElement.GetFillStyleColor(FillStyle0 - 1).a != 255) FillStyle0 = 0;
+ if (FillStyle1 != 0 && CurImageElement.GetFillStyleColor(FillStyle1 - 1).a != 255) FillStyle1 = 0;
+ }
+
+ if(FillStyle0 != 0 || FillStyle1 != 0)
+ {
+ CompoundRasterizer.styles(FillStyle0 - 1, FillStyle1 - 1);
+ CompoundRasterizer.add_path(Shape, CurImageElement.GetPathInfo(i).GetID());
+ }
+ }
+ agg::render_scanlines_compound_layered(CompoundRasterizer, Scanline, BaseRenderer, Alloc, ImageStyleHandler);
+
+
+ // Draw strokes
+ //----------------------
+ Rasterizer.clip_box(0, 0, Width, Height);
+ Stroke.line_join(agg::round_join);
+ Stroke.line_cap(agg::round_cap);
+ for(unsigned int i = 0; i < CurImageElement.GetPathCount(); ++i)
+ {
+ Rasterizer.reset();
+
+ unsigned int CurrentLineStyle = CurImageElement.GetPathInfo(i).GetLineStyle();
+ if (CurrentLineStyle != 0)
+ {
+ Stroke.width(ScaleFactorX * CurImageElement.GetLineStyleWidth(CurrentLineStyle - 1) * LineScaleFactor);
+ Rasterizer.add_path(Stroke, CurImageElement.GetPathInfo(i).GetID());
+ ScanlineRenderer.color(CurImageElement.GetLineStyleColor(CurrentLineStyle - 1));
+ // HACK
+ // Die SWF-Frames enthalten zum Teil Reste von grünen Linien, die wohl von Bernd als Umriss benutzt wurden.
+ // Damit diese Reste nicht störend auffallen werden grüne Linien schlichtweg ignoriert.
+ if (!(CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).a == 255 &&
+ CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).r == 0 &&
+ CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).g == 255 &&
+ CurImageElement.GetLineStyleColor(CurrentLineStyle - 1).b == 0))
+ agg::render_scanlines(Rasterizer, Scanline, ScanlineRenderer);
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/engines/sword25/gfx/image/vectorimagerenderer.h b/engines/sword25/gfx/image/vectorimagerenderer.h
new file mode 100755
index 0000000000..2dc29c480e
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimagerenderer.h
@@ -0,0 +1,76 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_VECTORIMAGERENDERER_H
+#define BS_VECTORIMAGERENDERER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include <vector>
+
+#include "agg_rendering_buffer.h"
+#include "agg_pixfmt_rgba.h"
+#include "agg_renderer_scanline.h"
+#include "agg_rasterizer_scanline_aa.h"
+#include "agg_rasterizer_compound_aa.h"
+#include "agg_scanline_u.h"
+#include "agg_scanline_bin.h"
+#include "agg_trans_affine.h"
+#include "agg_span_allocator.h"
+
+class BS_VectorImage;
+
+
+/**
+ @brief Rendert BS_VectorImage Objekte
+*/
+
+class BS_VectorImageRenderer
+{
+public:
+ BS_VectorImageRenderer();
+
+ bool Render(const BS_VectorImage & VectorImage,
+ float ScaleFactorX, float ScaleFactorY,
+ unsigned int & Width, unsigned int & Height,
+ std::vector<char> & ImageData,
+ float LineScaleFactor = 1.0f,
+ bool NoAlphaShapes = false);
+
+private:
+ typedef agg::pixfmt_rgba32_pre PixelFormatType;
+ typedef agg::renderer_base<PixelFormatType> BaseRendererType;
+ typedef agg::renderer_scanline_aa_solid<BaseRendererType> ScanlineRendererType;
+
+ agg::rendering_buffer rbuf;
+ PixelFormatType PixelFormat;
+ BaseRendererType BaseRenderer;
+ ScanlineRendererType ScanlineRenderer;
+ agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> Rasterizer;
+ agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> CompoundRasterizer;
+ agg::scanline_u8 Scanline;
+ agg::scanline_bin ScanlineBin;
+ agg::trans_affine Scale;
+ agg::span_allocator<agg::rgba8> Alloc;
+};
+
+#endif
diff --git a/engines/sword25/gfx/opengl/glimage.cpp b/engines/sword25/gfx/opengl/glimage.cpp
new file mode 100755
index 0000000000..1f5322516b
--- /dev/null
+++ b/engines/sword25/gfx/opengl/glimage.cpp
@@ -0,0 +1,208 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "util/glsprites/glsprites.h"
+#include "package/packagemanager.h"
+#include "gfx/image/imageloader.h"
+#include "openglgfx.h"
+#include "glimage.h"
+
+#define BS_LOG_PREFIX "GLIMAGE"
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+BS_GLImage::BS_GLImage(const std::string & Filename, bool & Result) :
+ m_Sprite(0),
+ m_Width(0),
+ m_Height(0)
+{
+ Result = false;
+
+ BS_PackageManager * pPackage = static_cast<BS_PackageManager*>(BS_Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ char* pFileData;
+ unsigned int FileSize;
+ if (!(pFileData = (char*) pPackage->GetFile(Filename, &FileSize)))
+ {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", Filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat;
+ int Pitch;
+ if (!BS_ImageLoader::ExtractImageProperties(pFileData, FileSize, ColorFormat, m_Width, m_Height))
+ {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ char * pUncompressedData;
+ if (!BS_ImageLoader::LoadImage(pFileData, FileSize, BS_GraphicEngine::CF_ABGR32, pUncompressedData, m_Width, m_Height, Pitch))
+ {
+ BS_LOG_ERRORLN("Could not decode image.");
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ // GLS-Sprite mit den Bilddaten erstellen
+ GLS_Result GLSResult = GLS_NewSprite(m_Width, m_Height,
+ (ColorFormat == BS_GraphicEngine::CF_ARGB32) ? GLS_True : GLS_False,
+ pUncompressedData,
+ &m_Sprite);
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Could not create GLS_Sprite. Reason: %s", GLS_ResultString(GLSResult));
+ return;
+ }
+
+ // Bilddaten freigeben
+ delete[] pUncompressedData;
+
+ Result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_GLImage::BS_GLImage(unsigned int Width, unsigned int Height, bool & Result) :
+ m_Sprite(0),
+ m_Width(Width),
+ m_Height(Height)
+{
+ Result = false;
+
+ // GLS-Sprite mit den Bilddaten erstellen
+ GLS_Result GLSResult = GLS_NewSprite(m_Width, m_Height,
+ GLS_True,
+ 0,
+ &m_Sprite);
+ if (GLSResult != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Could not create GLS_Sprite. Reason: %s", GLS_ResultString(GLSResult));
+ return;
+ }
+
+ Result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_GLImage::~BS_GLImage()
+{
+ if (m_Sprite) GLS_DeleteSprite(m_Sprite);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GLImage::Fill(const BS_Rect* pFillRect, unsigned int Color)
+{
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GLImage::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride)
+{
+ // Überprüfen, ob PixelData ausreichend viele Pixel enthält um ein Bild der Größe Width * Height zu erzeugen
+ if (Pixeldata.size() < static_cast<unsigned int>(m_Width * m_Height * 4))
+ {
+ BS_LOG_ERRORLN("PixelData vector is too small to define a 32 bit %dx%d image.", m_Width, m_Height);
+ return false;
+ }
+
+ // GLS-Sprite mit den Bilddaten füllen
+ GLS_Result GLSResult = GLS_SetSpriteData(m_Sprite, m_Width, m_Height, &Pixeldata[Offset], Stride / 4);
+ if (GLSResult != GLS_OK)
+ {
+ BS_LOG_ERRORLN("CGLS_SetSpriteData() failed. Reason: %s", GLS_ResultString(GLSResult));
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_GLImage::GetPixel(int X, int Y)
+{
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_GLImage::Blit(int PosX, int PosY,
+ int Flipping,
+ BS_Rect* pPartRect,
+ unsigned int Color,
+ int Width, int Height)
+{
+ // BS_Rect nach GLS_Rect konvertieren
+ GLS_Rect SubImage;
+ if (pPartRect)
+ {
+ SubImage.x1 = pPartRect->left;
+ SubImage.y1 = pPartRect->top;
+ SubImage.x2 = pPartRect->right;
+ SubImage.y2 = pPartRect->bottom;
+ }
+
+ // Farbe nach GLS_Color konvertieren
+ GLS_Color GLSColor;
+ GLSColor.a = Color >> 24;
+ GLSColor.r = (Color >> 16) & 0xff;
+ GLSColor.g = (Color >> 8) & 0xff;
+ GLSColor.b = Color & 0xff;
+
+ // Skalierungen berechnen
+ GLS_Float ScaleX, ScaleY;
+ if (Width == -1) Width = m_Width;
+ ScaleX = (GLS_Float) Width / (GLS_Float) m_Width;
+
+ if (Height == -1) Height = m_Height;
+ ScaleY = (GLS_Float) Height / (GLS_Float) m_Height;
+
+ // Rendern
+ // TODO:
+ // Die Bedeutung von FLIP_V und FLIP_H ist vertauscht. Allerdings glaubt der Rest der Engine auch daran, daher war es einfacher diesen Fehler
+ // weiterzuführen. Bei Gelegenheit ist dieses aber zu ändern.
+ GLS_Result Result = GLS_Blit(m_Sprite,
+ PosX, PosY,
+ pPartRect ? &SubImage : 0, &GLSColor,
+ (Flipping & BS_Image::FLIP_V) ? GLS_True : GLS_False,
+ (Flipping & BS_Image::FLIP_H) ? GLS_True : GLS_False,
+ ScaleX, ScaleY);
+ if (Result != GLS_OK) BS_LOG_ERRORLN("GLS_Blit() failed. Reason: %s", GLS_ResultString(Result));
+
+ return Result == GLS_OK;
+}
diff --git a/engines/sword25/gfx/opengl/glimage.h b/engines/sword25/gfx/opengl/glimage.h
new file mode 100755
index 0000000000..d9c81dc4a5
--- /dev/null
+++ b/engines/sword25/gfx/opengl/glimage.h
@@ -0,0 +1,85 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_GL_IMAGE_H
+#define BS_GL_IMAGE_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "gfx/image/image.h"
+#include "gfx/graphicengine.h"
+
+#include <vector>
+
+// -----------------------------------------------------------------------------
+// FORWARD DECLARATION
+// -----------------------------------------------------------------------------
+
+typedef void * GLS_Sprite;
+
+// -----------------------------------------------------------------------------
+// CLASS DEFINITION
+// -----------------------------------------------------------------------------
+
+class BS_GLImage : public BS_Image
+{
+public:
+ BS_GLImage(const std::string & Filename, bool & Result);
+
+ /**
+ @brief Erzeugt ein leeres BS_GLImage
+
+ @param Width die Breite des zu erzeugenden Bildes.
+ @param Height die Höhe des zu erzeugenden Bildes
+ @param Result gibt dem Aufrufer bekannt, ob der Konstruktor erfolgreich ausgeführt wurde. Wenn es nach dem Aufruf false enthalten sollte,
+ dürfen keine Methoden am Objekt aufgerufen werden und das Objekt ist sofort zu zerstören.
+ */
+ BS_GLImage(unsigned int Width, unsigned int Height, bool & Result);
+ virtual ~BS_GLImage();
+
+ virtual int GetWidth() const { return m_Width; }
+ virtual int GetHeight() const { return m_Height; }
+ virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const { return BS_GraphicEngine::CF_ARGB32; }
+
+ virtual bool Blit(int PosX = 0, int PosY = 0,
+ int Flipping = BS_Image::FLIP_NONE,
+ BS_Rect* pPartRect = NULL,
+ unsigned int Color = BS_ARGB(255, 255, 255, 255),
+ int Width = -1, int Height = -1);
+ virtual bool Fill(const BS_Rect* pFillRect, unsigned int Color);
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset = 0, unsigned int Stride = 0);
+ virtual unsigned int GetPixel(int X, int Y);
+
+ virtual bool IsBlitSource() const { return true; }
+ virtual bool IsBlitTarget() const { return false; }
+ virtual bool IsScalingAllowed() const { return true; }
+ virtual bool IsFillingAllowed() const { return false; }
+ virtual bool IsAlphaAllowed() const { return true; }
+ virtual bool IsColorModulationAllowed() const { return true; }
+ virtual bool IsSetContentAllowed() const { return true; }
+private:
+ GLS_Sprite m_Sprite;
+ int m_Width;
+ int m_Height;
+};
+
+#endif
diff --git a/engines/sword25/gfx/opengl/glvectorimageblit.cpp b/engines/sword25/gfx/opengl/glvectorimageblit.cpp
new file mode 100755
index 0000000000..38cfd9fa85
--- /dev/null
+++ b/engines/sword25/gfx/opengl/glvectorimageblit.cpp
@@ -0,0 +1,133 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "gfx/image/vectorimage.h"
+#include "gfx/image/vectorimagerenderer.h"
+#include "util/glsprites/glsprites.h"
+
+#include <vector>
+using namespace std;
+
+#define BS_LOG_PREFIX "GLVECTORIMAGEBLIT"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const float LINE_SCALE_FACTOR = 1.0f;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_VectorImage::Blit(int PosX, int PosY,
+ int Flipping,
+ BS_Rect* pPartRect,
+ unsigned int Color,
+ int Width, int Height)
+{
+ static BS_VectorImageRenderer VectorImageRenderer;
+ static vector<char> PixelData;
+ static GLS_Sprite Sprite = 0;
+ static BS_VectorImage * OldThis = 0;
+ static int OldWidth;
+ static int OldHeight;
+ static GLS_Rect OldSubImage;
+
+ // Falls Breite oder Höhe 0 sind, muss nichts dargestellt werden.
+ if (Width == 0 || Height == 0) return true;
+
+ // Sprite erstellen, falls es noch nicht erstellt wurde
+ if (Sprite == 0)
+ {
+ GLS_Result Result = GLS_NewSprite(512, 512, GLS_True, 0, &Sprite);
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Could not create GLS_Sprite. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+ }
+
+ // Feststellen, ob das alte Bild im Cache nicht wiederbenutzt werden kann und neu Berechnet werden muss
+ if (!(OldThis == this && OldWidth == Width && OldHeight == Height && Sprite != 0))
+ {
+ float ScaleFactorX = (Width == - 1) ? 1 : static_cast<float>(Width) / static_cast<float>(GetWidth());
+ float ScaleFactorY = (Height == - 1) ? 1: static_cast<float>(Height) / static_cast<float>(GetHeight());
+
+ unsigned int RenderedWidth;
+ unsigned int RenderedHeight;
+ if (!VectorImageRenderer.Render(*this, ScaleFactorX, ScaleFactorY, RenderedWidth, RenderedHeight, PixelData, LINE_SCALE_FACTOR))
+ {
+ BS_LOG_ERRORLN("Call to BS_VectorImageRenderer::Render() failed.");
+ return false;
+ }
+
+ if (RenderedWidth > 512 || RenderedHeight > 512)
+ {
+ BS_LOG_WARNINGLN("Currently the maximum size for scaled vector images is 512x512.");
+ return true;
+ }
+
+ GLS_Result Result = GLS_SetSpriteData(Sprite, RenderedWidth, RenderedHeight, &PixelData[0], 0);
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Call to GLS_SetSpriteData() failed. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+
+ OldThis = this;
+ OldHeight = Height;
+ OldWidth = Width;
+
+ OldSubImage.x1 = 0;
+ OldSubImage.y1 = 0;
+ OldSubImage.x2 = RenderedWidth;
+ OldSubImage.y2 = RenderedHeight;
+ }
+
+ // Rendern
+ // -------
+
+ // pDest wird ignoriert. Es wird einfach angenommen, dass der Backbuffer gemeint ist, da nur auf den Backbuffer gerendert werden kann.
+ // Ebenso werden pPartRect ignoriert. Es wird immer das gesamte Sprite gerendert.
+
+ // Farbe nach GLS_Color konvertieren
+ GLS_Color GLSColor;
+ GLSColor.a = Color >> 24;
+ GLSColor.r = (Color >> 16) & 0xff;
+ GLSColor.g = (Color >> 8) & 0xff;
+ GLSColor.b = Color & 0xff;
+
+ // Rendern
+ // TODO:
+ // Die Bedeutung von FLIP_V und FLIP_H ist vertauscht. Allerdings glaubt der Rest der Engine auch daran, daher war es einfacher diesen Fehler
+ // weiterzuführen. Bei Gelegenheit ist dieses aber zu ändern.
+ GLS_Result Result = GLS_Blit(Sprite,
+ PosX, PosY,
+ &OldSubImage, &GLSColor,
+ (Flipping & BS_Image::FLIP_V) ? GLS_True : GLS_False,
+ (Flipping & BS_Image::FLIP_H) ? GLS_True : GLS_False,
+ 1.0f, 1.0f);
+ if (Result != GLS_OK) BS_LOG_ERRORLN("GLS_Blit() failed. Reason: %s", GLS_ResultString(Result));
+
+ return Result == GLS_OK;
+}
diff --git a/engines/sword25/gfx/opengl/openglgfx.cpp b/engines/sword25/gfx/opengl/openglgfx.cpp
new file mode 100755
index 0000000000..6131a229a5
--- /dev/null
+++ b/engines/sword25/gfx/opengl/openglgfx.cpp
@@ -0,0 +1,505 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <GL/GL.h>
+
+#include "util/glsprites/glsprites.h"
+#include "../bitmapresource.h"
+#include "../animationresource.h"
+#include "../fontresource.h"
+#include "../panel.h"
+#include "../renderobjectmanager.h"
+#include "../image/vectorimage.h"
+#include "package/packagemanager.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+
+#include "openglgfx.h"
+#include "glimage.h"
+#include "swimage.h"
+
+#include <algorithm>
+
+using namespace std;
+
+#define BS_LOG_PREFIX "OPENGLGFX"
+
+
+// -----------------------------------------------------------------------------
+// CONSTANTS
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const unsigned int BIT_DEPTH = 32;
+ const unsigned int BACKBUFFER_COUNT = 1;
+ const std::string PNG_EXTENSION(".png");
+ const std::string PNG_S_EXTENSION("_s.png");
+ const std::string ANI_EXTENSION("_ani.xml");
+ const std::string FNT_EXTENSION("_fnt.xml");
+ const std::string SWF_EXTENSION(".swf");
+ const std::string B25S_EXTENSION(".b25s");
+}
+
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+BS_OpenGLGfx::BS_OpenGLGfx(BS_Kernel * pKernel) :
+ BS_GraphicEngine(pKernel),
+ m_GLspritesInitialized(false)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_OpenGLGfx::~BS_OpenGLGfx()
+{
+ if (m_GLspritesInitialized) GLS_Quit();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_OpenGLGfx_CreateObject(BS_Kernel* pKernel)
+{
+ return new BS_OpenGLGfx(pKernel);
+}
+
+
+// -----------------------------------------------------------------------------
+// INTERFACE
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::Init(int Width, int Height, int BitDepth, int BackbufferCount, bool Windowed)
+{
+ // Warnung ausgeben, wenn eine nicht unterstützte Bittiefe gewählt wurde.
+ if (BitDepth != BIT_DEPTH)
+ {
+ BS_LOG_WARNINGLN("Can't use a bit depth of %d (not supported). Falling back to %d.", BitDepth, BIT_DEPTH);
+ m_BitDepth = BIT_DEPTH;
+ }
+
+ // Warnung ausgeben, wenn nicht genau ein Backbuffer gewählt wurde.
+ if (BackbufferCount != BACKBUFFER_COUNT)
+ {
+ BS_LOG_WARNINGLN("Can't use %d backbuffers (not supported). Falling back to %d.", BackbufferCount, BACKBUFFER_COUNT);
+ BackbufferCount = BACKBUFFER_COUNT;
+ }
+
+ // Parameter in lokale Variablen kopieren
+ m_Width = Width;
+ m_Height = Height;
+ m_BitDepth = BitDepth;
+ m_Windowed = Windowed;
+ m_ScreenRect.left = 0;
+ m_ScreenRect.top = 0;
+ m_ScreenRect.right = m_Width;
+ m_ScreenRect.bottom = m_Height;
+
+ // GLsprites initialisieren
+ HWND hwnd = reinterpret_cast<HWND>(BS_Kernel::GetInstance()->GetWindow()->GetWindowHandle());
+ GLS_Result Result = GLS_InitExternalWindow(Width, Height, Windowed ? GLS_False : GLS_True, hwnd);
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Could not initialize GLsprites. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+ m_GLspritesInitialized = true;
+
+ // Standardmäßig ist Vsync an.
+ SetVsync(true);
+
+ // Layer-Manager initialisieren.
+ m_RenderObjectManagerPtr.reset(new BS_RenderObjectManager(Width, Height, BackbufferCount + 1));
+
+ // Hauptpanel erstellen
+ m_MainPanelPtr = m_RenderObjectManagerPtr->GetTreeRoot()->AddPanel(Width, Height, BS_ARGB(0,0,0,0));
+ if (!m_MainPanelPtr.IsValid()) return false;
+ m_MainPanelPtr->SetVisible(true);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::StartFrame(bool UpdateAll)
+{
+ // Berechnen, wie viel Zeit seit dem letzten Frame vergangen ist.
+ // Dieser Wert kann über GetLastFrameDuration() von Modulen abgefragt werden, die zeitabhängig arbeiten.
+ UpdateLastFrameDuration();
+
+ // Den Layer-Manager auf den nächsten Frame vorbereiten
+ m_RenderObjectManagerPtr->StartFrame();
+
+ // GLsprites bescheidgeben
+ GLS_Result Result = GLS_StartFrame();
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Call to GLS_StartFrame() failed. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::EndFrame()
+{
+ // Scene zeichnen
+ m_RenderObjectManagerPtr->Render();
+
+ // Debug-Lines zeichnen
+ if (!m_DebugLines.empty())
+ {
+ glEnable(GL_LINE_SMOOTH);
+ glBegin(GL_LINES);
+
+ std::vector<DebugLine>::const_iterator iter = m_DebugLines.begin();
+ for (; iter != m_DebugLines.end(); ++iter)
+ {
+ const unsigned int & Color = (*iter).Color;
+ const BS_Vertex & Start = (*iter).Start;
+ const BS_Vertex & End = (*iter).End;
+
+ glColor4ub((Color >> 16) & 0xff, (Color >> 8) & 0xff, Color & 0xff, Color >> 24);
+ glVertex2d(Start.X, Start.Y);
+ glVertex2d(End.X, End.Y);
+ }
+
+ glEnd();
+ glDisable(GL_LINE_SMOOTH);
+
+ m_DebugLines.clear();
+ }
+
+ // Flippen
+ GLS_Result Result = GLS_EndFrame();
+ if (Result != GLS_OK)
+ {
+ BS_LOG_ERRORLN("Call to GLS_EndFrame() failed. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+
+ // Framecounter aktualisieren
+ m_FPSCounter.Update();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Panel> BS_OpenGLGfx::GetMainPanel()
+{
+ return m_MainPanelPtr;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OpenGLGfx::SetVsync(bool Vsync)
+{
+ GLS_Result Result = GLS_SetVSync(Vsync ? GLS_True : GLS_False);
+ if (Result != GLS_OK) BS_LOG_WARNINGLN("Could not set vsync status. Reason: %s", GLS_ResultString(Result));
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::GetVsync() const
+{
+ GLS_Bool Status;
+ GLS_Result Result = GLS_IsVsync(&Status);
+ if (Result != GLS_OK)
+ {
+ BS_LOG_WARNINGLN("Could not get vsync status. Returning false. Reason: %s", GLS_ResultString(Result));
+ return false;
+ }
+
+ return Status == GLS_True ? true : false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::Fill(const BS_Rect* FillRectPtr, unsigned int Color)
+{
+ BS_Rect Rect;
+
+ if (!FillRectPtr)
+ {
+ Rect.left = 0;
+ Rect.top = 0;
+ Rect.right = m_Width;
+ Rect.bottom = m_Height;
+ FillRectPtr = &Rect;
+ }
+
+ glBegin(GL_QUADS);
+ glColor4ub((Color >> 16) & 0xff, (Color >> 8) & 0xff, Color & 0xff, Color >> 24);
+
+ glVertex2i(FillRectPtr->left, FillRectPtr->top);
+ glVertex2i(FillRectPtr->right, FillRectPtr->top);
+ glVertex2i(FillRectPtr->right, FillRectPtr->bottom);
+ glVertex2i(FillRectPtr->left, FillRectPtr->bottom);
+ glEnd();
+
+ return glGetError() == 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::GetScreenshot(unsigned int & Width, unsigned int & Height, vector<unsigned int> & Data)
+{
+ if (!ReadFramebufferContents(m_Width, m_Height, Data)) return false;
+
+ // Die Größe des Framebuffers zurückgeben.
+ Width = m_Width;
+ Height = m_Height;
+
+ // Bilddaten vom OpenGL-Format in unser eigenes Format umwandeln.
+ ReverseRGBAComponentOrder(Data);
+ FlipImagedataVertical(Width, Height, Data);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::ReadFramebufferContents(unsigned int Width, unsigned int Height, std::vector<unsigned int> & Data)
+{
+ Data.resize(Width * Height);
+ glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &Data[0]);
+
+ if (glGetError() == 0)
+ return true;
+ else
+ {
+ Data.clear();
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OpenGLGfx::ReverseRGBAComponentOrder(vector<unsigned int> & Data)
+{
+ vector<unsigned int>::iterator It = Data.begin();
+ while (It != Data.end())
+ {
+ unsigned int Pixel = *It;
+ *It = (Pixel & 0xff00ff00) | ((Pixel >> 16) & 0xff) | ((Pixel & 0xff) << 16);
+ ++It;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OpenGLGfx::FlipImagedataVertical(unsigned int Width, unsigned int Height, vector<unsigned int> & Data)
+{
+ vector<unsigned int> LineBuffer(Width);
+
+ for (unsigned int Y = 0; Y < Height / 2; ++Y)
+ {
+ vector<unsigned int>::iterator Line1It = Data.begin() + Y * Width;
+ vector<unsigned int>::iterator Line2It = Data.begin() + (Height - 1 - Y) * Width;
+ copy(Line1It, Line1It + Width, LineBuffer.begin());
+ copy(Line2It, Line2It + Width, Line1It);
+ copy(LineBuffer.begin(), LineBuffer.end(), Line2It);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// RESOURCE MANAGING
+// -----------------------------------------------------------------------------
+
+static bool DoesStringEndWith(const std::string & String, const std::string & OtherString)
+{
+ std::string::size_type StringPos = String.rfind(OtherString);
+ if (StringPos == std::string::npos) return false;
+
+ return StringPos + OtherString.size() == String.size();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Resource * BS_OpenGLGfx::LoadResource(const std::string& FileName)
+{
+ BS_ASSERT(CanLoadResource(FileName));
+
+ // Bild für den Softwarebuffer laden
+ if (DoesStringEndWith(FileName, PNG_S_EXTENSION))
+ {
+ bool Result;
+ BS_SWImage * pImage = new BS_SWImage(FileName, Result);
+ if (!Result)
+ {
+ delete pImage;
+ return 0;
+ }
+
+ BS_BitmapResource * pResource = new BS_BitmapResource(FileName, pImage);
+ if (!pResource->IsValid())
+ {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+ // Sprite-Bild laden
+ if (DoesStringEndWith(FileName, PNG_EXTENSION) || DoesStringEndWith(FileName, B25S_EXTENSION))
+ {
+ bool Result;
+ BS_GLImage * pImage = new BS_GLImage(FileName, Result);
+ if (!Result)
+ {
+ delete pImage;
+ return 0;
+ }
+
+ BS_BitmapResource * pResource = new BS_BitmapResource(FileName, pImage);
+ if (!pResource->IsValid())
+ {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+
+ // Vectorgraphik laden
+ if (DoesStringEndWith(FileName, SWF_EXTENSION))
+ {
+ // Pointer auf Package-Manager holen
+ BS_PackageManager * pPackage = BS_Kernel::GetInstance()->GetPackage();
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ unsigned char* pFileData;
+ unsigned int FileSize;
+ if (!(pFileData = static_cast<unsigned char*>(pPackage->GetFile(FileName, &FileSize))))
+ {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", FileName.c_str());
+ return 0;
+ }
+
+ bool Result;
+ BS_VectorImage * pImage = new BS_VectorImage(pFileData, FileSize, Result);
+ if (!Result)
+ {
+ delete pImage;
+ delete [] pFileData;
+ return 0;
+ }
+
+ BS_BitmapResource * pResource = new BS_BitmapResource(FileName, pImage);
+ if (!pResource->IsValid())
+ {
+ delete pResource;
+ delete [] pFileData;
+ return 0;
+ }
+
+ delete [] pFileData;
+ return pResource;
+ }
+
+ // Animation laden
+ if (DoesStringEndWith(FileName, ANI_EXTENSION))
+ {
+ BS_AnimationResource * pResource = new BS_AnimationResource(FileName);
+ if (pResource->IsValid())
+ return pResource;
+ else
+ {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ // Font laden
+ if (DoesStringEndWith(FileName, FNT_EXTENSION))
+ {
+ BS_FontResource * pResource = new BS_FontResource(BS_Kernel::GetInstance(), FileName);
+ if (pResource->IsValid())
+ return pResource;
+ else
+ {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ BS_LOG_ERRORLN("Service cannot load \"%s\".", FileName.c_str());
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::CanLoadResource(const std::string& FileName)
+{
+ return DoesStringEndWith(FileName, PNG_EXTENSION) ||
+ DoesStringEndWith(FileName, ANI_EXTENSION) ||
+ DoesStringEndWith(FileName, FNT_EXTENSION) ||
+ DoesStringEndWith(FileName, SWF_EXTENSION) ||
+ DoesStringEndWith(FileName, B25S_EXTENSION);
+}
+
+
+// -----------------------------------------------------------------------------
+// DEBUGGING
+// -----------------------------------------------------------------------------
+
+void BS_OpenGLGfx::DrawDebugLine(const BS_Vertex & Start, const BS_Vertex & End, unsigned int Color)
+{
+ m_DebugLines.push_back(DebugLine(Start, End, Color));
+}
+
+// -----------------------------------------------------------------------------
+// PERSISTENZ
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool result = true;
+
+ result &= BS_GraphicEngine::Persist(Writer);
+ result &= m_RenderObjectManagerPtr->Persist(Writer);
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_OpenGLGfx::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool result = true;
+
+ result &= BS_GraphicEngine::Unpersist(Reader);
+ result &= m_RenderObjectManagerPtr->Unpersist(Reader);
+
+ return result && Reader.IsGood();
+}
diff --git a/engines/sword25/gfx/opengl/openglgfx.h b/engines/sword25/gfx/opengl/openglgfx.h
new file mode 100755
index 0000000000..ec305bee8f
--- /dev/null
+++ b/engines/sword25/gfx/opengl/openglgfx.h
@@ -0,0 +1,114 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OPENGLGFX_H
+#define BS_OPENGLGFX_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "../graphicengine.h"
+#include "../renderobjectptr.h"
+#include "util/glsprites/glsprites.h"
+
+// -----------------------------------------------------------------------------
+// FORWARD DECLARATIONS
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_Service;
+class BS_Resource;
+class BS_Panel;
+class BS_Image;
+class BS_RenderObjectManager;
+
+
+// -----------------------------------------------------------------------------
+// CLASS DECLARATION
+// -----------------------------------------------------------------------------
+
+class BS_OpenGLGfx : public BS_GraphicEngine
+{
+public:
+ BS_OpenGLGfx(BS_Kernel* pKernel);
+ virtual ~BS_OpenGLGfx();
+
+ // Interface
+ // ---------
+ virtual bool Init(int Width, int Height, int BitDepth, int BackbufferCount, bool Windowed);
+ virtual bool StartFrame(bool UpdateAll);
+ virtual bool EndFrame();
+
+ virtual BS_RenderObjectPtr<BS_Panel> GetMainPanel();
+
+ virtual void SetVsync(bool Vsync);
+ virtual bool GetVsync() const;
+
+ virtual bool Fill(const BS_Rect* FillRectPtr = 0, unsigned int Color = BS_RGB(0, 0, 0));
+ virtual bool GetScreenshot(unsigned int & Width, unsigned int & Height, std::vector<unsigned int> & Data);
+
+ // Resource-Managing Methoden
+ // --------------------------
+ virtual BS_Resource* LoadResource(const std::string& FileName);
+ virtual bool CanLoadResource(const std::string& FileName);
+
+ // Debugging Methoden
+ // ------------------
+ virtual void DrawDebugLine(const BS_Vertex & Start, const BS_Vertex & End, unsigned int Color);
+ static const char * GetGLSResultString(GLS_Result Result);
+
+ // Persistenz Methoden
+ // -------------------
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ bool m_GLspritesInitialized;
+
+ BS_RenderObjectPtr<BS_Panel> m_MainPanelPtr;
+
+ std::auto_ptr<BS_RenderObjectManager> m_RenderObjectManagerPtr;
+
+ struct DebugLine
+ {
+ DebugLine(const BS_Vertex & _Start, const BS_Vertex & _End, unsigned int _Color) :
+ Start(_Start),
+ End(_End),
+ Color(_Color) {};
+
+ BS_Vertex Start;
+ BS_Vertex End;
+ unsigned int Color;
+ };
+
+ std::vector<DebugLine> m_DebugLines;
+
+ static bool ReadFramebufferContents(unsigned int Width, unsigned int Height, std::vector<unsigned int> & Data);
+ static void ReverseRGBAComponentOrder(std::vector<unsigned int> & Data);
+ static void FlipImagedataVertical(unsigned int Width, unsigned int Height, std::vector<unsigned int> & Data);
+};
+
+#endif
diff --git a/engines/sword25/gfx/opengl/swimage.cpp b/engines/sword25/gfx/opengl/swimage.cpp
new file mode 100755
index 0000000000..7bc36b5f69
--- /dev/null
+++ b/engines/sword25/gfx/opengl/swimage.cpp
@@ -0,0 +1,125 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "package/packagemanager.h"
+#include "gfx/image/imageloader.h"
+
+#include "swimage.h"
+
+#define BS_LOG_PREFIX "SWIMAGE"
+
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+BS_SWImage::BS_SWImage(const std::string & Filename, bool & Result) :
+ _ImageDataPtr(0),
+ m_Width(0),
+ m_Height(0)
+{
+ Result = false;
+
+ BS_PackageManager * pPackage = static_cast<BS_PackageManager*>(BS_Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ char* pFileData;
+ unsigned int FileSize;
+ if (!(pFileData = (char*) pPackage->GetFile(Filename, &FileSize)))
+ {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", Filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ BS_GraphicEngine::COLOR_FORMATS ColorFormat;
+ int Pitch;
+ if (!BS_ImageLoader::ExtractImageProperties(pFileData, FileSize, ColorFormat, m_Width, m_Height))
+ {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ char * pUncompressedData;
+ if (!BS_ImageLoader::LoadImage(pFileData, FileSize, BS_GraphicEngine::CF_ABGR32, pUncompressedData, m_Width, m_Height, Pitch))
+ {
+ BS_LOG_ERRORLN("Could not decode image.");
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ _ImageDataPtr = (unsigned int *) pUncompressedData;
+
+ Result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_SWImage::~BS_SWImage()
+{
+ delete [] _ImageDataPtr;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool BS_SWImage::Blit(int PosX, int PosY,
+ int Flipping,
+ BS_Rect* pPartRect,
+ unsigned int Color,
+ int Width, int Height)
+{
+ BS_LOG_ERRORLN("Blit() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_SWImage::Fill(const BS_Rect* pFillRect, unsigned int Color)
+{
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_SWImage::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride)
+{
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_SWImage::GetPixel(int X, int Y)
+{
+ BS_ASSERT(X >= 0 && X < m_Width);
+ BS_ASSERT(Y >= 0 && Y < m_Height);
+
+ return _ImageDataPtr[m_Width * Y + X];
+}
diff --git a/engines/sword25/gfx/opengl/swimage.h b/engines/sword25/gfx/opengl/swimage.h
new file mode 100755
index 0000000000..1f48c238b3
--- /dev/null
+++ b/engines/sword25/gfx/opengl/swimage.h
@@ -0,0 +1,69 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_SWIMAGE_H
+#define BS_SWIMAGE_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "gfx/image/image.h"
+#include "gfx/graphicengine.h"
+
+
+// -----------------------------------------------------------------------------
+// CLASS DEFINITION
+// -----------------------------------------------------------------------------
+
+class BS_SWImage : public BS_Image
+{
+public:
+ BS_SWImage(const std::string & Filename, bool & Result);
+ virtual ~BS_SWImage();
+
+ virtual int GetWidth() const { return m_Width; }
+ virtual int GetHeight() const { return m_Height; }
+ virtual BS_GraphicEngine::COLOR_FORMATS GetColorFormat() const { return BS_GraphicEngine::CF_ARGB32; }
+
+ virtual bool Blit(int PosX = 0, int PosY = 0,
+ int Flipping = BS_Image::FLIP_NONE,
+ BS_Rect* pPartRect = NULL,
+ unsigned int Color = BS_ARGB(255, 255, 255, 255),
+ int Width = -1, int Height = -1);
+ virtual bool Fill(const BS_Rect* FillRectPtr, unsigned int Color);
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride);
+ virtual unsigned int GetPixel(int X, int Y);
+
+ virtual bool IsBlitSource() const { return false; }
+ virtual bool IsBlitTarget() const { return false; }
+ virtual bool IsScalingAllowed() const { return false; }
+ virtual bool IsFillingAllowed() const { return false; }
+ virtual bool IsAlphaAllowed() const { return false; }
+ virtual bool IsColorModulationAllowed() const { return false; }
+ virtual bool IsSetContentAllowed() const { return false; }
+private:
+ unsigned int * _ImageDataPtr;
+
+ int m_Width;
+ int m_Height;
+};
+
+#endif
diff --git a/engines/sword25/gfx/panel.cpp b/engines/sword25/gfx/panel.cpp
new file mode 100755
index 0000000000..715f306305
--- /dev/null
+++ b/engines/sword25/gfx/panel.cpp
@@ -0,0 +1,123 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "panel.h"
+
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "graphicengine.h"
+#include "image/image.h"
+
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "PANEL"
+
+// -----------------------------------------------------------------------------
+// Construction/Destruction
+// -----------------------------------------------------------------------------
+
+BS_Panel::BS_Panel(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, int Width, int Height, unsigned int Color) :
+ BS_RenderObject(ParentPtr, BS_RenderObject::TYPE_PANEL),
+ m_Color(Color)
+{
+ m_InitSuccess = false;
+
+ m_Width = Width;
+ m_Height = Height;
+
+ if (m_Width < 0)
+ {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid width (%d).", m_Width);
+ return;
+ }
+
+ if (m_Height < 0)
+ {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid height (%d).", m_Height);
+ return;
+ }
+
+ m_InitSuccess = true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Panel::BS_Panel(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle) :
+ BS_RenderObject(ParentPtr, BS_RenderObject::TYPE_PANEL, Handle)
+{
+ m_InitSuccess = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Panel::~BS_Panel()
+{
+}
+
+// -----------------------------------------------------------------------------
+// Rendern
+// -----------------------------------------------------------------------------
+
+bool BS_Panel::DoRender()
+{
+ // Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden.
+ if (m_Color >> 24 == 0) return true;
+
+ BS_GraphicEngine * GfxPtr = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(GfxPtr);
+
+ return GfxPtr->Fill(&m_BBox, m_Color);
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_Panel::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Persist(Writer);
+ Writer.Write(m_Color);
+
+ Result &= BS_RenderObject::PersistChildren(Writer);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Panel::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Unpersist(Reader);
+
+ unsigned int Color;
+ Reader.Read(Color);
+ SetColor(Color);
+
+ Result &= BS_RenderObject::UnpersistChildren(Reader);
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/panel.h b/engines/sword25/gfx/panel.h
new file mode 100755
index 0000000000..b1ecd7ce69
--- /dev/null
+++ b/engines/sword25/gfx/panel.h
@@ -0,0 +1,58 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_PANEL_H
+#define BS_PANEL_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "renderobject.h"
+
+// -----------------------------------------------------------------------------
+// Class Definition
+// -----------------------------------------------------------------------------
+
+class BS_Panel : public BS_RenderObject
+{
+friend BS_RenderObject;
+
+private:
+ BS_Panel(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, int Width, int Height, unsigned int Color);
+ BS_Panel(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle);
+
+public:
+ virtual ~BS_Panel();
+
+ unsigned int GetColor() const { return m_Color; }
+ void SetColor(unsigned int Color) { m_Color = Color; ForceRefresh(); }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ virtual bool DoRender();
+
+private:
+ unsigned int m_Color;
+};
+
+#endif
diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp
new file mode 100755
index 0000000000..427038e477
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -0,0 +1,571 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "renderobject.h"
+
+#include <algorithm>
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include "renderobjectregistry.h"
+#include "renderobjectmanager.h"
+#include "graphicengine.h"
+
+#include "bitmap.h"
+#include "staticbitmap.h"
+#include "dynamicbitmap.h"
+#include "animation.h"
+#include "panel.h"
+#include "text.h"
+#include "animationtemplate.h"
+
+#define BS_LOG_PREFIX "RENDEROBJECT"
+
+// Konstruktion / Destruktion
+// --------------------------
+BS_RenderObject::BS_RenderObject(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, TYPES Type, unsigned int Handle) :
+ m_ManagerPtr(0),
+ m_ParentPtr(ParentPtr),
+ m_X(0),
+ m_Y(0),
+ m_Z(0),
+ m_Width(0),
+ m_Height(0),
+ m_Visible(true),
+ m_ChildChanged(true),
+ m_Type(Type),
+ m_InitSuccess(false),
+ m_RefreshForced(true),
+ m_Handle(0)
+{
+ // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle.
+ if (Handle == 0)
+ m_Handle = BS_RenderObjectRegistry::GetInstance().RegisterObject(this);
+ else
+ m_Handle = BS_RenderObjectRegistry::GetInstance().RegisterObject(this, Handle);
+ if (m_Handle == 0) return;
+
+ UpdateAbsolutePos();
+
+ // Dieses Objekt zu den Kindern der Elternobjektes hinzufügen, falls nicht Wurzel (ParentPtr ungültig) und dem
+ // selben RenderObjektManager zuweisen.
+ if (m_ParentPtr.IsValid())
+ {
+ m_ManagerPtr = m_ParentPtr->GetManager();
+ m_ParentPtr->AddObject(this);
+ }
+ else
+ {
+ if (GetType() != TYPE_ROOT)
+ {
+ BS_LOG_ERRORLN("Tried to create a non-root render object and has no parent. All non-root render objects have to have a parent.");
+ return;
+ }
+ }
+
+ UpdateObjectState();
+
+ m_InitSuccess = true;
+}
+
+BS_RenderObject::~BS_RenderObject()
+{
+ // Objekt aus dem Elternobjekt entfernen.
+ if (m_ParentPtr.IsValid()) m_ParentPtr->DetatchChildren(this);
+
+ DeleteAllChildren();
+
+ // Objekt deregistrieren.
+ BS_RenderObjectRegistry::GetInstance().DeregisterObject(this);
+}
+
+// Rendern
+// -------
+bool BS_RenderObject::Render()
+{
+ // Objektänderungen validieren
+ ValidateObject();
+
+ // Falls das Objekt nicht sichtbar ist, muss gar nichts gezeichnet werden
+ if (!m_Visible) return true;
+
+ // Falls notwendig, wird die Renderreihenfolge der Kinderobjekte aktualisiert.
+ if (m_ChildChanged)
+ {
+ SortRenderObjects();
+ m_ChildChanged = false;
+ }
+
+ // Objekt zeichnen.
+ DoRender();
+
+ // Dann müssen die Kinder gezeichnet werden
+ RENDEROBJECT_ITER it = m_Children.begin();
+ for (; it != m_Children.end(); ++it)
+ if (!(*it)->Render()) return false;
+
+ return true;
+}
+
+// Objektverwaltung
+// ----------------
+
+void BS_RenderObject::ValidateObject()
+{
+ // Die Veränderungen in den Objektvariablen aufheben
+ m_OldBBox = m_BBox;
+ m_OldVisible = m_Visible;
+ m_OldX = m_X;
+ m_OldY = m_Y;
+ m_OldZ = m_Z;
+ m_RefreshForced = false;
+}
+
+bool BS_RenderObject::UpdateObjectState()
+{
+ // Falls sich das Objekt verändert hat, muss der interne Zustand neu berechnet werden und evtl. Update-Regions für den nächsten Frame
+ // registriert werden.
+ if ((CalcBoundingBox() != m_OldBBox) ||
+ (m_Visible != m_OldVisible) ||
+ (m_X != m_OldX) ||
+ (m_Y != m_OldY) ||
+ (m_Z != m_OldZ) ||
+ m_RefreshForced)
+ {
+ // Renderrang des Objektes neu bestimmen, da sich dieser verändert haben könnte
+ if (m_ParentPtr.IsValid()) m_ParentPtr->SignalChildChange();
+
+ // Die Bounding-Box neu berechnen und Update-Regions registrieren.
+ UpdateBoxes();
+
+ // Änderungen Validieren
+ ValidateObject();
+ }
+
+ // Dann muss der Objektstatus der Kinder aktualisiert werden.
+ RENDEROBJECT_ITER it = m_Children.begin();
+ for (; it != m_Children.end(); ++it)
+ if (!(*it)->UpdateObjectState()) return false;
+
+ return true;
+}
+
+void BS_RenderObject::UpdateBoxes()
+{
+ // Bounding-Box aktualisieren
+ m_BBox = CalcBoundingBox();
+}
+
+BS_Rect BS_RenderObject::CalcBoundingBox() const
+{
+ // Die Bounding-Box mit der Objektgröße initialisieren.
+ BS_Rect BBox(0, 0, m_Width, m_Height);
+
+ // Die absolute Position der Bounding-Box berechnen.
+ BBox.Move(m_AbsoluteX, m_AbsoluteY);
+
+ // Die Bounding-Box am Elternobjekt clippen.
+ if (m_ParentPtr.IsValid()) BBox.Intersect(m_ParentPtr->GetBBox(), BBox);
+
+ return BBox;
+}
+
+void BS_RenderObject::CalcAbsolutePos(int& X, int& Y) const
+{
+ X = CalcAbsoluteX();
+ Y = CalcAbsoluteY();
+}
+
+int BS_RenderObject::CalcAbsoluteX() const
+{
+ if (m_ParentPtr.IsValid())
+ return m_ParentPtr->GetAbsoluteX() + m_X;
+ else
+ return m_X;
+}
+
+int BS_RenderObject::CalcAbsoluteY() const
+{
+ if (m_ParentPtr.IsValid())
+ return m_ParentPtr->GetAbsoluteY() + m_Y;
+ else
+ return m_Y;
+}
+
+// Baumverwaltung
+// --------------
+
+void BS_RenderObject::DeleteAllChildren()
+{
+ // Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit
+ // selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist.
+ while (!m_Children.empty())
+ {
+ BS_RenderObjectPtr<BS_RenderObject> CurPtr = m_Children.back();
+ CurPtr.Erase();
+ }
+}
+
+bool BS_RenderObject::AddObject(BS_RenderObjectPtr<BS_RenderObject> pObject)
+{
+ if (!pObject.IsValid())
+ {
+ BS_LOG_ERRORLN("Tried to add a null object to a renderobject.");
+ return false;
+ }
+
+ // Objekt in die Kinderliste einfügen.
+ m_Children.push_back(pObject);
+
+ // Sicherstellen, dass vor dem nächsten Rendern die Renderreihenfolge aktualisiert wird.
+ if (m_ParentPtr.IsValid()) m_ParentPtr->SignalChildChange();
+
+ return true;
+}
+
+bool BS_RenderObject::DetatchChildren(BS_RenderObjectPtr<BS_RenderObject> pObject)
+{
+ // Kinderliste durchgehen und Objekt entfernen falls vorhanden
+ RENDEROBJECT_ITER it = m_Children.begin();
+ for (; it != m_Children.end(); ++it)
+ if (*it == pObject)
+ {
+ m_Children.erase(it);
+ return true;
+ }
+
+ BS_LOG_ERRORLN("Tried to detach children from a render object that isn't its parent.");
+ return false;
+}
+
+void BS_RenderObject::SortRenderObjects()
+{
+ std::sort(m_Children.begin(), m_Children.end(), Greater);
+}
+
+void BS_RenderObject::UpdateAbsolutePos()
+{
+ CalcAbsolutePos(m_AbsoluteX, m_AbsoluteY);
+
+ RENDEROBJECT_ITER it = m_Children.begin();
+ for (; it != m_Children.end(); ++it)
+ (*it)->UpdateAbsolutePos();
+}
+
+// Get-Methoden
+// ------------
+
+bool BS_RenderObject::GetObjectIntersection(BS_RenderObjectPtr<BS_RenderObject> pObject, BS_Rect& Result)
+{
+ return m_BBox.Intersect(pObject->GetBBox(), Result);
+}
+
+// Set-Methoden
+// ------------
+void BS_RenderObject::SetPos(int X, int Y)
+{
+ m_X = X;
+ m_Y = Y;
+ UpdateAbsolutePos();
+}
+
+void BS_RenderObject::SetX(int X)
+{
+ m_X = X;
+ UpdateAbsolutePos();
+}
+
+void BS_RenderObject::SetY(int Y)
+{
+ m_Y = Y;
+ UpdateAbsolutePos();
+}
+
+void BS_RenderObject::SetZ(int Z)
+{
+ if (Z < 0)
+ BS_LOG_ERRORLN("Tried to set a negative Z value (%d).", Z);
+ else
+ m_Z = Z;
+}
+
+void BS_RenderObject::SetVisible(bool Visible)
+{
+ m_Visible = Visible;
+}
+
+// -----------------------------------------------------------------------------
+// Objekterzeuger
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Animation> BS_RenderObject::AddAnimation(const std::string& Filename)
+{
+ BS_RenderObjectPtr<BS_Animation> AniPtr(new BS_Animation(this, Filename));
+ if (AniPtr.IsValid() && AniPtr->GetInitSuccess())
+ return AniPtr;
+ else
+ {
+ if (AniPtr.IsValid()) AniPtr.Erase();
+ return BS_RenderObjectPtr<BS_Animation>();
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Animation> BS_RenderObject::AddAnimation(const BS_AnimationTemplate & AnimationTemplate)
+{
+ BS_Animation * AniPtr = new BS_Animation(this, AnimationTemplate);
+ if (AniPtr && AniPtr->GetInitSuccess())
+ return AniPtr;
+ else
+ {
+ delete AniPtr;
+ return BS_RenderObjectPtr<BS_Animation>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Bitmap> BS_RenderObject::AddBitmap(const std::string& Filename)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr(new BS_StaticBitmap(this, Filename));
+ if (BitmapPtr.IsValid() && BitmapPtr->GetInitSuccess())
+ return BS_RenderObjectPtr<BS_Bitmap>(BitmapPtr);
+ else
+ {
+ if (BitmapPtr.IsValid()) BitmapPtr.Erase();
+ return BS_RenderObjectPtr<BS_Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Bitmap> BS_RenderObject::AddDynamicBitmap(unsigned int Width, unsigned int Height)
+{
+ BS_RenderObjectPtr<BS_Bitmap> BitmapPtr(new BS_DynamicBitmap(this, Width, Height));
+ if (BitmapPtr.IsValid() && BitmapPtr->GetInitSuccess())
+ return BitmapPtr;
+ else
+ {
+ if (BitmapPtr.IsValid()) BitmapPtr.Erase();
+ return BS_RenderObjectPtr<BS_Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Panel> BS_RenderObject::AddPanel(int Width, int Height, unsigned int Color)
+{
+ BS_RenderObjectPtr<BS_Panel> PanelPtr(new BS_Panel(this, Width, Height, Color));
+ if (PanelPtr.IsValid() && PanelPtr->GetInitSuccess())
+ return PanelPtr;
+ else
+ {
+ if (PanelPtr.IsValid()) PanelPtr.Erase();
+ return BS_RenderObjectPtr<BS_Panel>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_Text> BS_RenderObject::AddText(const std::string & Font, const std::string & Text)
+{
+ BS_RenderObjectPtr<BS_Text> TextPtr(new BS_Text(this));
+ if (TextPtr.IsValid() && TextPtr->GetInitSuccess() && TextPtr->SetFont(Font))
+ {
+ TextPtr->SetText(Text);
+ return TextPtr;
+ }
+ else
+ {
+ if (TextPtr.IsValid()) TextPtr.Erase();
+ return BS_RenderObjectPtr<BS_Text>();
+ }
+}
+
+// Persistenz-Methoden
+// -------------------
+
+bool BS_RenderObject::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ // Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
+ Writer.Write(static_cast<unsigned int>(m_Type));
+ Writer.Write(m_Handle);
+
+ // Restliche Objekteigenschaften speichern.
+ Writer.Write(m_X);
+ Writer.Write(m_Y);
+ Writer.Write(m_AbsoluteX);
+ Writer.Write(m_AbsoluteY);
+ Writer.Write(m_Z);
+ Writer.Write(m_Width);
+ Writer.Write(m_Height);
+ Writer.Write(m_Visible);
+ Writer.Write(m_ChildChanged);
+ Writer.Write(m_InitSuccess);
+ Writer.Write(m_BBox.left);
+ Writer.Write(m_BBox.top);
+ Writer.Write(m_BBox.right);
+ Writer.Write(m_BBox.bottom);
+ Writer.Write(m_OldBBox.left);
+ Writer.Write(m_OldBBox.top);
+ Writer.Write(m_OldBBox.right);
+ Writer.Write(m_OldBBox.bottom);
+ Writer.Write(m_OldX);
+ Writer.Write(m_OldY);
+ Writer.Write(m_OldZ);
+ Writer.Write(m_OldVisible);
+ Writer.Write(m_ParentPtr.IsValid() ? m_ParentPtr->GetHandle() : 0);
+ Writer.Write(m_RefreshForced);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObject::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ // Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
+ Reader.Read(m_X);
+ Reader.Read(m_Y);
+ Reader.Read(m_AbsoluteX);
+ Reader.Read(m_AbsoluteY);
+ Reader.Read(m_Z);
+ Reader.Read(m_Width);
+ Reader.Read(m_Height);
+ Reader.Read(m_Visible);
+ Reader.Read(m_ChildChanged);
+ Reader.Read(m_InitSuccess);
+ Reader.Read(m_BBox.left);
+ Reader.Read(m_BBox.top);
+ Reader.Read(m_BBox.right);
+ Reader.Read(m_BBox.bottom);
+ Reader.Read(m_OldBBox.left);
+ Reader.Read(m_OldBBox.top);
+ Reader.Read(m_OldBBox.right);
+ Reader.Read(m_OldBBox.bottom);
+ Reader.Read(m_OldX);
+ Reader.Read(m_OldY);
+ Reader.Read(m_OldZ);
+ Reader.Read(m_OldVisible);
+ unsigned int ParentHandle;
+ Reader.Read(ParentHandle);
+ m_ParentPtr = BS_RenderObjectPtr<BS_RenderObject>(ParentHandle);
+ Reader.Read(m_RefreshForced);
+
+ UpdateAbsolutePos();
+ UpdateObjectState();
+
+ return Reader.IsGood();
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObject::PersistChildren(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Kinderanzahl speichern.
+ Writer.Write(m_Children.size());
+
+ // Rekursiv alle Kinder speichern.
+ RENDEROBJECT_LIST::iterator It = m_Children.begin();
+ while (It != m_Children.end())
+ {
+ Result &= (*It)->Persist(Writer);
+ ++It;
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObject::UnpersistChildren(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Kinderanzahl einlesen.
+ unsigned int ChildrenCount;
+ Reader.Read(ChildrenCount);
+ if (!Reader.IsGood()) return false;
+
+ // Alle Kinder rekursiv wieder herstellen.
+ for (unsigned int i = 0; i < ChildrenCount; ++i)
+ {
+ if (!RecreatePersistedRenderObject(Reader).IsValid()) return false;
+ }
+
+ return Result && Reader.IsGood();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectPtr<BS_RenderObject> BS_RenderObject::RecreatePersistedRenderObject(BS_InputPersistenceBlock & Reader)
+{
+ BS_RenderObjectPtr<BS_RenderObject> Result;
+
+ // Typ und Handle auslesen.
+ unsigned int Type;
+ unsigned int Handle;
+ Reader.Read(Type);
+ Reader.Read(Handle);
+ if (!Reader.IsGood()) return Result;
+
+ switch (Type)
+ {
+ case TYPE_PANEL:
+ Result = new BS_Panel(Reader, this, Handle);
+ break;
+
+ case TYPE_STATICBITMAP:
+ Result = new BS_StaticBitmap(Reader, this, Handle);
+ break;
+
+ case TYPE_DYNAMICBITMAP:
+ Result = new BS_DynamicBitmap(Reader, this, Handle);
+ break;
+
+ case TYPE_TEXT:
+ Result = new BS_Text(Reader, this, Handle);
+ break;
+
+ case TYPE_ANIMATION:
+ Result = new BS_Animation(Reader, this, Handle);
+ break;
+
+ default:
+ BS_LOG_ERRORLN("Cannot recreate render object of unknown type %d.", Type);
+ }
+
+ return Result;
+}
+
+// Hilfs-Methoden
+// --------------
+bool BS_RenderObject::Greater(const BS_RenderObjectPtr<BS_RenderObject> lhs, const BS_RenderObjectPtr<BS_RenderObject> rhs)
+{
+ // Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
+ if (lhs->m_Z != rhs->m_Z)
+ return lhs->m_Z < rhs->m_Z;
+ // Falls der Z-Wert gleich ist, wird das weiter oben gelegenen Objekte zuerst gezeichnet.
+ return lhs->m_Y < rhs->m_Y;
+}
diff --git a/engines/sword25/gfx/renderobject.h b/engines/sword25/gfx/renderobject.h
new file mode 100755
index 0000000000..c0fd6f3afc
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.h
@@ -0,0 +1,477 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_RenderObject
+ ---------------
+ Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt. Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjectManager in einem Baum verwaltet.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_RENDEROBJECT_H
+#define _BS_RENDEROBJECT_H
+
+// Includes
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "math/rect.h"
+#include "renderobjectptr.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_RenderObjectManager;
+class BS_Bitmap;
+class BS_Animation;
+class BS_AnimationTemplate;
+class BS_Panel;
+class BS_Text;
+
+// Klassendefinition
+/**
+ @brief Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt.
+
+ Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjektManager in einem Baum verwaltet.
+ */
+class BS_RenderObject
+{
+public:
+ // Konstanten
+ // ----------
+ enum TYPES
+ {
+ /// Das Wurzelobjekt. Siehe BS_RenderObjectManager
+ TYPE_ROOT,
+ /// Ein Image. Siehe BS_Bitmap.
+ TYPE_STATICBITMAP,
+ TYPE_DYNAMICBITMAP,
+ /// Eine Animation. Siehe BS_Animation.
+ TYPE_ANIMATION,
+ /// Eine farbige Fläche. Siehe BS_Panel.
+ TYPE_PANEL,
+ /// Ein Text. Siehe BS_Text.
+ TYPE_TEXT,
+ /// Ein unbekannter Objekttyp. Diesen Typ sollte kein Renderobjekt annehmen.
+ TYPE_UNKNOWN,
+ };
+
+ // Add-Methoden
+ // ------------
+
+ /**
+ @brief Erzeugt ein Bitmap als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quellbilddatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Bitmap> AddBitmap(const std::string& FileName);
+ /**
+ @brief Erzeugt ein veränderbares Bitmap als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Bitmaps
+ @param Height die Höhe des Bitmaps
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Bitmap> AddDynamicBitmap(unsigned int Width, unsigned int Height);
+ /**
+ @brief Erzeugt eine Animation auf Basis einer Animationsdatei als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quelldatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Animation> AddAnimation(const std::string& FileName);
+ /**
+ @brief Erzeugt eine Animation auf Basis eines Animationstemplate als Kinderobjekt des Renderobjektes.
+ @param pAnimationTemplate ein Pointer auf das Animationstemplate
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ @remark Das Renderobjekt übernimmt die Verwaltung des Animationstemplate.
+ */
+ BS_RenderObjectPtr<BS_Animation> AddAnimation(const BS_AnimationTemplate & AnimationTemplate);
+ /**
+ @brief Erzeugt ein neues Farbpanel als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Panels
+ @param Height die Höhe des Panels
+ @param Color die Farbe des Panels.<br>
+ Der Standardwert ist Schwarz (BS_RGB(0, 0, 0)).
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+
+ BS_RenderObjectPtr<BS_Panel> AddPanel(int Width, int Height, unsigned int Color = 0xff000000);
+ /**
+ @brief Erzeugt ein Textobjekt als Kinderobjekt des Renderobjektes.
+ @param Font der Dateiname des zu verwendenen Fonts
+ @param Text der anzuzeigende Text.<br>
+ Der Standardwert ist "".
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Text> AddText(const std::string & Font, const std::string & Text = "");
+
+ // Cast-Methoden
+ // -------------
+ /**
+ @brief Castet das Objekt zu einem BS_Bitmap-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Bitmap> ToBitmap()
+ {
+ if (m_Type == TYPE_STATICBITMAP || m_Type == TYPE_DYNAMICBITMAP) return BS_RenderObjectPtr<BS_Bitmap>(this); else return BS_RenderObjectPtr<BS_Bitmap>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Animation-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Animation> ToAnimation()
+ {
+ if (m_Type == TYPE_ANIMATION) return BS_RenderObjectPtr<BS_Animation>(this); else return BS_RenderObjectPtr<BS_Animation>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Panel-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Panel> ToPanel()
+ {
+ if (m_Type == TYPE_PANEL) return BS_RenderObjectPtr<BS_Panel>(this); else return BS_RenderObjectPtr<BS_Panel>();
+ }
+ /**
+ @brief Castet das Object zu einem BS_Text-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ BS_RenderObjectPtr<BS_Text> ToText()
+ {
+ if (m_Type == TYPE_TEXT) return BS_RenderObjectPtr<BS_Text>(this); else return BS_RenderObjectPtr<BS_Text>();
+ }
+
+ // Konstruktor / Desktruktor
+ // -------------------------
+ /**
+ @brief Erzeugt ein neues BS_RenderObject.
+ @param pKernel ein Pointer auf den Kernel
+ @param pParent ein Pointer auf das Elternobjekt des neuen Objektes im Objektbaum.<br>
+ Der Pointer darf nicht NULL sein.
+ @param Type der Objekttyp<br>
+ Der Typ BS_RenderObject::TYPE_ROOT ist nicht zulässig. Wurzelknoten müssen mit dem alternativen Konstruktor erzeugt
+ werden.
+ @param Handle das Handle, welches dem Objekt zugewiesen werden soll.<br>
+ Dieser Parameter erzwingt ein bestimmtes Handle für das neue Objekt, oder wählt automatisch ein Handle, wenn der Parameter 0 ist.
+ Ist das gewünschte Handle bereits vergeben, gibt GetInitSuccess() false zurück.<br>
+ Der Standardwert ist 0.
+ @remark Nach dem Aufruf des Konstruktors kann über die Methode GetInitSuccess() abgefragt werden, ob die Konstruktion erfolgreich war.<br>
+ Es ist nicht notwendig alle BS_RenderObject Instanzen einzeln zu löschen. Dieses geschiet automatisch beim Löschen eines
+ Vorfahren oder beim Löschen des zuständigen BS_RenderObjectManager.
+ */
+ BS_RenderObject(BS_RenderObjectPtr<BS_RenderObject> pParent, TYPES Type, unsigned int Handle = 0);
+ virtual ~BS_RenderObject();
+
+ // Interface
+ // ---------
+ /**
+ @brief Rendert des Objekt und alle seine Unterobjekte.
+ @return Gibt false zurück, falls beim Rendern ein Fehler aufgetreten ist.
+ @remark Vor jedem Aufruf dieser Methode muss ein Aufruf von UpdateObjectState() erfolgt sein.
+ Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.<br>
+ Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool Render();
+ /**
+ @brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor.
+ Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert.
+ @return Gibt false zurück, falls ein Fehler aufgetreten ist.
+ @remark Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool UpdateObjectState();
+ /**
+ @brief Löscht alle Kinderobjekte.
+ */
+ void DeleteAllChildren();
+
+ // Accessor-Methoden
+ // -----------------
+ /**
+ @brief Setzt die Position des Objektes.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void SetPos(int X, int Y);
+ /**
+ @brief Setzt die Position des Objektes auf der X-Achse.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void SetX(int X);
+ /**
+ @brief Setzt die Position des Objektes auf der Y-Achse.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void SetY(int Y);
+ /**
+ @brief Setzt den Z-Wert des Objektes.
+ @param Z der neue Z-Wert des Objektes relativ zum Elternobjekt<br>
+ Negative Z-Werte sind nicht zulässig.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ virtual void SetZ(int Z);
+ /**
+ @brief Setzt die Sichtbarkeit eine Objektes.
+ @param Visible der neue Sichtbarkeits-Zustand des Objektes<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ virtual void SetVisible(bool Visible);
+ /**
+ @brief Gibt die Position des Objektes auf der X-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int GetX() const { return m_X; }
+ /**
+ @brief Gibt die Position des Objektes auf der Y-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int GetY() const { return m_Y; }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der X-Achse zurück.
+ */
+ virtual int GetAbsoluteX() const { return m_AbsoluteX; }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der Y-Achse zurück.
+ */
+ virtual int GetAbsoluteY() const { return m_AbsoluteY; }
+ /**
+ @brief Gibt den Z-Wert des Objektes relativ zum Elternobjekt zurück.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ int GetZ() const { return m_Z; }
+ /**
+ @brief Gibt die Breite des Objektes zurück.
+ */
+ int GetWidth() const { return m_Width; }
+ /**
+ @brief Gibt die Höhe des Objektes zurück.
+ */
+ int GetHeight() const { return m_Height; }
+ /**
+ @brief Gibt den Sichtbarkeitszustand des Objektes zurück.
+ @return Gibt den Sichtbarkeitszustand des Objektes zurück.<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ bool IsVisible() const { return m_Visible; }
+ /**
+ @brief Gibt den Typ des Objektes zurück.
+ */
+ TYPES GetType() const { return m_Type; }
+ /**
+ @brief Gibt zurück, ob das Objekt erfolgreich initialisiert wurde.
+ @remark Hässlicher Workaround um das Problem, dass Konstruktoren keine Rückgabewerte haben.
+ */
+ bool GetInitSuccess() const { return m_InitSuccess; }
+ /**
+ @brief Gibt die Bounding-Box des Objektes zurück.
+ @remark Diese Angabe erfolgt ausnahmsweise in Bildschirmkoordianten und nicht relativ zum Elternobjekt.
+ */
+ const BS_Rect& GetBBox() const { return m_BBox; }
+ /**
+ @brief Stellt sicher, dass das Objekt im nächsten Frame neu gezeichnet wird.
+ */
+ void ForceRefresh() { m_RefreshForced = true; };
+ /**
+ @brief Gibt das Handle des Objekte zurück.
+ */
+ unsigned int GetHandle() const { return m_Handle; }
+
+ // Persistenz-Methoden
+ // -------------------
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+ // TODO: Evtl. protected
+ bool PersistChildren(BS_OutputPersistenceBlock & Writer);
+ bool UnpersistChildren(BS_InputPersistenceBlock & Reader);
+ // TODO: Evtl. private
+ BS_RenderObjectPtr<BS_RenderObject> RecreatePersistedRenderObject(BS_InputPersistenceBlock & Reader);
+
+protected:
+ // Typen
+ // -----
+ typedef std::vector<BS_RenderObjectPtr<BS_RenderObject> > RENDEROBJECT_LIST;
+ typedef std::vector<BS_RenderObjectPtr<BS_RenderObject> >::iterator RENDEROBJECT_ITER;
+
+ int m_X; ///< Die X-Position des Objektes relativ zum Eltern-Objekt
+ int m_Y; ///< Die Y-Position des Objektes relativ zum Eltern-Objekt
+ int m_AbsoluteX; ///< Die absolute X-Position des Objektes
+ int m_AbsoluteY; ///< Die absolute Y-Position des Objektes
+ int m_Z; ///< Der Z-Wert des Objektes relativ zum Eltern-Objekt
+ int m_Width; ///< Die Breite des Objektes
+ int m_Height; ///< Die Höhe des Objektes
+ bool m_Visible; ///< Ist true, wenn das Objekt sichtbar ist
+ bool m_ChildChanged; ///< Ist true, wenn sich ein Kinderobjekt verändert hat
+ TYPES m_Type; ///< Der Objekttyp
+ bool m_InitSuccess; ///< Ist true, wenn Objekt erfolgreich intialisiert werden konnte
+ BS_Rect m_BBox; ///< Die Bounding-Box des Objektes in Bildschirmkoordinaten
+
+ // Kopien der Variablen, die für die Errechnung des Dirty-Rects und zur Bestimmung der Objektveränderung notwendig sind
+ BS_Rect m_OldBBox;
+ int m_OldX;
+ int m_OldY;
+ int m_OldZ;
+ bool m_OldVisible;
+
+ /// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet.
+ BS_RenderObjectManager* m_ManagerPtr;
+
+ // Render-Methode
+ // --------------
+ /**
+ @brief Einschubmethode, die den tatsächlichen Redervorgang durchführt.
+
+ Diese Methode wird von Render() aufgerufen um das Objekt darzustellen.
+ Diese Methode sollte von allen Klassen implementiert werden, die von BS_RederObject erben, um das Zeichnen umzusetzen.
+
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark
+ */
+ virtual bool DoRender() = 0; // { return true; };
+
+ // RenderObject-Baum Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Alle Eigenschaften wie X, Y, Z und Visible eines BS_RenderObjects sind relativ zu denen seines Vaters.
+ // Außerdem sind die Objekte von links nach rechts in ihrer Renderreihenfolge sortiert.
+ // Das primäre Sortierkriterium ist hierbei der Z-Wert und das sekundäre der Y-Wert (von oben nach unten).
+ // Beispiel:
+ // Screen
+ // / | \
+ // / | \
+ // / | \
+ // / | \
+ // Background Interface Maus
+ // / \ / | \
+ // / \ / | \
+ // George Tür Icn1 Icn2 Icn3
+ //
+ // Wenn jetzt das Interface mit SetVisible() ausgeblendet würde, verschwinden auch die Icons, die sich im Interface
+ // befinden.
+ // Wenn der Hintergrund bewegt wird (Scrolling), bewegen sich auch die darauf befindlichen Gegenstände und Personen.
+
+ /// Ein Pointer auf das Elternobjekt.
+ BS_RenderObjectPtr<BS_RenderObject> m_ParentPtr;
+ /// Die Liste der Kinderobjekte nach der Renderreihenfolge geordnet
+ RENDEROBJECT_LIST m_Children;
+
+ /**
+ @brief Gibt einen Pointer auf den BS_RenderObjektManager zurück, der das Objekt verwaltet.
+ */
+ BS_RenderObjectManager* GetManager() const { return m_ManagerPtr; }
+ /**
+ @brief Fügt dem Objekt ein neues Kinderobjekt hinzu.
+ @param pObject ein Pointer auf das einzufügende Objekt
+ @return Gibt false zurück, falls das Objekt nicht eingefügt werden konnte.
+ */
+ bool AddObject(BS_RenderObjectPtr<BS_RenderObject> pObject);
+
+private:
+ /// Ist true, wenn das Objekt in nächsten Frame neu gezeichnet werden soll
+ bool m_RefreshForced;
+
+ unsigned int m_Handle;
+
+ /**
+ @brief Entfernt ein Objekt aus der Kinderliste.
+ @param pObject ein Pointer auf das zu entfernende Objekt
+ @return Gibt false zurück, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
+ */
+ bool DetatchChildren(BS_RenderObjectPtr<BS_RenderObject> pObject);
+ /**
+ @brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
+ */
+ void UpdateBoxes();
+ /**
+ @brief Berechnet die Bounding-Box des Objektes.
+ @return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zurück.
+ */
+ BS_Rect CalcBoundingBox() const;
+ /**
+ @brief Berechnet das Dirty-Rectangle des Objektes.
+ @return Gibt das Dirty-Rectangle des Objektes in Bildschirmkoordinaten zurück.
+ */
+ BS_Rect CalcDirtyRect() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ void CalcAbsolutePos(int& X, int& Y) const;
+ /**
+ @brief Berechnet die absolute Position des Objektes auf der X-Achse.
+ */
+ int CalcAbsoluteX() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ int CalcAbsoluteY() const;
+ /**
+ @brief Sortiert alle Kinderobjekte nach ihrem Renderang.
+ */
+ void SortRenderObjects();
+ /**
+ @brief Validiert den Zustand eines Objektes nachdem die durch die Veränderung verursachten Folgen abgearbeitet wurden.
+ */
+ void ValidateObject();
+ /**
+ @brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.
+
+ Diese Methode muss aufgerufen werden, wann immer sich die Position des Objektes verändert. Damit die Kinderobjekte immer die
+ richtige absolute Position haben.
+ */
+ void UpdateAbsolutePos();
+ /**
+ @brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend verändert hat, die eine erneute Bestimmung der
+ Rendereihenfolge verlangt.
+ */
+ void SignalChildChange() { m_ChildChanged = true; }
+ /**
+ @brief Berechnet des Schnittrechteck der Bounding-Box des Objektes mit einem anderen Objekt.
+ @param pObjekt ein Pointer auf das Objekt mit dem geschnitten werden soll
+ @param Result das Ergebnisrechteck
+ @return Gibt false zurück, falls sich die Objekte gar nicht schneiden.
+ */
+ bool GetObjectIntersection(BS_RenderObjectPtr<BS_RenderObject> pObject, BS_Rect& Result);
+ /**
+ @brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
+ @remark Diese Methode wird fürs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
+ */
+ static bool Greater(const BS_RenderObjectPtr<BS_RenderObject> lhs, const BS_RenderObjectPtr<BS_RenderObject> rhs);
+};
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp
new file mode 100755
index 0000000000..af9e97afb6
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.cpp
@@ -0,0 +1,163 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "renderobjectmanager.h"
+
+#include "kernel/kernel.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "gfx/graphicengine.h"
+#include "gfx/animationtemplateregistry.h"
+#include "math/rect.h"
+#include "renderobject.h"
+#include "timedrenderobject.h"
+#include "rootrenderobject.h"
+
+#define BS_LOG_PREFIX "RENDEROBJECTMANAGER"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Desktruktion
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectManager::BS_RenderObjectManager(int Width, int Height, int FramebufferCount) :
+ m_FrameStarted(false)
+{
+ // Wurzel des BS_RenderObject-Baumes erzeugen.
+ m_RootPtr = new BS_RootRenderObject(this, Width, Height);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_RenderObjectManager::~BS_RenderObjectManager()
+{
+ // Die Wurzel des Baumes löschen, damit werden alle BS_RenderObjects mitgelöscht.
+ m_RootPtr.Erase();
+}
+
+// -----------------------------------------------------------------------------
+// Interface
+// -----------------------------------------------------------------------------
+
+void BS_RenderObjectManager::StartFrame()
+{
+ m_FrameStarted = true;
+
+ // Verstrichene Zeit bestimmen
+ int TimeElapsed = BS_Kernel::GetInstance()->GetGfx()->GetLastFrameDurationMicro();
+
+ // Alle BS_TimedRenderObject Objekte über den Framestart und die verstrichene Zeit in Kenntnis setzen
+ RenderObjectList::iterator Iter = m_TimedRenderObjects.begin();
+ for (; Iter != m_TimedRenderObjects.end(); ++Iter)
+ (*Iter)->FrameNotification(TimeElapsed);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObjectManager::Render()
+{
+ // Den Objekt-Status des Wurzelobjektes aktualisieren. Dadurch werden rekursiv alle Baumelemente aktualisiert.
+ // Beim aktualisieren des Objekt-Status werden auch die Update-Rects gefunden, so dass feststeht, was neu gezeichnet
+ // werden muss.
+ if (!m_RootPtr.IsValid() || !m_RootPtr->UpdateObjectState()) return false;
+
+ m_FrameStarted = false;
+
+ // Die Render-Methode der Wurzel aufrufen. Dadurch wird das rekursive Rendern der Baumelemente angestoßen.
+ return m_RootPtr->Render();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_RenderObjectManager::AttatchTimedRenderObject(BS_RenderObjectPtr<BS_TimedRenderObject> RenderObjectPtr)
+{
+ m_TimedRenderObjects.push_back(RenderObjectPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_RenderObjectManager::DetatchTimedRenderObject(BS_RenderObjectPtr<BS_TimedRenderObject> RenderObjectPtr)
+{
+ RenderObjectList::iterator Iter = find(m_TimedRenderObjects.begin(), m_TimedRenderObjects.end(), RenderObjectPtr);
+ if (Iter != m_TimedRenderObjects.end()) m_TimedRenderObjects.erase(Iter);
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObjectManager::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Alle Kinder des Wurzelknotens speichern. Dadurch werden alle BS_RenderObjects gespeichert rekursiv gespeichert.
+ Result &= m_RootPtr->PersistChildren(Writer);
+
+ Writer.Write(m_FrameStarted);
+
+ // Referenzen auf die TimedRenderObjects persistieren.
+ Writer.Write(m_TimedRenderObjects.size());
+ RenderObjectList::const_iterator Iter = m_TimedRenderObjects.begin();
+ while (Iter != m_TimedRenderObjects.end())
+ {
+ Writer.Write((*Iter)->GetHandle());
+ ++Iter;
+ }
+
+ // Alle BS_AnimationTemplates persistieren.
+ Result &= BS_AnimationTemplateRegistry::GetInstance().Persist(Writer);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RenderObjectManager::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Alle Kinder des Wurzelknotens löschen. Damit werden alle BS_RenderObjects gelöscht.
+ m_RootPtr->DeleteAllChildren();
+
+ // Alle BS_RenderObjects wieder hestellen.
+ if (!m_RootPtr->UnpersistChildren(Reader)) return false;
+
+ Reader.Read(m_FrameStarted);
+
+ // Momentan gespeicherte Referenzen auf TimedRenderObjects löschen.
+ m_TimedRenderObjects.resize(0);
+
+ // Referenzen auf die TimedRenderObjects wieder herstellen.
+ unsigned int TimedObjectCount;
+ Reader.Read(TimedObjectCount);
+ for (unsigned int i = 0; i < TimedObjectCount; ++i)
+ {
+ unsigned int Handle;
+ Reader.Read(Handle);
+ m_TimedRenderObjects.push_back(Handle);
+ }
+
+ // Alle BS_AnimationTemplates wieder herstellen.
+ Result &= BS_AnimationTemplateRegistry::GetInstance().Unpersist(Reader);
+
+ return Result;
+}
diff --git a/engines/sword25/gfx/renderobjectmanager.h b/engines/sword25/gfx/renderobjectmanager.h
new file mode 100755
index 0000000000..6a46cfa9e9
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.h
@@ -0,0 +1,115 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_RenderObjectManager
+ ----------------------
+ Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt z.B. dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_RENDEROBJECTMANAGER_H
+#define _BS_RENDEROBJECTMANAGER_H
+
+// Includes
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "renderobjectptr.h"
+#include "kernel/persistable.h"
+
+// Klassendefinition
+class BS_Kernel;
+class BS_Rect;
+class BS_RenderObject;
+class BS_TimedRenderObject;
+
+/**
+ @brief Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden und ermöglicht den Zugriff auf die
+ BS_RenderObjects über einen String.
+*/
+class BS_RenderObjectManager : public BS_Persistable
+{
+public:
+ /**
+ @brief Erzeugt ein neues BS_RenderObjectManager-Objekt.
+ @param Width die horizontale Bildschirmauflösung in Pixeln
+ @param Height die vertikale Bildschirmauflösung in Pixeln
+ @param Die Anzahl an Framebuffern, die eingesetzt wird (Backbuffer + Primary).
+ */
+ BS_RenderObjectManager(int Width, int Height, int FramebufferCount);
+ virtual ~BS_RenderObjectManager();
+
+ // Interface
+ // ---------
+ /**
+ @brief Initialisiert den Manager für einen neuen Frame.
+ @remark Alle Veränderungen an Objekten müssen nach einem Aufruf dieser Methode geschehen, damit sichergestellt ist, dass diese
+ visuell umgesetzt werden.<br>
+ Mit dem Aufruf dieser Methode werden die Rückgabewerte von GetUpdateRects() und GetUpdateRectCount() auf ihre Startwerte
+ zurückgesetzt. Wenn man also mit diesen Werten arbeiten möchten, muss man dies nach einem Aufruf von Render() und vor
+ einem Aufruf von StartFrame() tun.
+ */
+ void StartFrame();
+ /**
+ @brief Rendert alle Objekte die sich während des letzten Aufrufes von Render() verändert haben.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ */
+ bool Render();
+ /**
+ @brief Gibt einen Pointer auf die Wurzel des Objektbaumes zurück.
+ */
+ BS_RenderObjectPtr<BS_RenderObject> GetTreeRoot() { return m_RootPtr; }
+ /**
+ @brief Fügt ein BS_TimedRenderObject in die Liste der zeitabhängigen Render-Objekte.
+
+ Alle Objekte die sich in dieser Liste befinden werden vor jedem Frame über die seit dem letzten Frame
+ vergangene Zeit informiert, so dass sich ihren Zustand zeitabhängig verändern können.
+
+ @param RenderObject das einzufügende BS_TimedRenderObject
+ */
+ void AttatchTimedRenderObject(BS_RenderObjectPtr<BS_TimedRenderObject> pRenderObject);
+ /**
+ @brief Entfernt ein BS_TimedRenderObject aus der Liste für zeitabhängige Render-Objekte.
+ */
+ void DetatchTimedRenderObject(BS_RenderObjectPtr<BS_TimedRenderObject> pRenderObject);
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ bool m_FrameStarted;
+ typedef std::vector<BS_RenderObjectPtr<BS_TimedRenderObject> > RenderObjectList;
+ RenderObjectList m_TimedRenderObjects;
+
+ // RenderObject-Tree Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Zu weiteren Informationen siehe: "renderobject.h"
+ BS_RenderObjectPtr<BS_RenderObject> m_RootPtr; // Die Wurzel der Baumes
+};
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectptr.h b/engines/sword25/gfx/renderobjectptr.h
new file mode 100755
index 0000000000..bc2d00caac
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectptr.h
@@ -0,0 +1,80 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RENDER_OBJECT_PTR_H
+#define BS_RENDER_OBJECT_PTR_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "renderobjectregistry.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_RenderObject;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+template<class T>
+class BS_RenderObjectPtr
+{
+public:
+ BS_RenderObjectPtr() : m_Handle(0)
+ {}
+
+ BS_RenderObjectPtr(unsigned int Handle) : m_Handle(Handle)
+ {}
+
+ BS_RenderObjectPtr(BS_RenderObject * RenderObjectPtr)
+ {
+ m_Handle = RenderObjectPtr->GetHandle();
+ }
+
+ T * operator->() const
+ {
+ return static_cast<T *>(BS_RenderObjectRegistry::GetInstance().ResolveHandle(m_Handle));
+ }
+
+ bool operator==(const BS_RenderObjectPtr<T> & other)
+ {
+ return m_Handle == other.m_Handle;
+ }
+
+ bool IsValid() const
+ {
+ return BS_RenderObjectRegistry::GetInstance().ResolveHandle(m_Handle) != 0;
+ }
+
+ void Erase()
+ {
+ delete static_cast<T *>(BS_RenderObjectRegistry::GetInstance().ResolveHandle(m_Handle));
+ m_Handle = 0;
+ }
+
+private:
+ unsigned int m_Handle;
+};
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectregistry.cpp b/engines/sword25/gfx/renderobjectregistry.cpp
new file mode 100755
index 0000000000..bf96436eee
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.cpp
@@ -0,0 +1,50 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "RENDEROBJECTREGISTRY"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "renderobjectregistry.h"
+
+// -----------------------------------------------------------------------------
+// Implementation
+// -----------------------------------------------------------------------------
+
+std::auto_ptr<BS_RenderObjectRegistry> BS_RenderObjectRegistry::m_InstancePtr;
+
+// -----------------------------------------------------------------------------
+
+void BS_RenderObjectRegistry::LogErrorLn(const char * Message) const
+{
+ BS_LOG_ERRORLN(Message);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_RenderObjectRegistry::LogWarningLn(const char * Message) const
+{
+ BS_LOG_WARNINGLN(Message);
+}
diff --git a/engines/sword25/gfx/renderobjectregistry.h b/engines/sword25/gfx/renderobjectregistry.h
new file mode 100755
index 0000000000..96df59ebe1
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.h
@@ -0,0 +1,60 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RENDEROBJECTREGISTRY_H
+#define BS_RENDEROBJECTREGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/objectregistry.h"
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+class BS_RenderObject;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_RenderObjectRegistry : public BS_ObjectRegistry<BS_RenderObject>
+{
+public:
+ static BS_RenderObjectRegistry & GetInstance()
+ {
+ if (!m_InstancePtr.get()) m_InstancePtr.reset(new BS_RenderObjectRegistry);
+ return *m_InstancePtr.get();
+ }
+
+private:
+ virtual void LogErrorLn(const char * Message) const;
+ virtual void LogWarningLn(const char * Message) const;
+
+ static std::auto_ptr<BS_RenderObjectRegistry> m_InstancePtr;
+};
+
+#endif
diff --git a/engines/sword25/gfx/rootrenderobject.h b/engines/sword25/gfx/rootrenderobject.h
new file mode 100755
index 0000000000..8d92cb7a70
--- /dev/null
+++ b/engines/sword25/gfx/rootrenderobject.h
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_ROOTRENDEROBJECT_H
+#define BS_ROOTRENDEROBJECT_H
+
+// Includes
+#include "kernel/common.h"
+#include "renderobject.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+
+// Klassendefinition
+class BS_RenderObjectManager;
+
+class BS_RootRenderObject : public BS_RenderObject
+{
+friend BS_RenderObjectManager;
+
+private:
+ BS_RootRenderObject(BS_RenderObjectManager * ManagerPtr, int Width, int Height) :
+ BS_RenderObject(BS_RenderObjectPtr<BS_RenderObject>(), TYPE_ROOT)
+ {
+ m_ManagerPtr = ManagerPtr;
+ m_Width = Width;
+ m_Height = Height;
+ }
+
+protected:
+ virtual bool DoRender() { return true; }
+};
+
+#endif
diff --git a/engines/sword25/gfx/screenshot.cpp b/engines/sword25/gfx/screenshot.cpp
new file mode 100755
index 0000000000..82ba4a9374
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.cpp
@@ -0,0 +1,202 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "SCREENSHOT"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "screenshot.h"
+#include "util/libpng/png.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+
+struct RGB_PIXEL
+{
+ RGB_PIXEL(unsigned char _Red, unsigned char _Green, unsigned char _Blue) :
+ Red(_Red),
+ Green(_Green),
+ Blue(_Blue)
+ {};
+
+ unsigned char Red;
+ unsigned char Green;
+ unsigned char Blue;
+};
+
+bool BS_Screenshot::SaveToFile(unsigned int Width, unsigned int Height, const vector<unsigned int> & Data, const string & Filename)
+{
+ BS_ASSERT(Data.size() == Width * Height);
+
+ // Buffer für Bildschirminhalt in RGB reservieren
+ vector<RGB_PIXEL> PixelBuffer;
+ PixelBuffer.reserve(Width * Height);
+
+ // Framebufferdaten pixelweise von RGBA nach RGB konvertieren
+ vector<unsigned int>::const_iterator it = Data.begin();
+ for (unsigned int y = 0; y < Height; y++)
+ {
+ for (unsigned int x = 0; x < Width; x++)
+ {
+ unsigned int SrcPixel = *it++;
+ PixelBuffer.push_back(RGB_PIXEL((SrcPixel >> 16) & 0xff, (SrcPixel >> 8) & 0xff, SrcPixel & 0xff));
+ }
+ }
+ BS_ASSERT(it == Data.end());
+ BS_ASSERT(Data.size() == PixelBuffer.size());
+
+ // Variablen für die PNG-Erstellung
+ FILE * OutFile = 0;
+ png_structp png_ptr = 0;
+ png_infop info_ptr = 0;
+
+ try
+ {
+ OutFile = fopen(Filename.c_str(), "wb");
+ if (!OutFile)
+ {
+ BS_LOG_ERRORLN("Could not create screenshot-file \"%s\".", Filename.c_str());
+ throw(0);
+ }
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create PNG write-struct.");
+ throw(0);
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ BS_LOG_ERRORLN("Could not create PNG info-struct.");
+ throw(0);
+ }
+
+ // Der Kompressionsbuffer muss groß genug sein um das gesamte Bild zu beinhalten.
+ // Dieses stellt sicher, dass nur ein IDAT Chunk erstellt wird.
+ // Als Buffergröße wird 110% der Rohdatengröße verwandt, um sicher zu gehen.
+ png_set_compression_buffer_size(png_ptr, (Width * Height * 3 * 110) / 100);
+
+ // PNG-Info Struktur initialisieren
+ png_set_IHDR(png_ptr, info_ptr,
+ Width, // Breite
+ Height, // Höhe
+ 8, // Bittiefe pro Kanal
+ PNG_COLOR_TYPE_RGB, // Farbformat
+ PNG_INTERLACE_NONE, // Interlacing-Typ
+ PNG_COMPRESSION_TYPE_DEFAULT, // Kompressions-Typ
+ PNG_FILTER_TYPE_DEFAULT); // Filter-Typ
+
+ // Rowpointer erstellen
+ vector<png_bytep> RowPointers;
+ RowPointers.reserve(Height);
+ for (unsigned int i = 0; i < Height; i++)
+ {
+ RowPointers.push_back((png_bytep)(&PixelBuffer[Width * i]));
+ }
+ png_set_rows(png_ptr, info_ptr, &RowPointers[0]);
+
+ png_init_io(png_ptr, OutFile);
+
+ // Bild schreiben
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(OutFile);
+ }
+
+ catch (int)
+ {
+ // Wenn die Datei bereits erstellt wurde, Datei schließen und löschen.
+ if (OutFile)
+ {
+ fclose(OutFile);
+ remove(Filename.c_str());
+ }
+
+ if (info_ptr) png_destroy_write_struct(0, &info_ptr);
+ if (png_ptr) png_destroy_write_struct(&png_ptr, (png_infopp) 0);
+
+ BS_LOG_ERRORLN("Could not create screenshot (\"%s\").", Filename.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Screenshot::SaveThumbnailToFile(unsigned int Width, unsigned int Height, const vector<unsigned int> & Data, const string & Filename)
+{
+ //
+ // Diese Methode nimmt ein Screenshot mit den Maßen von 800x600 und erzeugt einen Screenshot mit den Maßen von 200x125.
+ // Dabei werden je 50 Pixel oben und unten abgeschnitten (die Interface-Leisten im Spiel). Das verbleibende Bild von 800x500 wird auf
+ // ein 16tel seiner Größe reduziert, indem es in 4x4 Pixelblöcke ausgeteilt wird und der Durchschnitt jedes Blockes einen Pixel des Zielbildes generiert.
+ // Abschließend wird das Ergebnis als PNG-Datei unter dem übergebenen Dateinamen gespeichert.
+ //
+
+ // Die Ausgangsgröße muss 800x600 sein.
+ if (Width != 800 || Height != 600)
+ {
+ BS_LOG_ERRORLN("The sreenshot dimensions have to be 800x600 in order to be saved as a thumbnail.");
+ return false;
+ }
+
+ // Buffer für die Zieldaten erstellen (RGBA Bild mit den Maßen 200x125).
+ vector<unsigned int> ThumbnailData(200 * 125);
+
+ // Über das Zielbild iterieren und einen Pixel zur Zeit berechnen.
+ unsigned int x, y;
+ x = y = 0;
+ for(vector<unsigned int>::iterator Iter = ThumbnailData.begin(); Iter != ThumbnailData.end(); ++Iter)
+ {
+ // Durchschnitt über 4x4 Pixelblock im Quellbild bilden.
+ unsigned int Alpha, Red, Green, Blue;
+ Alpha = Red = Green = Blue = 0;
+ for (unsigned int j = 0; j < 4; ++j)
+ {
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ unsigned int Pixel = Data[((y * 4) + j + 50) * 800 + ((x * 4) + i)];
+ Alpha += (Pixel >> 24);
+ Red += (Pixel >> 16) & 0xff;
+ Green += (Pixel >> 8) & 0xff;
+ Blue += Pixel & 0xff;
+ }
+ }
+
+ // Zielpixel schreiben.
+ *Iter = ((Alpha / 16) << 24) | ((Red / 16) << 16) | ((Green / 16) << 8) | (Blue / 16);
+
+ // Mitzählen an welcher Stelle im Zielbild wir uns befinden.
+ ++x;
+ if (x == 200)
+ {
+ x = 0;
+ ++y;
+ }
+ }
+
+ // Bild als PNG Speichern.
+ return SaveToFile(200, 125, ThumbnailData, Filename);
+}
diff --git a/engines/sword25/gfx/screenshot.h b/engines/sword25/gfx/screenshot.h
new file mode 100755
index 0000000000..453579cb53
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.h
@@ -0,0 +1,44 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_SCREENSHOT_H
+#define BS_SCREENSHOT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/memlog_off.h"
+#include <string>
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_Screenshot
+{
+public:
+ static bool SaveToFile(unsigned int Width, unsigned int Height, const std::vector<unsigned int> & Data, const std::string & Filename);
+ static bool SaveThumbnailToFile(unsigned int Width, unsigned int Height, const std::vector<unsigned int> & Data, const std::string & Filename);
+};
+
+#endif
diff --git a/engines/sword25/gfx/staticbitmap.cpp b/engines/sword25/gfx/staticbitmap.cpp
new file mode 100755
index 0000000000..34d89107f1
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.cpp
@@ -0,0 +1,218 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "staticbitmap.h"
+#include "bitmapresource.h"
+#include "package/packagemanager.h"
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "STATICBITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_StaticBitmap::BS_StaticBitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const std::string& Filename) :
+ BS_Bitmap(ParentPtr, TYPE_STATICBITMAP)
+{
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!m_InitSuccess) return;
+
+ m_InitSuccess = InitBitmapResource(Filename);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_StaticBitmap::BS_StaticBitmap(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle) :
+ BS_Bitmap(ParentPtr, TYPE_STATICBITMAP, Handle)
+{
+ m_InitSuccess = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::InitBitmapResource(const std::string & Filename)
+{
+ // Bild-Resource laden
+ BS_Resource* ResourcePtr = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(Filename);
+ if (!ResourcePtr)
+ {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", Filename.c_str());
+ return false;
+ }
+ if (ResourcePtr->GetType() != BS_Resource::TYPE_BITMAP)
+ {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", Filename.c_str());
+ return false;
+ }
+
+ BS_BitmapResource * BitmapPtr = static_cast<BS_BitmapResource*>(ResourcePtr);
+
+ // Den eindeutigen Dateinamen zum späteren Referenzieren speichern
+ m_ResourceFilename = BitmapPtr->GetFileName();
+
+ // RenderObject Eigenschaften aktualisieren
+ m_OriginalWidth = m_Width = BitmapPtr->GetWidth();
+ m_OriginalHeight = m_Height = BitmapPtr->GetHeight();
+
+ // Bild-Resource freigeben
+ BitmapPtr->Release();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_StaticBitmap::~BS_StaticBitmap()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::DoRender()
+{
+ // Bitmap holen
+ BS_Resource* ResourcePtr = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(m_ResourceFilename);
+ BS_ASSERT(ResourcePtr);
+ BS_ASSERT(ResourcePtr->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource * BitmapResourcePtr = static_cast<BS_BitmapResource*>(ResourcePtr);
+
+ // Framebufferobjekt holen
+ BS_GraphicEngine * GfxPtr = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(GfxPtr);
+
+ // Bitmap zeichnen
+ bool Result;
+ if (m_ScaleFactorX == 1.0f && m_ScaleFactorY == 1.0f)
+ {
+ Result = BitmapResourcePtr->Blit(m_AbsoluteX, m_AbsoluteY,
+ (m_FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (m_FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, -1, -1);
+ }
+ else
+ {
+ Result = BitmapResourcePtr->Blit(m_AbsoluteX, m_AbsoluteY,
+ (m_FlipV ? BS_BitmapResource::FLIP_V : 0) |
+ (m_FlipH ? BS_BitmapResource::FLIP_H : 0),
+ 0, m_ModulationColor, m_Width, m_Height);
+ }
+
+ // Resource freigeben
+ BitmapResourcePtr->Release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_StaticBitmap::GetPixel(int X, int Y) const
+{
+ BS_ASSERT(X >= 0 && X < m_Width);
+ BS_ASSERT(Y >= 0 && Y < m_Height);
+
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(m_ResourceFilename);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ BS_BitmapResource* pBitmapResource = static_cast<BS_BitmapResource*>(pResource);
+ unsigned int Result = pBitmapResource->GetPixel(X, Y);
+ pResource->Release();
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride)
+{
+ BS_LOG_ERRORLN("SetContent() ist not supported with this object.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+// Auskunftsmethoden
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::IsAlphaAllowed() const
+{
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(m_ResourceFilename);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ bool Result = static_cast<BS_BitmapResource*>(pResource)->IsAlphaAllowed();
+ pResource->Release();
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::IsColorModulationAllowed() const
+{
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(m_ResourceFilename);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ bool Result = static_cast<BS_BitmapResource*>(pResource)->IsColorModulationAllowed();
+ pResource->Release();
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::IsScalingAllowed() const
+{
+ BS_Resource* pResource = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(m_ResourceFilename);
+ BS_ASSERT(pResource->GetType() == BS_Resource::TYPE_BITMAP);
+ bool Result = static_cast<BS_BitmapResource*>(pResource)->IsScalingAllowed();
+ pResource->Release();
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_StaticBitmap::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_Bitmap::Persist(Writer);
+ Writer.Write(m_ResourceFilename);
+
+ Result &= BS_RenderObject::PersistChildren(Writer);
+
+ return Result;
+}
+
+bool BS_StaticBitmap::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_Bitmap::Unpersist(Reader);
+ std::string ResourceFilename;
+ Reader.Read(ResourceFilename);
+ Result &= InitBitmapResource(ResourceFilename);
+
+ Result &= BS_RenderObject::UnpersistChildren(Reader);
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/staticbitmap.h b/engines/sword25/gfx/staticbitmap.h
new file mode 100755
index 0000000000..bdf99e8ce0
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.h
@@ -0,0 +1,69 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_STATIC_BITMAP_H
+#define BS_STATIC_BITMAP_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "bitmap.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_StaticBitmap : public BS_Bitmap
+{
+friend BS_RenderObject;
+
+private:
+ /**
+ @remark Filename muss absoluter Pfad sein
+ */
+ BS_StaticBitmap(BS_RenderObjectPtr<BS_RenderObject> ParentPtr, const std::string& Filename);
+ BS_StaticBitmap(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle);
+
+public:
+ virtual ~BS_StaticBitmap();
+
+ virtual unsigned int GetPixel(int X, int Y) const;
+
+ virtual bool SetContent(const std::vector<unsigned char> & Pixeldata, unsigned int Offset, unsigned int Stride);
+
+ virtual bool IsScalingAllowed() const;
+ virtual bool IsAlphaAllowed() const;
+ virtual bool IsColorModulationAllowed() const;
+ virtual bool IsSetContentAllowed() const { return false; }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ virtual bool DoRender();
+
+private:
+ std::string m_ResourceFilename;
+
+ bool InitBitmapResource(const std::string & Filename);
+};
+
+#endif
diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp
new file mode 100755
index 0000000000..c51d728cc4
--- /dev/null
+++ b/engines/sword25/gfx/text.cpp
@@ -0,0 +1,404 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// TODO:
+// Entweder Fontfile absolut abspeichern, oder Verzeichniswechseln verbieten
+// Eine relative Fontfile-Angabe könnte verwandt werden nachdem das Verzeichnis bereits gewechselt wurde und die Datei würde nicht mehr gefunden
+
+#define BS_LOG_PREFIX "TEXT"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+#include "fontresource.h"
+#include "bitmapresource.h"
+
+#include "text.h"
+
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const unsigned int AUTO_WRAP_THRESHOLD_DEFAULT = 300;
+}
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_Text::BS_Text(BS_RenderObjectPtr<BS_RenderObject> ParentPtr) :
+ BS_RenderObject(ParentPtr, BS_RenderObject::TYPE_TEXT),
+ m_ModulationColor(0xffffffff),
+ m_AutoWrap(false),
+ m_AutoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT)
+{
+
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Text::BS_Text(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle) :
+ BS_RenderObject(ParentPtr, TYPE_TEXT, Handle)
+{
+ m_InitSuccess = Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Text::SetFont(const std::string & Font)
+{
+ // Font precachen.
+ if (GetResourceManager()->PrecacheResource(Font))
+ {
+ m_Font = Font;
+ UpdateFormat();
+ ForceRefresh();
+ return true;
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Could not precache font \"%s\". Font probably does not exist.", Font.c_str());
+ return false;
+ }
+
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::SetText(const std::string & Text)
+{
+ m_Text = Text;
+ UpdateFormat();
+ ForceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::SetColor(unsigned int ModulationColor)
+{
+ unsigned int NewModulationColor = (ModulationColor & 0x00ffffff) | (m_ModulationColor & 0xff000000);
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::SetAlpha(int Alpha)
+{
+ BS_ASSERT(Alpha >= 0 && Alpha < 256);
+ unsigned int NewModulationColor = (m_ModulationColor & 0x00ffffff) | Alpha << 24;
+ if (NewModulationColor != m_ModulationColor)
+ {
+ m_ModulationColor = NewModulationColor;
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::SetAutoWrap(bool AutoWrap)
+{
+ if (AutoWrap != m_AutoWrap)
+ {
+ m_AutoWrap = AutoWrap;
+ UpdateFormat();
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::SetAutoWrapThreshold(unsigned int AutoWrapThreshold)
+{
+ if (AutoWrapThreshold != m_AutoWrapThreshold)
+ {
+ m_AutoWrapThreshold = AutoWrapThreshold;
+ UpdateFormat();
+ ForceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Text::DoRender()
+{
+ // Font-Resource locken.
+ BS_FontResource * FontPtr = LockFontResource();
+ if (!FontPtr) return false;
+
+ // Charactermap-Resource locken.
+ BS_ResourceManager * RMPtr = GetResourceManager();
+ BS_BitmapResource * CharMapPtr;
+ {
+ BS_Resource * pResource = RMPtr->RequestResource(FontPtr->GetCharactermapFileName());
+ if (!pResource)
+ {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", FontPtr->GetCharactermapFileName().c_str());
+ return false;
+ }
+ if (pResource->GetType() != BS_Resource::TYPE_BITMAP)
+ {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", FontPtr->GetCharactermapFileName().c_str());
+ return false;
+ }
+
+ CharMapPtr = static_cast<BS_BitmapResource*>(pResource);
+ }
+
+ // Framebufferobjekt holen.
+ BS_GraphicEngine * GfxPtr = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(GfxPtr);
+
+ bool Result = true;
+ std::vector<LINE>::iterator Iter = m_Lines.begin();
+ for (; Iter != m_Lines.end(); ++Iter)
+ {
+ // Feststellen, ob überhaupt Buchstaben der aktuellen Zeile vom Update betroffen sind.
+ BS_Rect CheckRect = (*Iter).BBox;
+ CheckRect.Move(m_AbsoluteX, m_AbsoluteY);
+
+ // Jeden Buchstaben einzeln Rendern.
+ int CurX = m_AbsoluteX + (*Iter).BBox.left;
+ int CurY = m_AbsoluteY + (*Iter).BBox.top;
+ for (unsigned int i = 0; i < (*Iter).Text.size(); ++i)
+ {
+ BS_Rect CurRect = FontPtr->GetCharacterRect((unsigned char)(*Iter).Text.at(i));
+
+ BS_Rect RenderRect(CurX, CurY, CurX + CurRect.GetWidth(), CurY + CurRect.GetHeight());
+ int RenderX = CurX + (RenderRect.left - RenderRect.left);
+ int RenderY = CurY + (RenderRect.top - RenderRect.top);
+ RenderRect.Move(CurRect.left - CurX, CurRect.top - CurY);
+ Result = CharMapPtr->Blit(RenderX, RenderY, BS_Image::FLIP_NONE, &RenderRect, m_ModulationColor);
+ if (!Result) break;
+
+ CurX += CurRect.GetWidth() + FontPtr->GetGapWidth();
+ }
+ }
+
+ // Charactermap-Resource freigeben.
+ CharMapPtr->Release();
+
+ // Font-Resource freigeben.
+ FontPtr->Release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_ResourceManager * BS_Text::GetResourceManager()
+{
+ // Pointer auf den Resource-Manager holen.
+ return BS_Kernel::GetInstance()->GetResourceManager();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_FontResource * BS_Text::LockFontResource()
+{
+ BS_ResourceManager * RMPtr = GetResourceManager();
+
+ // Font-Resource locken.
+ BS_FontResource * FontPtr;
+ {
+ BS_Resource * ResourcePtr = RMPtr->RequestResource(m_Font);
+ if (!ResourcePtr)
+ {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", m_Font.c_str());
+ return NULL;
+ }
+ if (ResourcePtr->GetType() != BS_Resource::TYPE_FONT)
+ {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", m_Font.c_str());
+ return NULL;
+ }
+
+ FontPtr = static_cast<BS_FontResource*>(ResourcePtr);
+ }
+
+ return FontPtr;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::UpdateFormat()
+{
+ BS_FontResource * FontPtr = LockFontResource();
+ BS_ASSERT(FontPtr);
+
+ UpdateMetrics(*FontPtr);
+
+ m_Lines.resize(1);
+ if (m_AutoWrap && (unsigned int) m_Width >= m_AutoWrapThreshold && m_Text.size() >= 2)
+ {
+ m_Width = 0;
+ unsigned int CurLineWidth = 0;
+ unsigned int CurLineHeight = 0;
+ unsigned int CurLine = 0;
+ unsigned int TempLineWidth = 0;
+ unsigned int LastSpace = 0; // we need at least 1 space character to start a new line...
+ m_Lines[0].Text = "";
+ for (unsigned int i = 0; i < m_Text.size(); ++i)
+ {
+ unsigned int j;
+ TempLineWidth = 0;
+ LastSpace = 0;
+ for (j = i; j < m_Text.size(); ++j)
+ {
+ if ((unsigned char)m_Text[j] == ' ') LastSpace = j;
+
+ const BS_Rect & CurCharRect = FontPtr->GetCharacterRect((unsigned char)m_Text[j]);
+ TempLineWidth += CurCharRect.GetWidth();
+ TempLineWidth += FontPtr->GetGapWidth();
+
+ if ((TempLineWidth >= m_AutoWrapThreshold) && (LastSpace > 0))
+ break;
+ }
+
+ if (j == m_Text.size()) LastSpace = m_Text.size(); // everything in 1 line.
+
+ CurLineWidth = 0;
+ CurLineHeight = 0;
+ for (j = i; j < LastSpace; ++j)
+ {
+ m_Lines[CurLine].Text += m_Text[j];
+
+ const BS_Rect & CurCharRect = FontPtr->GetCharacterRect((unsigned char)m_Text[j]);
+ CurLineWidth += CurCharRect.GetWidth();
+ CurLineWidth += FontPtr->GetGapWidth();
+ if ((unsigned int) CurCharRect.GetHeight() > CurLineHeight) CurLineHeight = CurCharRect.GetHeight();
+ }
+
+ m_Lines[CurLine].BBox.right = CurLineWidth;
+ m_Lines[CurLine].BBox.bottom = CurLineHeight;
+ if ((unsigned int) m_Width < CurLineWidth) m_Width = CurLineWidth;
+
+ if(LastSpace < m_Text.size())
+ {
+ ++CurLine;
+ BS_ASSERT(CurLine == m_Lines.size());
+ m_Lines.resize(CurLine + 1);
+ m_Lines[CurLine].Text = "";
+ }
+
+ i = LastSpace;
+ }
+
+ // Bounding-Box der einzelnen Zeilen relativ zur ersten festlegen (vor allem zentrieren).
+ m_Height = 0;
+ std::vector<LINE>::iterator Iter = m_Lines.begin();
+ for (; Iter != m_Lines.end(); ++Iter)
+ {
+ BS_Rect & BBox = (*Iter).BBox;
+ BBox.left = (m_Width - BBox.right) / 2;
+ BBox.right = BBox.left + BBox.right;
+ BBox.top = (Iter - m_Lines.begin()) * FontPtr->GetLineHeight();
+ BBox.bottom = BBox.top + BBox.bottom;
+ m_Height += BBox.GetHeight();
+ }
+ }
+ else
+ {
+ // Keine automatische Formatierung, also wird der gesamte Text in nur eine Zeile kopiert.
+ m_Lines[0].Text = m_Text;
+ m_Lines[0].BBox = BS_Rect(0, 0, m_Width, m_Height);
+ }
+
+ FontPtr->Release();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Text::UpdateMetrics(BS_FontResource & FontResource)
+{
+ m_Width = 0;
+ m_Height = 0;
+
+ for (unsigned int i = 0; i < m_Text.size(); ++i)
+ {
+ const BS_Rect & CurRect = FontResource.GetCharacterRect((unsigned char)m_Text.at(i));
+ m_Width += CurRect.GetWidth();
+ if (i != m_Text.size() - 1) m_Width += FontResource.GetGapWidth();
+ if (m_Height < CurRect.GetHeight()) m_Height = CurRect.GetHeight();
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_Text::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Persist(Writer);
+
+ Writer.Write(m_ModulationColor);
+ Writer.Write(m_Font);
+ Writer.Write(m_Text);
+ Writer.Write(m_AutoWrap);
+ Writer.Write(m_AutoWrapThreshold);
+
+ Result &= BS_RenderObject::PersistChildren(Writer);
+
+ return Result;
+}
+
+bool BS_Text::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ Result &= BS_RenderObject::Unpersist(Reader);
+
+ // Farbe und Alpha einlesen.
+ Reader.Read(m_ModulationColor);
+
+ // Beim Laden der anderen Member werden die Set-Methoden benutzt statt der tatsächlichen Member.
+ // So wird das Layout automatisch aktualisiert und auch alle anderen notwendigen Methoden ausgeführt.
+
+ std::string Font;
+ Reader.Read(Font);
+ SetFont(Font);
+
+ std::string Text;
+ Reader.Read(Text);
+ SetText(Text);
+
+ bool AutoWrap;
+ Reader.Read(AutoWrap);
+ SetAutoWrap(AutoWrap);
+
+ unsigned int AutoWrapThreshold;
+ Reader.Read(AutoWrapThreshold);
+ SetAutoWrapThreshold(AutoWrapThreshold);
+
+ Result &= BS_RenderObject::UnpersistChildren(Reader);
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/gfx/text.h b/engines/sword25/gfx/text.h
new file mode 100755
index 0000000000..5b0fefe7d7
--- /dev/null
+++ b/engines/sword25/gfx/text.h
@@ -0,0 +1,156 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_TEXT_H
+#define BS_TEXT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "math/rect.h"
+#include "renderobject.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_FontResource;
+class BS_ResourceManager;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_Text : public BS_RenderObject
+{
+friend BS_RenderObject;
+
+public:
+ /**
+ @brief Setzt den Font mit dem der Text dargestellt werden soll.
+ @param Font der Dateiname der Fontdatei.
+ @return Gibt false zurück, wenn der Font nicht gefunden wurde.
+ */
+ bool SetFont(const std::string & Font);
+
+ /**
+ @brief Setzt den darzustellenden Text.
+ @param Text der darzustellende Text
+ */
+ void SetText(const std::string & Text);
+
+ /**
+ @brief Setzt den Alphawert des Textes.
+ @param Alpha der neue Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ void SetAlpha(int Alpha);
+
+ /**
+ @brief Legt fest, ob der Text automatisch umgebrochen werden soll.
+
+ Wenn dieses Attribut auf true gesetzt ist, wird der Text umgebrochen, sofern er länger als GetAutoWrapThreshold() ist.
+
+ @param AutoWrap gibt an, ob der automatische Umbruch aktiviert oder deaktiviert werden soll.
+ @remark Dieses Attribut wird mit dem Wert false initialisiert.
+ */
+ void SetAutoWrap(bool AutoWrap);
+
+ /**
+ @brief Legt die Längengrenze des Textes in Pixeln fest, ab der ein automatischer Zeilenumbruch vorgenommen wird.
+ @remark Dieses Attribut wird mit dem Wert 300 initialisiert.
+ @remark Eine automatische Formatierung wird nur vorgenommen, wenn diese durch einen Aufruf von SetAutoWrap() aktiviert wurde.
+ */
+ void SetAutoWrapThreshold(unsigned int AutoWrapThreshold);
+
+ /**
+ @brief Gibt den dargestellten Text zurück.
+ */
+ const std::string & GetText() { return m_Text; }
+
+ /**
+ @brief Gibt den Namen das momentan benutzten Fonts zurück.
+ */
+ const std::string & GetFont() { return m_Font; }
+
+ /**
+ @brief Setzt die Farbe des Textes.
+ @param Color eine 24-Bit RGB Farbe, die die Farbe des Textes festlegt.
+ */
+ void SetColor(unsigned int ModulationColor);
+
+ /**
+ @brief Gibt den Alphawert des Textes zurück.
+ @return Der Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ int GetAlpha() const { return m_ModulationColor >> 24; }
+
+ /**
+ @brief Gibt die Farbe des Textes zurück.
+ @return Eine 24-Bit RGB Farbe, die die Farbe des Textes angibt.
+ */
+ int GetColor() const { return m_ModulationColor & 0x00ffffff; }
+
+ /**
+ @brief Gibt zurück, ob die automatische Formatierung aktiviert ist.
+ */
+ bool IsAutoWrapActive() const { return m_AutoWrap; }
+
+ /**
+ @brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
+ */
+ unsigned int GetAutoWrapThreshold() const { return m_AutoWrapThreshold; }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ virtual bool DoRender();
+
+private:
+ BS_Text(BS_RenderObjectPtr<BS_RenderObject> ParentPtr);
+ BS_Text(BS_InputPersistenceBlock & Reader, BS_RenderObjectPtr<BS_RenderObject> ParentPtr, unsigned int Handle);
+
+ unsigned int m_ModulationColor;
+ std::string m_Font;
+ std::string m_Text;
+ bool m_AutoWrap;
+ unsigned int m_AutoWrapThreshold;
+
+ struct LINE
+ {
+ BS_Rect BBox;
+ std::string Text;
+ };
+
+ std::vector<LINE> m_Lines;
+
+ void UpdateFormat();
+ void UpdateMetrics(BS_FontResource & FontResource);
+ BS_ResourceManager * GetResourceManager();
+ BS_FontResource * LockFontResource();
+};
+
+#endif
diff --git a/engines/sword25/gfx/timedrenderobject.cpp b/engines/sword25/gfx/timedrenderobject.cpp
new file mode 100755
index 0000000000..aecf1038e6
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.cpp
@@ -0,0 +1,39 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "timedrenderobject.h"
+
+#include "renderobjectmanager.h"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_TimedRenderObject::BS_TimedRenderObject(BS_RenderObjectPtr<BS_RenderObject> pParent, TYPES Type, unsigned int Handle) :
+ BS_RenderObject(pParent, Type, Handle)
+{
+ BS_ASSERT(GetManager());
+ GetManager()->AttatchTimedRenderObject(this);
+}
+
+BS_TimedRenderObject::~BS_TimedRenderObject()
+{
+ BS_ASSERT(GetManager());
+ GetManager()->DetatchTimedRenderObject(this);
+} \ No newline at end of file
diff --git a/engines/sword25/gfx/timedrenderobject.h b/engines/sword25/gfx/timedrenderobject.h
new file mode 100755
index 0000000000..61a9709a94
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.h
@@ -0,0 +1,57 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// System Includes
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Engine Includes
+// -----------------------------------------------------------------------------
+
+#include "../kernel/common.h"
+#include "renderobject.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Class Definition
+// -----------------------------------------------------------------------------
+
+/**
+ @brief
+*/
+
+class BS_TimedRenderObject : public BS_RenderObject
+{
+public:
+ BS_TimedRenderObject(BS_RenderObjectPtr<BS_RenderObject> pParent, TYPES Type, unsigned int Handle = 0);
+ BS_TimedRenderObject::~BS_TimedRenderObject();
+
+ /**
+ @brief Teilt dem Objekt mit, dass ein neuer Frame begonnen wird.
+
+ Diese Methode wird jeden Frame an jedem BS_TimedRenderObject aufgerufen um diesen zu ermöglichen
+ ihren Zustand Zeitabhängig zu verändern (z.B. Animationen).<br>
+ @param int TimeElapsed gibt an wie viel Zeit (in Microsekunden) seit dem letzten Frame vergangen ist.
+ */
+ virtual void FrameNotification(int TimeElapsed) = 0;
+}; \ No newline at end of file
diff --git a/engines/sword25/input/inputengine.cpp b/engines/sword25/input/inputengine.cpp
new file mode 100755
index 0000000000..85164447ce
--- /dev/null
+++ b/engines/sword25/input/inputengine.cpp
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "INPUTENGINE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "inputengine.h"
+
+// -----------------------------------------------------------------------------
+
+BS_InputEngine::BS_InputEngine(BS_Kernel * pKernel) : BS_Service(pKernel)
+{
+ if (!_RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
diff --git a/engines/sword25/input/inputengine.h b/engines/sword25/input/inputengine.h
new file mode 100755
index 0000000000..d2c4c92c5d
--- /dev/null
+++ b/engines/sword25/input/inputengine.h
@@ -0,0 +1,291 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/**
+ BS_InputEngine
+ -------------
+ Dies ist das Inputengine Interface, dass alle Methoden enthält, die eine Inputengine implementieren muss.
+ Implementationen der Inputengine müssen von dieser Klasse abgeleitet werden.
+
+ Autor: Alex Arnst
+**/
+
+#ifndef BS_INPUTENGINE_H
+#define BS_INPUTENGINE_H
+
+/// Includes
+#include "kernel/common.h"
+#include "kernel/service.h"
+#include "kernel/persistable.h"
+
+/// Klassendefinition
+class BS_InputEngine : public BS_Service, public BS_Persistable
+{
+public:
+ BS_InputEngine(BS_Kernel* pKernel);
+ virtual ~BS_InputEngine(){};
+
+ // ACHTUNG: Diese Codes werden in inputengine_script.cpp beim Skript-Service registriert. Bei Änderungen an diesem Enum muss auch diese
+ // Datei angepasst werden.
+ enum KEY_CODES
+ {
+ KEY_BACKSPACE = 0x08,
+ KEY_TAB = 0x09,
+ KEY_CLEAR = 0x0C,
+ KEY_RETURN = 0x0D,
+ KEY_PAUSE = 0x13,
+ KEY_CAPSLOCK = 0x14,
+ KEY_ESCAPE = 0x1B,
+ KEY_SPACE = 0x20,
+ KEY_PAGEUP = 0x21,
+ KEY_PAGEDOWN = 0x22,
+ KEY_END = 0x23,
+ KEY_HOME = 0x24,
+ KEY_LEFT = 0x25,
+ KEY_UP = 0x26,
+ KEY_RIGHT = 0x27,
+ KEY_DOWN = 0x28,
+ KEY_PRINTSCREEN = 0x2C,
+ KEY_INSERT = 0x2D,
+ KEY_DELETE = 0x2E,
+ KEY_0 = 0x30,
+ KEY_1 = 0x31,
+ KEY_2 = 0x32,
+ KEY_3 = 0x33,
+ KEY_4 = 0x34,
+ KEY_5 = 0x35,
+ KEY_6 = 0x36,
+ KEY_7 = 0x37,
+ KEY_8 = 0x38,
+ KEY_9 = 0x39,
+ KEY_A = 0x41,
+ KEY_B = 0x42,
+ KEY_C = 0x43,
+ KEY_D = 0x44,
+ KEY_E = 0x45,
+ KEY_F = 0x46,
+ KEY_G = 0x47,
+ KEY_H = 0x48,
+ KEY_I = 0x49,
+ KEY_J = 0x4A,
+ KEY_K = 0x4B,
+ KEY_L = 0x4C,
+ KEY_M = 0x4D,
+ KEY_N = 0x4E,
+ KEY_O = 0x4F,
+ KEY_P = 0x50,
+ KEY_Q = 0x51,
+ KEY_R = 0x52,
+ KEY_S = 0x53,
+ KEY_T = 0x54,
+ KEY_U = 0x55,
+ KEY_V = 0x56,
+ KEY_W = 0x57,
+ KEY_X = 0x58,
+ KEY_Y = 0x59,
+ KEY_Z = 0x5A,
+ KEY_NUMPAD0 = 0x60,
+ KEY_NUMPAD1 = 0x61,
+ KEY_NUMPAD2 = 0x62,
+ KEY_NUMPAD3 = 0x63,
+ KEY_NUMPAD4 = 0x64,
+ KEY_NUMPAD5 = 0x65,
+ KEY_NUMPAD6 = 0x66,
+ KEY_NUMPAD7 = 0x67,
+ KEY_NUMPAD8 = 0x68,
+ KEY_NUMPAD9 = 0x69,
+ KEY_MULTIPLY = 0x6A,
+ KEY_ADD = 0x6B,
+ KEY_SEPARATOR = 0x6C,
+ KEY_SUBTRACT = 0x6D,
+ KEY_DECIMAL = 0x6E,
+ KEY_DIVIDE = 0x6F,
+ KEY_F1 = 0x70,
+ KEY_F2 = 0x71,
+ KEY_F3 = 0x72,
+ KEY_F4 = 0x73,
+ KEY_F5 = 0x74,
+ KEY_F6 = 0x75,
+ KEY_F7 = 0x76,
+ KEY_F8 = 0x77,
+ KEY_F9 = 0x78,
+ KEY_F10 = 0x79,
+ KEY_F11 = 0x7A,
+ KEY_F12 = 0x7B,
+ KEY_NUMLOCK = 0x90,
+ KEY_SCROLL = 0x91,
+ KEY_LSHIFT = 0xA0,
+ KEY_RSHIFT = 0xA1,
+ KEY_LCONTROL = 0xA2,
+ KEY_RCONTROL = 0xA3,
+ };
+
+ // ACHTUNG: Diese Codes werden in inputengine_script.cpp beim Skript-Service registriert. Bei Änderungen an diesem Enum muss auch diese
+ // Datei angepasst werden.
+ enum KEY_COMMANDS
+ {
+ KEY_COMMAND_ENTER = 1,
+ KEY_COMMAND_LEFT = 2,
+ KEY_COMMAND_RIGHT = 3,
+ KEY_COMMAND_HOME = 4,
+ KEY_COMMAND_END = 5,
+ KEY_COMMAND_BACKSPACE = 6,
+ KEY_COMMAND_TAB = 7,
+ KEY_COMMAND_INSERT = 8,
+ KEY_COMMAND_DELETE = 9,
+ };
+
+ /// --------------------------------------------------------------
+ /// DIESE METHODEN MÜSSEN VON DER INPUTENGINE IMPLEMENTIERT WERDEN
+ /// --------------------------------------------------------------
+
+ /**
+ @brief Initialisiert die Inputengine
+ @return Gibt bei Erfolg true zurück, ansonsten false.
+ */
+ virtual bool Init() = 0;
+
+ /**
+ @brief Führt einen "Tick" der Input-Engine aus
+
+ Diese Methode sollte mindestens ein mal pro Frame aufgerufen werden. Sie dient dazu Implementationen der
+ Input-Engine zu ermöglichen, die nicht in einem eigenen Thread laufen oder zusätzliche Verwaltungsaufgaben
+ durchführen müssen.
+ */
+ virtual void Update() = 0;
+
+ /**
+ @brief Gibt true zurück, wenn die linke Maustaste gedrückt ist.
+ */
+ virtual bool IsLeftMouseDown() = 0;
+
+ /**
+ @brief Gibt true zurück, wenn die rechte Maustaste gedrückt ist.
+ */
+ virtual bool IsRightMouseDown() = 0;
+
+ /**
+ @brief Gibt true zurück, wenn die linke Maustaste gedrückt und losgelassen wurde.
+
+ Der Unterschied zu IsLeftMouseDown() besteht darin, dass erst true zurückgegegen wird, wenn der Tastendruck beendet ist, die Taste also
+ wieder losgelassen wurde.
+ */
+ virtual bool WasLeftMouseDown() = 0;
+
+ /**
+ @brief Gibt true zurück, wenn die linke Maustaste gedrückt und losgelassen wurde.
+
+ Der Unterschied zu IsRightMouseDown() besteht darin, dass erst true zurückgegegen wird, wenn der Tastendruck beendet ist, die Taste also
+ wieder losgelassen wurde.
+ */
+ virtual bool WasRightMouseDown() = 0;
+
+ /**
+ @brief Gibt true zurück wenn mit der linken Maustaste ein Doppelklick ausgelöst wurde.
+ */
+ virtual bool IsLeftDoubleClick() = 0;
+
+ /**
+ @brief Gibt die Position des Mauszeigers auf der X-Achse in Pixeln zurück.
+ */
+ virtual int GetMouseX() = 0;
+
+ /**
+ @brief Gibt die Position des Mauszeigers auf der Y-Achse in Pixeln zurück.
+ */
+ virtual int GetMouseY() = 0;
+
+ /**
+ @brief Setzt die Position des Mauszeigers auf der X-Achse in Pixeln.
+ */
+ virtual void SetMouseX(int PosX) = 0;
+
+ /**
+ @brief Setzt die Position des Mauszeigers auf der Y-Achse in Pixeln.
+ */
+ virtual void SetMouseY(int PosY) = 0;
+
+ /**
+ @brief Gibt true zurück wenn eine bestimmte Taste gedrückt ist.
+ @param KeyCode der Key-Code der zu testenden Taste
+ @return Gibt true zurück, wenn die Taste mit dem übergebenen Key-Code gedrückt ist, ansonsten false.
+ */
+ virtual bool IsKeyDown(unsigned int KeyCode) = 0;
+
+ /**
+ @brief Gibt true zurück wenn eine bestimmte Taste gerückt und losgelassen wurde.
+
+ Der Unterschied zu IsKeyDown() besteht darin, dass erst true zurückgegegen wird, wenn der Tastendruck beendet ist, die Taste also
+ wieder losgelassen wurde. Diese Methode erleichtert das Abfragen von Funktionstasten und das Einlesen von Zeichenketten, die vom
+ Benutzer getippt werden.
+
+ @param KeyCode der Key-Code der zu testenden Taste
+ */
+ virtual bool WasKeyDown(unsigned int KeyCode) = 0;
+
+ typedef void (*CharacterCallback)(unsigned char Character);
+
+ /**
+ @brief Registriert eine Callbackfunktion für die Eingabe von Buchstaben.
+
+ Die Callbacks, die mit dieser Funktion registriert werden, werden immer dann aufgerufen, wenn der Input-Service eine Buchstabeneingabe
+ feststellt. Eine Buchstabeneingabe unterscheidet sich von der Abfrage mittels der Methoden IsKeyDown() und WasKeyDown() in der Hinsicht,
+ dass statt Scan-Coded tatsächliche Buchstaben behandelt werden. Dabei wurden unter anderem Berücksichtigt:des Tastaturlayout, der Zustand
+ der Shift und Caps Lock Tasten und die Wiederholung durch längeres Halten der Taste.<br>
+ Die Eingabe von Zeichenketten durch den Benutzer sollte durch Benutzung dieses Callbacks realisiert werden.
+
+ @return Gibt true zurück, wenn die Funktion registriert werden konnte, ansonsten false.
+ */
+ virtual bool RegisterCharacterCallback(CharacterCallback Callback) = 0;
+
+ /**
+ @brief Deregistriert eine Callbackfunktion für die Eingabe von Buchstaben.
+
+ @return Gibt true zurück, wenn die Funktion deregistriert werden konnte, ansonsten false.
+ */
+ virtual bool UnregisterCharacterCallback(CharacterCallback Callback) = 0;
+
+ typedef void (*CommandCallback)(KEY_COMMANDS Command);
+
+ /**
+ @brief Registriert eine Callbackfunktion für die Eingabe von Kommandos, die auf die Zeichenketteneingabe Einfluss haben können.
+
+ Die Callbacks, die mit dieser Funktion registriert werden , werden immer dann aufgerufen, wenn der Input-Service einen Tastendruck
+ feststellt, der die Zeichenketteneingabe beeinflussen kann. Dies könnten folgende Tasten sein: Enter, Pos 1, Ende, Links, Rechts, ...<br>
+ Die Eingabe von Zeichenketten durch den Benutzer sollte durch Benutzung dieses Callbacks realisiert werden.
+
+ @return Gibt true zurück, wenn die Funktion registriert werden konnte, ansonsten false.
+ */
+ virtual bool RegisterCommandCallback(CommandCallback Callback) = 0;
+
+ /**
+ @brief Deregistriert eine Callbackfunktion für die Eingabe von Kommandos, die auf die Zeichenketteneingabe Einfluss haben können.
+
+ @return Gibt true zurück, wenn die Funktion deregistriert werden konnte, ansonsten false.
+ */
+ virtual bool UnregisterCommandCallback(CommandCallback Callback) = 0;
+
+ virtual void ReportCharacter(unsigned char Character) = 0;
+ virtual void ReportCommand(KEY_COMMANDS Command) = 0;
+
+private:
+ bool _RegisterScriptBindings();
+};
+
+#endif
diff --git a/engines/sword25/input/inputengine_script.cpp b/engines/sword25/input/inputengine_script.cpp
new file mode 100755
index 0000000000..7c2868186b
--- /dev/null
+++ b/engines/sword25/input/inputengine_script.cpp
@@ -0,0 +1,364 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <algorithm>
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "kernel/callbackregistry.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+#include "script/luacallback.h"
+
+#include "inputengine.h"
+
+#define BS_LOG_PREFIX "INPUTENGINE"
+
+// -----------------------------------------------------------------------------
+// Callback-Objekte
+// -----------------------------------------------------------------------------
+
+static void TheCharacterCallback(unsigned char Character);
+static void TheCommandCallback(BS_InputEngine::KEY_COMMANDS Command);
+
+namespace
+{
+ class CharacterCallbackClass : public BS_LuaCallback
+ {
+ public:
+ CharacterCallbackClass(lua_State * L) : BS_LuaCallback(L) {};
+
+ std::string Character;
+
+ protected:
+ int PreFunctionInvokation(lua_State * L)
+ {
+ lua_pushstring(L, Character.c_str());
+ return 1;
+ }
+ };
+ std::auto_ptr<CharacterCallbackClass> CharacterCallbackPtr;
+
+ // -----------------------------------------------------------------------------
+
+ class CommandCallbackClass : public BS_LuaCallback
+ {
+ public:
+ CommandCallbackClass(lua_State * L) : BS_LuaCallback(L) { Command = BS_InputEngine::KEY_COMMAND_BACKSPACE; }
+
+ BS_InputEngine::KEY_COMMANDS Command;
+
+ protected:
+ int PreFunctionInvokation(lua_State * L)
+ {
+ lua_pushnumber(L, Command);
+ return 1;
+ }
+ };
+ std::auto_ptr<CommandCallbackClass> CommandCallbackPtr;
+
+ // -------------------------------------------------------------------------
+
+ struct CallbackfunctionRegisterer
+ {
+ CallbackfunctionRegisterer()
+ {
+ BS_CallbackRegistry::GetInstance().RegisterCallbackFunction("LuaCommandCB", TheCommandCallback);
+ BS_CallbackRegistry::GetInstance().RegisterCallbackFunction("LuaCharacterCB", TheCharacterCallback);
+ }
+ };
+ static CallbackfunctionRegisterer Instance;
+}
+
+// -----------------------------------------------------------------------------
+
+static BS_InputEngine * GetIE()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_InputEngine * pIE = static_cast<BS_InputEngine *>(pKernel->GetService("input"));
+ BS_ASSERT(pIE);
+ return pIE;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Init(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->Init());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Update(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ // Beim ersten Aufruf der Update()-Methode werden die beiden Callbacks am Input-Objekt registriert.
+ // Dieses kann nicht in _RegisterScriptBindings() passieren, da diese Funktion vom Konstruktor der abstrakten Basisklasse aufgerufen wird und die
+ // Register...()-Methoden abstrakt sind, im Konstruktor der Basisklasse also nicht aufgerufen werden können.
+ static bool FirstCall = true;
+ if (FirstCall)
+ {
+ FirstCall = false;
+ pIE->RegisterCharacterCallback(TheCharacterCallback);
+ pIE->RegisterCommandCallback(TheCommandCallback);
+ }
+
+ pIE->Update();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLeftMouseDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsLeftMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsRightMouseDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsRightMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasLeftMouseDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasLeftMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasRightMouseDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasRightMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLeftDoubleClick(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsLeftDoubleClick());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMouseX(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushnumber(L, pIE->GetMouseX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMouseY(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushnumber(L, pIE->GetMouseY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsKeyDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsKeyDown((unsigned int) luaL_checknumber(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasKeyDown(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasKeyDown((unsigned int) luaL_checknumber(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMouseX(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ pIE->SetMouseX((int) luaL_checknumber(L, 1));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMouseY(lua_State * L)
+{
+ BS_InputEngine * pIE = GetIE();
+
+ pIE->SetMouseY((int) luaL_checknumber(L, 1));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void TheCharacterCallback(unsigned char Character)
+{
+ CharacterCallbackPtr->Character = Character;
+ lua_State * L = static_cast<lua_State *>(BS_Kernel::GetInstance()->GetScript()->GetScriptObject());
+ CharacterCallbackPtr->InvokeCallbackFunctions(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+static int RegisterCharacterCallback(lua_State * L)
+{
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CharacterCallbackPtr->RegisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int UnregisterCharacterCallback(lua_State * L)
+{
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CharacterCallbackPtr->UnregisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void TheCommandCallback(BS_InputEngine::KEY_COMMANDS Command)
+{
+ CommandCallbackPtr->Command = Command;
+ lua_State * L = static_cast<lua_State *>(BS_Kernel::GetInstance()->GetScript()->GetScriptObject());
+ CommandCallbackPtr->InvokeCallbackFunctions(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+static int RegisterCommandCallback(lua_State * L)
+{
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CommandCallbackPtr->RegisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int UnregisterCommandCallback(lua_State * L)
+{
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CommandCallbackPtr->UnregisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * PACKAGE_LIBRARY_NAME = "Input";
+
+static const luaL_reg PACKAGE_FUNCTIONS[] =
+{
+ "Init", Init,
+ "Update", Update,
+ "IsLeftMouseDown", IsLeftMouseDown,
+ "IsRightMouseDown", IsRightMouseDown,
+ "WasLeftMouseDown", WasLeftMouseDown,
+ "WasRightMouseDown", WasRightMouseDown,
+ "IsLeftDoubleClick", IsLeftDoubleClick,
+ "GetMouseX", GetMouseX,
+ "GetMouseY", GetMouseY,
+ "SetMouseX", SetMouseX,
+ "SetMouseY", SetMouseY,
+ "IsKeyDown", IsKeyDown,
+ "WasKeyDown", WasKeyDown,
+ "RegisterCharacterCallback", RegisterCharacterCallback,
+ "UnregisterCharacterCallback", UnregisterCharacterCallback,
+ "RegisterCommandCallback", RegisterCommandCallback,
+ "UnregisterCommandCallback", UnregisterCommandCallback,
+ 0, 0,
+};
+
+#define X(k) "KEY_" #k, BS_InputEngine::KEY_##k
+#define Y(k) "KEY_COMMAND_" #k, BS_InputEngine::KEY_COMMAND_##k
+static const lua_constant_reg PACKAGE_CONSTANTS[] =
+{
+ X(BACKSPACE), X(TAB), X(CLEAR), X(RETURN), X(PAUSE), X(CAPSLOCK), X(ESCAPE), X(SPACE), X(PAGEUP), X(PAGEDOWN), X(END), X(HOME), X(LEFT),
+ X(UP), X(RIGHT), X(DOWN), X(PRINTSCREEN), X(INSERT), X(DELETE), X(0), X(1), X(2), X(3), X(4), X(5), X(6), X(7), X(8), X(9), X(A), X(B),
+ X(C), X(D), X(E), X(F), X(G), X(H), X(I), X(J), X(K), X(L), X(M), X(N), X(O), X(P), X(Q), X(R), X(S), X(T), X(U), X(V), X(W), X(X), X(Y),
+ X(Z), X(NUMPAD0), X(NUMPAD1), X(NUMPAD2), X(NUMPAD3), X(NUMPAD4), X(NUMPAD5), X(NUMPAD6), X(NUMPAD7), X(NUMPAD8), X(NUMPAD9), X(MULTIPLY),
+ X(ADD), X(SEPARATOR), X(SUBTRACT), X(DECIMAL), X(DIVIDE), X(F1), X(F2), X(F3), X(F4), X(F5), X(F6), X(F7), X(F8), X(F9), X(F10), X(F11),
+ X(F12), X(NUMLOCK), X(SCROLL), X(LSHIFT), X(RSHIFT), X(LCONTROL), X(RCONTROL),
+ Y(ENTER), Y(LEFT), Y(RIGHT), Y(HOME), Y(END), Y(BACKSPACE), Y(TAB), Y(INSERT), Y(DELETE),
+ 0, 0,
+};
+#undef X
+#undef Y
+
+// -----------------------------------------------------------------------------
+
+bool BS_InputEngine::_RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS)) return false;
+ if (!BS_LuaBindhelper::AddConstantsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_CONSTANTS)) return false;
+
+ CharacterCallbackPtr.reset(new CharacterCallbackClass(L));
+ CommandCallbackPtr.reset(new CommandCallbackClass(L));
+
+ return true;
+}
diff --git a/engines/sword25/input/stdwininput.cpp b/engines/sword25/input/stdwininput.cpp
new file mode 100755
index 0000000000..16e3832b67
--- /dev/null
+++ b/engines/sword25/input/stdwininput.cpp
@@ -0,0 +1,401 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "kernel/callbackregistry.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "stdwininput.h"
+
+#include <algorithm>
+using namespace std;
+
+#define BS_LOG_PREFIX "WININPUT"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_StdWinInput::BS_StdWinInput(BS_Kernel* pKernel) :
+ m_CurrentState(0),
+ m_LeftMouseDown(false),
+ m_RightMouseDown(false),
+ m_MouseX(0),
+ m_MouseY(0),
+ m_LeftDoubleClick(false),
+ m_DoubleClickTime(GetDoubleClickTime()),
+ m_DoubleClickRectWidth(GetSystemMetrics(SM_CXDOUBLECLK)),
+ m_DoubleClickRectHeight(GetSystemMetrics(SM_CYDOUBLECLK)),
+ m_LastLeftClickTime(0),
+ m_LastLeftClickMouseX(0),
+ m_LastLeftClickMouseY(0),
+ BS_InputEngine(pKernel)
+{
+ memset(m_KeyboardState[0], 0, sizeof(m_KeyboardState[0]));
+ memset(m_KeyboardState[1], 0, sizeof(m_KeyboardState[1]));
+ m_LeftMouseState[0] = false;
+ m_LeftMouseState[1] = false;
+ m_RightMouseState[0] = false;
+ m_RightMouseState[1] = false;
+}
+
+BS_StdWinInput::~BS_StdWinInput()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_StdWinInput_CreateObject(BS_Kernel* pKernel) { return new BS_StdWinInput(pKernel); }
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::Init()
+{
+ // Keine Inialisierung notwendig
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::Update()
+{
+ // Der Status wird nur aktualisiert, wenn das Applikationsfenster den Fokus hat, so wird verhindert, dass
+ // Eingaben verarbeitet werden, die eigentlich für eine andere Applikation gedacht waren.
+ if (BS_Kernel::GetInstance()->GetWindow()->HasFocus())
+ {
+ m_CurrentState ^= 1;
+
+ // Der Status der Eingabegeräte wird nur einmal pro Frame ausgelesen, damit für
+ // jeden Frame gleiche Anfragen die gleiche Antwort erhalten.
+
+ POINT MousePos;
+ if (GetCursorPos(&MousePos))
+ {
+ m_MouseX = MousePos.x - BS_Kernel::GetInstance()->GetWindow()->GetClientX();
+ m_MouseY = MousePos.y - BS_Kernel::GetInstance()->GetWindow()->GetClientY();
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Call to GetCursorPos() failed.");
+ m_MouseX = 0;
+ m_MouseY = 0;
+ }
+
+ GetKeyboardState(m_KeyboardState[m_CurrentState]);
+
+ m_LeftMouseDown = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
+ m_RightMouseDown = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
+ m_LeftMouseState[m_CurrentState] = m_LeftMouseDown;
+ m_RightMouseState[m_CurrentState] = m_RightMouseDown;
+
+ TestForLeftDoubleClick();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::IsLeftMouseDown()
+{
+ return m_LeftMouseDown;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::IsRightMouseDown()
+{
+ return m_RightMouseDown;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::TestForLeftDoubleClick()
+{
+ // Das Doppelklick-Flag wird gelöscht, für den Fall, dass im letzten Frame ein Doppelklick ausgetreten ist.
+ m_LeftDoubleClick = false;
+
+ // Die linke Maustaste wurde geklickt, also muss getestet werden, ob ein Doppelklick vorliegt.
+ if (WasLeftMouseDown())
+ {
+ // Die Zeit auslesen.
+ unsigned int Now = BS_Kernel::GetInstance()->GetMilliTicks();
+
+ // Ein Doppelklick wird erkannt, wenn:
+ // 1. Die zwei Klicks liegen nah genug zusammen.
+ // 2. Der Mauscursor wurde zwischen den Klicks nicht zu viel bewegt.
+ if (Now - m_LastLeftClickTime <= m_DoubleClickTime &&
+ abs(m_MouseX - m_LastLeftClickMouseX) <= m_DoubleClickRectWidth / 2 &&
+ abs(m_MouseY - m_LastLeftClickMouseY) <= m_DoubleClickRectHeight / 2)
+ {
+ m_LeftDoubleClick = true;
+
+ // Die Zeit und Position des letzten Linksklicks zurücksetzen, damit dieser Klick nicht als erster Klick eines weiteren Doppelklicks
+ // interpretiert wird.
+ m_LastLeftClickTime = 0;
+ m_LastLeftClickMouseX = 0;
+ m_LastLeftClickMouseY = 0;
+ }
+ else
+ {
+ // Es liegt kein Doppelklick vor, die Zeit und die Position dieses Klicks merken, für den Fall das dies der erste Klick eines
+ // zukünftigen Doppelklicks wird.
+ m_LastLeftClickTime = Now;
+ m_LastLeftClickMouseX = m_MouseX;
+ m_LastLeftClickMouseY = m_MouseY;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::IsLeftDoubleClick()
+{
+ return m_LeftDoubleClick;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::WasLeftMouseDown()
+{
+ return (m_LeftMouseState[m_CurrentState] == false) && (m_LeftMouseState[m_CurrentState ^ 1] == true);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::WasRightMouseDown()
+{
+ return (m_RightMouseState[m_CurrentState] == false) && (m_RightMouseState[m_CurrentState ^ 1] == true);
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_StdWinInput::GetMouseX()
+{
+ return m_MouseX;
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_StdWinInput::GetMouseY()
+{
+ return m_MouseY;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::IsKeyDown(unsigned int KeyCode)
+{
+ return (m_KeyboardState[m_CurrentState][KeyCode] & 0x80) != 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::WasKeyDown(unsigned int KeyCode)
+{
+ return ((m_KeyboardState[m_CurrentState][KeyCode] & 0x80) == 0) && ((m_KeyboardState[m_CurrentState ^ 1][KeyCode] & 0x80) != 0);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::SetMouseX(int PosX)
+{
+ m_MouseX = PosX;
+ SetCursorPos(m_MouseX + BS_Kernel::GetInstance()->GetWindow()->GetClientX(), m_MouseY + BS_Kernel::GetInstance()->GetWindow()->GetClientY());
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::SetMouseY(int PosY)
+{
+ m_MouseY = PosY;
+ SetCursorPos(m_MouseX + BS_Kernel::GetInstance()->GetWindow()->GetClientX(), m_MouseY + BS_Kernel::GetInstance()->GetWindow()->GetClientY());
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::RegisterCharacterCallback(CharacterCallback Callback)
+{
+ if (find(m_CharacterCallbacks.begin(), m_CharacterCallbacks.end(), Callback) == m_CharacterCallbacks.end())
+ {
+ m_CharacterCallbacks.push_back(Callback);
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Tried to register an CharacterCallback that was already registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::UnregisterCharacterCallback(CharacterCallback Callback)
+{
+ list<CharacterCallback>::iterator CallbackIter = find(m_CharacterCallbacks.begin(), m_CharacterCallbacks.end(), Callback);
+ if (CallbackIter != m_CharacterCallbacks.end())
+ {
+ m_CharacterCallbacks.erase(CallbackIter);
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Tried to unregister an CharacterCallback that was not previously registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::RegisterCommandCallback(CommandCallback Callback)
+{
+ if (find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback) == m_CommandCallbacks.end())
+ {
+ m_CommandCallbacks.push_back(Callback);
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Tried to register an CommandCallback that was already registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::UnregisterCommandCallback(CommandCallback Callback)
+{
+ list<CommandCallback>::iterator CallbackIter = find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback);
+ if (CallbackIter != m_CommandCallbacks.end())
+ {
+ m_CommandCallbacks.erase(CallbackIter);
+ return true;
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("Tried to unregister an CommandCallback that was not previously registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::ReportCharacter(unsigned char Character)
+{
+ list<CharacterCallback>::const_iterator CallbackIter = m_CharacterCallbacks.begin();
+ while (CallbackIter != m_CharacterCallbacks.end())
+ {
+ // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
+ // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
+ // invalidiert wird.
+ list<CharacterCallback>::const_iterator CurCallbackIter = CallbackIter;
+ ++CallbackIter;
+
+ (*CurCallbackIter)(Character);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_StdWinInput::ReportCommand(KEY_COMMANDS Command)
+{
+ list<CommandCallback>::const_iterator CallbackIter = m_CommandCallbacks.begin();
+ while (CallbackIter != m_CommandCallbacks.end())
+ {
+ // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
+ // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
+ // invalidiert wird.
+ list<CommandCallback>::const_iterator CurCallbackIter = CallbackIter;
+ ++CallbackIter;
+
+ (*CurCallbackIter)(Command);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ // Anzahl an Command-Callbacks persistieren.
+ Writer.Write(m_CommandCallbacks.size());
+
+ // Alle Command-Callbacks einzeln persistieren.
+ {
+ list<CommandCallback>::const_iterator It = m_CommandCallbacks.begin();
+ while (It != m_CommandCallbacks.end())
+ {
+ Writer.Write(BS_CallbackRegistry::GetInstance().ResolveCallbackPointer(*It));
+ ++It;
+ }
+ }
+
+ // Anzahl an Character-Callbacks persistieren.
+ Writer.Write(m_CharacterCallbacks.size());
+
+ // Alle Character-Callbacks einzeln persistieren.
+ {
+ list<CharacterCallback>::const_iterator It = m_CharacterCallbacks.begin();
+ while (It != m_CharacterCallbacks.end())
+ {
+ Writer.Write(BS_CallbackRegistry::GetInstance().ResolveCallbackPointer(*It));
+ ++It;
+ }
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_StdWinInput::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ // Command-Callbackliste leeren.
+ m_CommandCallbacks.clear();
+
+ // Anzahl an Command-Callbacks lesen.
+ unsigned int CommandCallbackCount;
+ Reader.Read(CommandCallbackCount);
+
+ // Alle Command-Callbacks wieder herstellen.
+ for (unsigned int i = 0; i < CommandCallbackCount; ++i)
+ {
+ std::string CallbackFunctionName;
+ Reader.Read(CallbackFunctionName);
+
+ m_CommandCallbacks.push_back(reinterpret_cast<CommandCallback>(BS_CallbackRegistry::GetInstance().ResolveCallbackFunction(CallbackFunctionName)));
+ }
+
+ // Character-Callbackliste leeren.
+ m_CharacterCallbacks.clear();
+
+ // Anzahl an Character-Callbacks lesen.
+ unsigned int CharacterCallbackCount;
+ Reader.Read(CharacterCallbackCount);
+
+ // Alle Character-Callbacks wieder herstellen.
+ for (unsigned int i = 0; i < CharacterCallbackCount; ++i)
+ {
+ std::string CallbackFunctionName;
+ Reader.Read(CallbackFunctionName);
+
+ m_CharacterCallbacks.push_back(reinterpret_cast<CharacterCallback>(BS_CallbackRegistry::GetInstance().ResolveCallbackFunction(CallbackFunctionName)));
+ }
+
+ return Reader.IsGood();
+}
diff --git a/engines/sword25/input/stdwininput.h b/engines/sword25/input/stdwininput.h
new file mode 100755
index 0000000000..20e02fdd11
--- /dev/null
+++ b/engines/sword25/input/stdwininput.h
@@ -0,0 +1,88 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_STDWININPUT_H
+#define BS_STDWININPUT_H
+
+/// Includes
+#include "kernel/memlog_off.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <list>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "inputengine.h"
+
+/// Klassendefinitionen
+class BS_Kernel;
+
+/// Klassendefinition
+class BS_StdWinInput : public BS_InputEngine
+{
+public:
+ BS_StdWinInput(BS_Kernel* pKernel);
+ virtual ~BS_StdWinInput();
+
+ virtual bool Init();
+ virtual void Update();
+ virtual bool IsLeftMouseDown();
+ virtual bool IsRightMouseDown();
+ virtual bool WasLeftMouseDown();
+ virtual bool WasRightMouseDown();
+ virtual bool IsLeftDoubleClick();
+ virtual int GetMouseX();
+ virtual int GetMouseY();
+ virtual bool IsKeyDown(unsigned int KeyCode);
+ virtual bool WasKeyDown(unsigned int KeyCode);
+ virtual void SetMouseX(int PosX);
+ virtual void SetMouseY(int PosY);
+ virtual bool RegisterCharacterCallback(CharacterCallback Callback);
+ virtual bool UnregisterCharacterCallback(CharacterCallback Callback);
+ virtual bool RegisterCommandCallback(CommandCallback Callback);
+ virtual bool UnregisterCommandCallback(CommandCallback Callback);
+ virtual void ReportCharacter(unsigned char Character);
+ virtual void ReportCommand(KEY_COMMANDS Command);
+
+ bool Persist(BS_OutputPersistenceBlock & Writer);
+ bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ void TestForLeftDoubleClick();
+
+ BYTE m_KeyboardState[2][256];
+ bool m_LeftMouseState[2];
+ bool m_RightMouseState[2];
+ unsigned int m_CurrentState;
+ int m_MouseX;
+ int m_MouseY;
+ bool m_LeftMouseDown;
+ bool m_RightMouseDown;
+ bool m_LeftDoubleClick;
+ unsigned int m_DoubleClickTime;
+ int m_DoubleClickRectWidth;
+ int m_DoubleClickRectHeight;
+ unsigned int m_LastLeftClickTime;
+ int m_LastLeftClickMouseX;
+ int m_LastLeftClickMouseY;
+ std::list<CommandCallback> m_CommandCallbacks;
+ std::list<CharacterCallback> m_CharacterCallbacks;
+};
+
+#endif
diff --git a/engines/sword25/kernel/bs_stdint.h b/engines/sword25/kernel/bs_stdint.h
new file mode 100755
index 0000000000..5334c3051c
--- /dev/null
+++ b/engines/sword25/kernel/bs_stdint.h
@@ -0,0 +1,17 @@
+#ifndef BS_STDINT_H
+#define BS_STDINT_H
+
+#ifdef _MSC_VER
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+ typedef unsigned __int64 uint64_t;
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef signed __int64 int64_t;
+#else
+ #include <stdint.h>
+#endif
+
+#endif
diff --git a/engines/sword25/kernel/callbackregistry.cpp b/engines/sword25/kernel/callbackregistry.cpp
new file mode 100755
index 0000000000..abdf2bd704
--- /dev/null
+++ b/engines/sword25/kernel/callbackregistry.cpp
@@ -0,0 +1,123 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// Alle Callbackfunktionen die von Objekten gerufen werden, die persistiert werden können, müssen hier registriert werden.
+// Beim Speichern wird statt des Pointers der Bezeichner gespeichert. Beim Laden wird der Bezeichner wieder in einen Pointer umgewandelt.
+// Diese Klasse führt also so etwas ähnliches wie eine Importtabelle für Callback-Funktionen.
+//
+// Dieses Vorgehen hat mehrere Vorteile:
+// 1. Die Speicherstände sind plattformunabhängig. Es werden keine Pointer auf Funktionen gespeichert, sondern nur Namen von Callbackfunktionen.
+// Diese können beim Laden über diese Klasse in systemabhängige Pointer umgewandelt werden.
+// 2. Speicherstände können auch nach einem Engineupdate weiterhin benutzt werden. Beim Erstellen einer neun Binary verschieben sich häufig die
+// Funktionen. Eine Callbackfunktion könnte sich also nach einem Update an einer anderen Stelle befinden als davor. Wenn im Spielstand der
+// Pointer gespeichert war, stürtzt das Programm beim Äufrufen dieser Callbackfunktion ab. Durch das Auflösungverfahren wird beim Laden der
+// Callbackbezeichner in den neuen Funktionspointer umgewandelt und der Aufruf kann erfolgen.
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "CALLBACKREGISTRY"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "callbackregistry.h"
+
+// -----------------------------------------------------------------------------
+
+bool BS_CallbackRegistry::RegisterCallbackFunction(const std::string & Name, void * Ptr)
+{
+ if (Name == "")
+ {
+ BS_LOG_ERRORLN("The empty string is not allowed as a callback function name.");
+ return false;
+ }
+
+ if (FindPtrByName(Name) != 0)
+ {
+ BS_LOG_ERRORLN("There is already a callback function with the name \"%s\".", Name.c_str());
+ return false;
+ }
+ if (FindNameByPtr(Ptr) != "")
+ {
+ BS_LOG_ERRORLN("There is already a callback function with the pointer 0x%x.", Ptr);
+ return false;
+ }
+
+ StoreCallbackFunction(Name, Ptr);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void * BS_CallbackRegistry::ResolveCallbackFunction(const std::string & Name) const
+{
+ void * Result = FindPtrByName(Name);
+
+ if (!Result)
+ {
+ BS_LOG_ERRORLN("There is no callback function with the name \"%s\".", Name.c_str());
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+std::string BS_CallbackRegistry::ResolveCallbackPointer(void * Ptr) const
+{
+ const std::string & Result = FindNameByPtr(Ptr);
+
+ if (Result == "")
+ {
+ BS_LOG_ERRORLN("There is no callback function with the pointer 0x%x.", Ptr);
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+void * BS_CallbackRegistry::FindPtrByName(const std::string & Name) const
+{
+ // Eintrag in der Map finden und den Pointer zurückgeben.
+ NameToPtrMap::const_iterator It = m_NameToPtrMap.find(Name);
+ return It == m_NameToPtrMap.end() ? 0 : It->second;
+}
+
+// -----------------------------------------------------------------------------
+
+std::string BS_CallbackRegistry::FindNameByPtr(void * Ptr) const
+{
+ // Eintrag in der Map finden und den Namen zurückgeben.
+ PtrToNameMap::const_iterator It = m_PtrToNameMap.find(Ptr);
+ return It == m_PtrToNameMap.end() ? "" : It->second;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_CallbackRegistry::StoreCallbackFunction(const std::string & Name, void * Ptr)
+{
+ // Callback-Funktion in beide Maps eintragen.
+ m_NameToPtrMap[Name] = Ptr;
+ m_PtrToNameMap[Ptr] = Name;
+}
diff --git a/engines/sword25/kernel/callbackregistry.h b/engines/sword25/kernel/callbackregistry.h
new file mode 100755
index 0000000000..68d2e85b80
--- /dev/null
+++ b/engines/sword25/kernel/callbackregistry.h
@@ -0,0 +1,62 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_CALLBACK_REGISTRY_H
+#define BS_CALLBACK_REGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+#include "kernel/memlog_off.h"
+#include <map>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_CallbackRegistry
+{
+public:
+ static BS_CallbackRegistry & GetInstance()
+ {
+ static BS_CallbackRegistry Instance;
+ return Instance;
+ }
+
+ bool RegisterCallbackFunction(const std::string & Name, void * Ptr);
+ void * ResolveCallbackFunction(const std::string & Name) const;
+ std::string ResolveCallbackPointer(void * Ptr) const;
+
+private:
+ typedef std::map<std::string, void *> NameToPtrMap;
+ NameToPtrMap m_NameToPtrMap;
+ typedef std::map<void *, std::string> PtrToNameMap;
+ PtrToNameMap m_PtrToNameMap;
+
+ void * FindPtrByName(const std::string & Name) const;
+ std::string FindNameByPtr(void * Ptr) const;
+ void StoreCallbackFunction(const std::string & Name, void * Ptr);
+};
+
+
+#endif
diff --git a/engines/sword25/kernel/common.h b/engines/sword25/kernel/common.h
new file mode 100755
index 0000000000..29e4a20e73
--- /dev/null
+++ b/engines/sword25/kernel/common.h
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ common.h
+ -----------
+ Diese Datei enthält Funktionen und Makros, die im gesamten Projekt bekannt sein müssen.
+ Daher ist es äußerst wichtig, dass diese Headerdatei in jede andere Headerdatei des Projektes
+ eingefügt wird.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_COMMON_H
+#define BS_COMMON_H
+
+// Globale Konstanten
+#if _DEBUG && !DEBUG
+ #define DEBUG
+#endif
+
+#define BS_ACTIVATE_LOGGING // Wenn definiert, wird Logging aktiviert
+
+// Engine Includes
+#include "memleaks.h"
+#include "log.h"
+
+#ifdef DEBUG
+#define BS_ASSERT(EXP) \
+ if (!(EXP)) \
+ { \
+ BS_Log::Log("!!ASSERTION FAILED!! - FILE: %s - LINE: %d.\n", __FILE__, __LINE__); \
+ __asm { int 3 }; \
+ }
+#else
+#define BS_ASSERT(EXP) do { (void)(EXP); } while(0)
+#endif
+
+#endif
diff --git a/engines/sword25/kernel/cpuinfo.cpp b/engines/sword25/kernel/cpuinfo.cpp
new file mode 100755
index 0000000000..f65f7e6125
--- /dev/null
+++ b/engines/sword25/kernel/cpuinfo.cpp
@@ -0,0 +1,260 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "CPUINFO"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include "cpuinfo.h"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+// Standard CPU-Features
+static const unsigned int MMX_BITMASK = 1 << 23;
+static const unsigned int SSE_BITMASK = 1 << 25;
+static const unsigned int SSE2_BITMASK = 1 << 26;
+
+// Erweiterte CPU-Features
+static const unsigned int _3DNOW_BITMASK = 1 << 30;
+static const unsigned int _3DNOWEXT_BITMASK = 1 << 31;
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+BS_CPUInfo::BS_CPUInfo() :
+ _VendorID(V_UNKNOWN),
+ _VendorString("unknown"),
+ _CPUName("unknown"),
+ _MMXSupported(false),
+ _SSESupported(false),
+ _SSE2Supported(false),
+ _3DNowSupported(false),
+ _3DNowExtSupported(false)
+{
+ if (!_IsCPUIDSupported())
+ {
+ BS_LOG_ERRORLN("CPUID instruction ist not supported. Could not gather processor information.");
+ return;
+ }
+
+ if (!_ReadVendor())
+ {
+ BS_LOG_WARNINGLN("Unrecognized CPU vendor.");
+ }
+
+ if (!_ReadCPUName())
+ {
+ BS_LOG_WARNINGLN("Could not determine CPU name.");
+ }
+
+ if (!_ReadCPUFeatures())
+ {
+ BS_LOG_WARNINGLN("Could not determine CPU-features.");
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_CPUInfo::_IsCPUIDSupported() const
+{
+ __try
+ {
+ __asm
+ {
+ mov eax, 0
+ cpuid
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_CPUInfo::_ReadVendor()
+{
+ static struct
+ {
+ char* VendorString;
+ BS_CPUInfo::VENDORID ID;
+ } VENDOR_TABLE[] =
+ {
+ "GenuineIntel", V_INTEL,
+ "AuthenticAMD", V_AMD,
+ "CyrixInstead", V_CYRIX,
+ "CentaurHauls", V_CENTAUR,
+ "NexGenDriven", V_NEXGEN,
+ "GenuineTMx86", V_TRANSMETA,
+ "RiseRiseRise", V_RISE,
+ "UMC UMC UMC", V_UMC,
+ "SiS SiS SiS", V_SIS,
+ "Geode by NSC", V_NSC,
+ 0, V_UNKNOWN,
+ };
+
+ // Vendor-String bestimmen
+ char Buffer[13];
+ __asm
+ {
+ xor eax, eax
+ cpuid
+ mov dword ptr [Buffer], ebx
+ mov dword ptr [Buffer + 4], edx
+ mov dword ptr [Buffer + 8], ecx
+ mov byte ptr [Buffer + 12], 0
+ }
+ _VendorString = Buffer;
+
+ // Vendor-ID bestimmen
+ int i;
+ for (i = 0; VENDOR_TABLE[i].VendorString; i++) if (_VendorString == VENDOR_TABLE[i].VendorString) break;
+ _VendorID = VENDOR_TABLE[i].ID;
+
+ return _VendorID != V_UNKNOWN;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_CPUInfo::_ReadCPUName()
+{
+ // Feststellen, ob das CPU-Name Feature vorhanden ist.
+ unsigned int Result;
+ __asm
+ {
+ mov eax, 0x80000000
+ cpuid
+ mov Result, eax
+ }
+ if (Result < 0x80000004) return false;
+
+ // CPU-Namen einlesen
+ char Buffer[49];
+ __asm
+ {
+ mov eax,0x80000002
+ cpuid
+ mov dword ptr [Buffer + 0], eax
+ mov dword ptr [Buffer + 4], ebx
+ mov dword ptr [Buffer + 8], ecx
+ mov dword ptr [Buffer + 12], edx
+ mov eax,0x80000003
+ cpuid
+ mov dword ptr [Buffer + 16], eax
+ mov dword ptr [Buffer + 20], ebx
+ mov dword ptr [Buffer + 24], ecx
+ mov dword ptr [Buffer + 28], edx
+ mov eax,0x80000004
+ cpuid
+ mov dword ptr [Buffer + 32], eax
+ mov dword ptr [Buffer + 36], ebx
+ mov dword ptr [Buffer + 40], ecx
+ mov dword ptr [Buffer + 44], edx
+ mov byte ptr [Buffer + 48], 0
+ }
+ std::string TempCPUName = Buffer;
+ if (TempCPUName.size() != 0)
+ {
+ // Führende und nachfolgende Leerzeichen entfernen
+ std::string::const_iterator StringBegin = TempCPUName.begin();
+ for (; StringBegin != TempCPUName.end() && *StringBegin == ' '; StringBegin++);
+ std::string::const_iterator StringEnd = TempCPUName.end() - 1;
+ for(; StringEnd >= TempCPUName.begin() && *StringEnd == ' '; StringEnd--);
+
+ if (StringBegin != TempCPUName.end() && StringEnd >= TempCPUName.begin())
+ {
+ _CPUName = std::string(StringBegin, StringEnd + 1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_CPUInfo::_ReadCPUFeatures()
+{
+ {
+ // Feststellen ob die Standard-Features abgefragt werden können
+ unsigned int Result;
+ __asm
+ {
+ xor eax, eax
+ cpuid
+ mov Result, eax
+ }
+
+ // Nicht einmal die Standard-Features können abgefragt werden, also muss abgebrochen werden
+ if (Result < 1) return false;
+
+ // Standard-Features abfragen
+ unsigned int Features;
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov Features, edx
+ }
+
+ _MMXSupported = (Features & MMX_BITMASK) != 0;
+ _SSESupported = (Features & SSE_BITMASK) != 0;
+ _SSE2Supported = (Features & SSE2_BITMASK) != 0;
+ }
+
+
+ // Feststellen ob erweiterte CPU-Features abgefragt werden können
+ {
+ unsigned int Result;
+ __asm
+ {
+ mov eax, 0x80000000
+ cpuid
+ mov Result, eax
+ }
+
+ // Die erweiterten Features können nicht abgefragt werden, aber die Standard-Features wurden schon
+ // abgefragt, daher wird true zurückgegeben.
+ if (Result < 0x80000001) return true;
+
+ // Erweiterte Features abfragen
+ unsigned int Features;
+ __asm
+ {
+ mov eax, 0x80000001
+ cpuid
+ mov Features, edx
+ }
+
+ _3DNowSupported = (Features & _3DNOW_BITMASK) != 0;
+ _3DNowExtSupported = (Features & _3DNOWEXT_BITMASK) != 0;
+ }
+
+ return true;
+}
diff --git a/engines/sword25/kernel/cpuinfo.h b/engines/sword25/kernel/cpuinfo.h
new file mode 100755
index 0000000000..fab763861a
--- /dev/null
+++ b/engines/sword25/kernel/cpuinfo.h
@@ -0,0 +1,128 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_CPUINFO_H
+#define BS_CPUINFO_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+/**
+ @brief Diese Singleton-Klasse stellt Informationen über die CPU zur verfügung.
+*/
+
+class BS_CPUInfo
+{
+public:
+ /**
+ @brief Definiert die Vendor-IDs
+ */
+ enum VENDORID
+ {
+ V_UNKNOWN,
+ V_INTEL,
+ V_AMD,
+ V_CYRIX,
+ V_CENTAUR,
+ V_NEXGEN,
+ V_TRANSMETA,
+ V_RISE,
+ V_UMC,
+ V_SIS,
+ V_NSC,
+ };
+
+ /**
+ @brief Gibt eine Referenz auf die einzige Instanz dieser Klasse zurück.
+ */
+ static const BS_CPUInfo & GetInstance()
+ {
+ static BS_CPUInfo Instance;
+ return Instance;
+ }
+
+ /**
+ @brief Gibt die Vendor-ID des CPU-Herstellers zurück.
+ @remark Gibt BS_CPUInfo::V_UNKNOWN zurück, wenn die Vendor-ID nicht bestimmt werden konnte.
+ */
+ VENDORID GetVendorID() const { return _VendorID; }
+
+ /**
+ @brief Gibt den Vendor-String zurück.
+ @remark Gibt "unknown" zurück, wenn der Vendor-String nicht bestimmt werden konnte.
+ */
+ const std::string & GetVendorString() const { return _VendorString; }
+
+ /**
+ @brief Gibt den CPU-Namen zurück.
+ @remark Gibt "unknown" zurück, wenn der CPU-Name nicht bestimmt werden konnte.
+ */
+ const std::string & GetCPUName() const { return _CPUName; }
+
+ /**
+ @brief Gibt zurück, ob der Prozessor MMX untersützt.
+ */
+ bool IsMMXSupported() const { return _MMXSupported; }
+
+ /**
+ @brief Gibt zurück, ob der Prozessor SSE unterstützt.
+ */
+ bool IsSSESupported() const { return _SSESupported; }
+
+ /**
+ @brief Gibt zurück, ob der Prozessor SSE2 unterstützt.
+ */
+ bool IsSSE2Supported() const { return _SSE2Supported; }
+
+ /**
+ @brief Gibt zurück, ob der Prozessor 3DNow! unterstützt.
+ */
+ bool Is3DNowSupported() const { return _3DNowSupported; }
+
+ /**
+ @brief Gibt zurück, ob der Prozessor 3DNow!-Ext. unterstützt.
+ */
+ bool Is3DNowExtSupported() const { return _3DNowExtSupported; }
+
+private:
+ BS_CPUInfo();
+
+ VENDORID _VendorID;
+ std::string _VendorString;
+ std::string _CPUName;
+ bool _MMXSupported;
+ bool _SSESupported;
+ bool _SSE2Supported;
+ bool _3DNowSupported;
+ bool _3DNowExtSupported;
+
+ bool _ReadVendor();
+ bool _ReadCPUFeatures();
+ bool _ReadCPUName();
+ bool _IsCPUIDSupported() const;
+};
+
+#endif
diff --git a/engines/sword25/kernel/debug/debugtools.cpp b/engines/sword25/kernel/debug/debugtools.cpp
new file mode 100755
index 0000000000..91abe66ba7
--- /dev/null
+++ b/engines/sword25/kernel/debug/debugtools.cpp
@@ -0,0 +1,156 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <fstream>
+using namespace std;
+
+#include "kernel/md5.h"
+#include "kernel/filesystemutil.h"
+#include "debugtools.h"
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * VERSION_ID_ERROR = "???";
+ const unsigned int READBUFFER_SIZE = 1024 * 100;
+
+ const char * SUBVERSION_ENTRIES_FILENAME = ".svn\\entries";
+
+ // -------------------------------------------------------------------------
+
+ unsigned int ParseUnsignedInt(const string & Str, bool & Success)
+ {
+ istringstream iss(Str);
+
+ unsigned int Result = 0;
+ iss >> Result;
+
+ Success = !iss.fail();
+
+ return Result;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+const char * BS_Debugtools::GetVersionID()
+{
+ // Falls die Versions-ID noch nicht bekannt ist, muss sie bestimmt werden
+ static string VersionIDString;
+ if (VersionIDString.size() == 0)
+ {
+ // Dateinamen der EXE-Datei bestimmen
+ char FileName[MAX_PATH + 1];
+ if (GetModuleFileName(0, FileName, sizeof(FileName)) == 0) return VERSION_ID_ERROR;
+
+ // Datei öffnen
+ HANDLE FileHandle = CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (INVALID_HANDLE_VALUE == FileHandle) return VERSION_ID_ERROR;
+
+ // Datei stückchenweise Einlesen und MD5-Hash bilden
+ BS_MD5 md5;
+ std::vector<unsigned char> ReadBuffer(READBUFFER_SIZE);
+ DWORD BytesRead = 0;
+ do
+ {
+ // MD5-Hash für das eingelesene Dateistück berechnen.
+ md5.Update(&ReadBuffer[0], BytesRead);
+
+ if (ReadFile(FileHandle, &ReadBuffer[0], READBUFFER_SIZE, &BytesRead, 0) == FALSE)
+ {
+ CloseHandle(FileHandle);
+ return VERSION_ID_ERROR;
+ }
+ } while (BytesRead > 0);
+
+ // Datei schließen
+ CloseHandle(FileHandle);
+
+ // Falls sich das aktuelle Verzeichnis in einem Subversion-Repository befindet, wird auch die Subversion-Revision mit in die ID gehasht.
+ // Dieses stellt im Beta-Test sicher, dass jede Änderung einer Datei, und nicht nur der EXE, zu einer neuen Versions-ID führt.
+ unsigned int SubversionRevision = GetSubversionRevision();
+ if (SubversionRevision != 0) md5.Update(reinterpret_cast<unsigned char *>(&SubversionRevision), sizeof(unsigned int));
+
+ // MD5 abschließen
+ unsigned char Digest[16];
+ md5.GetDigest(Digest);
+
+ // VersionsID-String erstellen
+ std::ostringstream VersionIDBuf;
+ VersionIDBuf << std::hex;
+ for (unsigned int i = 0; i < sizeof(Digest); i++)
+ {
+ VersionIDBuf << (unsigned int) Digest[i];
+ }
+ VersionIDString = VersionIDBuf.str();
+ }
+
+ return VersionIDString.c_str();
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_Debugtools::GetSubversionRevision()
+{
+ // Existiert eine entries Datei?
+ if (BS_FileSystemUtil::GetInstance().FileExists(SUBVERSION_ENTRIES_FILENAME))
+ {
+ bool Success;
+ char Buffer[512];
+
+ // entries Datei öffnen.
+ ifstream File(SUBVERSION_ENTRIES_FILENAME);
+ if (File.fail()) return 0;
+
+ // Das Format auslesen und feststellen, ob wir es unterstützen.
+ File.getline(Buffer, sizeof(Buffer), 0x0A);
+ unsigned int FormatVersion = ParseUnsignedInt(Buffer, Success);
+ if (File.fail() || !Success || FormatVersion < 7) return 0;
+
+ // Den Namen des ersten Eintrages auslesen. Dieses muss ein leerer String sein und somit das aktuelle Verzeichnis benennen.
+ File.getline(Buffer, sizeof(Buffer), 0x0A);
+ if (File.fail() || strlen(Buffer) != 0) return 0;
+
+ // Den Typ des Eintrages auslesen. Dieser muss "dir" sein.
+ File.getline(Buffer, sizeof(Buffer), 0x0A);
+ if (File.fail() || strcmp(Buffer, "dir") != 0) return 0;
+
+ // Die Revision des Eintrages auslesen.
+ File.getline(Buffer, sizeof(Buffer), 0x0A);
+ unsigned int Revision = ParseUnsignedInt(Buffer, Success);
+ if (File.fail() || !Success) return 0;
+
+ return Revision;
+ }
+
+ return 0;
+}
diff --git a/engines/sword25/kernel/debug/debugtools.h b/engines/sword25/kernel/debug/debugtools.h
new file mode 100755
index 0000000000..30dd04a83d
--- /dev/null
+++ b/engines/sword25/kernel/debug/debugtools.h
@@ -0,0 +1,52 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef DEBUGTOOLS_H
+#define DEBUGTOOLS_H
+
+class BS_Debugtools
+{
+public:
+ /**
+ @brief Gibt eine ID zurück, die die benutzte Programmversion eindeutig identifiziert.
+
+ Um die Version zu ermitteln wird der MD5-Hash über die EXE-Datei gebildet.
+ Falls die ausführende Datei in einem SVN-Repository liegt wird zusätzlich die Revision des Verzeichnisses gehasht.
+
+ @return Gibt einen String zurück, der die Versions-ID des Programmes angibt.<br>
+ Falls die Versions-ID nicht bestimmt werden konnte wird "???" zurückgegeben.
+ @remark Diese Methode ist momentan nur für WIN32 implementiert.
+ */
+ static const char * GetVersionID();
+
+ /**
+
+ @brief Gibt die Subversion-Revisionsnummer der Engine zurück.
+
+ Diese Funktion versucht die aktuelle Revision aus der SVN entries Datei für das aktuelle Verzeichnis zu extrahieren.
+ Dabei werden die SVN entries Formatversionen 7 und größer unterstützt.
+ Die neueste Version ist aktuell 9. Für folgende Versionen wird angenommen, dass sich das Format des Headers nicht mehr ändert.
+
+ @return Gibt die Revisionsnummer zurück. Falls die ausführende Datei nicht in einem SVN-Repository liegt oder die Revision nicht
+ festgestellt werden konnte wird 0 zurückgegeben.
+ */
+ static unsigned int GetSubversionRevision();
+};
+
+#endif
diff --git a/engines/sword25/kernel/debug/memorydumper.cpp b/engines/sword25/kernel/debug/memorydumper.cpp
new file mode 100755
index 0000000000..3c79f67bf8
--- /dev/null
+++ b/engines/sword25/kernel/debug/memorydumper.cpp
@@ -0,0 +1,184 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <ctime>
+#include <sstream>
+#include <iomanip>
+#include "..\filesystemutil.h"
+#include "memorydumper.h"
+#include "debugtools.h"
+
+using namespace std;
+
+#define BS_LOG_PREFIX "MEMORYDUMPER"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * DBG_HELP_DLL_FILENAME = "dbghelp.dll";
+ const char * MINIDUMPWRITEDUMP_FUNCTIONNAME = "MiniDumpWriteDump";
+ const char * DUMPS_DIRECTORYNAME = "dumps";
+ const char * DUMPFILE_EXTENSION = ".dmp";
+}
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ HMODULE LoadDbghelpDLL()
+ {
+ // Zunächst wird versucht die DLL in dem Verzeichnis der EXE-Datei zu finden, da diese die gewünschte Funktionalität unterstützt
+
+ // Pfad der EXE-Datei bestimmen
+ char ExePath[MAX_PATH];
+ if (GetModuleFileNameA(0, ExePath, sizeof(ExePath)))
+ {
+ // EXE-Dateinamen abschneiden und den DLL-Dateinamen anhängen
+ string DllPath = ExePath;
+ string::size_type SlashPos;
+ if ((SlashPos = DllPath.rfind("\\")) != string::npos)
+ {
+ DllPath.resize(SlashPos + 1);
+ DllPath += DBG_HELP_DLL_FILENAME;
+
+ HMODULE DllModule = LoadLibraryA(DllPath.c_str());
+ if (DllModule) return DllModule;
+ }
+ }
+
+ // Falls dies fehlgeschlagen ist, wird versucht die Version zu laden, die Windows uns anbietet, wenn wir keinen kompletten Pfad angeben.
+ return LoadLibraryA(DBG_HELP_DLL_FILENAME);
+ }
+}
+
+BS_MemoryDumper::BS_MemoryDumper() :
+ m_DbghelpDLL(0)
+{
+ m_DbghelpDLL = LoadDbghelpDLL();
+ if (m_DbghelpDLL)
+ {
+ m_MiniDumpWriteDump = reinterpret_cast<MINIDUMPWRITEDUMP>(GetProcAddress(m_DbghelpDLL, MINIDUMPWRITEDUMP_FUNCTIONNAME));
+ if (!m_MiniDumpWriteDump)
+ {
+ BS_LOG_ERRORLN("Your version of \"%s\" is too old. Dumping is not possible.", DBG_HELP_DLL_FILENAME);
+ }
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Could not load \"%s\". Dumping is not possible.", DBG_HELP_DLL_FILENAME);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_MemoryDumper::~BS_MemoryDumper()
+{
+ // DLL-Entladen
+ if (m_DbghelpDLL) FreeLibrary(m_DbghelpDLL);
+}
+
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ bool EnsureOutputDirectoryExists()
+ {
+ // Windows.h definiert ein Makro CreateDirectory. Damit wir die Methode CreateDirectory aufrufen können müssen wir das Makro entfernen.
+ #ifdef CreateDirectory
+ #undef CreateDirectory
+ #endif
+
+ return BS_FileSystemUtil::GetInstance().CreateDirectory(
+ BS_FileSystemUtil::GetInstance().GetUserdataDirectory() +
+ BS_FileSystemUtil::GetInstance().GetPathSeparator() +
+ DUMPS_DIRECTORYNAME);
+ }
+
+ string CreateOutputFilename()
+ {
+ // Aktuelle Zeit bestimmen.
+ time_t Time = time(0);
+ tm * Timeinfo = localtime(&Time);
+
+ // Den Ausgabedateinamen in folgender Form erstellen:
+ // <BenutzerdatenVerzeichnis>\dumps\YYYY-MM-DD HH-MM.dmp
+ ostringstream oss;
+ oss << BS_FileSystemUtil::GetInstance().GetUserdataDirectory() << BS_FileSystemUtil::GetInstance().GetPathSeparator()
+ << DUMPS_DIRECTORYNAME << BS_FileSystemUtil::GetInstance().GetPathSeparator()
+ << setfill('0')
+ << setw(4) << (Timeinfo->tm_year + 1900) << "-"
+ << setw(2) << (Timeinfo->tm_mon + 1) << "-"
+ << setw(2) << Timeinfo->tm_mday << " "
+ << setw(2) << Timeinfo->tm_hour << "-"
+ << setw(2) << Timeinfo->tm_min << "-"
+ << BS_Debugtools::GetVersionID()
+ << DUMPFILE_EXTENSION;
+ return oss.str();
+ }
+}
+
+bool BS_MemoryDumper::WriteDump(_EXCEPTION_POINTERS * ExceptionInfoPtr, string & Filename)
+{
+ // Dumpen ist nur möglich, wenn zuvor die dbghelp.dll geladen werden konnte.
+ if (!m_DbghelpDLL)
+ {
+ BS_LOG_ERRORLN("Cannot write dump because \"%s\" could not be loaded previously.", DBG_HELP_DLL_FILENAME);
+ return false;
+ }
+
+ // Temporäre Datei erstellen, die den Dump aufnehmen soll
+ HANDLE File;
+ Filename = CreateOutputFilename();
+ if (!EnsureOutputDirectoryExists() ||
+ (File = CreateFile(Filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)) != INVALID_HANDLE_VALUE)
+ {
+ _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
+
+ ExInfo.ThreadId = GetCurrentThreadId();
+ ExInfo.ExceptionPointers = ExceptionInfoPtr;
+ ExInfo.ClientPointers = 0;
+
+ // Dump schreiben
+ bool Result = m_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), File, MiniDumpNormal, &ExInfo, 0, 0) != 0;
+ if (!Result)
+ {
+ BS_LOG_ERRORLN("MiniDumpWriteDump() failed. Memory dump could not be created.");
+ DeleteFileA(Filename.c_str());
+ }
+
+ CloseHandle(File);
+ return Result;
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Could not create a file to accomodate the memory dump.");
+ return false;
+ }
+}
diff --git a/engines/sword25/kernel/debug/memorydumper.h b/engines/sword25/kernel/debug/memorydumper.h
new file mode 100755
index 0000000000..5fac12e830
--- /dev/null
+++ b/engines/sword25/kernel/debug/memorydumper.h
@@ -0,0 +1,57 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_MEMORYDUMPER_H
+#define BS_MEMORYDUMPER_H
+
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbghelp.h>
+#include <string>
+
+#include "kernel/common.h"
+
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_MemoryDumper
+{
+public:
+ BS_MemoryDumper();
+ ~BS_MemoryDumper();
+
+ bool WriteDump(_EXCEPTION_POINTERS * ExceptionInfoPtr, std::string & Filename);
+
+private:
+ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+ MINIDUMPWRITEDUMP m_MiniDumpWriteDump;
+ HMODULE m_DbghelpDLL;
+};
+
+#endif
diff --git a/engines/sword25/kernel/filesystemutil.h b/engines/sword25/kernel/filesystemutil.h
new file mode 100755
index 0000000000..63f76617c0
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil.h
@@ -0,0 +1,102 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ Die Klasse BS_FileSystemUtil stellt einen Wrapper für dateisystemspezifische Operationen dar, die nicht über die C/C++ Bibliotheken
+ abgedeckt werden.
+
+ Jede unterstützte Plattform muss dieses Interface implementieren und die Singleton-Methode BS_FileSystemUtil::GetInstance()
+ implementieren.
+*/
+
+#ifndef BS_FILESYSTEMUTIL_H
+#define BS_FILESYSTEMUTIL_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/bs_stdint.h"
+
+#include "kernel/memlog_off.h"
+#include <string>
+#include <vector>
+#include <ctime>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FileSystemUtil
+{
+public:
+ static BS_FileSystemUtil & GetInstance();
+ virtual ~BS_FileSystemUtil() {};
+
+ /*
+ Diese Funktion gibt den Namen des Verzeichnisses zurück, in dem sämtliche Benutzerdaten gespeichert werden sollen.
+
+ Dieses sind z.B. Screenshots, Spielstände, Konfigurationsdateien, Logdateien, ...
+ Unter Windows Vista ist dieses beispielsweise: C:\Users\Malte\Documents\Broken Sword 2.5
+ Falls dieses Verzeichnis noch nicht existiert, wird es automatisch erstellt.
+
+ @return Gibt den Namen des Verzeichnisses für Benutzerdaten zurück.
+ */
+ virtual std::string GetUserdataDirectory() = 0;
+ /*
+ @return Gibt den Pfadtrenner zurück (z.B. \ unter Windows und / unter Linux).
+ */
+ virtual std::string GetPathSeparator() = 0;
+ /*
+ @param Filename der Pfad zu einer Datei.
+ @return Gibt die Größe der angegebenen Datei zurück. Falls die Größe nicht bestimmt werden konnte, oder die Datei nicht existiert wird -1 zurückgegeben.
+ */
+ virtual uint64_t GetFileSize(const std::string & Filename) = 0;
+ /*
+ @param Filename der Pfad zu einer Datei.
+ @return Gibt den Zeitstempel des Zeitpunktes zurück an dem die Datei zuletzt verändert wurde. Bei einem Fehler wird 0 zurückgegeben.
+ */
+ virtual time_t GetFileTime(const std::string & Filename) = 0;
+ /*
+ @param Filename der Pfad zu einer Datei.
+ @return Gibt true zurück, wenn die Datei existiert.
+ */
+ virtual bool FileExists(const std::string & Filename) = 0;
+ /*
+ Diese Funktion erstellt einen Verzeichnis mit sämtlichen Unterverzeichnissen.
+
+ Wenn des Parameter "\b\c\d\e" ist und der Pfad "\b\c" bereits existiert, werden die Verzeichnisse
+ "d" und "e" erstellt.
+
+ @param DirectoryName der Name des zu erstellenden Verzeichnisses.
+ @return Gibt true zurück, wenn die Verzeichnisse erstellt werden konnten, ansonsten false.
+ */
+ virtual bool CreateDirectory(const std::string & DirectoryName) = 0;
+ /*
+ Erstellt eine Liste aller Dateinamen in einem Verzeichnis.
+
+ @param Directory das zu durchsuchende Verzeichnis.
+ @return Gibt einen vector mit allen gefundenen Dateinamen zurück.
+ */
+ virtual std::vector<std::string> GetFilesInDirectory(const std::string & Path) = 0;
+};
+
+#endif
diff --git a/engines/sword25/kernel/filesystemutil_win32.cpp b/engines/sword25/kernel/filesystemutil_win32.cpp
new file mode 100755
index 0000000000..2b4ce49f3d
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil_win32.cpp
@@ -0,0 +1,256 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "filesystemutil.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ShlObj.h>
+#include <memory.h>
+
+using namespace std;
+
+#define BS_LOG_PREFIX "FILESYSTEMUTILWIN32"
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * DIRECTORY_NAME = "Broken Sword 2.5";
+
+ // -------------------------------------------------------------------------
+
+ string GetAbsolutePath(const string & Path)
+ {
+ char Buffer[MAX_PATH];
+ if (!::GetFullPathNameA(Path.c_str(), MAX_PATH, Buffer, 0))
+ {
+ // Bei der Ausführung von GetFullPathNameA() ist ein Fehler aufgetreten.
+ // Wir können an dieser Stelle nichts andere machen, als einen leeren String zurückzugeben.
+ BS_LOG_ERRORLN("A call to GetFullPathNameA() failed.");
+ return "";
+ }
+
+ // Ergebnis zurückgeben.
+ return string(Buffer);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FileSystemUtilWin32 : public BS_FileSystemUtil
+{
+public:
+ virtual string GetUserdataDirectory()
+ {
+ // Die C++ Dateisystemfunktionen können leider nicht mit Unicode-Dateinamen umgehen.
+ // Für uns ist das problematisch, wenn wir in das %APPDATA%-Verzeichnis eines Benutzers zugreifen wollen,
+ // dessen Name Unicode-Zeichen enthält, die sich mit der aktuellen Codepage nicht als ANSI-String darstellen
+ // lassen.
+ // Wir behelfen uns damit, dass wir das Verzeichnis als Unicode-String abfragen, in die kurze
+ // Verzeichnisdarstellung konvertieren und das Ergebnis in einen ANSI-String konvertieren.
+ // Kurze Dateinamen sollten keine Unicode-Zeichen enthalten, ich habe allerdings keine offizielle Aussage dazu
+ // gefunden.
+
+ WCHAR PathBuffer[MAX_PATH];
+
+ // Das %APPDATA%-Verzeichnis erfragen.
+ if (::SHGetSpecialFolderPathW(0, PathBuffer, CSIDL_APPDATA, FALSE) == FALSE)
+ {
+ BS_LOG_ERRORLN("SHGetSpecialFolderPathW() failed");
+ return "";
+ }
+
+ // Die kurze Variante des Verzeichnisses erfragen.
+ if (::GetShortPathNameW(PathBuffer, PathBuffer, MAX_PATH) == 0)
+ {
+ BS_LOG_ERRORLN("GetShortPathNameW() failed");
+ return "";
+ }
+
+ // Die Verzeichnisangabe in einen ANSI-String konvertieren.
+ char AnsiPathBuffer[MAX_PATH];
+ BOOL UsedDefaultChar = FALSE;
+ if (::WideCharToMultiByte(CP_ACP, 0, PathBuffer, -1, AnsiPathBuffer, MAX_PATH, 0, &UsedDefaultChar) == 0)
+ {
+ BS_LOG_ERRORLN("WideCharToMultiByte() failed");
+ return "";
+ }
+
+ // Falls bei der Konvertierung ein zum Einsatz kam, ist das Ergebnis nicht eindeutig und damit nicht
+ // verwendbar.
+ if (UsedDefaultChar)
+ {
+ BS_LOG_ERRORLN("Conversion from unicode to ANSI is ambiguous.");
+ return "";
+ }
+
+ // Verzeichnis zurückgeben.
+ return string(AnsiPathBuffer) + "\\" + DIRECTORY_NAME;
+ }
+
+ virtual string GetPathSeparator()
+ {
+ return string("\\");
+ }
+
+ virtual uint64_t GetFileSize(const std::string & Filename)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA fileAttributeData;
+ // Dateiattribute einlesen.
+ if (::GetFileAttributesExA(Filename.c_str(), GetFileExInfoStandard, &fileAttributeData) != 0)
+ {
+ // Die Dateigröße wird von Windows in zwei 32-Bit Zahlen angegeben. Diese werden an dieser Stelle in eine 64-Bit Zahl umgewandelt.
+ uint64_t fileSize = fileAttributeData.nFileSizeHigh;
+ fileSize <<= 32;
+ fileSize |= fileAttributeData.nFileSizeLow;
+ return fileSize;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ virtual time_t GetFileTime(const std::string & Filename)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA fileAttributeData;
+ if (::GetFileAttributesExA(Filename.c_str(), GetFileExInfoStandard, &fileAttributeData) != 0)
+ {
+ __int64 timestamp;
+ memcpy(&timestamp, &fileAttributeData.ftLastWriteTime, sizeof(FILETIME));
+ return (timestamp - 0x19DB1DED53E8000) / 10000000;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ virtual bool FileExists(const std::string & Filename)
+ {
+ return ::GetFileAttributesA(Filename.c_str()) != INVALID_FILE_ATTRIBUTES;
+ }
+
+ // Windows.h enthält ein Makro mit dem Namen CreateDirectory. Dieses muss entfernt werden bevor die Definition von
+ // unserem CreateDirectory() folgt.
+ #ifdef CreateDirectory
+ #undef CreateDirectory
+ #endif
+
+ virtual bool CreateDirectory(const string & DirectoryName)
+ {
+ return CreateDirectoryRecursive(GetAbsolutePath(DirectoryName));
+ }
+
+ virtual vector<string> GetFilesInDirectory(const std::string & Directory)
+ {
+ vector<string> Result;
+
+ // Suchstring erstellen, dabei muss der leere String (aktuelles Verzeichnis) gesondert behandelt werden.
+ string SearchPattern;
+ if (Directory.empty())
+ SearchPattern = "*";
+ else
+ SearchPattern = Directory + "\\*";
+
+ // Die erste Datei suchen.
+ WIN32_FIND_DATAA FindData;
+ HANDLE FindHandle = ::FindFirstFileA(SearchPattern.c_str(), &FindData);
+
+ while (FindHandle != INVALID_HANDLE_VALUE)
+ {
+ // Verzeichnisse ignorieren.
+ // Beim erstellen des Ergebnispfades muss wieder der Sonderfall der leeren Verzeichnisangabe berücksichtigt werden.
+ if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ Result.push_back(FindData.cFileName);
+
+ // Solange weitermachen, bis keine Dateien mehr gefunden werden.
+ if (FindNextFileA(FindHandle, &FindData) == 0)
+ {
+ FindClose(&FindHandle);
+ break;
+ }
+ }
+
+ return Result;
+ }
+
+private:
+ bool CreateDirectoryRecursive(string & DirectoryName)
+ {
+ //
+ // http://www.codeguru.com/Cpp/W-P/files/article.php/c4439/
+ // (c) Assaf Tzur-El
+ //
+
+ DWORD attr;
+ int pos;
+ bool result = true;
+
+ // Check for trailing slash:
+ pos = DirectoryName.find_last_of("\\");
+ if (DirectoryName.length() == pos + 1) // last character is "\"
+ {
+ DirectoryName.resize(pos);
+ }
+
+ // Look for existing object:
+ attr = ::GetFileAttributesA(DirectoryName.c_str());
+ if (0xFFFFFFFF == attr) // doesn't exist yet - create it!
+ {
+ pos = DirectoryName.find_last_of("\\");
+ if (0 < pos)
+ {
+ // Create parent dirs:
+ result = CreateDirectoryRecursive(DirectoryName.substr(0, pos));
+ }
+ // Create node:
+ result = result && ::CreateDirectoryA(DirectoryName.c_str(), NULL);
+ }
+ else if (!(FILE_ATTRIBUTE_DIRECTORY & attr))
+ { // object already exists, but is not a dir
+ ::SetLastError(ERROR_FILE_EXISTS);
+ result = false;
+ }
+
+ return result;
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Singleton-Methode der Elternklasse
+// Hiermit wird sichergestellt, dass wenn immer diese Datei kompiliert wird,
+// die Singleton-Methode der Oberklasse diese Klasse instanziiert.
+// Unterscheidung zwischen den Plattformen wird so nur durch Linken gegen andere
+// Dateien realisiert.
+// -----------------------------------------------------------------------------
+
+BS_FileSystemUtil & BS_FileSystemUtil::GetInstance()
+{
+ static BS_FileSystemUtilWin32 Instance;
+ return Instance;
+}
diff --git a/engines/sword25/kernel/hashmap.h b/engines/sword25/kernel/hashmap.h
new file mode 100755
index 0000000000..0aa663332c
--- /dev/null
+++ b/engines/sword25/kernel/hashmap.h
@@ -0,0 +1,34 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_HASHMAP_H
+#define BS_HASHMAP_H
+
+// stdext::hash_map wird erst seit VC7 untersützt, bei älteren Microsoft-Compilern wird auf std::map zurückgegriffen
+#include "kernel/memlog_off.h"
+#if _MSC_VER >= 1300
+#include <hash_map>
+#define BS_Hashmap stdext::hash_map
+#else
+#include <map>
+#define BS_Hashmap std::map
+#endif
+#include "kernel/memlog_on.h"
+
+#endif
diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp
new file mode 100755
index 0000000000..dd0e6437c3
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.cpp
@@ -0,0 +1,191 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "INPUTPERSISTENCEBLOCK"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "inputpersistenceblock.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+BS_InputPersistenceBlock::BS_InputPersistenceBlock(const void * Data, unsigned int DataLength) :
+ m_Data(static_cast<const unsigned char *>(Data), static_cast<const unsigned char *>(Data) + DataLength),
+ m_ErrorState(NONE)
+{
+ m_Iter = m_Data.begin();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_InputPersistenceBlock::~BS_InputPersistenceBlock()
+{
+ if (m_Iter != m_Data.end()) BS_LOG_WARNINGLN("Persistence block was not read to the end.");
+}
+
+// -----------------------------------------------------------------------------
+// Reading
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(signed int & Value)
+{
+ if (CheckMarker(SINT_MARKER))
+ {
+ RawRead(&Value, sizeof(signed int));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ }
+ else
+ {
+ Value = 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(unsigned int & Value)
+{
+ if (CheckMarker(UINT_MARKER))
+ {
+ RawRead(&Value, sizeof(unsigned int));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ }
+ else
+ {
+ Value = 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(float & Value)
+{
+ if (CheckMarker(FLOAT_MARKER))
+ {
+ RawRead(&Value, sizeof(float));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ }
+ else
+ {
+ Value = 0.0f;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(bool & Value)
+{
+ if (CheckMarker(BOOL_MARKER))
+ {
+ unsigned int UIntBool;
+ RawRead(&UIntBool, sizeof(float));
+ UIntBool = ConvertEndianessFromStorageToSystem(UIntBool);
+ Value = UIntBool == 0 ? false : true;
+ }
+ else
+ {
+ Value = 0.0f;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(std::string & Value)
+{
+ Value = "";
+
+ if (CheckMarker(STRING_MARKER))
+ {
+ unsigned int Size;
+ Read(Size);
+
+ if (CheckBlockSize(Size))
+ {
+ Value = std::string(reinterpret_cast<const char *>(&*m_Iter), Size);
+ m_Iter += Size;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::Read(vector<unsigned char> & Value)
+{
+ if (CheckMarker(BLOCK_MARKER))
+ {
+ unsigned int Size;
+ Read(Size);
+
+ if (CheckBlockSize(Size))
+ {
+ Value = vector<unsigned char>(m_Iter, m_Iter + Size);
+ m_Iter += Size;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_InputPersistenceBlock::RawRead(void * DestPtr, size_t Size)
+{
+ if (CheckBlockSize(Size))
+ {
+ memcpy(DestPtr, &*m_Iter, Size);
+ m_Iter += Size;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_InputPersistenceBlock::CheckBlockSize(int Size)
+{
+ if (m_Data.end() - m_Iter >= Size)
+ {
+ return true;
+ }
+ else
+ {
+ m_ErrorState = END_OF_DATA;
+ BS_LOG_ERRORLN("Unexpected end of persistence block.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_InputPersistenceBlock::CheckMarker(unsigned char Marker)
+{
+ if (!IsGood() || !CheckBlockSize(1)) return false;
+
+ if (*m_Iter++ == Marker)
+ {
+ return true;
+ }
+ else
+ {
+ m_ErrorState = OUT_OF_SYNC;
+ BS_LOG_ERRORLN("Wrong type marker found in persistence block.");
+ return false;
+ }
+}
diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h
new file mode 100755
index 0000000000..3a1335943b
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.h
@@ -0,0 +1,71 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_INPUTPERSISTENCEBLOCK_H
+#define BS_INPUTPERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistenceblock.h"
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_InputPersistenceBlock : public BS_PersistenceBlock
+{
+public:
+ enum ErrorState
+ {
+ NONE,
+ END_OF_DATA,
+ OUT_OF_SYNC,
+ };
+
+ BS_InputPersistenceBlock(const void * Data, unsigned int DataLength);
+ virtual ~BS_InputPersistenceBlock();
+
+ void Read(signed int & Value);
+ void Read(unsigned int & Value);
+ void Read(float & Value);
+ void Read(bool & Value);
+ void Read(std::string & Value);
+ void Read(std::vector<unsigned char> & Value);
+
+ bool IsGood() const { return m_ErrorState == NONE; }
+ ErrorState GetErrorState() const { return m_ErrorState; }
+
+private:
+ bool CheckMarker(unsigned char Marker);
+ bool CheckBlockSize(int Size);
+ void RawRead(void * DestPtr, size_t Size);
+
+ std::vector<unsigned char> m_Data;
+ std::vector<unsigned char>::const_iterator m_Iter;
+ ErrorState m_ErrorState;
+};
+
+#endif
diff --git a/engines/sword25/kernel/kernel.cpp b/engines/sword25/kernel/kernel.cpp
new file mode 100755
index 0000000000..e6d8ec2872
--- /dev/null
+++ b/engines/sword25/kernel/kernel.cpp
@@ -0,0 +1,412 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+#include <psapi.h>
+#pragma comment(lib, "psapi.lib")
+
+#include <math.h>
+#include <algorithm>
+
+#include "kernel.h"
+#include "timer.h"
+#include "service_ids.h"
+
+#include "gfx/graphicengine.h"
+#include "sfx/soundengine.h"
+#include "input/inputengine.h"
+#include "package/packagemanager.h"
+#include "script/script.h"
+#include "fmv/movieplayer.h"
+#include "persistenceservice.h"
+#include "cpuinfo.h"
+
+#define BS_LOG_PREFIX "KERNEL"
+
+BS_Kernel * BS_Kernel::_Instance = 0;
+
+// Konstruktion / Destruktion
+// --------------------------
+BS_Kernel::BS_Kernel() :
+ _pWindow(NULL),
+ _Running(false),
+ _pResourceManager(NULL),
+ _InitSuccess(false)
+{
+ // TODO:
+ // Messagebox ausgeben wenn nicht gelogged werden kann -> log.txt schreibgeschützt
+ BS_LOGLN("created.");
+
+ // CPU-Daten in die Log-Datei schreiben
+ const BS_CPUInfo & CI = BS_CPUInfo::GetInstance();
+ BS_LOGLN("CPU detected (vendor name: \"%s\", CPU name: \"%s\").", CI.GetVendorString().c_str(), CI.GetCPUName().c_str());
+ BS_LOGLN("CPU features: %s%s%s%s%s.",
+ CI.IsMMXSupported() ? "MMX" : "",
+ CI.IsSSESupported() ? " SSE" : "",
+ CI.IsSSE2Supported() ? " SSE2" : "",
+ CI.Is3DNowSupported() ? " 3DNow!" : "",
+ CI.Is3DNowExtSupported() ? " 3DNow!Ext" : "");
+
+ // Sicherstellen, dass der Prozessor über MMX verfügt
+ if (!CI.IsMMXSupported())
+ {
+ BS_LOG_ERRORLN("MMX support needed.");
+ return;
+ }
+
+ // Feststellen, ob der Timer unterstützt wird.
+ if (!BS_Timer::IsTimerAvaliable())
+ {
+ BS_LOG_ERRORLN("This machine doesn't support a performance counter.");
+ return;
+ }
+
+ // Die BS_SERVICE_TABLE auslesen und kernelinterne Strukturen vorbereiten
+ for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++)
+ {
+ // Ist die Superclass schon registriert?
+ Superclass* pCurSuperclass = NULL;
+ std::vector<Superclass*>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter)
+ if ((*Iter)->GetIdentifier() == BS_SERVICE_TABLE[i].SuperclassIdentifier)
+ {
+ pCurSuperclass = *Iter;
+ break;
+ }
+
+ // Falls die Superclass noch nicht registriert war, wird dies jetzt gemacht
+ if (!pCurSuperclass)
+ _SuperclassList.push_back(new Superclass(this, BS_SERVICE_TABLE[i].SuperclassIdentifier));
+ }
+
+ // Fensterobjekt erstellen
+ _pWindow = BS_Window::CreateBSWindow(0,0,0,0,false);
+ if (!_pWindow)
+ {
+ BS_LOG_ERRORLN("Failed to create the window.");
+ }
+ else
+ BS_LOGLN("Window created.");
+
+ // Resource-Manager erstellen
+ _pResourceManager = new BS_ResourceManager(this);
+
+ // Random-Number-Generator initialisieren
+ srand(GetMilliTicks());
+
+ // Die Skriptengine initialisieren
+ // Die Skriptengine muss bereits von Kernel und nicht vom Benutzer gestartet werden, damit der Kernel seine Funktionen bei seiner Erzeugung
+ // registrieren kann.
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(NewService("script", "lua"));
+ if (!pScript || !pScript->Init())
+ {
+ _InitSuccess = false;
+ return;
+ }
+
+ // Scriptbindings des Kernels registrieren
+ if (!_RegisterScriptBindings())
+ {
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ _InitSuccess = false;
+ return;
+ }
+ BS_LOGLN("Script bindings registered.");
+
+ _InitSuccess = true;
+}
+
+BS_Kernel::~BS_Kernel()
+{
+ // Services in umgekehrter Reihenfolge der Erstellung endladen.
+ while (!_ServiceCreationOrder.empty())
+ {
+ Superclass * superclass = GetSuperclassByIdentifier(_ServiceCreationOrder.top());
+ if (superclass) superclass->DisconnectService();
+ _ServiceCreationOrder.pop();
+ }
+
+ // Superclasslist leeren
+ while (_SuperclassList.size())
+ {
+ delete _SuperclassList.back();
+ _SuperclassList.pop_back();
+ }
+
+ // Fensterobjekt freigeben
+ delete _pWindow;
+ BS_LOGLN("Window destroyed.");
+
+ // Resource-Manager freigeben
+ delete _pResourceManager;
+
+ BS_LOGLN("destroyed.");
+}
+
+// Service Methoden
+// ----------------
+BS_Kernel::Superclass::Superclass (BS_Kernel* pKernel, const std::string& Identifier) :
+ _pKernel(pKernel),
+ _Identifier(Identifier),
+ _ServiceCount(0),
+ _ActiveService(NULL)
+{
+ for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++)
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier)
+ _ServiceCount++;
+}
+
+BS_Kernel::Superclass::~Superclass()
+{
+ DisconnectService();
+}
+
+std::string BS_Kernel::Superclass::GetServiceIdentifier(unsigned int Number)
+{
+ if (Number > _ServiceCount) return NULL;
+
+ unsigned int CurServiceOrd = 0;
+ for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++)
+ {
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier)
+ if (Number == CurServiceOrd)
+ return BS_SERVICE_TABLE[i].ServiceIdentifier;
+ else
+ CurServiceOrd++;
+ }
+
+ return std::string("");
+}
+
+BS_Service* BS_Kernel::Superclass::NewService(const std::string& ServiceIdentifier)
+{
+ for (unsigned int i = 0; i < BS_SERVICE_COUNT; i++)
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier &&
+ BS_SERVICE_TABLE[i].ServiceIdentifier == ServiceIdentifier)
+ {
+ BS_Service* NewService = BS_SERVICE_TABLE[i].CreateMethod(_pKernel);
+
+ if (NewService)
+ {
+ DisconnectService();
+ BS_LOGLN("Service '%s' created from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ _ActiveService = NewService;
+ _ActiveServiceName = BS_SERVICE_TABLE[i].ServiceIdentifier;
+ return _ActiveService;
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Failed to create service '%s' from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ return NULL;
+ }
+ }
+
+ BS_LOG_ERRORLN("Service '%s' is not avaliable from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ return NULL;
+}
+
+bool BS_Kernel::Superclass::DisconnectService()
+{
+ if (_ActiveService)
+ {
+ delete _ActiveService;
+ _ActiveService = 0;
+ BS_LOGLN("Active service '%s' disconnected from superclass '%s'.", _ActiveServiceName.c_str(), _Identifier.c_str());
+ return true;
+ }
+
+ return false;
+}
+
+BS_Kernel::Superclass* BS_Kernel::GetSuperclassByIdentifier(const std::string & Identifier)
+{
+ std::vector<Superclass*>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter)
+ {
+ if ((*Iter)->GetIdentifier() == Identifier)
+ return *Iter;
+ }
+
+ // BS_LOG_ERRORLN("Superclass '%s' does not exist.", Identifier.c_str());
+ return NULL;
+}
+
+unsigned int BS_Kernel::GetSuperclassCount()
+{
+ return _SuperclassList.size();
+}
+
+std::string BS_Kernel::GetSuperclassIdentifier(unsigned int Number)
+{
+ if (Number > _SuperclassList.size()) return NULL;
+
+ unsigned int CurSuperclassOrd = 0;
+ std::vector<Superclass*>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter)
+ {
+ if (CurSuperclassOrd == Number)
+ return ((*Iter)->GetIdentifier());
+
+ CurSuperclassOrd++;
+ }
+
+ return std::string("");
+}
+
+unsigned int BS_Kernel::GetServiceCount(const std::string & SuperclassIdentifier)
+{
+ Superclass* pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return 0;
+
+ return pSuperclass->GetServiceCount();
+
+}
+
+std::string BS_Kernel::GetServiceIdentifier(const std::string & SuperclassIdentifier, unsigned int Number)
+{
+ Superclass* pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ return (pSuperclass->GetServiceIdentifier(Number));
+}
+
+BS_Service* BS_Kernel::NewService(const std::string& SuperclassIdentifier, const std::string& ServiceIdentifier)
+{
+ Superclass* pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ // Die Reihenfolge merken, in der Services erstellt werden, damit sie später in umgekehrter Reihenfolge entladen werden können.
+ _ServiceCreationOrder.push(SuperclassIdentifier);
+
+ return pSuperclass->NewService(ServiceIdentifier);
+}
+
+bool BS_Kernel::DisconnectService(const std::string& SuperclassIdentifier)
+{
+ Superclass* pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return false;
+
+ return pSuperclass->DisconnectService();
+}
+
+BS_Service* BS_Kernel::GetService(const std::string& SuperclassIdentifier)
+{
+ Superclass* pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ return (pSuperclass->GetActiveService());
+}
+
+std::string BS_Kernel::GetActiveServiceIdentifier(const std::string& SuperclassIdentifier)
+{
+ Superclass * pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier);
+ if (!pSuperclass) return std::string("");
+
+ return (pSuperclass->GetActiveServiceName());
+}
+
+// -----------------------------------------------------------------------------
+
+int BS_Kernel::GetRandomNumber(int Min, int Max)
+{
+ BS_ASSERT(Min <= Max);
+ unsigned int MaxInternal = (Min - Max + 1) < 0 ? - (Min - Max + 1) : (Min - Max + 1);
+ return (rand() % MaxInternal) + Min;
+}
+
+// Timer Methoden
+// --------------
+unsigned int BS_Kernel::GetMilliTicks()
+{
+ return BS_Timer::GetMilliTicks();
+}
+
+uint64_t BS_Kernel::GetMicroTicks()
+{
+ return BS_Timer::GetMicroTicks();
+}
+
+// Sonstige Methoden
+// -----------------
+
+size_t BS_Kernel::GetUsedMemory()
+{
+ PROCESS_MEMORY_COUNTERS pmc;
+ pmc.cb = sizeof(pmc);
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
+ {
+ return pmc.WorkingSetSize;
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Call to GetProcessMemoryInfo() failed. Error code: %d", GetLastError());
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_GraphicEngine * BS_Kernel::GetGfx()
+{
+ return static_cast<BS_GraphicEngine *>(GetService("gfx"));
+}
+
+// -----------------------------------------------------------------------------
+
+BS_SoundEngine * BS_Kernel::GetSfx()
+{
+ return static_cast<BS_SoundEngine *>(GetService("sfx"));
+}
+
+// -----------------------------------------------------------------------------
+
+BS_InputEngine * BS_Kernel::GetInput()
+{
+ return static_cast<BS_InputEngine *>(GetService("input"));
+}
+
+// -----------------------------------------------------------------------------
+
+BS_PackageManager * BS_Kernel::GetPackage()
+{
+ return static_cast<BS_PackageManager *>(GetService("package"));
+}
+
+// -----------------------------------------------------------------------------
+
+BS_ScriptEngine * BS_Kernel::GetScript()
+{
+ return static_cast<BS_ScriptEngine *>(GetService("script"));
+}
+
+// -----------------------------------------------------------------------------
+
+BS_MoviePlayer * BS_Kernel::GetFMV()
+{
+ return static_cast<BS_MoviePlayer *>(GetService("fmv"));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Kernel::Sleep(unsigned int Msecs) const
+{
+ ::Sleep(Msecs);
+}
diff --git a/engines/sword25/kernel/kernel.h b/engines/sword25/kernel/kernel.h
new file mode 100755
index 0000000000..539ce7a9fb
--- /dev/null
+++ b/engines/sword25/kernel/kernel.h
@@ -0,0 +1,344 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Kernel
+ ---------
+ Dies ist die Hauptklasse der Engine.
+ Diese Klasse erzeugt und verwaltet alle anderen Enginelemente, wie Soundengine, Graphikengine...
+ Es ist nicht notwendig alle Enginenelemente einzeln freizugeben, dieses wird von der Kernelklasse übernommen.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_KERNEL_H
+#define _BS_KERNEL_H
+
+// Includes
+#include "memlog_off.h"
+#include <vector>
+#include <stack>
+#include <string>
+#include "memlog_on.h"
+
+#include "common.h"
+#include "bs_stdint.h"
+#include "window.h"
+#include "resmanager.h"
+
+
+// Klassendefinition
+class BS_Service;
+class BS_GraphicEngine;
+class BS_ScriptEngine;
+class BS_SoundEngine;
+class BS_InputEngine;
+class BS_PackageManager;
+class BS_MoviePlayer;
+
+/**
+ @brief Dies ist die Hauptklasse der Engine.
+
+ Diese Klasse erzeugt und verwaltet alle anderen Engineelemente, wie Soundengine, Graphikengine...<br>
+ Es ist nicht notwendig alle Enginenelemente einzeln freizugeben, dieses wird von der Kernelklasse übernommen.
+*/
+class BS_Kernel
+{
+public:
+ // Fenster Methoden
+ // ----------------
+ /**
+ @brief Gibt einen Pointer auf das Fensterobjekt zurück.
+ @return Gibt einen Pointer auf das Fensterobjekt zurück.
+ */
+ BS_Window* GetWindow() {return _pWindow; }
+
+ // Service Methoden
+ // ----------------
+
+ /**
+ @brief Erzeugt einen neuen Service der angegebenen Superclass mit dem übergebenen Identifier.
+ @param SuperclassIdentifier der Name der Superclass des Services<br>
+ z.B: "sfx", "gfx", "package" ...
+ @param ServiceIdentifier der Name des Services<br>
+ Für die Superclass "sfx" könnten das z.B. "fmod" oder "directsound" sein.
+ @return Gibt einen Pointer auf den Service zurück, oder NULL wenn der Service nicht erstellt werden konnte.
+ @remark Alle Services müssen in service_ids.h eingetragen sein, sonst können sie hier nicht erstellt werden.
+ */
+ BS_Service* NewService(const std::string & SuperclassIdentifier, const std::string & ServiceIdentifier);
+ /**
+ @brief Beendet den aktuellen Service einer Superclass.
+ @param SuperclassIdentfier der Name der Superclass dessen aktiver Service beendet werden soll<br>
+ z.B: "sfx", "gfx", "package" ...
+ @return Gibt bei Erfolg true zurück und false wenn die Superclass nicht existiert oder wenn kein Service aktiv war.
+ */
+ bool DisconnectService(const std::string & SuperclassIdentifier);
+
+ /**
+ @brief Gibt einen Pointer auf das momentan aktive Serviceobjekt einer Superclass zurück.
+ @param SuperclassIdentfier der Name der Superclass<br>
+ z.B: "sfx", "gfx", "package" ...
+ @return Gibt einen Pointer auf den Service zurück, oder NULL wenn kein Service aktiv war.
+ */
+ BS_Service* GetService(const std::string& SuperclassIdentifier);
+
+ /**
+ @brief Gibt den Namen des aktuell aktiven Serviceobjektes einer Superclass zurück.
+ @param SuperclassIdentfier der Name der Superclass<br>
+ z.B: "sfx", "gfx", "package" ...
+ @return Gibt den Namen des Serviceobjektes zurück, oder einen leeren String, wenn ein Fehler aufgetreten ist.
+ */
+ std::string GetActiveServiceIdentifier(const std::string& SuperclassIdentifier);
+
+ /**
+ @brief Gibt die Anzahl der Registrierten Superclasses zurück.
+ @return Gibt die Anzahl der Registrierten Superclasses zurück.
+ */
+ unsigned int GetSuperclassCount();
+
+ // Gibt den Identifier der mit Number bezeichneten Superclass zurück
+ /**
+ @brief Gibt den Identifier einer Superclass zurück.
+ @param Number die Nummer der Superclass, dessen Bezeichner man erfahren möchte<br>
+ Hierbei ist zu beachten, dass die erste Superclass die Nummer 0 erhält. Number muss also eine Zahl zwischen
+ 0 und GetSuperclassCount() - 1 sein.
+ @return Gibt den Identifier der Superclass zurück.
+ @remark Die Anzahl der Superclasses kann man mit GetSuperclassCount() erfahren.
+ */
+ std::string GetSuperclassIdentifier(unsigned int Number);
+
+ // Gibt die Anzahl der für die mit SuperclassIdentifier bezeichneten Superclass vorhandenen
+ // Services zurück
+ /**
+ @brief Gibt die Anzahl an Services zurück, die in einer Superclass registriert sind.
+ @param SuperclassIdentifier der Name der Superclass<br>
+ z.B: "sfx", "gfx", "package" ...
+ @return Gibt die Anzahl an Services zurück, die in der Superclass registriert sind.
+ */
+ unsigned int GetServiceCount(const std::string & SuperclassIdentifier);
+
+ /**
+ @brief Gibt den Identifier eines Services in einer Superclass zurück.
+ @param SuperclassIdentifier der Name der Superclass<br>
+ z.B: "sfx", "gfx", "package" ...
+ @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
+ Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
+ 0 und GetServiceCount() - 1 sein.
+ @return Gibt den Identifier des Services zurück
+ @remark Die Anzahl der Services in einer Superclass kann man mit GetServiceCount() erfahren.
+ */
+ std::string GetServiceIdentifier(const std::string & SuperclassIdentifier, unsigned int Number);
+ /**
+ @brief Gibt die vergangene Zeit seit dem Systemstart in Millisekunden zurück.
+ */
+ unsigned int GetMilliTicks();
+ /**
+ @brief Gibt die vergangene Zeit seit dem Systemstart in Microsekunden zurück.
+ @remark Diese Methode sollte nur verwendet werden, falls GetMilliTick() für den gewünschten Einsatz zu ungenau ist.
+ */
+ uint64_t GetMicroTicks();
+ /**
+ @brief Gibt an, ob die Konstruktion erfolgreich war.
+ @return Gibt true zurück, wenn die Konstruktion erfolgreich war.
+ */
+ bool GetInitSuccess() { return _InitSuccess; }
+ /**
+ @brief Gibt einen Pointer auf den BS_ResourceManager zurück.
+ */
+ BS_ResourceManager* GetResourceManager() { return _pResourceManager; }
+ /**
+ @brief Gibt zurück wie viel Speicher von diesem Prozess belegt ist.
+ */
+ size_t GetUsedMemory();
+ /**
+ @brief Gibt eine Zufallszahl zurück.
+ @param Min der minimale Wert, den die Zufallszahl haben darf
+ @param Max der maximale Wert, den die Zufallszahl haben darf
+ @return Gibt eine Zufallszahl zurück, die zwischen Min und Max liegt.
+ */
+ int GetRandomNumber(int Min, int Max);
+ /**
+ @brief Gibt einen Pointer auf den aktiven Gfx-Service zurück oder NULL wenn kein Gfx-Service aktiv.
+ */
+ BS_GraphicEngine * GetGfx();
+ /**
+ @brief Gibt einen Pointer auf den aktiven Sfx-Service zurück oder NULL wenn kein Sfx-Service aktiv.
+ */
+ BS_SoundEngine * GetSfx();
+ /**
+ @brief Gibt einen Pointer auf den aktiven Input-Service zurück oder NULL wenn kein Input-Service aktiv.
+ */
+ BS_InputEngine * GetInput();
+ /**
+ @brief Gibt einen Pointer auf den aktiven Package-Service zurück oder NULL wenn kein Package-Service aktiv.
+ */
+ BS_PackageManager * GetPackage();
+ /**
+ @brief Gibt einen Pointer auf den aktiven Script-Service zurück oder NULL wenn kein Script-Service aktiv.
+ */
+ BS_ScriptEngine * GetScript();
+ /**
+ @brief Gibt einen Pointer auf den aktiven FMV-Service zurück oder NULL wenn kein FMV-Service aktiv.
+ */
+ BS_MoviePlayer * GetFMV();
+
+ /**
+ @brief Stoppt den Prozess für eine gewisse Zeit.
+ @param Msecs Zeit in Millisekunden, die der Prozess gestoppt werden soll.
+ @remark Es wird nicht garantiert, dass der Prozess genau nach der angegebenen Zeit wieder aktiv wird.
+ */
+ void Sleep(unsigned int Msecs) const;
+
+ /**
+ @brief Gibt das einzige Exemplar des Kernels zurück (Singleton).
+ */
+
+ static BS_Kernel * GetInstance()
+ {
+ if (!_Instance) _Instance = new BS_Kernel();
+ return _Instance;
+ }
+
+ /**
+ @brief Zerstört das einzige Exemplar des Kernels.
+ @remark Diese Methode darf nur zum Beenden des System aufgerufen werden. Nachfolgende Aufrufe sämtlicher Kernelmethoden liefern
+ undefinierte Ergebnisse.
+ */
+
+ static void DeleteInstance()
+ {
+ if (_Instance)
+ {
+ delete(_Instance);
+ _Instance = 0;
+ }
+ }
+
+ /**
+ @brief Löst eine Schutzverletzung aus.
+ @remark Diese Methode dient zum Testen des Crash-Handlings.
+ */
+ void Crash() const
+ {
+ __asm
+ {
+ xor eax, eax
+ mov [eax], 0
+ }
+ }
+
+private:
+
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // Private da Singleton
+ // -----------------------------------------------------------------------------
+
+ BS_Kernel();
+ virtual ~BS_Kernel();
+
+ // -----------------------------------------------------------------------------
+ // Singleton-Exemplar
+ // -----------------------------------------------------------------------------
+ static BS_Kernel * _Instance;
+
+ // Service Daten
+ // -------------
+ class Superclass
+ {
+ private:
+ BS_Kernel* _pKernel;
+ unsigned int _ServiceCount;
+ std::string _Identifier;
+ BS_Service* _ActiveService;
+ std::string _ActiveServiceName;
+
+ public:
+ Superclass (BS_Kernel* pKernel, const std::string& Identifier);
+ ~Superclass();
+
+ unsigned int GetServiceCount() const { return _ServiceCount; }
+ std::string GetIdentifier() const { return _Identifier; }
+ BS_Service* GetActiveService() const { return _ActiveService; }
+ std::string GetActiveServiceName() const { return _ActiveServiceName; }
+ std::string GetServiceIdentifier(unsigned int Number);
+ BS_Service* NewService(const std::string& ServiceIdentifier);
+ bool DisconnectService();
+ };
+
+ std::vector<Superclass*> _SuperclassList;
+ std::stack<std::string> _ServiceCreationOrder;
+ Superclass* GetSuperclassByIdentifier(const std::string& Identifier);
+
+ bool _InitSuccess; // Gibt an, ob die Konstruktion erfolgreich war
+ bool _Running; // Gibt an, ob die Applikation im nächsten Durchlauf des Main-Loops noch weiterlaufen soll
+
+ // Fenster Variablen
+ // -----------------
+ BS_Window* _pWindow;
+
+ /*
+ // CPU-Feature Variablen und Methoden
+ // ----------------------------------
+ enum _CPU_FEATURES_BITMASKS
+ {
+ _MMX_BITMASK = (1 << 23),
+ _SSE_BITMASK = (1 << 25),
+ _SSE2_BITMASK = (1 << 26),
+ _3DNOW_BITMASK = (1 << 30),
+ _3DNOWEXT_BITMASK = (1 << 31)
+ };
+
+ bool _DetectCPU();
+
+ bool _MMXPresent;
+ bool _SSEPresent;
+ bool _SSE2Present;
+ bool _3DNowPresent;
+ bool _3DNowExtPresent;
+ CPU_TYPES _CPUType;
+ std::string _CPUVendorID;
+ */
+
+ // Resourcemanager
+ // ---------------
+ BS_ResourceManager* _pResourceManager;
+
+ bool _RegisterScriptBindings();
+};
+
+// Dies ist nur eine kleine Klasse, die die Daten eines Services verwaltet.
+// Ist ein wenig unschön, ich weiss, aber mit std::string ließ sich dass nicht mit einer
+// einfachen struct bewerkstelligen.
+class BS_ServiceInfo
+{
+public:
+ BS_ServiceInfo(const std::string& SuperclassIdentifier, const std::string& ServiceIdentifier, BS_Service* (*CreateMethod)(BS_Kernel*))
+ {
+ this->SuperclassIdentifier = SuperclassIdentifier;
+ this->ServiceIdentifier = ServiceIdentifier;
+ this->CreateMethod = CreateMethod;
+ };
+
+ std::string SuperclassIdentifier;
+ std::string ServiceIdentifier;
+ BS_Service* (*CreateMethod)(BS_Kernel*);
+};
+
+#endif
diff --git a/engines/sword25/kernel/kernel_script.cpp b/engines/sword25/kernel/kernel_script.cpp
new file mode 100755
index 0000000000..4436e05e55
--- /dev/null
+++ b/engines/sword25/kernel/kernel_script.cpp
@@ -0,0 +1,775 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common.h"
+#include "kernel.h"
+#include "filesystemutil.h"
+#include "window.h"
+#include "resmanager.h"
+#include "persistenceservice.h"
+#include "wincodegenerator.h"
+#include "debug/debugtools.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+
+// -----------------------------------------------------------------------------
+
+static int DisconnectService(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushboolean(L, pKernel->DisconnectService(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetActiveServiceIdentifier(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetActiveServiceIdentifier(luaL_checkstring(L,1)).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSuperclassCount(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetSuperclassCount());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSuperclassIdentifier(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetSuperclassIdentifier(static_cast<unsigned int>(luaL_checknumber(L,1))).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetServiceCount(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetServiceCount(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetServiceIdentifier(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetServiceIdentifier(luaL_checkstring(L, 1), static_cast<unsigned int>(luaL_checknumber(L, 2))).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMilliTicks(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetMilliTicks());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetTimer(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, static_cast<lua_Number>(pKernel->GetMicroTicks()) / 1000000.0);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StartService(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushbooleancpp(L, pKernel->NewService(luaL_checkstring(L, 1), luaL_checkstring(L, 2)) != NULL);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Sleep(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ pKernel->Sleep(static_cast<unsigned int>(luaL_checknumber(L, 1) * 1000));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Crash(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ pKernel->Crash();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ExecuteFile(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pSE = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pSE);
+
+ lua_pushbooleancpp(L, pSE->ExecuteFile(luaL_checkstring(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetUserdataDirectory(lua_State * L)
+{
+ lua_pushstring(L, BS_FileSystemUtil::GetInstance().GetUserdataDirectory().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetPathSeparator(lua_State * L)
+{
+ lua_pushstring(L, BS_FileSystemUtil::GetInstance().GetPathSeparator().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int FileExists(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_FileSystemUtil::GetInstance().FileExists(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int CreateDirectory(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_FileSystemUtil::GetInstance().CreateDirectory(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetWinCode(lua_State * L)
+{
+ lua_pushstring(L, BS_WinCodeGenerator::GetWinCode().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSubversionRevision(lua_State * L)
+{
+ lua_pushnumber(L, BS_Debugtools::GetSubversionRevision());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetUsedMemory(lua_State * L)
+{
+ lua_pushnumber(L, BS_Kernel::GetInstance()->GetUsedMemory());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * KERNEL_LIBRARY_NAME = "Kernel";
+
+static const luaL_reg KERNEL_FUNCTIONS[] =
+{
+ "DisconnectService", DisconnectService,
+ "GetActiveServiceIdentifier", GetActiveServiceIdentifier,
+ "GetSuperclassCount", GetSuperclassCount,
+ "GetSuperclassIdentifier", GetSuperclassIdentifier,
+ "GetServiceCount", GetServiceCount,
+ "GetServiceIdentifier", GetServiceIdentifier,
+ "GetMilliTicks", GetMilliTicks,
+ "GetTimer", GetTimer,
+ "StartService", StartService,
+ "Sleep", Sleep,
+ "Crash", Crash,
+ "ExecuteFile", ExecuteFile,
+ "GetUserdataDirectory", GetUserdataDirectory,
+ "GetPathSeparator", GetPathSeparator,
+ "FileExists", FileExists,
+ "CreateDirectory", CreateDirectory,
+ "GetWinCode", GetWinCode,
+ "GetSubversionRevision", GetSubversionRevision,
+ "GetUsedMemory", GetUsedMemory,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static int IsVisible(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->IsVisible());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetVisible(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetVisible(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetX(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetY(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetX(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetX(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetY(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetY(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetClientX(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetClientX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetClientY(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetClientY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetWidth(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetHeight(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetWidth(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetWidth(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetHeight(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetHeight(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetTitle(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushstring(L, pWindow->GetTitle().c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetTitle(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetTitle(luaL_checkstring(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ProcessMessages(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->ProcessMessages());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int CloseWanted(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->CloseWanted());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WaitForFocus(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->WaitForFocus());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int HasFocus(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_Window * pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->HasFocus());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * WINDOW_LIBRARY_NAME = "Window";
+
+static const luaL_reg WINDOW_FUNCTIONS[] =
+{
+ "IsVisible", IsVisible,
+ "SetVisible", SetVisible,
+ "GetX", GetX,
+ "SetX", SetX,
+ "GetY", GetY,
+ "SetY", SetY,
+ "GetClientX", GetClientX,
+ "GetClientY", GetClientY,
+ "GetWidth", GetWidth,
+ "GetHeight", GetHeight,
+ "SetWidth", SetWidth,
+ "SetHeight", SetHeight,
+ "GetTitle", GetTitle,
+ "SetTitle", SetTitle,
+ "ProcessMessages", ProcessMessages,
+ "CloseWanted", CloseWanted,
+ "WaitForFocus", WaitForFocus,
+ "HasFocus", HasFocus,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static int PrecacheResource(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ForcePrecacheResource(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1), true));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMaxMemoryUsage(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushnumber(L, pResource->GetMaxMemoryUsage());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMaxMemoryUsage(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->SetMaxMemoryUsage(static_cast<unsigned int>(lua_tonumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int EmptyCache(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->EmptyCache();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLogCacheMiss(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->IsLogCacheMiss());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetLogCacheMiss(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->SetLogCacheMiss(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int DumpLockedResources(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ResourceManager * pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->DumpLockedResources();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * RESOURCE_LIBRARY_NAME = "Resource";
+
+static const luaL_reg RESOURCE_FUNCTIONS[] =
+{
+ "PrecacheResource", PrecacheResource,
+ "ForcePrecacheResource", ForcePrecacheResource,
+ "GetMaxMemoryUsage", GetMaxMemoryUsage,
+ "SetMaxMemoryUsage", SetMaxMemoryUsage,
+ "EmptyCache", EmptyCache,
+ "IsLogCacheMiss", IsLogCacheMiss,
+ "SetLogCacheMiss", SetLogCacheMiss,
+ "DumpLockedResources", DumpLockedResources,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static int ReloadSlots(lua_State * L)
+{
+ BS_PersistenceService::GetInstance().ReloadSlots();
+ lua_pushnil(L);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSlotCount(lua_State * L)
+{
+ lua_pushnumber(L, BS_PersistenceService::GetInstance().GetSlotCount());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSlotOccupied(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().IsSlotOccupied(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameDirectory(lua_State * L)
+{
+ lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameDirectory().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSavegameCompatible(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().IsSavegameCompatible(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameDescription(lua_State * L)
+{
+ lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameDescription(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1).c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameFilename(lua_State * L)
+{
+ lua_pushstring(L, BS_PersistenceService::GetInstance().GetSavegameFilename(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1).c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int LoadGame(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().LoadGame(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveGame(lua_State * L)
+{
+ lua_pushbooleancpp(L, BS_PersistenceService::GetInstance().SaveGame(static_cast<unsigned int>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * PERSISTENCE_LIBRARY_NAME = "Persistence";
+
+static const luaL_reg PERSISTENCE_FUNCTIONS[] =
+{
+ "ReloadSlots", ReloadSlots,
+ "GetSlotCount", GetSlotCount,
+ "IsSlotOccupied", IsSlotOccupied,
+ "GetSavegameDirectory", GetSavegameDirectory,
+ "IsSavegameCompatible", IsSavegameCompatible,
+ "GetSavegameDescription", GetSavegameDescription,
+ "GetSavegameFilename", GetSavegameFilename,
+ "LoadGame", LoadGame,
+ "SaveGame", SaveGame,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_Kernel::_RegisterScriptBindings()
+{
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, KERNEL_LIBRARY_NAME, KERNEL_FUNCTIONS)) return false;
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, WINDOW_LIBRARY_NAME, WINDOW_FUNCTIONS)) return false;
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, RESOURCE_LIBRARY_NAME, RESOURCE_FUNCTIONS)) return false;
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, PERSISTENCE_LIBRARY_NAME, PERSISTENCE_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/kernel/log.cpp b/engines/sword25/kernel/log.cpp
new file mode 100755
index 0000000000..bf1d5147b1
--- /dev/null
+++ b/engines/sword25/kernel/log.cpp
@@ -0,0 +1,224 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string>
+
+#include "filesystemutil.h"
+#include "log.h"
+#include "debug/debugtools.h"
+
+// Konstanten
+static const char* BF_LOG_FILENAME = "log.txt";
+static const size_t LOG_BUFFERSIZE = 1024 * 16;
+
+// Logging soll nur stattfinden wenn es aktiviert ist
+#ifdef BS_ACTIVATE_LOGGING
+
+FILE* BS_Log::_LogFile = NULL;
+bool BS_Log::_LineBegin = true;
+const char* BS_Log::_Prefix = NULL;
+const char* BS_Log::_File = NULL;
+int BS_Log::_Line = 0;
+bool BS_Log::_AutoNewline = false;
+std::vector<BS_Log::LOG_LISTENER_CALLBACK> BS_Log::_LogListener;
+
+bool BS_Log::_CreateLog()
+{
+ // Logfile öffnen
+ BS_FileSystemUtil::GetInstance().CreateDirectory(BS_FileSystemUtil::GetInstance().GetUserdataDirectory());
+ _LogFile = fopen((BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + "\\" + BF_LOG_FILENAME).c_str(), "w");
+
+ if (_LogFile)
+ {
+ // Sicherstellen, dass es beim Beenden geschlossen wird
+ atexit(_CloseLog);
+
+ // Titelzeile in das Logfile schreiben
+ Log("Broken Sword 2.5 Engine - Build: %s - %s - VersionID: %s\n", __DATE__, __TIME__, BS_Debugtools::GetVersionID());
+ Log("-----------------------------------------------------------------------------------------------------\n");
+
+ return true;
+ }
+
+ // Log-File konnte nicht erstellt werden
+ return false;
+}
+
+void BS_Log::_CloseLog()
+{
+ if (_LogFile) fclose(_LogFile);
+}
+
+void BS_Log::Log(const char* Format, ...)
+{
+ char Message[LOG_BUFFERSIZE];
+
+ // Nachricht erzeugen
+ va_list ArgList;
+ va_start(ArgList, Format);
+ _vsnprintf(Message, sizeof(Message), Format, ArgList);
+
+ // Nachricht loggen
+ _WriteLog(Message);
+
+ _FlushLog();
+}
+
+void BS_Log::LogPrefix(const char* Prefix, const char* Format, ...)
+{
+ char Message[LOG_BUFFERSIZE];
+ char ExtFormat[LOG_BUFFERSIZE];
+
+ // Falls die Ausgabe am Anfang einer neuen Zeile aufgehört hat, muss die neue Ausgabe mit dem Präfix
+ // beginnen
+ ExtFormat[0] = 0;
+ if (_LineBegin)
+ {
+ _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
+ _LineBegin = false;
+ }
+ // Formatstring zeilenweise durchgehen und an jeden Zeilenanfang das Präfix setzen
+ for (;;)
+ {
+ const char* NextLine = strstr(Format, "\n");
+ if (!NextLine || *(NextLine + strlen("\n")) == 0)
+ {
+ _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s", ExtFormat, Format);
+ if (NextLine) _LineBegin = true;
+ break;
+ }
+ else
+ {
+ strncat(ExtFormat, Format, (NextLine - Format) + strlen("\n"));
+ _snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
+ }
+
+ Format = NextLine + strlen("\n");
+ }
+
+ // Nachricht erzeugen
+ va_list ArgList;
+ va_start(ArgList, Format);
+ _vsnprintf(Message, sizeof(Message), ExtFormat, ArgList);
+
+ // Nachricht schreiben
+ _WriteLog(Message);
+
+ _FlushLog();
+}
+
+void BS_Log::LogDecorated(const char* Format, ...)
+{
+ // Nachricht erzeugen
+ char Message[LOG_BUFFERSIZE];
+ va_list ArgList;
+ va_start(ArgList, Format);
+ _vsnprintf(Message, sizeof(Message), Format, ArgList);
+
+ // Zweiten Prefix erzeugen, falls gewünscht
+ char SecondaryPrefix[1024];
+ if (_File && _Line)
+ _snprintf(SecondaryPrefix, sizeof(SecondaryPrefix), "(file: %s, line: %d) - ", _File, _Line);
+
+ // Nachricht zeilenweise ausgeben und an jeden Zeilenanfang das Präfix setzen
+ char* MessageWalker = Message;
+ for (;;)
+ {
+ char* NextLine = strstr(MessageWalker, "\n");
+ if (NextLine)
+ {
+ *NextLine = 0;
+ if (_LineBegin)
+ {
+ _WriteLog(_Prefix);
+ if (_File && _Line)
+ _WriteLog(SecondaryPrefix);
+ }
+ _WriteLog(MessageWalker);
+ _WriteLog("\n");
+ MessageWalker = NextLine + sizeof("\n") - 1;
+ _LineBegin = true;
+ }
+ else
+ {
+ if (_LineBegin)
+ {
+ _WriteLog(_Prefix);
+ if (_File && _Line)
+ _WriteLog(SecondaryPrefix);
+ }
+ _WriteLog(MessageWalker);
+ _LineBegin = false;
+ break;
+ }
+ }
+
+ // Falls gewünscht, wird ans Ende der Nachricht automatisch ein Newline angehängt.
+ if (_AutoNewline)
+ {
+ _WriteLog("\n");
+ _LineBegin = true;
+ }
+
+ // Pseudoparameter zurücksetzen
+ _Prefix = NULL;
+ _File = 0;
+ _Line = 0;
+ _AutoNewline = false;
+
+ _FlushLog();
+}
+
+int BS_Log::_WriteLog(const char* Message)
+{
+ if (!_LogFile) if (!_CreateLog()) return false;
+
+ std::vector<LOG_LISTENER_CALLBACK>::iterator Iter = _LogListener.begin();
+ for (; Iter != _LogListener.end(); ++Iter)
+ (*Iter)(Message);
+
+ fprintf(_LogFile, Message);
+
+ return true;
+}
+
+void BS_Log::_FlushLog()
+{
+ fflush(_LogFile);
+}
+
+void (*BS_LogPtr)(const char *, ...) = BS_Log::Log;
+extern "C"
+{
+ void BS_Log_C(const char* Message)
+ {
+ BS_LogPtr(Message);
+ }
+}
+
+#else
+
+extern "C"
+{
+ void BS_Log_C(const char* Message) {};
+}
+
+#endif
diff --git a/engines/sword25/kernel/log.h b/engines/sword25/kernel/log.h
new file mode 100755
index 0000000000..867b19d70e
--- /dev/null
+++ b/engines/sword25/kernel/log.h
@@ -0,0 +1,123 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_LOG_H
+#define BS_LOG_H
+
+// Includes
+#include "memlog_off.h"
+#include <stdio.h>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include "memlog_on.h"
+
+#include "common.h"
+
+// Logging soll nur stattfinden wenn es aktiviert ist
+#ifdef BS_ACTIVATE_LOGGING
+
+// Logging-Makros
+#define BS_LOG BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::LogDecorated
+#define BS_LOGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_WARNING BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::LogDecorated
+#define BS_LOG_WARNINGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_ERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::LogDecorated
+#define BS_LOG_ERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_EXTERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::LogDecorated
+#define BS_LOG_EXTERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+
+// Die Version der Logging-Klasse mit aktiviertem Logging
+class BS_Log
+{
+public:
+ static void Clear();
+ static void Log(const char* Format, ...);
+ static void LogPrefix(const char* Prefix, const char* Format, ...);
+ static void LogDecorated(const char* Format, ...);
+
+ static void SetPrefix(const char* Prefix) { _Prefix = Prefix; }
+ static void SetFile(const char* File) { _File = File; }
+ static void SetLine(int Line) { _Line = Line; }
+ static void SetAutoNewline(bool AutoNewline) { _AutoNewline = AutoNewline; }
+
+ typedef void (*LOG_LISTENER_CALLBACK)(const char *);
+ static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) { _LogListener.push_back(Callback); }
+ static bool IsListenerRegistered(LOG_LISTENER_CALLBACK Callback) { return std::find(_LogListener.begin(), _LogListener.end(), Callback) != _LogListener.end(); }
+
+private:
+ static FILE* _LogFile;
+ static bool _LineBegin;
+ static const char* _Prefix;
+ static const char* _File;
+ static int _Line;
+ static bool _AutoNewline;
+ static std::vector<LOG_LISTENER_CALLBACK> _LogListener;
+
+ static bool _CreateLog();
+ static void _CloseLog();
+
+ static int _WriteLog(const char* Message);
+ static void _FlushLog();
+};
+
+// Hilfsfunktion, die es C-Funktionen ermöglicht zu loggen (wird für Lua gebraucht).
+extern "C"
+{
+ void BS_Log_C(const char* Message);
+}
+
+#else
+
+// Logging-Makros
+#define BS_LOG
+#define BS_LOGLN
+#define BS_LOG_WARNING
+#define BS_LOG_WARNINGLN
+#define BS_LOG_ERROR
+#define BS_LOG_ERRORLN
+#define BS_LOG_EXTERROR
+#define BS_LOG_EXTERRORLN
+
+// Die Version der Logging-Klasse mit deaktiviertem Logging
+class BS_Log
+{
+public:
+ // Die Log Funktionen werden zu do-nothing Funktionen und wird daher vom Compiler (hoffentlich) rausoptimiert
+ static void Log(const char* Text, ...) {};
+ static void LogPrefix(const char* Prefix, const char* Format, ...) {};
+ static void LogDecorated(const char* Format, ...) {};
+
+ static void SetPrefix(const char* Prefix) {};
+ static void SetFile(const char* File) {};
+ static void SetLine(int Line) {};
+ static void SetAutoNewline(bool AutoNewline) {};
+
+ typedef void (*LOG_LISTENER_CALLBACK)(const char *);
+ static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {};
+};
+
+extern "C"
+{
+ void BS_Log_C(const char* Message);
+}
+
+#endif
+
+#endif
diff --git a/engines/sword25/kernel/md5.cpp b/engines/sword25/kernel/md5.cpp
new file mode 100755
index 0000000000..a336f3ab61
--- /dev/null
+++ b/engines/sword25/kernel/md5.cpp
@@ -0,0 +1,385 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// The code in this file is based in part on code from Aladdin
+// Enterprises released under the following terms:
+//
+// Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+//
+// 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.
+//
+// L. Peter Deutsch
+// ghost@aladdin.com
+
+#include "md5.h"
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+BS_MD5::BS_MD5()
+{
+ md5_init(&m_state);
+}
+
+void BS_MD5::Update(const unsigned char * Buffer, unsigned int Length)
+{
+ md5_append(&m_state, Buffer, Length);
+}
+
+void BS_MD5::GetDigest(unsigned char Digest[16])
+{
+ md5_finish(&m_state, Digest);
+}
diff --git a/engines/sword25/kernel/md5.h b/engines/sword25/kernel/md5.h
new file mode 100755
index 0000000000..d2ac60afc8
--- /dev/null
+++ b/engines/sword25/kernel/md5.h
@@ -0,0 +1,56 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_MD5_H
+#define BS_MD5_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/bs_stdint.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+typedef uint8_t md5_byte_t; /* 8-bit byte */
+typedef uint32_t md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+class BS_MD5
+{
+public:
+ BS_MD5();
+
+ void Update(const unsigned char * Buffer, unsigned int Length);
+ void GetDigest(unsigned char Digest[16]);
+
+private:
+ md5_state_t m_state;
+};
+
+#endif
diff --git a/engines/sword25/kernel/memleaks.cpp b/engines/sword25/kernel/memleaks.cpp
new file mode 100755
index 0000000000..646465f5b2
--- /dev/null
+++ b/engines/sword25/kernel/memleaks.cpp
@@ -0,0 +1,130 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifdef BS_MEMLOG
+
+// Die folgende Zeile stellt sicher, dass alle Objekte in dieser Datei vor allen anderen erstellt und nach allen anderen
+// zerstört werden.
+// Damit wird sichergestellt, dass z.B. Singletons nicht fälschlicherweise als Memory-Leaks erkannt werden.
+// TODO Visual C++ 8 kommt mit der aktuellen Implementation nicht klar und stürzt sowohl beim Start aus auch beim
+// Beenden ab. Es muss eine Alternativimplementation her. An sichersten ist es wohl, wenn gar keine STL-Objekte benutzt
+// werden.
+// #pragma warning (disable : 4074)
+// #pragma init_seg(compiler)
+
+#include "filesystemutil.h"
+
+#include "memlog_off.h"
+#include <vector>
+#include <algorithm>
+#include <string>
+
+#include <stdio.h>
+#include <string.h>
+
+typedef struct
+{
+ unsigned int address;
+ unsigned int size;
+ std::string file;
+ unsigned int line;
+} ALLOC_INFO;
+
+static const char * MEMLEAK_LOG_FILE = "memory_leaks.txt";
+static const unsigned int BUCKET_COUNT = 1021;
+std::vector< std::vector<ALLOC_INFO> > TrackData(BUCKET_COUNT);
+
+static unsigned int TotalSize = 0;
+
+// Diese Klasse stellt sicher, dass beim Programmende, das Memory-Leak Log geschrieben wird.
+static class LeakDumper
+{
+public:
+ LeakDumper() : OutputFilename(BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + "\\" + MEMLEAK_LOG_FILE)
+ {
+ // Sicherstellen, dass das Ausgabeverzeichnis für die Datei existiert.
+ BS_FileSystemUtil::GetInstance().CreateDirectory(BS_FileSystemUtil::GetInstance().GetUserdataDirectory());
+ }
+
+ ~LeakDumper()
+ {
+ DumpUnfreed(OutputFilename.c_str());
+ }
+
+ std::string OutputFilename;
+} LeakDumperInstance;
+
+void DumpUnfreed(const char * OutputFilename)
+{
+ FILE * Log = fopen(OutputFilename, "w");
+ fputs("MEMORY LEAK REPORT:\n----------------------\n", Log);
+ std::vector< std::vector<ALLOC_INFO> >::iterator BucketIter = TrackData.begin();
+ for (; BucketIter != TrackData.end(); ++BucketIter)
+ {
+ std::vector<ALLOC_INFO>::iterator Iter = (*BucketIter).begin();
+ for (; Iter != (*BucketIter).end(); ++Iter)
+ {
+ ALLOC_INFO & CurItem = (*Iter);
+ fprintf(Log, "%-50s LINE:%d ADDRESS:0x%x SIZE:%d\n",
+ CurItem.file.c_str(),
+ CurItem.line,
+ CurItem.address,
+ CurItem.size);
+ }
+ }
+
+ fprintf(Log, "----------------------\nTotal unfreed bytes: %d\n", TotalSize);
+
+ fclose(Log);
+}
+
+void AddTrack(unsigned int addr, unsigned int asize, const char *fname, unsigned int lnum)
+{
+ std::vector<ALLOC_INFO> & CurBucket = TrackData[(addr >> 3) % BUCKET_COUNT];
+ ALLOC_INFO Info;
+ Info.address = addr;
+ Info.size = asize;
+ Info.file = fname;
+ Info.line = lnum;
+ CurBucket.push_back(Info);
+
+ TotalSize += asize;
+}
+
+void RemoveTrack(unsigned int addr)
+{
+ if (addr != 0 && TrackData.size() == BUCKET_COUNT)
+ {
+ std::vector<ALLOC_INFO> & CurBucket = TrackData[(addr >> 3) % BUCKET_COUNT];
+ std::vector<ALLOC_INFO>::iterator Iter = CurBucket.begin();
+ for (; Iter != CurBucket.end(); ++Iter)
+ {
+ if ((*Iter).address == addr)
+ {
+ TotalSize -= (*Iter).size;
+
+ std::swap(*Iter, CurBucket.back());
+ CurBucket.pop_back();
+ return;
+ }
+ }
+ }
+}
+
+#endif
diff --git a/engines/sword25/kernel/memleaks.h b/engines/sword25/kernel/memleaks.h
new file mode 100755
index 0000000000..c4193e3b74
--- /dev/null
+++ b/engines/sword25/kernel/memleaks.h
@@ -0,0 +1,60 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BF_MEMLEAKS_H
+#define BF_MEMLEAKS_H
+
+#ifdef BS_MEMLOG
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4291)
+#endif
+
+#include "memlog_off.h"
+
+#include <malloc.h>
+
+void DumpUnfreed(const char * OutputFilename);
+void AddTrack(unsigned int addr, unsigned int asize, const char *fname, unsigned int lnum);
+void RemoveTrack(unsigned int addr);
+
+inline void * __cdecl operator new(unsigned int size, const char *file, int line)
+{
+ void *ptr = malloc(size);
+ if (ptr) AddTrack((unsigned int)ptr, size, file, line);
+ return(ptr);
+};
+
+inline void __cdecl operator delete(void *p)
+{
+ RemoveTrack((unsigned int)p);
+ free(p);
+};
+
+inline void __cdecl operator delete[](void *p)
+{
+ RemoveTrack((unsigned int)p);
+ free(p);
+};
+
+#endif
+
+#include "memlog_on.h"
+
+#endif
diff --git a/engines/sword25/kernel/memlog_off.h b/engines/sword25/kernel/memlog_off.h
new file mode 100755
index 0000000000..81acac991f
--- /dev/null
+++ b/engines/sword25/kernel/memlog_off.h
@@ -0,0 +1,27 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// Deaktivieren der Memory-Leak Detektion
+
+#ifdef BS_MEMLOG
+ #ifdef new
+ #undef new
+ #undef DEBUG_NEW
+ #endif
+#endif
diff --git a/engines/sword25/kernel/memlog_on.h b/engines/sword25/kernel/memlog_on.h
new file mode 100755
index 0000000000..7dbdd81330
--- /dev/null
+++ b/engines/sword25/kernel/memlog_on.h
@@ -0,0 +1,27 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// Aktivieren der Memory-Leak Detektion
+
+#ifdef BS_MEMLOG
+ #ifndef DEBUG_NEW
+ #define DEBUG_NEW new(__FILE__, __LINE__)
+ #endif
+ #define new DEBUG_NEW
+#endif
diff --git a/engines/sword25/kernel/objectregistry.h b/engines/sword25/kernel/objectregistry.h
new file mode 100755
index 0000000000..e1c80c8b36
--- /dev/null
+++ b/engines/sword25/kernel/objectregistry.h
@@ -0,0 +1,182 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OBJECTREGISTRY_H
+#define BS_OBJECTREGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/hashmap.h"
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+template<typename T>
+class BS_ObjectRegistry
+{
+public:
+ BS_ObjectRegistry() : m_NextHandle(1) {};
+
+ // -------------------------------------------------------------------------
+
+ unsigned int RegisterObject(T * ObjectPtr)
+ {
+ // Null-Pointer können nicht registriert werden.
+ if (ObjectPtr == 0)
+ {
+ LogErrorLn("Cannot register a null pointer.");
+ return 0;
+ }
+
+ // Falls das Objekt bereits registriert wurde, wird eine Warnung ausgeben und das Handle zurückgeben.
+ unsigned int Handle = FindHandleByPtr(ObjectPtr);
+ if (Handle != 0)
+ {
+ LogWarningLn("Tried to register a object that was already registered.");
+ return Handle;
+ }
+ // Ansonsten wird das Objekt in beide Maps eingetragen und das neue Handle zurückgeben.
+ else
+ {
+ m_Handle2PtrMap[m_NextHandle] = ObjectPtr;
+ m_Ptr2HandleMap[ObjectPtr] = m_NextHandle;
+
+ return m_NextHandle++;
+ }
+ }
+
+ // -----------------------------------------------------------------------------
+
+ unsigned int RegisterObject(T * ObjectPtr, unsigned int Handle)
+ {
+ // Null-Pointer und Null-Handle können nicht registriert werden.
+ if (ObjectPtr == 0 || Handle == 0)
+ {
+ LogErrorLn("Cannot register a null pointer or a null handle.");
+ return 0;
+ }
+
+ // Falls das Objekt bereits registriert wurde, wird ein Fehler ausgegeben und 0 zurückgeben.
+ unsigned int HandleTest = FindHandleByPtr(ObjectPtr);
+ if (HandleTest != 0)
+ {
+ LogErrorLn("Tried to register a object that was already registered.");
+ return 0;
+ }
+ // Falls das Handle bereits vergeben ist, wird ein Fehler ausgegeben und 0 zurückgegeben.
+ else if (FindPtrByHandle(Handle) != 0)
+ {
+ LogErrorLn("Tried to register a handle that is already taken.");
+ return 0;
+ }
+ // Ansonsten wird das Objekt in beide Maps eingetragen und das gewünschte Handle zurückgeben.
+ else
+ {
+ m_Handle2PtrMap[Handle] = ObjectPtr;
+ m_Ptr2HandleMap[ObjectPtr] = Handle;
+
+ // Falls das vergebene Handle größer oder gleich dem nächsten automatische vergebenen Handle ist, wird das nächste automatisch
+ // vergebene Handle erhöht.
+ if (Handle >= m_NextHandle) m_NextHandle = Handle + 1;
+
+ return Handle;
+ }
+ }
+
+ // -----------------------------------------------------------------------------
+
+ void DeregisterObject(T * ObjectPtr)
+ {
+ unsigned int Handle = FindHandleByPtr(ObjectPtr);
+
+ if (Handle != 0)
+ {
+ // Registriertes Objekt aus beiden Maps entfernen.
+ m_Handle2PtrMap.erase(FindHandleByPtr(ObjectPtr));
+ m_Ptr2HandleMap.erase(ObjectPtr);
+ }
+ else
+ {
+ LogWarningLn("Tried to remove a object that was not registered.");
+ }
+ }
+
+ // -----------------------------------------------------------------------------
+
+ T * ResolveHandle(unsigned int Handle)
+ {
+ // Zum Handle gehöriges Objekt in der Hash-Map finden.
+ T * ObjectPtr = FindPtrByHandle(Handle);
+
+ // Pointer zurückgeben. Im Fehlerfall ist dieser 0.
+ return ObjectPtr;
+ }
+
+ // -----------------------------------------------------------------------------
+
+ unsigned int ResolvePtr(T * ObjectPtr)
+ {
+ // Zum Pointer gehöriges Handle in der Hash-Map finden.
+ unsigned int Handle = FindHandleByPtr(ObjectPtr);
+
+ // Handle zurückgeben. Im Fehlerfall ist dieses 0.
+ return Handle;
+ }
+
+protected:
+ typedef BS_Hashmap<unsigned int, T *> HANDLE2PTR_MAP;
+ typedef BS_Hashmap<T *, unsigned int> PTR2HANDLE_MAP;
+
+ HANDLE2PTR_MAP m_Handle2PtrMap;
+ PTR2HANDLE_MAP m_Ptr2HandleMap;
+ unsigned int m_NextHandle;
+
+ // -----------------------------------------------------------------------------
+
+ T * FindPtrByHandle(unsigned int Handle)
+ {
+ // Zum Handle gehörigen Pointer finden.
+ HANDLE2PTR_MAP::const_iterator it = m_Handle2PtrMap.find(Handle);
+
+ // Pointer zurückgeben, oder, falls keiner gefunden wurde, 0 zurückgeben.
+ return (it != m_Handle2PtrMap.end()) ? it->second : 0;
+ }
+
+ // -----------------------------------------------------------------------------
+
+ unsigned int FindHandleByPtr(T * ObjectPtr)
+ {
+ // Zum Pointer gehöriges Handle finden.
+ PTR2HANDLE_MAP::const_iterator it = m_Ptr2HandleMap.find(ObjectPtr);
+
+ // Handle zurückgeben, oder, falls keines gefunden wurde, 0 zurückgeben.
+ return (it != m_Ptr2HandleMap.end()) ? it->second : 0;
+ }
+
+ // -----------------------------------------------------------------------------
+
+ virtual void LogErrorLn(const char * Message) const = 0;
+ virtual void LogWarningLn(const char * Message) const = 0;
+};
+
+#endif
diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp
new file mode 100755
index 0000000000..37410caaec
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.cpp
@@ -0,0 +1,123 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "OUTPUTPERSISTENCEBLOCK"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "outputpersistenceblock.h"
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const unsigned int INITIAL_BUFFER_SIZE = 1024 * 64;
+}
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+BS_OutputPersistenceBlock::BS_OutputPersistenceBlock()
+{
+ m_Data.reserve(INITIAL_BUFFER_SIZE);
+}
+
+// -----------------------------------------------------------------------------
+// Writing
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(signed int Value)
+{
+ WriteMarker(SINT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(unsigned int Value)
+{
+ WriteMarker(UINT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(float Value)
+{
+ WriteMarker(FLOAT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(bool Value)
+{
+ WriteMarker(BOOL_MARKER);
+
+ unsigned int UIntBool = Value ? 1 : 0;
+ UIntBool = ConvertEndianessFromSystemToStorage(UIntBool);
+ RawWrite(&UIntBool, sizeof(UIntBool));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(const std::string & String)
+{
+ WriteMarker(STRING_MARKER);
+
+ Write(String.size());
+ RawWrite(String.c_str(), String.size());
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::Write(const void * BufferPtr, size_t Size)
+{
+ WriteMarker(BLOCK_MARKER);
+
+ Write(Size);
+ RawWrite(BufferPtr, Size);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::WriteMarker(unsigned char Marker)
+{
+ m_Data.push_back(Marker);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_OutputPersistenceBlock::RawWrite(const void * DataPtr, size_t Size)
+{
+ if (Size > 0)
+ {
+ unsigned int OldSize = m_Data.size();
+ m_Data.resize(OldSize + Size);
+ memcpy(&m_Data[OldSize], DataPtr, Size);
+ }
+}
diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h
new file mode 100755
index 0000000000..1f730769b8
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.h
@@ -0,0 +1,60 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_OUTPUTPERSISTENCEBLOCK_H
+#define BS_OUTPUTPERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistenceblock.h"
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_OutputPersistenceBlock : public BS_PersistenceBlock
+{
+public:
+ BS_OutputPersistenceBlock();
+
+ void Write(signed int Value);
+ void Write(unsigned int Value);
+ void Write(float Value);
+ void Write(bool Value);
+ void Write(const std::string & String);
+ void Write(const void * BufferPtr, size_t Size);
+
+ const void * GetData() const { return &m_Data[0]; }
+ unsigned int GetDataSize() const { return m_Data.size(); }
+
+private:
+ void WriteMarker(unsigned char Marker);
+ void RawWrite(const void * DataPtr, size_t Size);
+
+ std::vector<unsigned char> m_Data;
+};
+
+#endif
diff --git a/engines/sword25/kernel/persistable.h b/engines/sword25/kernel/persistable.h
new file mode 100755
index 0000000000..b1b8bd2c12
--- /dev/null
+++ b/engines/sword25/kernel/persistable.h
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_PERSISTABLE_H
+#define BS_PERSISTABLE_H
+
+class BS_OutputPersistenceBlock;
+class BS_InputPersistenceBlock;
+
+class BS_Persistable
+{
+public:
+ virtual ~BS_Persistable() {};
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer) = 0;
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader) = 0;
+
+};
+
+#endif
diff --git a/engines/sword25/kernel/persistenceblock.h b/engines/sword25/kernel/persistenceblock.h
new file mode 100755
index 0000000000..4dda4388b7
--- /dev/null
+++ b/engines/sword25/kernel/persistenceblock.h
@@ -0,0 +1,112 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_PERSISTENCEBLOCK_H
+#define BS_PERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+
+// -----------------------------------------------------------------------------
+// Class definition
+// -----------------------------------------------------------------------------
+
+class BS_PersistenceBlock
+{
+public:
+ static unsigned int GetSInt32Size() { return sizeof(signed int) + sizeof(unsigned char); }
+ static unsigned int GetUInt32Size() { return sizeof(unsigned int) + sizeof(unsigned char); }
+ static unsigned int GetFloat32Size() { return sizeof(float) + sizeof(unsigned char); }
+ static unsigned int GetBoolSize() { return sizeof(unsigned char) + sizeof(unsigned char); }
+ static unsigned int GetStringSize(const std::string & String) { return static_cast<unsigned int>(sizeof(unsigned int) + String.size() + sizeof(unsigned char)); }
+
+protected:
+ enum
+ {
+ SINT_MARKER,
+ UINT_MARKER,
+ FLOAT_MARKER,
+ STRING_MARKER,
+ BOOL_MARKER,
+ BLOCK_MARKER,
+ };
+
+ // -----------------------------------------------------------------------------
+ // Endianess Conversions
+ // -----------------------------------------------------------------------------
+ //
+ // Alles wird in Little Endian gespeichert.
+ // Auf Big Endian-Systemen muss die Bytereihenfolge daher vor dem Speichern und nach dem Einlesen gespeicherter Werte vertauscht werden.
+ //
+
+ template<typename T>
+ static T ConvertEndianessFromSystemToStorage(T Value)
+ {
+ if (IsBigEndian()) ReverseByteOrder(&Value);
+ return Value;
+ }
+
+ template<typename T>
+ static T ConvertEndianessFromStorageToSystem(T Value)
+ {
+ if (IsBigEndian()) ReverseByteOrder(&Value);
+ return Value;
+ }
+
+private:
+ static bool IsBigEndian()
+ {
+ unsigned int Dummy = 1;
+ unsigned char * DummyPtr = reinterpret_cast<unsigned char *>(&Dummy);
+ return DummyPtr[0] == 0;
+ }
+
+ template<typename T>
+ static void Swap(T & One, T & Two)
+ {
+ T Temp = One;
+ One = Two;
+ Two = Temp;
+ }
+
+ static void ReverseByteOrder(void * Ptr)
+ {
+ // Kehrt die Bytereihenfolge des 32-Bit Wortes um auf das Ptr zeigt.
+ unsigned char * CharPtr = static_cast<unsigned char *>(Ptr);
+ Swap(CharPtr[0], CharPtr[3]);
+ Swap(CharPtr[1], CharPtr[2]);
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Compile time asserts
+// -----------------------------------------------------------------------------
+
+#define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1];
+CTASSERT(sizeof(unsigned char) == 1);
+CTASSERT(sizeof(signed int) == 4);
+CTASSERT(sizeof(unsigned int) == 4);
+CTASSERT(sizeof(float) == 4);
+#undef CTASSERT
+
+#endif
diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp
new file mode 100755
index 0000000000..51491a6c2b
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.cpp
@@ -0,0 +1,459 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel.h"
+#include "persistenceservice.h"
+#include "inputpersistenceblock.h"
+#include "outputpersistenceblock.h"
+#include "filesystemutil.h"
+#include "gfx/graphicengine.h"
+#include "sfx/soundengine.h"
+#include "input/inputengine.h"
+#include "math/regionregistry.h"
+#include "script/script.h"
+#include "debug/debugtools.h"
+#include "util/zlib/zlib.h"
+
+#include "kernel/memlog_off.h"
+#include <sstream>
+#include <fstream>
+#include <algorithm>
+#include <locale>
+#include "kernel/memlog_on.h"
+
+using namespace std;
+
+#define BS_LOG_PREFIX "PERSISTENCESERVICE"
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * SAVEGAME_EXTENSION = ".b25s";
+ const char * SAVEGAME_DIRECTORY = "saves";
+ const char * FILE_MARKER = "BS25SAVEGAME";
+ const unsigned int SLOT_COUNT = 18;
+ const unsigned int FILE_COPY_BUFFER_SIZE = 1024 * 10;
+
+ // -------------------------------------------------------------------------
+
+ string GenerateSavegameFilename(unsigned int SlotID)
+ {
+ ostringstream oss;
+ oss << SlotID << SAVEGAME_EXTENSION;
+ return oss.str();
+ }
+
+ // -------------------------------------------------------------------------
+
+ string GenerateSavegamePath(unsigned int SlotID)
+ {
+ ostringstream oss;
+ oss << BS_PersistenceService::GetSavegameDirectory() << BS_FileSystemUtil::GetInstance().GetPathSeparator() << GenerateSavegameFilename(SlotID);
+ return oss.str();
+ }
+
+ // -------------------------------------------------------------------------
+
+ string FormatTimestamp(time_t Time)
+ {
+ // Zeitstempel in Einzelkomponenten auflösen.
+ tm * Timeinfo = localtime(&Time);
+
+ // Zeitangabe im lokalen Format in einen String-Stream schreiben.
+ locale Locale("");
+ ostringstream StringBuilder;
+ StringBuilder.imbue(Locale);
+ char * Pattern = "%x %X";
+ use_facet<time_put<char> >(Locale).put(StringBuilder,
+ StringBuilder, StringBuilder.fill(), Timeinfo, Pattern, Pattern + strlen(Pattern));
+
+ // Formatierten String zurückgeben.
+ return StringBuilder.str();
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Private Implementation (Pimpl-Pattern)
+// -----------------------------------------------------------------------------
+
+struct SavegameInformation
+{
+ bool IsOccupied;
+ bool IsCompatible;
+ string Description;
+ string Filename;
+ unsigned int GamedataLength;
+ unsigned int GamedataOffset;
+ unsigned int GamedataUncompressedLength;
+
+ SavegameInformation() { Clear(); }
+
+ void Clear()
+ {
+ IsOccupied = false;
+ IsCompatible = false;
+ Description = "";
+ Filename = "";
+ GamedataLength = 0;
+ GamedataOffset = 0;
+ GamedataUncompressedLength = 0;
+ }
+};
+
+struct BS_PersistenceService::Impl
+{
+ SavegameInformation m_SavegameInformations[SLOT_COUNT];
+
+ // -----------------------------------------------------------------------------
+
+ Impl()
+ {
+ ReloadSlots();
+ }
+
+ // -----------------------------------------------------------------------------
+
+ void ReloadSlots()
+ {
+ // Über alle Spielstanddateien iterieren und deren Infos einlesen.
+ for (unsigned int i = 0; i < SLOT_COUNT; ++i)
+ {
+ ReadSlotSavegameInformation(i);
+ }
+ }
+
+ void ReadSlotSavegameInformation(unsigned int SlotID)
+ {
+ // Aktuelle Slotinformationen in den Ausgangszustand versetzen, er wird im Folgenden neu gefüllt.
+ SavegameInformation & CurSavegameInfo = m_SavegameInformations[SlotID];
+ CurSavegameInfo.Clear();
+
+ // Den Dateinamen für den Spielstand des Slots generieren.
+ string Filename = GenerateSavegamePath(SlotID);
+
+ // Feststellen, ob eine Spielstanddatei dieses Namens existiert.
+ if (BS_FileSystemUtil::GetInstance().FileExists(Filename))
+ {
+ // Die Spielstanddatei öffnen.
+ ifstream File(Filename.c_str(), ifstream::binary);
+ if (File.good() && File.is_open())
+ {
+ // Die Headerdaten einlesen.
+ string StoredMarker, StoredVersionID;
+ File >> StoredMarker >> StoredVersionID >> CurSavegameInfo.GamedataLength >> CurSavegameInfo.GamedataUncompressedLength;
+
+ // Falls die Headerdaten gelesen werden konnten und der Marker stimmt, nehmen wir an eine gültige Spielstanddatei zu haben.
+ if (File.good() && StoredMarker == FILE_MARKER)
+ {
+ // Der Slot wird als belegt markiert.
+ CurSavegameInfo.IsOccupied = true;
+ // Speichern, ob der Spielstand kompatibel mit der aktuellen Engine-Version ist.
+ CurSavegameInfo.IsCompatible = (StoredVersionID == BS_Debugtools::GetVersionID());
+ // Dateinamen des Spielstandes speichern.
+ CurSavegameInfo.Filename = GenerateSavegameFilename(SlotID);
+ // Die Beschreibung des Spielstandes besteht aus einer textuellen Darstellung des Änderungsdatums der Spielstanddatei.
+ CurSavegameInfo.Description = FormatTimestamp(BS_FileSystemUtil::GetInstance().GetFileTime(Filename));
+ // Den Offset zu den gespeicherten Spieldaten innerhalb der Datei speichern.
+ // Dieses entspricht der aktuellen Position + 1, da nach der letzten Headerinformation noch ein Leerzeichen als trenner folgt.
+ CurSavegameInfo.GamedataOffset = static_cast<unsigned int>(File.tellg()) + 1;
+ }
+
+ }
+ }
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+BS_PersistenceService & BS_PersistenceService::GetInstance()
+{
+ static BS_PersistenceService Instance;
+ return Instance;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_PersistenceService::BS_PersistenceService() : m_impl(new Impl)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_PersistenceService::~BS_PersistenceService()
+{
+ delete m_impl;
+}
+
+// -----------------------------------------------------------------------------
+// Implementation
+// -----------------------------------------------------------------------------
+
+void BS_PersistenceService::ReloadSlots()
+{
+ m_impl->ReloadSlots();
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_PersistenceService::GetSlotCount()
+{
+ return SLOT_COUNT;
+}
+
+// -----------------------------------------------------------------------------
+
+std::string BS_PersistenceService::GetSavegameDirectory()
+{
+ return BS_FileSystemUtil::GetInstance().GetUserdataDirectory() + BS_FileSystemUtil::GetInstance().GetPathSeparator() + SAVEGAME_DIRECTORY;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ bool CheckSlotID(unsigned int SlotID)
+ {
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT)
+ {
+ BS_LOG_ERRORLN("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PersistenceService::IsSlotOccupied(unsigned int SlotID)
+{
+ if (!CheckSlotID(SlotID)) return false;
+ return m_impl->m_SavegameInformations[SlotID].IsOccupied;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PersistenceService::IsSavegameCompatible(unsigned int SlotID)
+{
+ if (!CheckSlotID(SlotID)) return false;
+ return m_impl->m_SavegameInformations[SlotID].IsCompatible;
+}
+
+// -----------------------------------------------------------------------------
+
+string & BS_PersistenceService::GetSavegameDescription(unsigned int SlotID)
+{
+ static string EmptyString;
+ if (!CheckSlotID(SlotID)) return EmptyString;
+ return m_impl->m_SavegameInformations[SlotID].Description;
+}
+
+// -----------------------------------------------------------------------------
+
+string & BS_PersistenceService::GetSavegameFilename(unsigned int SlotID)
+{
+ static string EmptyString;
+ if (!CheckSlotID(SlotID)) return EmptyString;
+ return m_impl->m_SavegameInformations[SlotID].Filename;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PersistenceService::SaveGame(unsigned int SlotID, const std::string & ScreenshotFilename)
+{
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT)
+ {
+ BS_LOG_ERRORLN("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ }
+
+ // Dateinamen erzeugen.
+ string Filename = GenerateSavegamePath(SlotID).c_str();
+
+ try
+ {
+ // Sicherstellen, dass das Verzeichnis für die Spielstanddateien existiert.
+ BS_FileSystemUtil::GetInstance().CreateDirectory(GetSavegameDirectory());
+
+ // Spielstanddatei öffnen und die Headerdaten schreiben.
+ ofstream File(Filename.c_str(), ofstream::binary);
+ File << string(FILE_MARKER) << " " << string(BS_Debugtools::GetVersionID()) << " ";
+ if (!File.good())
+ {
+ BS_LOG_ERRORLN("Unable to write header data to savegame file \"%s\".", Filename.c_str());
+ throw 0;
+ }
+
+ // Alle notwendigen Module persistieren.
+ BS_OutputPersistenceBlock Writer;
+ bool Success = true;
+ Success &= BS_Kernel::GetInstance()->GetScript()->Persist(Writer);
+ Success &= BS_RegionRegistry::GetInstance().Persist(Writer);
+ Success &= BS_Kernel::GetInstance()->GetGfx()->Persist(Writer);
+ Success &= BS_Kernel::GetInstance()->GetSfx()->Persist(Writer);
+ Success &= BS_Kernel::GetInstance()->GetInput()->Persist(Writer);
+ if (!Success)
+ {
+ BS_LOG_ERRORLN("Unable to persist modules for savegame file \"%s\".", Filename.c_str());
+ throw 0;
+ }
+
+ // Daten komprimieren.
+ vector<unsigned char> CompressionBuffer(Writer.GetDataSize() + (Writer.GetDataSize() + 500) / 1000 + 12);
+ uLongf CompressedLength = CompressionBuffer.size();
+ if (compress2(&CompressionBuffer[0], &CompressedLength, reinterpret_cast<const Bytef *>(Writer.GetData()), Writer.GetDataSize(), 6) != Z_OK)
+ {
+ BS_LOG_ERRORLN("Unable to compress savegame data in savegame file \"%s\".", Filename.c_str());
+ throw 0;
+ }
+
+ // Länge der komprimierten Daten und der unkomprimierten Daten in die Datei schreiben.
+ File << CompressedLength << " " << Writer.GetDataSize() << " ";
+
+ // Komprimierte Daten in die Datei schreiben.
+ File.write(reinterpret_cast<char *>(&CompressionBuffer[0]), CompressedLength);
+ if (!File.good())
+ {
+ BS_LOG_ERRORLN("Unable to write game data to savegame file \"%s\".", Filename.c_str());
+ throw 0;
+ }
+
+ // Screenshotdatei an die Datei anfügen.
+ if (BS_FileSystemUtil::GetInstance().FileExists(ScreenshotFilename))
+ {
+ ifstream ScreenshotFile(ScreenshotFilename.c_str(), ifstream::binary);
+
+ vector<char> Buffer(FILE_COPY_BUFFER_SIZE);
+ while (ScreenshotFile.good())
+ {
+ ScreenshotFile.read(&Buffer[0], Buffer.size());
+ File.write(&Buffer[0], ScreenshotFile.gcount());
+ }
+ }
+ else
+ {
+ BS_LOG_WARNINGLN("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", Filename.c_str());
+ }
+
+ // Savegameinformationen für diesen Slot aktualisieren.
+ m_impl->ReadSlotSavegameInformation(SlotID);
+ }
+ catch(...)
+ {
+ BS_LOG_ERRORLN("An error occured while create savegame file \"%s\".", Filename.c_str());
+
+ // Es ist ein Fehler aufgetreten, die Spielstanddatei wird gelöscht, da sie keinen konsistenten Zustand besitzt.
+ ::remove(Filename.c_str());
+
+ // Misserfolg signalisieren.
+ return false;
+ }
+
+ // Erfolg signalisieren.
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PersistenceService::LoadGame(unsigned int SlotID)
+{
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT)
+ {
+ BS_LOG_ERRORLN("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ }
+
+ SavegameInformation & CurSavegameInfo = m_impl->m_SavegameInformations[SlotID];
+
+ // Überprüfen, ob der Slot belegt ist.
+ if (!CurSavegameInfo.IsOccupied)
+ {
+ BS_LOG_ERRORLN("Tried to load from an empty slot (%d).", SlotID);
+ return false;
+ }
+
+ // Überprüfen, ob der Spielstand im angegebenen Slot mit der aktuellen Engine-Version kompatibel ist.
+ // Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen,
+ // da sich die Versions-ID bei jeder Codeänderung mitändert.
+#ifndef DEBUG
+ if (!CurSavegameInfo.IsCompatible)
+ {
+ BS_LOG_ERRORLN("Tried to load a savegame (%d) that is not compatible with this engine version.", SlotID);
+ return false;
+ }
+#endif
+
+ vector<unsigned char> UncompressedDataBuffer(CurSavegameInfo.GamedataUncompressedLength);
+ {
+ // Komprimierte gespeicherte Spieldaten laden.
+ vector<unsigned char> CompressedDataBuffer(CurSavegameInfo.GamedataLength);
+ {
+ ifstream File(GenerateSavegamePath(SlotID).c_str(), ifstream::binary);
+ File.seekg(CurSavegameInfo.GamedataOffset);
+ File.read(reinterpret_cast<char *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength);
+ if (!File.good())
+ {
+ BS_LOG_ERRORLN("Unable to load the gamedata from the savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ return false;
+ }
+ }
+
+ // Spieldaten dekomprimieren.
+ uLongf UncompressedBufferSize = UncompressedDataBuffer.size();
+ if (uncompress(reinterpret_cast<Bytef *>(&UncompressedDataBuffer[0]), &UncompressedBufferSize,
+ reinterpret_cast<Bytef *>(&CompressedDataBuffer[0]), CompressedDataBuffer.size()) != Z_OK)
+ {
+ BS_LOG_ERRORLN("Unable to decompress the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ return false;
+ }
+ }
+
+ BS_InputPersistenceBlock Reader(&UncompressedDataBuffer[0], UncompressedDataBuffer.size());
+
+ // Einzelne Engine-Module depersistieren.
+ bool Success = true;
+ Success &= BS_Kernel::GetInstance()->GetScript()->Unpersist(Reader);
+ // Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden.
+ Success &= BS_RegionRegistry::GetInstance().Unpersist(Reader);
+ Success &= BS_Kernel::GetInstance()->GetGfx()->Unpersist(Reader);
+ Success &= BS_Kernel::GetInstance()->GetSfx()->Unpersist(Reader);
+ Success &= BS_Kernel::GetInstance()->GetInput()->Unpersist(Reader);
+
+ if (!Success)
+ {
+ BS_LOG_ERRORLN("Unable to unpersist the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/engines/sword25/kernel/persistenceservice.h b/engines/sword25/kernel/persistenceservice.h
new file mode 100755
index 0000000000..2693f4cc2e
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.h
@@ -0,0 +1,71 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_PERSISTENCESERVICE_H
+#define BS_PERSISTENCESERVICE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/memlog_off.h"
+#include <string>
+#include <vector>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_PersistenceService
+{
+public:
+ BS_PersistenceService();
+ virtual ~BS_PersistenceService();
+
+ // -----------------------------------------------------------------------------
+ // Singleton-Methode
+ // -----------------------------------------------------------------------------
+
+ static BS_PersistenceService & GetInstance();
+
+
+ // -----------------------------------------------------------------------------
+ // Interface
+ // -----------------------------------------------------------------------------
+
+ static unsigned int GetSlotCount();
+ static std::string GetSavegameDirectory();
+
+ void ReloadSlots();
+ bool IsSlotOccupied(unsigned int SlotID);
+ bool IsSavegameCompatible(unsigned int SlotID);
+ std::string & GetSavegameDescription(unsigned int SlotID);
+ std::string & GetSavegameFilename(unsigned int SlotID);
+
+ bool SaveGame(unsigned int SlotID, const std::string & ScreenshotFilename);
+ bool LoadGame(unsigned int SlotID);
+
+private:
+ struct Impl;
+ Impl * m_impl;
+};
+
+#endif
diff --git a/engines/sword25/kernel/resmanager.cpp b/engines/sword25/kernel/resmanager.cpp
new file mode 100755
index 0000000000..2e7ab87af0
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.cpp
@@ -0,0 +1,293 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "resmanager.h"
+
+#include "resource.h"
+#include "resservice.h"
+#include "string.h"
+#include "../package/packagemanager.h"
+
+#define BS_LOG_PREFIX "RESOURCEMANAGER"
+
+BS_ResourceManager::~BS_ResourceManager()
+{
+ // Alle ungelockten Resourcen freigeben.
+ EmptyCache();
+
+ // Alle übriggebliebenen Resourcen sind nicht freigegeben worden, daher Warnungen ausgeben und freigeben.
+ std::list<BS_Resource*>::iterator Iter = m_Resources.begin();
+ for (; Iter != m_Resources.end(); ++Iter)
+ {
+ BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*Iter)->GetFileName().c_str());
+
+ // Lock-Count auf 0 setzen.
+ while ((*Iter)->GetLockCount() > 0) { (*Iter)->Release(); };
+
+ // Resource freigeben.
+ delete(*Iter);
+ }
+}
+
+BS_Resource* BS_ResourceManager::GetResourceByOrdinal(int Ord) const
+{
+ // Überprüfen ob der Index Ord innerhald der Listengrenzen liegt.
+ if (Ord < 0 || Ord >= GetResourceCount())
+ {
+ BS_LOG_ERRORLN("Resource ordinal (%d) out of bounds (0 - %d).", Ord, GetResourceCount() - 1);
+ return NULL;
+ }
+
+ // Liste durchlaufen und die Resource mit dem gewünschten Index zurückgeben.
+ int CurOrd = 0;
+ std::list<BS_Resource*>::const_iterator Iter = m_Resources.begin();
+ for (; Iter != m_Resources.end(); ++Iter, ++CurOrd)
+ {
+ if (CurOrd == Ord)
+ return (*Iter);
+ }
+
+ // Die Ausführung sollte nie an diesem Punkt ankommen.
+ BS_LOG_EXTERRORLN("Execution reached unexpected point.");
+ return NULL;
+}
+
+bool BS_ResourceManager::RegisterResourceService(BS_ResourceService* pService)
+{
+ if(!pService)
+ {
+ BS_LOG_ERRORLN("Can't register NULL resource service.");
+ return false;
+ }
+
+ m_ResourceServices.push_back(pService);
+
+ return true;
+}
+
+void BS_ResourceManager::DeleteResourcesIfNecessary()
+{
+ // Falls noch genügend Speicher frei ist, oder keine Ressourcen geladen sind, kann die Funktion vorzeitig beendet werden.
+ if (m_KernelPtr->GetUsedMemory() < m_MaxMemoryUsage || m_Resources.empty()) return;
+
+ // Solange Ressourcen löschen, bis der Speichernutzung des Prozesses unter den festgelegten Maximalwert fällt.
+ // Dabei wird die Liste von Hinten nach vorne durchlaufen um zunächst jene Resourcen freizugeben, deren
+ // Benutzung lange zurückliegt und sich somit am Ende der Liste befinden.
+ std::list<BS_Resource*>::iterator Iter = m_Resources.end();
+ do
+ {
+ --Iter;
+
+ // Die Resource darf nur freigegeben werden, wenn sie nicht gelockt ist.
+ if ((*Iter)->GetLockCount() == 0) Iter = DeleteResource(*Iter);
+ } while(Iter != m_Resources.begin() && m_KernelPtr->GetUsedMemory() > m_MaxMemoryUsage);
+}
+
+void BS_ResourceManager::EmptyCache()
+{
+ // Resourcenliste durchlaufen und alle nicht gelockten Resourcen freigeben
+ std::list<BS_Resource*>::iterator Iter = m_Resources.begin();
+ while (Iter != m_Resources.end())
+ {
+ if ((*Iter)->GetLockCount() == 0)
+ {
+ // Resource entfernen
+ Iter = DeleteResource(*Iter);
+ }
+ else
+ ++Iter;
+ }
+}
+
+BS_Resource* BS_ResourceManager::RequestResource(const std::string& FileName)
+{
+ // Absoluten, eindeutigen Pfad zur Datei erzeugen.
+ std::string UniqueFileName = GetUniqueFileName(FileName);
+ if (UniqueFileName == "")
+ return NULL;
+
+ // Feststellen, ob die Resource schon geladen ist.
+ // Wenn die Resource gefunden wurde wird sie an die Spitze der Resourcenliste gestellt, gelockt und zurückgegeben.
+ {
+ BS_Resource* pResource = GetResource(UniqueFileName);
+ if (pResource)
+ {
+ MoveToFront(pResource);
+ (pResource)->AddReference();
+ return pResource;
+ }
+ }
+
+ // Die Resource wurde nicht gefunden, muss also noch geladen werden.
+ if (m_LogCacheMiss) BS_LOG_WARNINGLN("\"%s\" was not precached.", UniqueFileName.c_str());
+
+ BS_Resource* pResource;
+ if (pResource = LoadResource(UniqueFileName))
+ {
+ pResource->AddReference();
+ return pResource;
+ }
+
+ return NULL;
+}
+
+bool BS_ResourceManager::PrecacheResource(const std::string& FileName, bool ForceReload)
+{
+ // Absoluten, eindeutigen Pfad zur Datei erzeugen.
+ std::string UniqueFileName = GetUniqueFileName(FileName);
+ if (UniqueFileName == "")
+ return false;
+
+ BS_Resource * ResourcePtr = GetResource(UniqueFileName);
+
+ if (ForceReload && ResourcePtr)
+ {
+ if (ResourcePtr->GetLockCount())
+ {
+ BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", FileName.c_str());
+ return false;
+ }
+ else
+ {
+ DeleteResource(ResourcePtr);
+ ResourcePtr = 0;
+ }
+ }
+
+ if (!ResourcePtr && LoadResource(UniqueFileName) == NULL)
+ {
+ BS_LOG_ERRORLN("Could not precache \"%s\",", FileName.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+void BS_ResourceManager::MoveToFront(BS_Resource* pResource)
+{
+ // Resource aus der Liste löschen
+ m_Resources.erase(pResource->_Iterator);
+ // Resource an die Spitze der Liste setzen
+ m_Resources.push_front(pResource);
+ // Iterator aktualisieren
+ pResource->_Iterator = m_Resources.begin();
+}
+
+BS_Resource* BS_ResourceManager::LoadResource(const std::string& FileName)
+{
+ // ResourceService finden, der die Resource laden kann.
+ for(unsigned int i = 0; i < m_ResourceServices.size(); ++i)
+ {
+ if (m_ResourceServices[i]->CanLoadResource(FileName))
+ {
+ // Falls mehr Speicher belegt ist als gewünscht, muss Speicher freigegeben werden.
+ DeleteResourcesIfNecessary();
+
+ // Resource laden
+ BS_Resource* pResource;
+ if (!(pResource = m_ResourceServices[i]->LoadResource(FileName)))
+ {
+ BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", FileName.c_str());
+ return NULL;
+ }
+
+ // Resource an die Spitze der Resourcenliste stellen.
+ m_Resources.push_front(pResource);
+ pResource->_Iterator = m_Resources.begin();
+
+ // Resource in die Hashtabelle eintragen
+ m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].push_front(pResource);
+
+ return pResource;
+ }
+ }
+
+ BS_LOG_ERRORLN("Could not find a service that can load \"%s\".", FileName.c_str());
+ return NULL;
+}
+
+std::string BS_ResourceManager::GetUniqueFileName(const std::string& FileName) const
+{
+ // Pointer auf den PackageManager holen
+ BS_PackageManager* pPackage = (BS_PackageManager*) m_KernelPtr->GetService("package");
+ if (!pPackage)
+ {
+ BS_LOG_ERRORLN("Could not get package manager.");
+ return std::string("");
+ }
+
+ // Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen
+ std::string UniqueFileName = pPackage->GetAbsolutePath(FileName);
+ if (UniqueFileName == "")
+ BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", FileName.c_str());
+
+ return UniqueFileName;
+}
+
+std::list<BS_Resource*>::iterator BS_ResourceManager::DeleteResource(BS_Resource* pResource)
+{
+ // Resource aus der Hash-Tabelle entfernen
+ m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].remove(pResource);
+
+ BS_Resource* pDummy = pResource;
+
+ // Resource aus der Resourcenliste löschen
+ std::list<BS_Resource*>::iterator Result = m_Resources.erase(pResource->_Iterator);
+
+ // Resource freigeben
+ delete(pDummy);
+
+ // Iterator zurückgeben
+ return Result;
+}
+
+BS_Resource* BS_ResourceManager::GetResource(const std::string& UniqueFileName) const
+{
+ // Feststellen, ob die Resource schon geladen ist.
+ const std::list<BS_Resource*>& HashBucket = m_ResourceHashTable[BS_String::GetHash(UniqueFileName) % HASH_TABLE_BUCKETS];
+ {
+ std::list<BS_Resource*>::const_iterator Iter = HashBucket.begin();
+ for (; Iter != HashBucket.end(); ++Iter)
+ {
+ // Wenn die Resource gefunden wurde wird sie zurückgegeben.
+ if ((*Iter)->GetFileName() == UniqueFileName)
+ return *Iter;
+ }
+ }
+
+ // Resource wurde nicht gefunden, ist also nicht geladen
+ return NULL;
+}
+
+void BS_ResourceManager::DumpLockedResources()
+{
+ for (std::list<BS_Resource*>::iterator Iter = m_Resources.begin(); Iter != m_Resources.end(); ++Iter)
+ {
+ if ((*Iter)->GetLockCount() > 0)
+ {
+ BS_LOGLN("%s", (*Iter)->GetFileName().c_str());
+ }
+ }
+}
+
+void BS_ResourceManager::SetMaxMemoryUsage(unsigned int MaxMemoryUsage)
+{
+ m_MaxMemoryUsage = MaxMemoryUsage;
+ DeleteResourcesIfNecessary();
+} \ No newline at end of file
diff --git a/engines/sword25/kernel/resmanager.h b/engines/sword25/kernel/resmanager.h
new file mode 100755
index 0000000000..6bc6b2a8f8
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.h
@@ -0,0 +1,185 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RESOURCEMANAGER_H
+#define BS_RESOURCEMANAGER_H
+
+// Includes
+#include "memlog_off.h"
+#include <vector>
+#include <list>
+#include "memlog_on.h"
+
+#include "common.h"
+
+// Klassendefinition
+class BS_ResourceService;
+class BS_Resource;
+class BS_Kernel;
+
+class BS_ResourceManager
+{
+friend BS_Kernel;
+
+public:
+ /**
+ @brief Fordert eine Resource an.
+ @param FileName Dateiname
+ @return Gibt die Resource zurück, falls erfolgreich, sonst NULL
+ */
+ BS_Resource* RequestResource(const std::string& FileName);
+
+ /**
+ @brief Lädt eine Resource in den Cache.
+ @param FileName der Dateiname der zu cachenden Resource
+ @param ForceReload gibt an, ob die Datei auch neu geladen werden soll, wenn sie bereits geladen wurde.
+ Dies ist nützlich bei Dateien, die sich in der Zwischenzeit verändert haben.
+ @return Gibt true zurück, wenn das Caching durchgeführt werden konnte, ansonsten false.
+ */
+ bool PrecacheResource(const std::string& FileName, bool ForceReload = false);
+
+ /**
+ @brief Gibt die Anzahl der geladenen Resourcen zurück.
+ */
+ int GetResourceCount() const { return static_cast<int>(m_Resources.size()); }
+
+ /**
+ @brief Gibt einen Pointer auf eine Resource anhand deren laufender Nummer zurück.
+ @param Ord laufende Nummer der Resource. Dieser Wert muss zwischen 0 und GetResourceCount() - 1 liegen.
+ @return Gibt einen Pointer auf die Resource zurück wenn erfolgreich, ansonsten NULL.
+ @remark Diese Methode ist nicht auf Geschwindigkeit optimiert und sollte nur für Debugzwecke eingesetzt werden.
+ */
+ BS_Resource* GetResourceByOrdinal(int Ord) const;
+
+ /**
+ @brief RegisterResourceService Diese Methode wird vom Konstruktor von
+ BS_ResourceService aufgerufen und trägt somit alle
+ Resource-Services in einen Liste des
+ Resource-Managers ein.
+ @param pService welches Service
+ @return gibt true zurück, falls erfolgreich
+ */
+ bool RegisterResourceService(BS_ResourceService* pService);
+
+ /**
+ * @brief gibt alle Resourcen frei, die nicht gelocked sind.
+ **/
+ void EmptyCache();
+
+ /**
+ @brief Gibt zurück wie viel Speicher die Engine maximal belegen soll.
+ */
+ int GetMaxMemoryUsage() const { return m_MaxMemoryUsage; }
+
+ /**
+ @brief Legt fest wie viel Speicher die Engine maximal belegen soll.
+
+ Wenn dieser Wert überschritten wird, werden Resourcen entladen um Platz zu schaffen. Dieser Wert ist als Richtgröße zu verstehen und nicht als feste Grenze.
+ Es ist unter KEINEN Umständen garantiert, dass die Engine tatsächlich nur so viel Speicher benutzt.
+ */
+ void SetMaxMemoryUsage(unsigned int MaxMemoryUsage);
+
+ /**
+ @brief Gibt an, ob eine Warnung ins Log geschrieben wird, wenn ein Cache-Miss auftritt.
+ Der Standardwert ist "false".
+ */
+ bool IsLogCacheMiss() const { return m_LogCacheMiss; }
+
+ /**
+ @brief Legt fest, ob eine Warnung ins Log geschrieben wird, wenn in Cache-Miss auftritt.
+ @param Flag wenn "true" wird die Warnung in Zukunft ausgegeben, ansonsten nicht.
+ */
+ void SetLogCacheMiss(bool Flag) { m_LogCacheMiss = Flag; }
+
+ /**
+ @brief Schreibt die Namen aller gelockten Resourcen in die Log-Datei.
+ */
+ void DumpLockedResources();
+
+private:
+ /**
+ @brief Erzeugt einen neuen Resource-Manager.
+ @param pKernel ein Pointer auf den Kernel.
+ @remark Nur der BS_Kernel darf Exemplare dieser Klasse erzeugen. Daher ist der Konstruktor private.
+ */
+ BS_ResourceManager(BS_Kernel* pKernel) :
+ m_KernelPtr(pKernel),
+ m_MaxMemoryUsage(100000000),
+ m_LogCacheMiss(false)
+ {};
+ virtual ~BS_ResourceManager();
+
+ enum
+ {
+ HASH_TABLE_BUCKETS = 256
+ };
+
+ /**
+ @brief Verschiebt eine Resource an die Spitze der Resourcenliste.
+ @param pResource die Resource
+ */
+ void MoveToFront(BS_Resource* pResource);
+
+ /**
+ @brief Lädt eine Resource und aktualisiert m_UsedMemory.
+
+ Die Resource darf nicht bereits geladen sein.
+
+ @param FileName der absolute und eindeutige Dateiname der zu ladenen Resource
+ @return Gibt einen Pointer auf die geladene Resource zurück wenn das Laden erfolgreich war, ansonsten NULL.
+ */
+ BS_Resource* LoadResource(const std::string& FileName);
+
+ /**
+ @brief Gibt zu einer Datei ihren absoluten, eindeutigen Pfad zurück.
+ @param FileName der Dateiname
+ @return Der absolute, eindeutige Pfad zur Datei inklusive des Dateinamens.<br>
+ Gibt einen leeren std::string zurück, wenn der Pfad nicht erzeugt werden konnte.
+ */
+ std::string GetUniqueFileName(const std::string& FileName) const;
+
+ /**
+ @brief Löscht eine Resource entfernt sie aus den Listen und aktualisiert m_UsedMemory.
+ @param pResource die zu löschende Resource
+ @return Gibt einen Iterator zurück, der auf die nächste Resource in der Resourcenliste zeigt.
+ */
+ std::list<BS_Resource*>::iterator DeleteResource(BS_Resource* pResource);
+
+ /**
+ @brief Holt einen Pointer zu einer geladenen Resource.
+ @param UniqueFileName der absolute, eindeutige Pfad zur Datei inklusive des Dateinamens.
+ @return Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist.
+ */
+ BS_Resource* GetResource(const std::string& UniqueFileName) const;
+
+ /**
+ @brief Löscht solange Resourcen, bis der Prozess unter das angegebene Maximun an Speicherbelegung kommt.
+ */
+ void DeleteResourcesIfNecessary();
+
+ BS_Kernel* m_KernelPtr;
+ unsigned int m_MaxMemoryUsage;
+ std::vector<BS_ResourceService*> m_ResourceServices;
+ std::list<BS_Resource*> m_Resources;
+ std::list<BS_Resource*> m_ResourceHashTable[HASH_TABLE_BUCKETS];
+ bool m_LogCacheMiss;
+};
+
+#endif
+
diff --git a/engines/sword25/kernel/resource.cpp b/engines/sword25/kernel/resource.cpp
new file mode 100755
index 0000000000..bfcd5a54b4
--- /dev/null
+++ b/engines/sword25/kernel/resource.cpp
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "resource.h"
+#include "string.h"
+#include "kernel.h"
+#include "package/packagemanager.h"
+
+#define BS_LOG_PREFIX "RESOURCE"
+
+BS_Resource::BS_Resource(const std::string& FileName, RESOURCE_TYPES Type) :
+ _Type(Type),
+ _RefCount(0)
+{
+ BS_ASSERT(BS_Kernel::GetInstance()->GetService("package"));
+
+ _FileName = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"))->GetAbsolutePath(FileName);
+ _FileNameHash = BS_String::GetHash(FileName);
+};
+
+void BS_Resource::Release()
+{
+ if (_RefCount)
+ {
+ --_RefCount;
+ }
+ else
+ BS_LOG_WARNINGLN("Released unlocked resource \"%s\".", _FileName.c_str());
+}
diff --git a/engines/sword25/kernel/resource.h b/engines/sword25/kernel/resource.h
new file mode 100755
index 0000000000..5d6c542b07
--- /dev/null
+++ b/engines/sword25/kernel/resource.h
@@ -0,0 +1,97 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RESOURCE_H
+#define BS_RESOURCE_H
+
+#include "memlog_off.h"
+#include <list>
+#include "memlog_on.h"
+
+#include "common.h"
+
+class BS_Kernel;
+class BS_ResourceManager;
+
+class BS_Resource
+{
+friend BS_ResourceManager;
+
+public:
+ enum RESOURCE_TYPES
+ {
+ TYPE_UNKNOWN,
+ TYPE_BITMAP,
+ TYPE_ANIMATION,
+ TYPE_SOUND,
+ TYPE_FONT
+ };
+
+ BS_Resource(const std::string& UniqueFileName, RESOURCE_TYPES Type);
+
+ /**
+ * @brief `Lockt' die Resource, verhindert, dass sie freigegeben wird.
+ * @remarks Die Resource wird bereits `gelockt' initialisiert, sie muss also nach dem Anfordern nur
+ * gelockt werden, wenn sie mehrfach verwendet wird.
+ **/
+
+ void AddReference() { ++_RefCount; }
+
+ /**
+ * @brief Hebt ein vorhergehendes `lock' auf.
+ * @remarks Die Resource kann ruhig öfter freigegeben als `gelockt' werden, auch wenn das nicht gerade empfehlenswert ist.
+ **/
+
+ void Release();
+
+ /**
+ * @brief Gibt die Anzahl der aktuellen `locks' zurück.
+ * @return Die Zahl der `locks'.
+ **/
+
+ int GetLockCount() const { return _RefCount; }
+
+ /**
+ @brief Gibt den absoluten, eindeutigen Dateinamen der Resource zurück.
+ */
+
+ const std::string & GetFileName() const { return _FileName; }
+
+ /**
+ @brief Gibt den Hash des Dateinames der Resource zurück.
+ */
+ unsigned int GetFileNameHash() const { return _FileNameHash; }
+
+ /**
+ @brief Gibt den Typ der Ressource zurück.
+ */
+ unsigned int GetType() const { return _Type; }
+
+protected:
+ virtual ~BS_Resource() {};
+
+private:
+ std::string _FileName; //!< Der absolute Dateiname
+ unsigned int _FileNameHash; //!< Der Hashwert des Dateinames
+ unsigned int _RefCount; //!< Anzahl an Locks
+ unsigned int _Type; //!< Der Typ der Resource
+ std::list<BS_Resource*>::iterator _Iterator; //!< Der Iterator zeigt auf Position der Resource in der LRU-Liste
+};
+
+#endif
diff --git a/engines/sword25/kernel/resservice.h b/engines/sword25/kernel/resservice.h
new file mode 100755
index 0000000000..d2369c0d87
--- /dev/null
+++ b/engines/sword25/kernel/resservice.h
@@ -0,0 +1,110 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RESOURCESERVICE_H
+#define BS_RESOURCESERVICE_H
+
+// Includes
+#include "common.h"
+#include "service.h"
+#include "kernel.h"
+#include "resmanager.h"
+
+class BS_Resource;
+
+class BS_ResourceService : public BS_Service
+{
+public:
+ BS_ResourceService(BS_Kernel* pKernel) : BS_Service(pKernel)
+ {
+ BS_ResourceManager* pResource = pKernel->GetResourceManager();
+ pResource->RegisterResourceService(this);
+ }
+
+ virtual ~BS_ResourceService() {}
+
+
+ /**
+ @brief Lädt eine Resource.
+ @param FileName Dateiname
+ @return gibt die Resource zurück, falls erfolgreich, ansonsten NULL
+ */
+ virtual BS_Resource* LoadResource(const std::string& FileName) = 0;
+
+ /**
+ @brief CanLoadResource prüft, ob Resource vom Service geladen werden kann.
+ @param FileName Dateiname
+ @return true, falls Service Resource laden kann
+ @remark Überprüfung basiert auf dem Dateinamen, wenn das Dateiformat nicht passt, gibts Ärger.
+ */
+ virtual bool CanLoadResource(const std::string& FileName) = 0;
+
+protected:
+ // Hilfsmethoden für BS_ResourceService Klassen
+
+ /**
+ @brief Vergleicht zwei Strings, wobei der zweite String die Wildcards * und ? enthalten darf
+ @param String der erste Vergleichsstring. Dieser darf keine Wildcards enthalten.
+ @param Pattern der zweite Vergleichsstring. Dieser darf die Wildcards * und ? enthalten.
+ @return Gibt true zurück, wenn der String auf das Pattern passt, ansonsten false.
+ */
+ bool _WildCardStringMatch(const std::string& String, const std::string& Pattern)
+ {
+ return _WildCardStringMatchRecursion(String.c_str(), Pattern.c_str());
+ }
+
+private:
+ bool _WildCardStringMatchRecursion(const char* String, const char* Pattern)
+ {
+ // Rekursionsabschlüsse:
+ // 1. Der Pattern-String enthält nur noch * -> TRUE
+ if (*Pattern == '*')
+ {
+ // Es muss mit einer Kopie von Pattern gearbeitet werden um den aktuellen Zustand nicht zu zerstören
+ char* PatternCopy = (char*) Pattern;
+ while (*PatternCopy == '*') { PatternCopy++; }
+ if (!*PatternCopy) return true;
+ }
+ // 2. Der String ist zuende, das Pattern aber noch nicht -> FALSE
+ if (!*String && *Pattern) return false;
+ // 3. Der String ist zuende, also ist auch das Pattern zuende (s.o.) -> TRUE
+ if (!*String) return true;
+
+ // Rekursionsaufruf 1:
+ // Falls die beiden aktuellen Zeichen gleich sind, oder Pattern '?' ist wird das Ergebnis von restlichen String zurückgegeben
+ if (*String == *Pattern || *Pattern == '?') return _WildCardStringMatchRecursion(String + 1, Pattern + 1);
+
+ // Falls nicht, wird untersucht ob ein '*' vorliegt
+ if (*Pattern == '*')
+ {
+ // Rekursionsaufruf 2:
+ // Zunächst wird das Ergebnis von String und Pattern + 1 untersucht...
+ if (_WildCardStringMatchRecursion(String, Pattern + 1)) return true;
+ // Falls das fehlschlägt, wird das Ergebnis von String + 1 Pattern zurückgegeben
+ else return _WildCardStringMatchRecursion(String + 1, Pattern);
+ // Die Rekursion kehrt also immer wieder an diese Stelle zurück, bis das Zeichen in String,
+ // dem nach dem '*' in Pattern entspricht
+ }
+
+ // Wenn kein '*' in Pattern vorliegt, schlägt der Vergleich fehl
+ return false;
+ }
+};
+
+#endif
diff --git a/engines/sword25/kernel/service.h b/engines/sword25/kernel/service.h
new file mode 100755
index 0000000000..d79194f41a
--- /dev/null
+++ b/engines/sword25/kernel/service.h
@@ -0,0 +1,57 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Service
+ -------------
+ Dies ist die Basisklasse für alle Services der Engine.
+ Ein Service ist ein wesentlicher Bestandteil des Engine, z.B. die Graphiksystem.
+ Das Servicesystem macht es möglich mehrere verschiedene Services für ein System zu haben,
+ und je nach Betriebssystem einen passenden auszuwählen.
+ Denkbar wären z.B. zwei Graphiksysteme von denen eines hardwarebeschleunigt ist und ein
+ anderes nicht.
+ Die Services werden zur Laufzeit über die Kernelmethode NewService und NIEMALS mit new erzeugt.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_SERVICE_H
+#define _BS_SERVICE_H
+
+// Includes
+#include "common.h"
+
+// Klassendefinition
+class BS_Kernel;
+
+class BS_Service
+{
+private:
+ BS_Kernel* _pKernel;
+
+protected:
+ BS_Service(BS_Kernel* pKernel) : _pKernel(pKernel) {};
+
+ BS_Kernel* GetKernel() const { return _pKernel; }
+
+public:
+ virtual ~BS_Service(){};
+};
+
+#endif \ No newline at end of file
diff --git a/engines/sword25/kernel/service_ids.h b/engines/sword25/kernel/service_ids.h
new file mode 100755
index 0000000000..38ba941f5e
--- /dev/null
+++ b/engines/sword25/kernel/service_ids.h
@@ -0,0 +1,56 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ service_ids.h
+ -------------
+ In dieser Datei sind alle Services verzeichnet.
+ JEDER neuer Service muss hier eingetragen werden, ansonsten kann er nicht mit
+ pKernel->NewService(..) instanziiert werden.
+
+ Autor: Malte Thiesen
+*/
+
+#include "common.h"
+
+BS_Service * BS_OpenGLGfx_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_PhysfsPackageManager_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_StdWinInput_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_FMODExSound_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_LuaScriptEngine_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_Geometry_CreateObject(BS_Kernel* pKernel);
+BS_Service * BS_OggTheora_CreateObject(BS_Kernel* pKernel);
+
+// Services müssen in dieser Tabelle eingetragen werden
+const BS_ServiceInfo BS_SERVICE_TABLE[] =
+{
+ // Die ersten beiden Parameter sind die Namen der Superclass und des Services.
+ // Der dritte Parameter ist die statische Methode der Klasse, die ein Objekt der Klasse erzeugt und zurückgibt.
+ // Beispiel:
+ // BS_ServiceInfo("Superclass", "Service", CreateMethod)
+ BS_ServiceInfo("gfx", "opengl", BS_OpenGLGfx_CreateObject),
+ BS_ServiceInfo("package", "physfs", BS_PhysfsPackageManager_CreateObject),
+ BS_ServiceInfo("input", "winapi", BS_StdWinInput_CreateObject),
+ BS_ServiceInfo("sfx", "fmodex", BS_FMODExSound_CreateObject),
+ BS_ServiceInfo("script", "lua", BS_LuaScriptEngine_CreateObject),
+ BS_ServiceInfo("geometry", "std", BS_Geometry_CreateObject),
+ BS_ServiceInfo("fmv", "oggtheora", BS_OggTheora_CreateObject),
+};
+
+const unsigned int BS_SERVICE_COUNT = sizeof(BS_SERVICE_TABLE) / sizeof(BS_ServiceInfo);
diff --git a/engines/sword25/kernel/string.h b/engines/sword25/kernel/string.h
new file mode 100755
index 0000000000..b6802d3075
--- /dev/null
+++ b/engines/sword25/kernel/string.h
@@ -0,0 +1,117 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_STRING
+#define BS_STRING
+
+#include "memlog_off.h"
+#include <string>
+#include "memlog_on.h"
+
+namespace BS_String
+{
+ inline unsigned int GetHash(const std::string & Str)
+ {
+ unsigned int Result = 0;
+
+ for (unsigned int i = 0; i < Str.size(); i++)
+ Result = ((Result << 5) - Result) + Str[i];
+
+ return Result;
+ }
+
+ inline bool ToInt(const std::string & Str, int & Result)
+ {
+ std::string::const_iterator Iter = Str.begin();
+
+ // Whitespace überspringen
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; }
+ if (Iter == Str.end()) return false;
+
+ // Vorzeichen auslesen, wenn vorhanden
+ bool IsNegative = false;
+ if (*Iter == '-')
+ {
+ IsNegative = true;
+ ++Iter;
+ }
+ else if (*Iter == '+')
+ ++Iter;
+
+ // Whitespace überspringen
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; }
+ if (Iter ==Str.end()) return false;
+
+ // String in Ganzzahl umwandeln
+ Result = 0;
+ while (Iter != Str.end())
+ {
+ if (*Iter < '0' || *Iter > '9')
+ {
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) { ++Iter; }
+ if (Iter != Str.end()) return false;
+ break;
+ }
+ Result = (Result * 10) + (*Iter - '0');
+ ++Iter;
+ }
+
+ if (IsNegative) Result = -Result;
+
+ return true;
+ }
+
+ inline bool ToBool(const std::string & Str, bool & Result)
+ {
+ if (Str == "true" ||
+ Str == "TRUE")
+ {
+ Result = true;
+ return true;
+ }
+ else if (Str == "false" ||
+ Str == "FALSE")
+ {
+ Result = false;
+ return true;
+ }
+
+ return false;
+ }
+
+ inline void ToLower(std::string & Str)
+ {
+ static const unsigned char LowerCaseMap[256] =
+ {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
+ 64,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,228,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,246,215,216,217,218,219,252,221,222,223,
+ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+ };
+
+ for (unsigned int i = 0; i < Str.size(); ++i)
+ Str[i] = LowerCaseMap[Str[i]];
+ }
+}
+
+#endif
diff --git a/engines/sword25/kernel/timer.cpp b/engines/sword25/kernel/timer.cpp
new file mode 100755
index 0000000000..22eb1200f3
--- /dev/null
+++ b/engines/sword25/kernel/timer.cpp
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "timer.h"
+
+#define BS_LOG_PREFIX "BS_TIMER"
+
+uint64_t BS_Timer::GetMicroTicks()
+{
+ HANDLE ThreadID = ::GetCurrentThread();
+
+ DWORD_PTR OldAffinityMask = ::SetThreadAffinityMask(ThreadID, 1);
+
+ uint64_t Frequency;
+ ::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&Frequency));
+
+ uint64_t Tick;
+ ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&Tick));
+
+ ::SetThreadAffinityMask(ThreadID, OldAffinityMask);
+
+ return Tick * 1000000 / Frequency;
+}
+
+unsigned int BS_Timer::GetMilliTicks()
+{
+ return (unsigned int)(GetMicroTicks() / 1000);
+}
+
+bool BS_Timer::IsTimerAvaliable()
+{
+ LARGE_INTEGER Dummy;
+ return ::QueryPerformanceFrequency(&Dummy) ? true : false;
+}
diff --git a/engines/sword25/kernel/timer.h b/engines/sword25/kernel/timer.h
new file mode 100755
index 0000000000..52a7d70a83
--- /dev/null
+++ b/engines/sword25/kernel/timer.h
@@ -0,0 +1,69 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Timer
+ --------
+ Eine Klasse zum Auslesen des Systemtimers.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_TIMER_H
+#define BS_TIMER_H
+
+// Includes
+#include "common.h"
+#include "bs_stdint.h"
+
+/**
+ @brief Eine Klasse zum Auslesen des Systemtimers.
+
+ Vor der Benutzung sollte immer mit IsTimerAvaliable() getestet werden, ob der Timer von der benutzten
+ Hardware unterstützt wird.
+*/
+class BS_Timer
+{
+public:
+ /**
+ @brief List den Systemtimer in Microsekunden aus.
+ @return Die Zahl vergangener Microsekunden seit dem Systemstart.<br>
+ */
+ static uint64_t GetMicroTicks();
+
+ /**
+ @brief List den Systemtimer in Millisekunden aus.
+ @return Die Zahl vergangener Millisekunden seit dem Systemstart.<br>
+ */
+ static unsigned int GetMilliTicks();
+
+ /**
+ @brief Prüft, ob der Timer auf der vorhandenen Hardware existiert.
+ @return Gibt false zurück, fals der Timer auf der vorhandenen Hardware nicht existiert.
+ */
+ static bool IsTimerAvaliable();
+
+private:
+ /**
+ @brief Initialisiert den Timer.
+ */
+ static void Init();
+};
+
+#endif
diff --git a/engines/sword25/kernel/win32window.cpp b/engines/sword25/kernel/win32window.cpp
new file mode 100755
index 0000000000..b5c519f206
--- /dev/null
+++ b/engines/sword25/kernel/win32window.cpp
@@ -0,0 +1,425 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "win32window.h"
+#include "../../projects/resource.h"
+
+#include "kernel/kernel.h"
+#include "input/inputengine.h"
+
+bool BS_Win32Window::_ClassRegistered = false;
+
+#define BS_LOG_PREFIX "WIN32WINDOW"
+
+
+// Konstanten
+// ----------
+static const UINT WINDOW_STYLE = WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+static const UINT WINDOW_STYLE_EX = 0;
+static const UINT WINDOW_MAX_MESSAGES = 50;
+
+// Konstruktion/Destruktion
+// ------------------------
+BS_Win32Window::BS_Win32Window(int X, int Y, int Width, int Height, bool Visible)
+{
+ const char WINDOW_CLASS[] = "BSEngine-Class";
+
+ // Von negativen Fall ausgehen
+ _InitSuccess = false;
+
+ // Fensterklasse registrieren falls nötig
+ if (!_ClassRegistered)
+ {
+ //Fensterklasse
+ WNDCLASSEX wndclass;
+
+ //Werte der Fensterklasse festlegen
+ ZeroMemory(&wndclass, sizeof(WNDCLASSEX));
+ wndclass.cbSize = sizeof(WNDCLASSEX);
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wndclass.lpfnWndProc = BS_Win32Window::WindowProc;
+ wndclass.hInstance = GetModuleHandle(NULL);
+ wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
+ wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
+ wndclass.hCursor = NULL;
+ wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wndclass.lpszClassName = WINDOW_CLASS;
+
+ //Fensterklasse registrieren
+ if (!RegisterClassEx(&wndclass)) return;
+
+ _ClassRegistered = true;
+ }
+
+ //Fenster erstellen
+ if (!(_Window=CreateWindowEx(
+ WINDOW_STYLE_EX, // Erweiterte Darstellungsflags
+ WINDOW_CLASS, // Registrierter Fenstername
+ "", // Kein Fenstertitel
+ WINDOW_STYLE, // Darstellungsflags
+ 0,0, // Default-Position
+ 0,0, // Default-Grösse
+ NULL, // Kein Parent-Fenster
+ NULL, // Kein Menü
+ GetModuleHandle(NULL), // Instance-Handle
+ NULL)))
+ return;
+
+ // Fensterposition und Fenstergröße setzen
+ SetWidth(Width);
+ SetHeight(Height);
+ SetX(X);
+ SetY(Y);
+
+ // Fenstersichtbarkeit setzen
+ SetVisible(Visible);
+
+ // Icon setzen
+ HICON hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1));
+ if (hIcon)
+ {
+ SendMessage(_Window, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
+ SendMessage(_Window, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
+ }
+
+ // Erfolg signalisieren
+ _InitSuccess = true;
+ _WindowAlive = true;
+ _CloseWanted = false;
+}
+
+BS_Win32Window::~BS_Win32Window()
+{
+ // Fenster zerstören, falls dies nicht ohnehin schon passiert ist
+ if (_WindowAlive) DestroyWindow(_Window);
+}
+
+// Get-Methoden
+// ------------
+int BS_Win32Window::GetX()
+{
+ RECT Rect;
+ GetWindowRect(_Window, &Rect);
+ return Rect.left;
+}
+
+int BS_Win32Window::GetY()
+{
+ RECT Rect;
+ GetWindowRect(_Window, &Rect);
+ return Rect.top;
+}
+
+int BS_Win32Window::GetClientX()
+{
+ POINT Point = {0, 0};
+ ClientToScreen(_Window, &Point);
+ return Point.x;
+}
+
+int BS_Win32Window::GetClientY()
+{
+ POINT Point = {0, 0};
+ ClientToScreen(_Window, &Point);
+ return Point.y;
+}
+
+int BS_Win32Window::GetWidth()
+{
+ RECT Rect;
+ GetClientRect(_Window, &Rect);
+ return Rect.right - Rect.left;
+}
+
+int BS_Win32Window::GetHeight()
+{
+ RECT Rect;
+ GetClientRect(_Window, &Rect);
+ return Rect.bottom - Rect.top;
+}
+
+std::string BS_Win32Window::GetTitle()
+{
+ char String[512];
+ if (GetWindowText(_Window, String, sizeof(String)))
+ return std::string(String);
+
+ return std::string("");
+}
+
+bool BS_Win32Window::IsVisible()
+{
+ return IsWindowVisible(_Window) ? true : false;
+}
+
+bool BS_Win32Window::HasFocus()
+{
+ return GetForegroundWindow() == _Window ? true : false;
+}
+
+UINT BS_Win32Window::GetWindowHandle()
+{
+ return (UINT)_Window;
+}
+
+// Set Methoden
+// ------------
+
+void BS_Win32Window::SetX(int X)
+{
+ int RealX;
+ if (X == -1)
+ {
+ RECT Rect;
+ GetWindowRect(_Window, &Rect);
+ RealX = (GetSystemMetrics(SM_CXSCREEN) - (Rect.right - Rect.left)) / 2;
+ }
+ else
+ RealX = X;
+
+ SetWindowPos(_Window, NULL, RealX, GetY(), 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+}
+
+void BS_Win32Window::SetY(int Y)
+{
+ int RealY;
+ if (Y == -1)
+ {
+ RECT Rect;
+ GetWindowRect(_Window, &Rect);
+ RealY = (GetSystemMetrics(SM_CYSCREEN) - (Rect.bottom - Rect.top)) / 2;
+ }
+ else
+ RealY = Y;
+
+ SetWindowPos(_Window, NULL, GetX(), RealY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+}
+
+void BS_Win32Window::SetWidth(int Width)
+{
+ RECT Rect = {0, 0, Width, GetHeight()};
+ AdjustWindowRectEx(&Rect, WINDOW_STYLE, false, WINDOW_STYLE_EX);
+ SetWindowPos(_Window, NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void BS_Win32Window::SetHeight(int Height)
+{
+ RECT Rect = {0, 0, GetWidth(), Height};
+ AdjustWindowRectEx(&Rect, WINDOW_STYLE, false, WINDOW_STYLE_EX);
+ SetWindowPos(_Window, NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void BS_Win32Window::SetVisible(bool Visible)
+{
+ ShowWindow(_Window, Visible ? SW_SHOW : SW_HIDE);
+}
+
+void BS_Win32Window::SetTitle(std::string Title)
+{
+ SetWindowText(_Window, Title.c_str());
+}
+
+// Asynchroner Message-Loop
+bool BS_Win32Window::ProcessMessages()
+{
+ for (UINT i = 0; i < WINDOW_MAX_MESSAGES; i++)
+ {
+ MSG msg;
+ if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ _WindowAlive = false;
+ return false;
+ }
+
+ // Alle Nachrichten zur Verarbeitung durch WindowProc vorbereiten
+ TranslateMessage(&msg);
+ // Nachricht an WindowProc übergeben
+ DispatchMessage(&msg);
+ }
+ else
+ return true;
+ }
+
+ return true;
+}
+
+// Synchroner Message-Loop
+bool BS_Win32Window::WaitForFocus()
+{
+ MSG msg;
+
+ // Fenster minimieren
+ ShowWindow(_Window, SW_MINIMIZE);
+
+ for (;;)
+ {
+ // Auf Nachricht warten
+ WaitMessage();
+ // Nachricht einlesen
+ GetMessage(&msg, NULL, 0, 0);
+ // Nachricht zur Verarbeitung durch WindowProc vorbereiten
+ TranslateMessage(&msg);
+ // Nachricht an WindowProc übergeben
+ DispatchMessage(&msg);
+
+ // Überprüfen ob das Fenster geschlossen wurde
+ if (msg.message == WM_QUIT)
+ {
+ _WindowAlive = false;
+ return false;
+ }
+
+ // Überprüfen, ob das Fenster den Focus wiedererlangt hat
+ if (HasFocus()) return true;
+ }
+}
+
+// Die WindowProc aller Fenster der Klasse
+LRESULT CALLBACK BS_Win32Window::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ ValidateRect(hwnd, NULL);
+ break;
+
+ case WM_DESTROY:
+ // Das Fenster wird zerstört
+ PostQuitMessage(0);
+ break;
+
+ case WM_CLOSE:
+ {
+ BS_Window * WindowPtr = BS_Kernel::GetInstance()->GetWindow();
+ if (WindowPtr) {
+ WindowPtr->SetCloseWanted(true);
+ }
+ break;
+ }
+
+ case WM_KEYDOWN:
+ {
+ // Tastendrücke, die für das Inputmodul interessant sind, werden diesem gemeldet.
+ BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
+
+ if (InputPtr)
+ {
+ switch (wParam)
+ {
+ case VK_RETURN:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_ENTER);
+ break;
+
+ case VK_LEFT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_LEFT);
+ break;
+
+ case VK_RIGHT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_RIGHT);
+ break;
+
+ case VK_HOME:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_HOME);
+ break;
+
+ case VK_END:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_END);
+ break;
+
+ case VK_BACK:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_BACKSPACE);
+ break;
+
+ case VK_TAB:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_TAB);
+ break;
+
+ case VK_INSERT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_INSERT);
+ break;
+
+ case VK_DELETE:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_DELETE);
+ break;
+ }
+ }
+ break;
+ }
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ // Alle Tastendrücke werden ignoriert, damit Windows per DefWindowProc() nicht darauf
+ // reagieren kann und damit unerwartete Seiteneffekte auslöst.
+ // Zum Beispiel würden ALT und F10 Tastendrücke das "Menü" aktivieren und somit den Message-Loop zum Stillstand bringen.
+ break;
+
+ case WM_SYSCOMMAND:
+ // Verhindern, dass der Bildschirmschoner aktiviert wird, während das Spiel läuft
+ if (wParam != SC_SCREENSAVE) return DefWindowProc(hwnd,uMsg,wParam,lParam);
+ break;
+
+ case WM_CHAR:
+ {
+ unsigned char theChar = static_cast<unsigned char>(wParam & 0xff);
+
+ // Alle Zeichen, die keine Steuerzeichen sind, werden als Buchstaben dem Input-Service mitgeteilt.
+ if (theChar >= 32)
+ {
+ BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
+ if (InputPtr) InputPtr->ReportCharacter(theChar);
+ }
+ }
+ break;
+
+ case WM_SETCURSOR:
+ {
+ // Der Systemcursor wird in der Client-Area des Fensters nicht angezeigt, jedoch in der nicht Client-Area, damit der Benutzer das Fenster wie gewohnt
+ // schließen und verschieben kann.
+
+ // Koordinaten des Cursors in der Client-Area berechnen.
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwnd, &pt);
+
+ // Feststellen, ob sich der Cursor in der Client-Area befindet.
+ // Get client rect
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+
+ // See if cursor is in client area
+ if(PtInRect(&rc, pt))
+ // In der Client-Area keinen Cursor anzeigen.
+ SetCursor(NULL);
+ else
+ // Ausserhalb der Client-Area den Cursor anzeigen.
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ return TRUE;
+ }
+ break;
+
+ default:
+ // Um alle anderen Vorkommnisse kümmert sich Windows
+ return DefWindowProc(hwnd,uMsg,wParam,lParam);
+ }
+
+ return 0;
+}
diff --git a/engines/sword25/kernel/win32window.h b/engines/sword25/kernel/win32window.h
new file mode 100755
index 0000000000..e37892a0cb
--- /dev/null
+++ b/engines/sword25/kernel/win32window.h
@@ -0,0 +1,77 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Win32Window
+ -------------
+ Implementation des BS_Window Interfaces für Win32.
+ Zu den einzelnen Methoden bitte "window.h" konsultieren.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_WIN32WINDOW_H
+#define _BS_WIN32WINDOW_H
+
+// Includes
+#include "memlog_off.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include "memlog_on.h"
+
+#include "common.h"
+#include "window.h"
+
+// Klassendefinition
+class BS_Win32Window : public BS_Window
+{
+public:
+ BS_Win32Window(int X, int Y, int Width, int Height, bool Visible);
+ virtual ~BS_Win32Window();
+
+ bool IsVisible();
+ void SetVisible(bool Visible);
+ int GetX();
+ void SetX(int X);
+ int GetY();
+ void SetY(int X);
+ int GetClientX();
+ int GetClientY();
+ int GetWidth();
+ void SetWidth(int Width);
+ int GetHeight();
+ void SetHeight(int Height);
+ std::string GetTitle();
+ void SetTitle(std::string Title);
+ bool HasFocus();
+ UINT GetWindowHandle();
+ bool WaitForFocus();
+ bool ProcessMessages();
+
+private:
+ static bool _ClassRegistered;
+ bool _WindowAlive;
+ HWND _Window;
+ int _ClientXDelta;
+ int _ClientYDelta;
+
+ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+};
+
+#endif \ No newline at end of file
diff --git a/engines/sword25/kernel/wincodegenerator.cpp b/engines/sword25/kernel/wincodegenerator.cpp
new file mode 100755
index 0000000000..1c264ad17b
--- /dev/null
+++ b/engines/sword25/kernel/wincodegenerator.cpp
@@ -0,0 +1,72 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "md5.h"
+#include "wincodegenerator.h"
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Hilfsfunktionen und Konstanten
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char SECRET[] = "LSZNRVWQJHITMIEGESJMZAYVKGTCDT";
+
+ // -------------------------------------------------------------------------
+
+ string EncodeValue(unsigned int Value)
+ {
+ string Result;
+
+ for (unsigned int i = 0; i < 7; ++i)
+ {
+ Result.push_back(65 + Value % 26);
+ Value /= 26;
+ }
+
+ return Result;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+string BS_WinCodeGenerator::GetWinCode()
+{
+ // Die System-ID generieren und als String codieren.
+ string SystemID = EncodeValue(GetSystemID());
+
+ // Den Hashwert der System-ID mit dem geheimen String berechnen.
+ BS_MD5 md5;
+ string HashData = SystemID + SECRET;
+ md5.Update(reinterpret_cast<const unsigned char *>(&HashData[0]), HashData.size());
+ unsigned char Digest[16];
+ md5.GetDigest(Digest);
+
+ // Die ersten 32-Bit des Digest werden aus dem Digest extrahiert. Zudem wird das oberste Bit ausmaskiert.
+ // So ist es einfacher den Code serverseitig zu überprüfen, da viele Scriptsprachen mit 32-Bit signed integern rechnen.
+ unsigned int ValidationHash = ((Digest[3] & 0x7f) << 24) + (Digest[2] << 16) + (Digest[1] << 8) + Digest[0];
+
+ // Der Code besteht aus der codierten System-ID und dem codierten Hash.
+ return SystemID + EncodeValue(ValidationHash);
+}
diff --git a/engines/sword25/kernel/wincodegenerator.h b/engines/sword25/kernel/wincodegenerator.h
new file mode 100755
index 0000000000..7e1ee78bd0
--- /dev/null
+++ b/engines/sword25/kernel/wincodegenerator.h
@@ -0,0 +1,43 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_WIN_CODE_GENERATOR_H
+#define BS_WIN_CODE_GENERATOR_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include "common.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_WinCodeGenerator
+{
+public:
+ static std::string GetWinCode();
+
+private:
+ static unsigned int GetSystemID();
+};
+
+#endif
diff --git a/engines/sword25/kernel/wincodegenerator_win32.cpp b/engines/sword25/kernel/wincodegenerator_win32.cpp
new file mode 100755
index 0000000000..571a4b9f86
--- /dev/null
+++ b/engines/sword25/kernel/wincodegenerator_win32.cpp
@@ -0,0 +1,128 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <LMCons.h>
+
+#include <vector>
+using namespace std;
+
+#include "md5.h"
+#include "wincodegenerator.h"
+
+// -----------------------------------------------------------------------------
+// Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ void AddedFixedDrivesEntropy(BS_MD5 & md5)
+ {
+ if (DWORD LogicalDrivesMask = ::GetLogicalDrives())
+ {
+ // Über alle Laufwerke iterieren.
+ char CurrentDriveLetter[] = "A:\\";
+ while (LogicalDrivesMask && CurrentDriveLetter[0] <= 'Z')
+ {
+ if (LogicalDrivesMask & 1)
+ {
+ // Nur feste Laufwerke werden betrachtet, ansonsten würde sich die System-ID ändern, wenn jemand einen USB-Stick ansteckt oder
+ // eine CD einlegt.
+ if (::GetDriveTypeA(CurrentDriveLetter) == DRIVE_FIXED)
+ {
+ // Laufwerksinformationen auslesen.
+ CHAR VolumeNameBuffer[MAX_PATH + 1];
+ DWORD SerialNumber;
+ DWORD MaximumComponentLength;
+ DWORD FileSystemFlags;
+ CHAR FileSystemNameBuffer[MAX_PATH + 1];
+ if (::GetVolumeInformationA(CurrentDriveLetter,
+ VolumeNameBuffer, sizeof(VolumeNameBuffer),
+ &SerialNumber,
+ &MaximumComponentLength,
+ &FileSystemFlags,
+ FileSystemNameBuffer, sizeof(FileSystemNameBuffer)))
+ {
+ // Als Entropie werden genutzt: Laufwerksbuchstabe, Laufwerksbezeichnung, Seriennummer und Dateisystemname.
+ md5.Update(reinterpret_cast<const unsigned char *>(CurrentDriveLetter), strlen(CurrentDriveLetter));
+ md5.Update(reinterpret_cast<const unsigned char *>(VolumeNameBuffer), strlen(VolumeNameBuffer));
+ md5.Update(reinterpret_cast<const unsigned char *>(&SerialNumber), sizeof(SerialNumber));
+ md5.Update(reinterpret_cast<const unsigned char *>(FileSystemNameBuffer), strlen(FileSystemNameBuffer));
+ }
+ }
+ }
+
+ LogicalDrivesMask >>= 1;
+ ++CurrentDriveLetter[0];
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ void AddUserNameEntropy(BS_MD5 & md5)
+ {
+ // Benutzernamen auslesen und als Entropie nutzen.
+ DWORD UserNameLength = UNLEN + 1;
+ CHAR UserName[UNLEN + 1];
+ if (::GetUserNameA(UserName, &UserNameLength))
+ {
+ md5.Update(reinterpret_cast<const unsigned char *>(&UserName[0]), strlen(UserName));
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ void AddOSVersionEntropy(BS_MD5 & md5)
+ {
+ // Windows-Version auslesen und in die Einzelkomponenten MajorVersion, MinorVersion und Build aufspalten.
+ DWORD VersionInfo = ::GetVersion();
+
+ DWORD MajorVersion = (DWORD)(LOBYTE(LOWORD(VersionInfo)));
+ DWORD MinorVersion = (DWORD)(HIBYTE(LOWORD(VersionInfo)));
+ DWORD Build = 0;
+ if (VersionInfo < 0x80000000) Build = (DWORD)(HIWORD(VersionInfo));
+
+ // Diese drei Informationen als Entropie nutzen.
+ md5.Update(reinterpret_cast<const unsigned char *>(&MajorVersion), sizeof(DWORD));
+ md5.Update(reinterpret_cast<const unsigned char *>(&MinorVersion), sizeof(DWORD));
+ md5.Update(reinterpret_cast<const unsigned char *>(&Build), sizeof(DWORD));
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_WinCodeGenerator::GetSystemID()
+{
+ BS_MD5 md5;
+
+ AddedFixedDrivesEntropy(md5);
+ AddUserNameEntropy(md5);
+ AddOSVersionEntropy(md5);
+
+ unsigned char Digest[16];
+ md5.GetDigest(Digest);
+
+ return (Digest[3] << 24) + (Digest[2] << 16) + (Digest[1] << 8) + Digest[0];
+}
diff --git a/engines/sword25/kernel/window.cpp b/engines/sword25/kernel/window.cpp
new file mode 100755
index 0000000000..b7b6e0aa27
--- /dev/null
+++ b/engines/sword25/kernel/window.cpp
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "window.h"
+
+// Alle Implementationen von BS_Window müssen hier eingetragen werden
+#include "win32window.h"
+
+// Erstellt ein Fenster des GUI des aktuellen Betriebssystems
+BS_Window* BS_Window::CreateBSWindow(int X, int Y, int Width, int Height, bool Visible)
+{
+ // Fenster erstellen
+ BS_Window* pWindow = (BS_Window*) new BS_Win32Window(X, Y, Width, Height, Visible);
+
+ // Falls das Fenster erfolgreich initialisiert wurde, wird ein Pointer auf das Fensterobjekt
+ // zurückgegeben
+ if (pWindow->_InitSuccess)
+ return pWindow;
+
+ // Ansonsten wird das Fensterobjekt zerstört und NULL zurückgegeben
+ delete pWindow;
+ return NULL;
+}
+
+// Gibt True zurück wenn das Fenster WM_CLOSE empfangen hat -
+// solange, bis RejectClose() aufgerufen wurde.
+bool BS_Window::CloseWanted()
+{
+ bool result = _CloseWanted;
+ _CloseWanted = false;
+ return result;
+}
+
+void BS_Window::SetCloseWanted(bool Wanted)
+{
+ _CloseWanted = Wanted;
+}
diff --git a/engines/sword25/kernel/window.h b/engines/sword25/kernel/window.h
new file mode 100755
index 0000000000..6a6121f247
--- /dev/null
+++ b/engines/sword25/kernel/window.h
@@ -0,0 +1,176 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Window
+ ---------
+ Simples Fensterklasseninterface.
+ Ist nur aus Gründen der Portabilität in einer Klasse gekapselt.
+ TODO: Für andere Betriebssysteme implementieren
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_WINDOW_H
+#define _BS_WINDOW_H
+
+// Includes
+#include "common.h"
+
+// Klassendefinition
+/**
+ @brief Ein Simples Fensterklasseninterface.
+
+ Fenster werden ausschließlich mit BS_Window::CreateBSWindow() erstellt. BS_Window wählt
+ dann selbständig die richtige Klasse für das Betriebssystem aus.
+*/
+class BS_Window
+{
+protected:
+ bool _InitSuccess;
+ bool _CloseWanted;
+
+public:
+ virtual ~BS_Window(){};
+
+ /**
+ @brief Gibt den Sichtbarkeitsstatus des Fensters zurück
+ @return Gibt true zurück wenn das Fenster sichtbar ist, andernfalls false
+ */
+ virtual bool IsVisible() = 0;
+ /**
+ @brief Setzt den Sichtbarkeitsstatus des Fensters
+ @param Visible gibt an, ob das Fenster sichtbar oder versteckt sein soll
+ */
+ virtual void SetVisible(bool Visible) = 0;
+ /**
+ @brief Gibt die Position des Fensters auf der X-Achse zurück
+ @return Gibt die Position des Fensters auf der X-Achse zurück
+ */
+ virtual int GetX() = 0;
+ /**
+ @brief Setzt die Position des Fensters auf der X-Achse
+ @param X die X-Position des Fensters oder -1 für zentrierte Ausrichtung auf der X-Achse
+ */
+ virtual void SetX(int X) = 0;
+ /**
+ @brief Gibt die Position des Fensters auf der Y-Achse zurück
+ @return Gibt die Position des Fensters auf der Y-Achse zurück
+ */
+ virtual int GetY() = 0;
+ /**
+ @brief Setzt die Position des Fensters auf der Y-Achse
+ @param Y die Y-Position des Fensters oder -1 für zentrierte Ausrichtung auf der Y-Achse
+ */
+ virtual void SetY(int X) = 0;
+ /**
+ @brief Gibt die Position des Fensterinhaltes auf der X-Achse zurück
+ @return Gibt die Position des Fensterinhaltes auf der X-Achse zurück
+ */
+ virtual int GetClientX() = 0;
+ /**
+ @brief Gibt die Position des Fensterinhaltes auf der Y-Achse zurück
+ @return Gibt die Position des Fensterinhaltes auf der Y-Achse zurück
+ */
+ virtual int GetClientY() = 0;
+ /**
+ @brief Gibt die Breite des Fensters ohne Rahmen zurück
+ @return Gibt die Breite des Fensters ohne Rahmen zurück
+ */
+ virtual int GetWidth() = 0;
+ /**
+ @brief Setzt die Breite des Fensters ohne Rahmen
+ @param Width die Breite des Fensters ohne Rahmen
+ */
+ virtual void SetWidth(int Width) = 0;
+ /**
+ @brief Gibt die Höhe des Fensters ohne Rahmen und Kopfzeile zurück
+ @return Gibt die Höhe des Fensters ohne Rahmen und Kopfzeile zurück
+ */
+ virtual int GetHeight() = 0;
+ /**
+ @brief Setzt die Höhe des Fensters ohne Rahmen und Kopfzeile
+ @param Height die Höhe des Fensters ohne Rahmen und Kopfzeile
+ */
+ virtual void SetHeight(int Height) = 0;
+ /**
+ @brief Gibt den Titel der Fensters zurück
+ @return Gibt den Titel des Fenster zurück
+ */
+ virtual std::string GetTitle() = 0;
+ /**
+ @brief Setzt den Titel des Fensters
+ @param Title der neue Titel des Fensters
+ */
+ virtual void SetTitle(std::string Title) = 0;
+ /**
+ @brief Arbeitet die Windowmessages des Fensters ab.
+ Diese Methode sollte während des Main-Loops aufgerufen werden.
+ @return Gibt false zurück, falls das Fenster geschlossen wurde.
+ */
+ virtual bool ProcessMessages() = 0;
+ /**
+ @brief Pausiert die Applikation bis das Fenster wieder den Focus hat oder geschlossen wurde.
+ @return Gibt false zurück, falls das Fenster geschlossen wurde.
+ */
+ virtual bool WaitForFocus() = 0;
+ /**
+ @brief Gibt zurück, ob das Fenster den Focus hat.
+ @return Gibt true zurück, wenn das Fenster den Focus hat, ansonsten false
+ */
+ virtual bool HasFocus() = 0;
+ /**
+ @brief Gibt das Windowhandle, des Systems zurück.
+ @return Das Windowhandle des Fensters
+ @remark Wenn das Windowshandle benutzt wird, sind die entsprechenden Codeteile
+ natürlich nicht mehr portable.
+ */
+ virtual unsigned int GetWindowHandle() = 0;
+
+
+ /**
+ @brief Setzt den Rückgabewert für den nächsten Aufruf von CloseWanted. Sollte vom
+ Fenster selbst verwendet werden, wenn es geschlossen werden möchte. Dieser
+ Mechanismus erlaubt den Scripten abzufragen, wann das Hauptfenster geschlossen
+ werden soll, und sich entsprechend zu beenden, bzw. beim Nutzer nachzufragen.
+ **/
+ void SetCloseWanted(bool Wanted);
+ /**
+ @brief Gibt einmal den Wert des letztes Aufrufs von SetCloseWanted zurück,
+ und danach sofort wieder false, solange bis mit SetCloseWanted wieder
+ ein neuer Wert gesetzt wird.
+ **/
+ bool CloseWanted();
+
+
+ /**
+ @brief Erstellt eine neue Fensterinstanz.
+ @param X die X-Position des Fensters oder -1 für zentrierte Ausrichtung auf der X-Achse
+ @param Y des Y-Position des Fensters oder -1 für zentrierte Ausrichtung auf der Y-Achsen
+ @param Width die Breite des Fensters ohne Rahmen
+ @param Height die Höhe des Fensters ohne Rahmen und Kopfzeile
+ @param Visible gibt an, ob das Fenster dargestellt werden soll
+ @return Gibt einen Pointer auf ein Fensterobjekt zurück, oder NULL wenn das Erstellen
+ fehlgeschlagen ist.
+ @remark Das Fenster muss nach Benutzung mit delete freigegeben werden!
+ */
+ static BS_Window* CreateBSWindow(int X, int Y, int Width, int Height, bool Visible);
+};
+
+#endif \ No newline at end of file
diff --git a/engines/sword25/main.cpp b/engines/sword25/main.cpp
new file mode 100755
index 0000000000..0ef59ac7d7
--- /dev/null
+++ b/engines/sword25/main.cpp
@@ -0,0 +1,192 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+using namespace std;
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "kernel/filesystemutil.h"
+#include "kernel/debug/memorydumper.h"
+#include "script/script.h"
+#include "package/packagemanager.h"
+
+#define BS_LOG_PREFIX "MAIN"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * const PACKAGE_MANAGER = "physfs";
+ const char * const DEFAULT_SCRIPT_FILE = "/system/boot.lua";
+ const char * const MOUNT_DIR_PARAMETER = "-mount-dir";
+
+ // -------------------------------------------------------------------------
+
+ void LogToStdout(const char * Message)
+ {
+ printf("%s", Message);
+ }
+
+ // -------------------------------------------------------------------------
+
+ bool LoadPackages()
+ {
+ BS_PackageManager * PackageManagerPtr = reinterpret_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(PackageManagerPtr);
+
+ // Das Hauptpaket laden.
+ if (!PackageManagerPtr->LoadPackage("data.b25c", "/")) return false;
+
+ // Den Inhalt des Programmverzeichnisses bestimmen und alphabetisch sortieren.
+ vector<string> Filenames = BS_FileSystemUtil::GetInstance().GetFilesInDirectory(".");
+ sort(Filenames.begin(), Filenames.end());
+
+ // Alle Patch-Pakete identifizieren und mounten.
+ // Die Dateinamen der Patch-Pakete besitzen die Form: patch???.b25c, wobei die Fragezeichen Platzhalter für Ziffern sind.
+ // Da die Dateinamen sortiert wurden, werden Patches mit niedrigen Zahlen vor Patches mit hohen Zahlen gemounted.
+ // Dies ist wichtig, da neu gemountete Pakete vorhandene Dateien im virtuellen Dateisystem überschreiben, wenn sie
+ // gleichnamige Dateien beinhalten.
+ for (vector<string>::const_iterator it = Filenames.begin(); it != Filenames.end(); ++it)
+ {
+ const string & CurFilename = *it;
+
+ // Ist aktuelle Datei ein Patch-Paket?
+ static const string PatchPattern = "patch???.b25c";
+ if (CurFilename.size() == PatchPattern.size())
+ {
+ // Pattern Zeichen für Zeichen mit dem Dateinamen vergleichen.
+ string::const_iterator PatchPatternIt = PatchPattern.begin();
+ string::const_iterator CurFilenameIt = CurFilename.begin();
+ for (; PatchPatternIt != PatchPattern.end(); ++PatchPatternIt, ++CurFilenameIt)
+ {
+ if (*PatchPatternIt == '?')
+ {
+ if (*CurFilenameIt < '0' || *CurFilenameIt > '9') break;
+ }
+ else
+ {
+ if (*PatchPatternIt != *CurFilenameIt) break;
+ }
+ }
+
+ if (PatchPatternIt == PatchPattern.end())
+ {
+ // Pattern stimmt, Datei muss gemountet werden.
+ if (!PackageManagerPtr->LoadPackage(CurFilename, "/")) return false;
+ }
+ }
+ }
+
+ // Alle Sprach-Pakete identifizieren und mounten.
+ // Die Dateinamen der Sprach-Pakete besitzen die Form: lang_*.b25c (z.B. lang_de.b25c).
+ for (vector<string>::const_iterator it = Filenames.begin(); it != Filenames.end(); ++it)
+ {
+ const string & CurFilename = *it;
+
+ static const string Prefix = "lang_";
+ static const string Suffix = ".b25c";
+
+ if ((CurFilename.size() >= Prefix.size() && string(CurFilename.begin(), CurFilename.begin() + Prefix.size()) == Prefix) && // Präfix testen.
+ (CurFilename.size() >= Suffix.size() && string(CurFilename.end() - Suffix.size(), CurFilename.end()) == Suffix) && // Suffix testen.
+ (CurFilename.size() > Prefix.size() + Suffix.size())) // Sicherstellen, dass der Dateiname weitere Buchstaben zwischen Präfix und Suffix besitzt.
+ {
+ // Pattern stimmt, Datei muss gemountet werden.
+ if (!PackageManagerPtr->LoadPackage(CurFilename, "/")) return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool AppStart(const vector<string> & CommandLineParameters)
+{
+ // Alle Lognachrichten werden auch auf die Standardausgabe ausgegeben.
+ BS_Log::RegisterLogListener(LogToStdout);
+
+ // Kernel initialisieren.
+ if (!BS_Kernel::GetInstance()->GetInitSuccess())
+ {
+ BS_LOG_ERRORLN("Kernel initialization failed.");
+ return false;
+ }
+
+ // Package-Manager starten, damit die Packfiles geladen werden können.
+ BS_PackageManager * PackageManagerPtr = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->NewService("package", PACKAGE_MANAGER));
+ if (!PackageManagerPtr)
+ {
+ BS_LOG_ERRORLN("Packagemanager initialization failed.");
+ return false;
+ }
+
+ // Packages laden oder das aktuelle Verzeichnis mounten, wenn das über Kommandozeile angefordert wurde.
+ if (find(CommandLineParameters.begin(), CommandLineParameters.end(), MOUNT_DIR_PARAMETER) != CommandLineParameters.end())
+ {
+ if (!PackageManagerPtr->LoadDirectoryAsPackage(".", "/")) return false;
+ }
+ else
+ {
+ if (!LoadPackages()) return false;
+ }
+
+ // Einen Pointer auf den Skript-Engine holen.
+ BS_ScriptEngine * ScriptPtr = static_cast<BS_ScriptEngine *>(BS_Kernel::GetInstance()->GetService("script"));
+ if (!ScriptPtr)
+ {
+ BS_LOG_ERRORLN("Skript intialization failed.");
+ return false;
+ }
+
+ // Die Kommandozeilen-Parameter der Skriptumgebung zugänglich machen.
+ ScriptPtr->SetCommandLine(CommandLineParameters);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool AppEnd()
+{
+ // Den Kernel herunterfahren und alle Subsysteme deinitialisieren.
+ BS_Kernel::DeleteInstance();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool AppMain()
+{
+ // Das Hauptskript starten. Dieses Skript lädt alle anderen Skripte und startet das eigentliche Spiel.
+ BS_ScriptEngine * ScriptPtr = static_cast<BS_ScriptEngine *>(BS_Kernel::GetInstance()->GetService("script"));
+ BS_ASSERT(ScriptPtr);
+ ScriptPtr->ExecuteFile(DEFAULT_SCRIPT_FILE);
+
+ return true;
+}
diff --git a/engines/sword25/main_win.cpp b/engines/sword25/main_win.cpp
new file mode 100755
index 0000000000..cd83772d66
--- /dev/null
+++ b/engines/sword25/main_win.cpp
@@ -0,0 +1,244 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifdef BS_PLATFORM_WINDOWS
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <ShellAPI.h>
+#include <stdio.h>
+
+#include <vector>
+using namespace std;
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "kernel/debug/memorydumper.h"
+
+#define BS_LOG_PREFIX "MAIN_WIN"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * const ENGINE_STARTUP_ERROR_MESSAGE = "A fatal error occured during engine startup. Please refer to log.txt for further information.";
+ const char * const ENGINE_STARTUP_ERROR_CAPTION = "Broken Sword 2.5";
+ const char * const EXCEPTION_TERMINATION_MESSAGE = "!! PROGRAM TERMINATED DUE TO EXCEPTION !!";
+
+ // -------------------------------------------------------------------------
+
+ #define XXX(EX) case EX: return #EX;
+ const char * GetExceptionCodeString(DWORD ExceptionCode)
+ {
+ switch (ExceptionCode)
+ {
+ XXX(EXCEPTION_ACCESS_VIOLATION)
+ XXX(EXCEPTION_ARRAY_BOUNDS_EXCEEDED)
+ XXX(EXCEPTION_BREAKPOINT)
+ XXX(EXCEPTION_DATATYPE_MISALIGNMENT)
+ XXX(EXCEPTION_FLT_DENORMAL_OPERAND)
+ XXX(EXCEPTION_FLT_DIVIDE_BY_ZERO)
+ XXX(EXCEPTION_FLT_INEXACT_RESULT)
+ XXX(EXCEPTION_FLT_INVALID_OPERATION)
+ XXX(EXCEPTION_FLT_OVERFLOW)
+ XXX(EXCEPTION_FLT_STACK_CHECK)
+ XXX(EXCEPTION_FLT_UNDERFLOW)
+ XXX(EXCEPTION_GUARD_PAGE)
+ XXX(EXCEPTION_ILLEGAL_INSTRUCTION)
+ XXX(EXCEPTION_IN_PAGE_ERROR)
+ XXX(EXCEPTION_INT_DIVIDE_BY_ZERO)
+ XXX(EXCEPTION_INT_OVERFLOW)
+ XXX(EXCEPTION_INVALID_DISPOSITION)
+ XXX(EXCEPTION_INVALID_HANDLE)
+ XXX(EXCEPTION_NONCONTINUABLE_EXCEPTION)
+ XXX(EXCEPTION_PRIV_INSTRUCTION)
+ XXX(EXCEPTION_SINGLE_STEP)
+ XXX(EXCEPTION_STACK_OVERFLOW)
+ XXX(DBG_CONTROL_C)
+ XXX(DBG_CONTROL_BREAK)
+ XXX(DBG_TERMINATE_THREAD)
+ XXX(DBG_TERMINATE_PROCESS)
+ XXX(RPC_S_UNKNOWN_IF)
+ XXX(RPC_S_SERVER_UNAVAILABLE)
+ default:
+ static char Buffer[64];
+ _snprintf(Buffer, sizeof(Buffer), "UNKNOWN EXCEPTION 0x%08X", ExceptionCode);
+ return Buffer;
+ }
+ }
+ #undef XXX
+
+ // -------------------------------------------------------------------------
+
+ int HandleException(unsigned int ExceptionCode, _EXCEPTION_POINTERS * ExceptionPointersPtr)
+ {
+ BS_LOG_ERRORLN("Exception \"%s\" occured at 0x%08X.", GetExceptionCodeString(ExceptionCode), ExceptionPointersPtr->ContextRecord->Eip);
+
+ // Memorydump schreiben
+ std::string Filename;
+ BS_MemoryDumper Dumper;
+ if (Dumper.WriteDump(ExceptionPointersPtr, Filename))
+ {
+ BS_LOGLN("Memory dump written to \"%s\".", Filename.c_str());
+ }
+
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+}
+
+// -----------------------------------------------------------------------------
+
+extern bool AppStart(const vector<string> & CommandLineParameters);
+extern bool AppMain();
+extern bool AppEnd();
+
+bool main2(int argc, char ** argv)
+{
+ // Engine initialisieren.
+ vector<string> CommandLineParameters;
+ for (int i = 0; i < argc; ++i) CommandLineParameters.push_back(string(argv[i]));
+ if (!AppStart(CommandLineParameters))
+ {
+ MessageBoxA(0, ENGINE_STARTUP_ERROR_MESSAGE, ENGINE_STARTUP_ERROR_CAPTION, MB_ICONERROR);
+ AppEnd();
+ return 1;
+ }
+
+ // Engine starten.
+ bool RunSuccess = AppMain();
+
+ // Engine deinitialisieren.
+ bool DeinitSuccess = AppEnd();
+
+ return (RunSuccess && DeinitSuccess) ? 0 : 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int main(int argc, char ** argv)
+{
+ // Im Release-Modus wird die gesamte Ausführung wird in einen __try-Block eingebettet, damit alle eventuellen Exceptions abgefangen werden.
+ // Im Debug-Modus wollen wir, dass der Debugger die Exceptions fängt.
+#ifndef DEBUG
+ __try
+#endif
+ {
+ // Im Debugmodus erlauben wir mehrere Fenster, ansonsten beenden wir uns wenn das Spiel bereits läuft;
+ // Wichtig ist vor allem dass all dies vor Installation des Logservice passiert, da wir sonst das
+ // Log der laufenden Instanz überschreiben würden.
+#ifndef DEBUG
+ HANDLE hMutex;
+ hMutex = CreateMutex(NULL, TRUE, "137bd040-8f06-11dd-8299-0016e65b9c32");
+ if(GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ return 1;
+ }
+#endif
+
+ // Der weitere Teil ist in eine andere Funktion ausgelagert, da Visual C++ in Funktionen die auch SEH benutzen keine Objekte mit Destruktoren zulässt.
+ return main2(argc, argv);
+ }
+
+#ifndef DEBUG
+ __except(HandleException(GetExceptionCode(), GetExceptionInformation()))
+ {
+ BS_Kernel::DeleteInstance();
+ BS_LOG_ERRORLN(EXCEPTION_TERMINATION_MESSAGE);
+ return 1;
+ }
+#endif
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ size_t CalculateConvertedBufferSize(int NumArgs, LPWSTR * Args)
+ {
+ if (NumArgs > 0 && Args)
+ {
+ size_t BufferSize = NumArgs * sizeof(char *);
+ for (int i = 0; i < NumArgs; ++i)
+ {
+ int Length = WideCharToMultiByte(CP_ACP, 0, Args[i], -1, 0, 0, 0, 0);
+ if (Length == 0) return 0;
+ BufferSize += Length;
+ }
+
+ return BufferSize;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ bool ConvertCommandLineParametersToANSI(int & NumArgs, vector<char> & Buffer)
+ {
+ // Kommandozeilenparameter auslesen.
+ LPWSTR * Args = CommandLineToArgvW(GetCommandLineW(), &NumArgs);
+ if (NumArgs <= 0 || Args == 0) false;
+
+ // Zuerst berechnen, wie groß der Buffer sein muss, der alle konvertierten Strings und die Stringpointer aufnimmt.
+ size_t BufferSize = CalculateConvertedBufferSize(NumArgs, Args);
+ if (BufferSize == 0) false;
+
+ // Bufferspeicher reservieren.
+ Buffer.resize(BufferSize);
+
+ // Die benötigten Pointer erstellen.
+ char ** StringsPtrPtr = reinterpret_cast<char **>(&Buffer[0]); // Pointer auf den Anfang des Buffers, dort werden die Stringpointer abgelegt.
+ char * StringsPtr = &Buffer[NumArgs * sizeof(char *)]; // Pointer auf den Anfang des Bereiches in dem die Strings abgelegt werden.
+ char * EndPtr = &Buffer[0] + Buffer.size(); // Pointer auf das Ende des Buffers, wird zum Berechnen des verbleibenden Platzes benötigt.
+
+ // Alle Strings konvertieren und zusammen mit den Pointern in den Buffer schreiben.
+ for (int i = 0; i < NumArgs; ++i)
+ {
+ int BytesWritten = WideCharToMultiByte(CP_ACP, 0, Args[i], -1, StringsPtr, EndPtr - StringsPtr, 0, 0);
+ if (BytesWritten == 0) return false;
+ StringsPtrPtr[i] = StringsPtr;
+ StringsPtr += BytesWritten;
+ }
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
+{
+ int NumArgs;
+ vector<char> Buffer;
+ if (ConvertCommandLineParametersToANSI(NumArgs, Buffer))
+ {
+ main(NumArgs, reinterpret_cast<char **>(&Buffer[0]));
+ }
+ else
+ main(0, 0);
+}
+
+#endif // BS_PLATFORM_WINDOWS
diff --git a/engines/sword25/math/geometry.cpp b/engines/sword25/math/geometry.cpp
new file mode 100755
index 0000000000..5527d7f30d
--- /dev/null
+++ b/engines/sword25/math/geometry.cpp
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "geometry.h"
+
+#define BS_LOG_PREFIX "GEOMETRY"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_Geometry::BS_Geometry(BS_Kernel * pKernel) :
+ BS_Service(pKernel)
+{
+ if (!_RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Geometry::~BS_Geometry()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_Geometry_CreateObject(BS_Kernel* pKernel) { return new BS_Geometry(pKernel); } \ No newline at end of file
diff --git a/engines/sword25/math/geometry.h b/engines/sword25/math/geometry.h
new file mode 100755
index 0000000000..c63167269f
--- /dev/null
+++ b/engines/sword25/math/geometry.h
@@ -0,0 +1,46 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_GEOMETRY_H
+#define BS_GEOMETRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/service.h"
+
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+
+// -----------------------------------------------------------------------------
+
+class BS_Geometry : public BS_Service
+{
+public:
+ BS_Geometry(BS_Kernel * pKernel);
+ virtual ~BS_Geometry();
+
+private:
+ bool _RegisterScriptBindings();
+};
+
+#endif
diff --git a/engines/sword25/math/geometry_script.cpp b/engines/sword25/math/geometry_script.cpp
new file mode 100755
index 0000000000..688f5fd356
--- /dev/null
+++ b/engines/sword25/math/geometry_script.cpp
@@ -0,0 +1,602 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "gfx/graphicengine.h"
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+
+#include "geometry.h"
+#include "region.h"
+#include "regionregistry.h"
+#include "walkregion.h"
+#include "vertex.h"
+
+// -----------------------------------------------------------------------------
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+// Die Strings werden als #defines definiert um Stringkomposition zur Compilezeit zu ermöglichen.
+#define REGION_CLASS_NAME "Geo.Region"
+#define WALKREGION_CLASS_NAME "Geo.WalkRegion"
+
+// -----------------------------------------------------------------------------
+
+// Wie luaL_checkudata, nur ohne dass kein Fehler erzeugt wird.
+static void * my_checkudata (lua_State *L, int ud, const char *tname)
+{
+ int top = lua_gettop(L);
+
+ void * p = lua_touserdata(L, ud);
+ if (p != NULL) /* value is a userdata? */
+ {
+ if (lua_getmetatable(L, ud)) /* does it have a metatable? */
+ {
+ // lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ BS_LuaBindhelper::GetMetatable(L, tname);
+ if (lua_rawequal(L, -1, -2)) /* does it have the correct mt? */
+ {
+ lua_settop(L, top);
+ return p;
+ }
+ }
+ }
+
+ lua_settop(L, top);
+ return NULL;
+}
+
+// -----------------------------------------------------------------------------
+
+static void NewUintUserData(lua_State * L, unsigned int Value)
+{
+ void * UserData = lua_newuserdata(L, sizeof(Value));
+ memcpy(UserData, &Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+static bool IsValidPolygonDefinition(lua_State * L)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass wir wirklich eine Tabelle betrachten
+ if (!lua_istable(L, -1))
+ {
+ luaL_error(L, "Invalid polygon definition. Unexpected type, \"table\" needed.");
+ return false;
+ }
+
+ int TableSize = luaL_getn(L, -1);
+
+ // Sicherstellen, dass mindestens 3 Vertecies existieren.
+ if (TableSize < 6)
+ {
+ luaL_error(L, "Invalid polygon definition. At least three vertecies needed.");
+ return false;
+ }
+
+ // Sicherstellen, dass die Anzahl der Tabellenelemente durch zwei teilbar ist.
+ // Da je zwei Elemente ein Vertex definieren, ist eine ungerade Anzahl an Elementen nicht zulässig.
+ if ((TableSize % 2) != 0)
+ {
+ luaL_error(L, "Invalid polygon definition. Even number of table elements needed.");
+ return false;
+ }
+
+ // Sicherstellen, dass alle Elemente der Tabelle vom Typ Number sind.
+ for (int i = 1; i <= TableSize; i += 1)
+ {
+ lua_rawgeti(L, -1, i);
+ if (!lua_isnumber(L, -1))
+ {
+ luaL_error(L, "Invalid polygon definition. All table elements have to be numbers.");
+ return false;
+ }
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static void TablePolygonToPolygon(lua_State * L, BS_Polygon & Polygon)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass eine gültige Polygon-Definition auf dem Stack liegt.
+ // Es ist nicht notwendig den Rückgabewert abzufangen, da alle Fehler über luaL_error ausgegeben werden und somit die Ausführung des
+ // Skriptes beenden.
+ IsValidPolygonDefinition(L);
+
+ int VertexCount = luaL_getn(L, -1) / 2;
+
+ // Speicher für Vertecies reservieren
+ vector<BS_Vertex> Vertecies;
+ Vertecies.reserve(VertexCount);
+
+ // Vertecies erstellen
+ for (int i = 0; i < VertexCount; i++)
+ {
+ // X-Wert
+ lua_rawgeti(L, -1, (i * 2) + 1);
+ int X = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Y-Wert
+ lua_rawgeti(L, -1, (i * 2) + 2);
+ int Y = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Vertex
+ Vertecies.push_back(BS_Vertex(X, Y));
+ }
+ BS_ASSERT(Vertecies.size() == VertexCount);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ // Polygon erstellen
+ Polygon.Init(VertexCount, &Vertecies[0]);
+}
+
+// -----------------------------------------------------------------------------
+
+static unsigned int TableRegionToRegion(lua_State * L, const char * ClassName)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Man kann eine Region in Lua auf zwei Arten definieren:
+ // 1. Eine Tabelle, die ein Polygon definiert (Polygon = Tabelle mit Zahlen, wobei je zwei aufeinander folgende Zahlen ein Vertex definieren)
+ // Das eine Polygon definiert die Region vollständig (=> keine Löcher möglich)
+ // 2. Eine Tabelle, die mehrere Polygondefinitionen (wiederum Tabellen (siehe 1.)) enthält.
+ // Dann definiert das erste Polygon den Umriss der Region und die folgenden Löcher im ersten Polygon.
+
+ // Es darf nur ein Parameter übergeben werden und dieser muss eine Tabelle sein.
+ if (lua_gettop(L) != 1 || !lua_istable(L, -1))
+ {
+ luaL_error(L, "First and only parameter has to be of type \"table\".");
+ return 0;
+ }
+
+ unsigned int RegionHandle;
+ if (ClassName == REGION_CLASS_NAME)
+ {
+ RegionHandle = BS_Region::Create(BS_Region::RT_REGION);
+ }
+ else if (ClassName == WALKREGION_CLASS_NAME)
+ {
+ RegionHandle = BS_WalkRegion::Create(BS_Region::RT_WALKREGION);
+ }
+ else
+ {
+ BS_ASSERT(false);
+ }
+
+ BS_ASSERT(RegionHandle);
+
+ // Wenn das erste Element des Parameters eine Zahl ist, wird der 1. Fall angenommen.
+ // Wenn das erste Element des Parameters eine Tabelle ist, wird der 2. Fall angenommen.
+ // Wenn das erste Element des Parameters einen anderen Typ hat, liegt ein Fehler vor.
+ lua_rawgeti(L, -1, 1);
+ int FirstElementType = lua_type(L, -1);
+ lua_pop(L, 1);
+
+ switch(FirstElementType)
+ {
+ case LUA_TNUMBER:
+ {
+ BS_Polygon Polygon;
+ TablePolygonToPolygon(L, Polygon);
+ BS_RegionRegistry::GetInstance().ResolveHandle(RegionHandle)->Init(Polygon);
+ }
+ break;
+
+ case LUA_TTABLE:
+ {
+ lua_rawgeti(L, -1, 1);
+ BS_Polygon Polygon;
+ TablePolygonToPolygon(L, Polygon);
+ lua_pop(L, 1);
+
+ int PolygonCount = luaL_getn(L, -1);
+ if (PolygonCount == 1)
+ BS_RegionRegistry::GetInstance().ResolveHandle(RegionHandle)->Init(Polygon);
+ else
+ {
+ vector<BS_Polygon> Holes;
+ Holes.reserve(PolygonCount - 1);
+
+ for (int i = 2; i <= PolygonCount; i++)
+ {
+ lua_rawgeti(L, -1, i);
+ Holes.resize(Holes.size() + 1);
+ TablePolygonToPolygon(L, Holes.back());
+ lua_pop(L, 1);
+ }
+ BS_ASSERT(Holes.size() == PolygonCount - 1);
+
+ BS_RegionRegistry::GetInstance().ResolveHandle(RegionHandle)->Init(Polygon, &Holes);
+ }
+ }
+ break;
+
+ default:
+ luaL_error(L, "Illegal region definition.");
+ return 0;
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return RegionHandle;
+}
+
+// -----------------------------------------------------------------------------
+
+static void NewUserdataRegion(lua_State * L, const char * ClassName)
+{
+ // Region aufgrund des Lua-Codes erstellen.
+ // Fehler treten nicht auf, sondern werden von der Funktion über luaL_error abgefangen.
+ unsigned int RegionHandle = TableRegionToRegion(L, ClassName);
+ BS_ASSERT(RegionHandle);
+
+ NewUintUserData(L, RegionHandle);
+ // luaL_getmetatable(L, ClassName);
+ BS_LuaBindhelper::GetMetatable(L, ClassName);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+}
+
+// -----------------------------------------------------------------------------
+
+static int NewRegion(lua_State * L)
+{
+ NewUserdataRegion(L, REGION_CLASS_NAME);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int NewWalkRegion(lua_State * L)
+{
+ NewUserdataRegion(L, WALKREGION_CLASS_NAME);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * GEO_LIBRARY_NAME = "Geo";
+
+static const luaL_reg GEO_FUNCTIONS[] =
+{
+ "NewRegion", NewRegion,
+ "NewWalkRegion", NewWalkRegion,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_Region * CheckRegion(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Geo.Region oder Geo.WalkRegion
+ unsigned int * RegionHandlePtr;
+ if ((RegionHandlePtr = reinterpret_cast<unsigned int *>(my_checkudata(L, 1, REGION_CLASS_NAME))) != 0 ||
+ (RegionHandlePtr = reinterpret_cast<unsigned int *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0)
+ {
+ return BS_RegionRegistry::GetInstance().ResolveHandle(*RegionHandlePtr);
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" REGION_CLASS_NAME "' expected");
+ }
+
+ // Compiler ruhigstellen. Ausführung kommt nie an diesem Punkt an.
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_IsValid(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushbooleancpp(L, pR->IsValid());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_GetX(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushnumber(L, pR->GetPosX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_GetY(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushnumber(L, pR->GetPosY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_GetPos(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ BS_Vertex::VertexToLuaVertex(L, pR->GetPosition());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_IsPointInRegion(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ BS_Vertex Vertex;
+ BS_Vertex::LuaVertexToVertex(L, 2, Vertex);
+ lua_pushbooleancpp(L, pR->IsPointInRegion(Vertex));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_SetPos(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ BS_Vertex Vertex;
+ BS_Vertex::LuaVertexToVertex(L, 2, Vertex);
+ pR->SetPos(Vertex.X, Vertex.Y);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_SetX(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ pR->SetPosX(static_cast<int>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_SetY(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ pR->SetPosY(static_cast<int>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void DrawPolygon(const BS_Polygon & Polygon, unsigned int Color, const BS_Vertex & Offset)
+{
+ BS_GraphicEngine * pGE = static_cast<BS_GraphicEngine *>(BS_Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGE);
+
+ for (int i = 0; i < Polygon.VertexCount - 1; i++)
+ pGE->DrawDebugLine(Polygon.Vertecies[i] + Offset, Polygon.Vertecies[i + 1] + Offset, Color);
+
+ pGE->DrawDebugLine(Polygon.Vertecies[Polygon.VertexCount - 1] + Offset, Polygon.Vertecies[0] + Offset, Color);
+}
+
+// -----------------------------------------------------------------------------
+
+static void DrawRegion(const BS_Region & Region, unsigned int Color, const BS_Vertex & Offset)
+{
+ DrawPolygon(Region.GetContour(), Color, Offset);
+ for (int i = 0; i < Region.GetHoleCount(); i++)
+ DrawPolygon(Region.GetHole(i), Color, Offset);
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_Draw(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+
+ switch (lua_gettop(L))
+ {
+ case 3:
+ {
+ BS_Vertex Offset;
+ BS_Vertex::LuaVertexToVertex(L, 3, Offset);
+ DrawRegion(*pR, BS_GraphicEngine::LuaColorToARGBColor(L, 2), Offset);
+ }
+ break;
+
+ case 2:
+ DrawRegion(*pR, BS_GraphicEngine::LuaColorToARGBColor(L, 2), BS_Vertex(0, 0));
+ break;
+
+ default:
+ DrawRegion(*pR, BS_RGB(255, 255, 255), BS_Vertex(0, 0));
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_GetCentroid(lua_State * L)
+{
+ BS_Region * RPtr = CheckRegion(L);
+ BS_ASSERT(RPtr);
+
+ BS_Vertex::VertexToLuaVertex(L, RPtr->GetCentroid());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int R_Delete(lua_State * L)
+{
+ BS_Region * pR = CheckRegion(L);
+ BS_ASSERT(pR);
+ delete pR;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg REGION_METHODS[] =
+{
+ "SetPos", R_SetPos,
+ "SetX", R_SetX,
+ "SetY", R_SetY,
+ "GetPos", R_GetPos,
+ "IsPointInRegion", R_IsPointInRegion,
+ "GetX", R_GetX,
+ "GetY", R_GetY,
+ "IsValid", R_IsValid,
+ "Draw", R_Draw,
+ "GetCentroid", R_GetCentroid,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+static BS_WalkRegion * CheckWalkRegion(lua_State * L)
+{
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Geo.WalkRegion
+ unsigned int RegionHandle;
+ if ((RegionHandle = *reinterpret_cast<unsigned int *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0)
+ {
+ return reinterpret_cast<BS_WalkRegion *>(BS_RegionRegistry::GetInstance().ResolveHandle(RegionHandle));
+ }
+ else
+ {
+ luaL_argcheck(L, 0, 1, "'" WALKREGION_CLASS_NAME "' expected");
+ }
+
+ // Compiler ruhigstellen. Ausführung kommt nie an diesem Punkt an.
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WR_GetPath(lua_State * L)
+{
+ BS_WalkRegion * pWR = CheckWalkRegion(L);
+ BS_ASSERT(pWR);
+
+ BS_Vertex Start;
+ BS_Vertex::LuaVertexToVertex(L, 2, Start);
+ BS_Vertex End;
+ BS_Vertex::LuaVertexToVertex(L, 3, End);
+ BS_Path Path;
+ if (pWR->QueryPath(Start, End, Path))
+ {
+ lua_newtable(L);
+ BS_Path::const_iterator it = Path.begin();
+ for (; it != Path.end(); it++)
+ {
+ lua_pushnumber(L, (it - Path.begin()) + 1);
+ BS_Vertex::VertexToLuaVertex(L, *it);
+ lua_settable(L, -3);
+ }
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg WALKREGION_METHODS[] =
+{
+ "GetPath", WR_GetPath,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_Geometry::_RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, REGION_CLASS_NAME, REGION_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, WALKREGION_CLASS_NAME, REGION_METHODS)) return false;
+ if (!BS_LuaBindhelper::AddMethodsToClass(L, WALKREGION_CLASS_NAME, WALKREGION_METHODS)) return false;
+
+ if (!BS_LuaBindhelper::SetClassGCHandler(L, REGION_CLASS_NAME, R_Delete)) return false;
+ if (!BS_LuaBindhelper::SetClassGCHandler(L, WALKREGION_CLASS_NAME, R_Delete)) return false;
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, GEO_LIBRARY_NAME, GEO_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/math/line.h b/engines/sword25/math/line.h
new file mode 100755
index 0000000000..aaae3a0764
--- /dev/null
+++ b/engines/sword25/math/line.h
@@ -0,0 +1,206 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Line
+ -------
+ Diese Klasse enthält nur statische Methoden, die mit Geradensegmenten zu tun haben.
+ Es gibt keine wirkliche Geradensegment-Klasse, da diese Klasse vor allem zu
+ Berechnungen mit Polygonen herangezogen wird und es dabei wichtig ist, Start- und
+ Endpunkte der Linien dynamisch wählen zu können. Dieses würde sich verbieten, wenn
+ ein Polygon aus einer Menge von festen Geradensegmenten gebildet wäre.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_LINE_H_
+#define _BS_LINE_H_
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+// -----------------------------------------------------------------------------
+
+class BS_Line
+{
+public:
+ /**
+ @brief Bestimmt ob sich ein Punkt links von einer Linie befindet.
+ @param a der Startpunkt der Linie
+ @param b der Endpunkt der Linie
+ @param c der Testpunkt
+ @return Gibt true zurück, wenn sich der Punkt links von der Linie befindet.<br>
+ Falls sich der Punkt rechts von der Linie oder auf der Linie befindet wird false zurückgegeben.<br>
+ @remark Ein Punkt liegt links von einer Linie, wenn er vom Startpunkt aus betrachtet links neben der Linie liegt.
+ */
+ static bool IsVertexLeft(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return _TriangleArea2(a, b, c) > 0;
+ }
+
+ static bool IsVertexLeftOn(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return _TriangleArea2(a, b, c) >= 0;
+ }
+
+ /**
+ @brief Bestimmt ob sich ein Punkt rechts von einer Linie befindet.
+ @param a der Startpunkt der Linie
+ @param b der Endpunkt der Linie
+ @param c der Testpunkt
+ @return Gibt true zurück, wenn sich der Punkt recht von der Linie befindet.<br>
+ Falls sich der Punkt links von der Linie oder auf der Linie befindet wird false zurückgegeben.<br>
+ @remark Ein Punkt liegt rechts von einer Linie, wenn er vom Startpunkt aus betrachtet rechts neben der Linie liegt.
+ */
+ static bool IsVertexRight(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return _TriangleArea2(a, b, c) < 0;
+ }
+
+ static bool IsVertexRightOn(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return _TriangleArea2(a, b, c) <= 0;
+ }
+
+ /**
+ @brief Bestimmt ob sich ein Punkt auf einer Linie befindet.
+ @param a der Startpunkt der Linie
+ @param b der Endpunkt der Linie
+ @param c der Testpunkt
+ @return Gibt true zurück, wenn sich der Punkt auf der Linie befindet.
+ */
+ static bool IsVertexOn(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return _TriangleArea2(a, b, c) == 0;
+ }
+
+ enum VERTEX_CLASSIFICATION
+ {
+ LEFT,
+ RIGHT,
+ ON,
+ };
+
+ /**
+ @brief Bestimmt wo sich ein Punkt relativ zu einer Linie befindet.
+ @param a der Startpunkt der Linie
+ @param b der Endpunkt der Linie
+ @param c der Testpunkt
+ @return Gibt LEFT zurück, wenn sich der Punkt links von der Line befindet.<br>
+ Gibt RIGHT zurück, wenn sich der Punkt links von der Line befindet.<br>
+ Gibt ON zurück, wenn sich der Punkt auf der Linie befindet.
+ */
+ static VERTEX_CLASSIFICATION ClassifyVertexToLine(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ int Area = _TriangleArea2(a, b, c);
+ if (Area > 0) return LEFT;
+ if (Area < 0) return RIGHT;
+ return ON;
+ }
+
+ /**
+ @brief Bestimmt ob sich zwei Linien schneiden.
+ @param a der Startpunkt der ersten Linie
+ @param b der Endpunkt der ersten Linie
+ @param c der Startpunkt der zweiten Linie
+ @param d der Endpunkt der zweiten Linie
+ @remark In den Fällen in denen eine Linie die andere nur berührt, wird false zurückgegeben (improper intersection).
+ */
+ static bool DoesIntersectProperly(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c, const BS_Vertex & d)
+ {
+ VERTEX_CLASSIFICATION Class1 = ClassifyVertexToLine(a, b, c);
+ VERTEX_CLASSIFICATION Class2 = ClassifyVertexToLine(a, b, d);
+ VERTEX_CLASSIFICATION Class3 = ClassifyVertexToLine(c, d, a);
+ VERTEX_CLASSIFICATION Class4 = ClassifyVertexToLine(c, d, b);
+
+ if (Class1 == ON || Class2 == ON || Class3 == ON || Class4 == ON) return false;
+
+ return ((Class1 == LEFT) ^ (Class2 == LEFT)) && ((Class3 == LEFT) ^ (Class4 == LEFT));
+ }
+
+ /**
+ @brief Bestimmt ob sich ein Punkt auf einem Liniensegment befindet
+ @param a der Startpunkt der Liniensegmentes
+ @param b der Endpunkt der Liniensegmentes
+ @param c der Testpunkt
+ */
+ static bool IsOnLine(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ // Die Punkte müssen alle Kollinear sein, sonst liegt der Testpunkt nicht auf dem Liniensegment
+ if (_TriangleArea2(a, b, c) != 0) return false;
+
+ // Falls das Liniensegment nicht vertikal ist prüfe auf der X-Achse, ansonsten auf der Y-Achse
+ if (a.X != b.X)
+ {
+ return ((a.X <= c.X) &&
+ (c.X <= b.X)) ||
+ ((a.X >= c.X) &&
+ (c.X >= b.X));
+ }
+ else
+ {
+ return ((a.Y <= c.Y) &&
+ (c.Y <= b.Y)) ||
+ ((a.Y >= c.Y) &&
+ (c.Y >= b.Y));
+ }
+ }
+
+ static bool IsOnLineStrict(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ // Die Punkte müssen alle Kollinear sein, sonst liegt der Testpunkt nicht auf dem Liniensegment
+ if (_TriangleArea2(a, b, c) != 0) return false;
+
+ // Falls das Liniensegment nicht vertikal ist prüfe auf der X-Achse, ansonsten auf der Y-Achse
+ if (a.X != b.X)
+ {
+ return ((a.X < c.X) &&
+ (c.X < b.X)) ||
+ ((a.X > c.X) &&
+ (c.X > b.X));
+ }
+ else
+ {
+ return ((a.Y < c.Y) &&
+ (c.Y < b.Y)) ||
+ ((a.Y > c.Y) &&
+ (c.Y > b.Y));
+ }
+ }
+
+private:
+
+ /**
+ @brief Gibt die doppelte Größe des durch a, b und c definierten Dreiecks zurück.
+
+ Das Ergebnis ist positiv wenn die Punkte entgegen dem Uhrzeigersinn angeordnet sind und negativ wenn sie mit dem
+ Uhrzeigersinn angeordnet sind.
+ */
+ static int _TriangleArea2(const BS_Vertex & a, const BS_Vertex & b, const BS_Vertex & c)
+ {
+ return a.X * b.Y - a.Y * b.X +
+ a.Y * c.X - a.X * c.Y +
+ b.X * c.Y - c.X * b.Y;
+ }
+};
+
+#endif
diff --git a/engines/sword25/math/polygon.cpp b/engines/sword25/math/polygon.cpp
new file mode 100755
index 0000000000..c204a11d09
--- /dev/null
+++ b/engines/sword25/math/polygon.cpp
@@ -0,0 +1,506 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <utility>
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include <math.h>
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include "polygon.h"
+#include "line.h"
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+// Konstruktion / Destruktion
+// --------------------------
+
+BS_Polygon::BS_Polygon() : VertexCount(0), Vertecies(NULL)
+{
+}
+
+BS_Polygon::BS_Polygon(int VertexCount, const BS_Vertex* Vertecies) : VertexCount(0), Vertecies(NULL)
+{
+ Init(VertexCount, Vertecies);
+}
+
+BS_Polygon::BS_Polygon(const BS_Polygon& Other) : VertexCount(0), Vertecies(NULL)
+{
+ Init(Other.VertexCount, Other.Vertecies);
+}
+
+BS_Polygon::BS_Polygon(BS_InputPersistenceBlock & Reader) : VertexCount(0), Vertecies(NULL)
+{
+ Unpersist(Reader);
+}
+
+BS_Polygon::~BS_Polygon()
+{
+ delete[] Vertecies;
+}
+
+// Initialisierung
+// ---------------
+
+bool BS_Polygon::Init(int VertexCount, const BS_Vertex* Vertecies)
+{
+ // Alten Objektzustand merken um ihn wieder herstellen zu können, falls beim Initialisieren mit den neuen Daten ein Fehler auftreten
+ // sollte.
+ int OldVertexCount = this->VertexCount;
+ BS_Vertex* OldVertecies = this->Vertecies;
+
+ this->VertexCount = VertexCount;
+ this->Vertecies = new BS_Vertex[VertexCount + 1];
+ memcpy(this->Vertecies, Vertecies, sizeof(BS_Vertex) * VertexCount);
+ // TODO:
+ // Doppelte und überflüssige Vertecies entfernen (überflüssig = 3 Verts kollinear)
+ // _WeedRepeatedVertecies();
+ // Das erste Vertex wird am Ende des Vertex-Arrays wiederholt, dieses vereinfacht einige Algorithmen, die alle Edges durchgehen und
+ // sich so die Überlaufkontrolle sparen können.
+ this->Vertecies[VertexCount] = this->Vertecies[0];
+
+ // Falls das Polygon selbstüberschneidend ist, wird der alte Objektzustand wieder hergestellt und ein Fehler signalisiert.
+ if (CheckForSelfIntersection())
+ {
+ delete[] this->Vertecies;
+ this->Vertecies = OldVertecies;
+ this->VertexCount = OldVertexCount;
+
+ // BS_LOG_ERROR("POLYGON: Tried to create a self-intersecting polygon.\n");
+ return false;
+ }
+
+ // Alte Vertexliste freigeben
+ delete[] OldVertecies;
+
+ // Eigenschaften des Polygons berechnen.
+ m_IsCW = ComputeIsCW();
+ m_IsConvex = ComputeIsConvex();
+ m_Centroid = ComputeCentroid();
+
+ return true;
+}
+
+// Überprüfung der Reihenfolge der Vertecies
+// -----------------------------------------
+
+bool BS_Polygon::IsCW() const
+{
+ return m_IsCW;
+}
+
+bool BS_Polygon::IsCCW() const
+{
+ return !IsCW();
+}
+
+bool BS_Polygon::ComputeIsCW() const
+{
+ if (VertexCount)
+ {
+ // Vertex finden, dass am weitesten rechts unten liegt.
+ int V2Index = FindLRVertexIndex();
+
+ // Vertex vorher und nachher finden.
+ int V1Index = (V2Index + (VertexCount - 1)) % VertexCount;
+ int V3Index = (V2Index + 1) % VertexCount;
+
+ // Kreuzprodukt bilden.
+ // Wenn das Kreuzprodukt des am weitesten unten links liegenden Vertex positiv ist, sind die Vertecies im Uhrzeigersinn angeordnet
+ // ansonsten entgegen des Uhrzeigersinns.
+ if (CrossProduct(Vertecies[V1Index], Vertecies[V2Index], Vertecies[V3Index]) >= 0) return true;
+ }
+
+ return false;
+}
+
+int BS_Polygon::FindLRVertexIndex() const
+{
+ if (VertexCount)
+ {
+ int CurIndex = 0;
+ int MaxX = Vertecies[0].X;
+ int MaxY = Vertecies[0].Y;
+
+ for (int i = 1; i < VertexCount; i++)
+ {
+ if (Vertecies[i].Y > MaxY ||
+ (Vertecies[i].Y == MaxY && Vertecies[i].X > MaxX))
+ {
+ MaxX = Vertecies[i].X;
+ MaxY = Vertecies[i].Y;
+ CurIndex = i;
+ }
+ }
+
+ return CurIndex;
+ }
+
+ return -1;
+}
+
+// Testen auf Konvex/Konkav
+// ------------------------
+
+bool BS_Polygon::IsConvex() const
+{
+ return m_IsConvex;
+}
+
+bool BS_Polygon::IsConcave() const
+{
+ return !IsConvex();
+}
+
+bool BS_Polygon::ComputeIsConvex() const
+{
+ // Polygone mit 3 oder weniger Vertecies können nur Konvex sein.
+ if (VertexCount <= 3) return true;
+
+ // Alle Winkel im Polygon berechnen, wenn das Polygon Konvex ist, müssen alle Winkel das selbe Vorzeichen haben.
+ int Flag = 0;
+ for (int i = 0; i < VertexCount; i++)
+ {
+ // Die Indizies der beiden nächsten Vertecies nach i bestimmen.
+ int j = (i + 1) % VertexCount;
+ int k = (i + 2) % VertexCount;
+
+ // Kreuzprodukt der drei Vertecies berechnen.
+ int Cross = CrossProduct(Vertecies[i], Vertecies[j], Vertecies[k]);
+
+ // Die unteren beiden Bits von Flag haben folgende Bedeutung:
+ // 0 : negativer Winkel ist aufgetreten
+ // 1 : positiver Winkel ist aufgetreten
+
+ // Vorzeichen des aktuellen Winkels in Flag vermerken.
+ if (Cross < 0)
+ Flag |= 1;
+ else if (Cross > 0)
+ Flag |= 2;
+
+ // Falls Flag 3 ist, sind sowohl positive als auch negative Winkel vorhanden -> Polygon ist Konkav.
+ if (Flag == 3) return false;
+ }
+
+ // Polygon ist Konvex.
+ return true;
+}
+
+// Sicherstellen einer bestimmen Vertexordnung
+// -------------------------------------------
+
+void BS_Polygon::EnsureCWOrder()
+{
+ if (!IsCW())
+ ReverseVertexOrder();
+}
+
+void BS_Polygon::EnsureCCWOrder()
+{
+ if (!IsCCW())
+ ReverseVertexOrder();
+}
+
+// Umkehren der Reihenfolge der Vertecies
+// --------------------------------------
+
+void BS_Polygon::ReverseVertexOrder()
+{
+ // Vertecies paarweise vertauschen, bis die Liste komplett umgekehrt wurde.
+ for (int i = 0; i < VertexCount / 2; i++)
+ std::swap(Vertecies[i], Vertecies[VertexCount - i - 1]);
+
+ // Vertexordnung neu berechnen.
+ m_IsCW = ComputeIsCW();
+}
+
+// Kreuzprodukt
+// ------------
+
+int BS_Polygon::CrossProduct(const BS_Vertex& V1, const BS_Vertex& V2, const BS_Vertex& V3) const
+{
+ return (V2.X - V1.X) * (V3.Y - V2.Y) -
+ (V2.Y - V1.Y) * (V3.X - V2.X);
+}
+
+// Skalarproduct
+// -------------
+
+int BS_Polygon::DotProduct(const BS_Vertex& V1, const BS_Vertex& V2, const BS_Vertex& V3) const
+{
+ return (V1.X - V2.X) * (V3.X - V2.X) +
+ (V1.Y - V2.Y) * (V3.X - V2.Y);
+}
+
+// Überprüfen auf Selbstüberschneidung
+// -----------------------------------
+
+bool BS_Polygon::CheckForSelfIntersection() const
+{
+ // TODO: Fertigstellen
+ /*
+ float AngleSum = 0.0f;
+ for (int i = 0; i < VertexCount; i++)
+ {
+ int j = (i + 1) % VertexCount;
+ int k = (i + 2) % VertexCount;
+
+ float Dot = DotProduct(Vertecies[i], Vertecies[j], Vertecies[k]);
+
+ // Skalarproduct normalisieren
+ float Length1 = sqrt((Vertecies[i].X - Vertecies[j].X) * (Vertecies[i].X - Vertecies[j].X) +
+ (Vertecies[i].Y - Vertecies[j].Y) * (Vertecies[i].Y - Vertecies[j].Y));
+ float Length2 = sqrt((Vertecies[k].X - Vertecies[j].X) * (Vertecies[k].X - Vertecies[j].X) +
+ (Vertecies[k].Y - Vertecies[j].Y) * (Vertecies[k].Y - Vertecies[j].Y));
+ float Norm = Length1 * Length2;
+
+ if (Norm > 0.0f)
+ {
+ Dot /= Norm;
+ AngleSum += acos(Dot);
+ }
+ }
+ */
+
+ return false;
+}
+
+// Verschieben
+// -----------
+
+void BS_Polygon::operator+=(const BS_Vertex& Delta)
+{
+ // Alle Vetecies verschieben
+ for (int i = 0; i < VertexCount; i++)
+ Vertecies[i] += Delta;
+
+ // Den Schwerpunkt verschieben.
+ m_Centroid += Delta;
+}
+
+// Sichtlinie
+// ----------
+
+bool BS_Polygon::IsLineInterior(const BS_Vertex & a, const BS_Vertex & b) const
+{
+ // Beide Punkte müssen im Polygon sein
+ if (!IsPointInPolygon(a, true) || !IsPointInPolygon(b, true)) return false;
+
+ // Falls die Punkte identisch sind, ist die Linie trivialerweise innerhalb des Polygons
+ if (a == b) return true;
+
+ // Testen, ob die Linie ein Liniensegment strikt schneidet (proper intersection)
+ for (int i = 0; i < VertexCount; i++)
+ {
+ int j = (i + 1) % VertexCount;
+ const BS_Vertex & VS = Vertecies[i];
+ const BS_Vertex & VE = Vertecies[j];
+
+ // Falls die Linie ein Liniensegment strikt schneidet (proper intersection) ist die Linie nicht innerhalb des Polygons
+ if (BS_Line::DoesIntersectProperly(a, b, VS, VE)) return false;
+
+ // Falls einer der beiden Linienpunkte auf der Kante liegt und der andere rechts der Kante liegt, befindet sich die Linie nicht
+ // vollständig innerhalb des Polygons.
+ if (BS_Line::IsOnLineStrict(VS, VE, a) && BS_Line::IsVertexRight(VS, VE, b)) return false;
+ if (BS_Line::IsOnLineStrict(VS, VE, b) && BS_Line::IsVertexRight(VS, VE, a)) return false;
+
+ // Falls einer der beiden Linienpunkte auf einem Vertex liegt muss die Linie in das Polygon hinein verlaufen
+ if ((a == VS) && !IsLineInCone(i, b, true)) return false;
+ if ((b == VS) && !IsLineInCone(i, a, true)) return false;
+ }
+
+ return true;
+}
+
+bool BS_Polygon::IsLineExterior(const BS_Vertex & a, const BS_Vertex & b) const
+{
+ // Keiner der beiden Punkte darf strikt im Polygon sein (auf der Kante ist erlaubt)
+ if (IsPointInPolygon(a, false) || IsPointInPolygon(b, false)) return false;
+
+ // Falls die Punkte identisch sind, ist die Linie trivialerweise ausserhalb des Polygons
+ if (a == b) return true;
+
+ // Testen, ob die Linie ein Liniensegment strikt schneidet (proper intersection)
+ for (int i = 0; i < VertexCount; i++)
+ {
+ int j = (i + 1) % VertexCount;
+ const BS_Vertex & VS = Vertecies[i];
+ const BS_Vertex & VE = Vertecies[j];
+
+ // Falls die Linie ein Liniensegment strikt schneidet (proper intersection) ist die Linie teilweise innerhalb des Polygons
+ if (BS_Line::DoesIntersectProperly(a, b, VS, VE)) return false;
+
+ // Falls einer der beiden Linienpunkte auf der Kante liegt und der andere rechts der Kante liegt, befindet sich die Linie nicht vollständig
+ // ausserhalb des Polygons.
+ if (BS_Line::IsOnLineStrict(VS, VE, a) && BS_Line::IsVertexLeft(VS, VE, b)) return false;
+ if (BS_Line::IsOnLineStrict(VS, VE, b) && BS_Line::IsVertexLeft(VS, VE, a)) return false;
+
+ // Falls einer der beiden Linienpunkte auf einem Vertex liegt, darf die Linie nicht in das Polygon hinein verlaufen
+ if ((a == VS) && IsLineInCone(i, b, false)) return false;
+ if ((b == VS) && IsLineInCone(i, a, false)) return false;
+
+ // Falls das Vertex mit Start- und Zielpunkt kollinear ist, dürfen die beiden Liniensegmente (a, VS) und (b, VS) nicht in das Polygon hinein
+ // verlaufen
+ if (BS_Line::IsOnLine(a, b, VS))
+ {
+ if (IsLineInCone(i, a, false)) return false;
+ if (IsLineInCone(i, b, false)) return false;
+ }
+ }
+
+ return true;
+}
+
+bool BS_Polygon::IsLineInCone(int StartVertexIndex, const BS_Vertex & EndVertex, bool IncludeEdges) const
+{
+ const BS_Vertex & StartVertex = Vertecies[StartVertexIndex];
+ const BS_Vertex & NextVertex = Vertecies[(StartVertexIndex + 1) % VertexCount];
+ const BS_Vertex & PrevVertex = Vertecies[(StartVertexIndex + VertexCount - 1) % VertexCount];
+
+ if (BS_Line::IsVertexLeftOn(PrevVertex, StartVertex, NextVertex))
+ {
+ if (IncludeEdges)
+ return BS_Line::IsVertexLeftOn(EndVertex, StartVertex, NextVertex) &&
+ BS_Line::IsVertexLeftOn(StartVertex, EndVertex, PrevVertex);
+ else
+ return BS_Line::IsVertexLeft(EndVertex, StartVertex, NextVertex) &&
+ BS_Line::IsVertexLeft(StartVertex, EndVertex, PrevVertex);
+ }
+ else
+ {
+ if (IncludeEdges)
+ return !(BS_Line::IsVertexLeft(EndVertex, StartVertex, PrevVertex) &&
+ BS_Line::IsVertexLeft(StartVertex, EndVertex, NextVertex));
+ else
+ return !(BS_Line::IsVertexLeftOn(EndVertex, StartVertex, PrevVertex) &&
+ BS_Line::IsVertexLeftOn(StartVertex, EndVertex, NextVertex));
+ }
+}
+
+// Punkt-Polygon Tests
+// -------------------
+
+bool BS_Polygon::IsPointInPolygon(int X, int Y, bool BorderBelongsToPolygon) const
+{
+ return IsPointInPolygon(BS_Vertex(X, Y), BorderBelongsToPolygon);
+}
+
+bool BS_Polygon::IsPointInPolygon(const BS_Vertex & Point, bool EdgesBelongToPolygon) const
+{
+ int Rcross = 0; // Anzahl der rechtsseitigen Überschneidungen
+ int Lcross = 0; // Anzahl der linksseitigen Überschneidungen
+
+ // Jede Kante wird überprüft ob sie den vom Punkt ausgehenden Strahl schneidet
+ for (int i = 0; i < VertexCount; i++)
+ {
+ const BS_Vertex & EdgeStart = Vertecies[i];
+ const BS_Vertex & EdgeEnd = Vertecies[(i + 1) % VertexCount];
+
+ // Ist der Punkt ein Vertex? Dann liegt er auf einer Kante des Polygons
+ if (Point == EdgeStart) return EdgesBelongToPolygon;
+
+ if ((EdgeStart.Y > Point.Y) != (EdgeEnd.Y > Point.Y))
+ {
+ int Term1 = (EdgeStart.X - Point.X) * (EdgeEnd.Y - Point.Y) - (EdgeEnd.X - Point.X) * (EdgeStart.Y - Point.Y);
+ int Term2 = (EdgeEnd.Y - Point.Y) - (EdgeStart.Y - EdgeEnd.Y);
+ if ((Term1 > 0) == (Term2 >= 0)) Rcross++;
+ }
+
+ if ((EdgeStart.Y < Point.Y) != (EdgeEnd.Y < Point.Y))
+ {
+ int Term1 = (EdgeStart.X - Point.X) * (EdgeEnd.Y - Point.Y) - (EdgeEnd.X - Point.X) * (EdgeStart.Y - Point.Y);
+ int Term2 = (EdgeEnd.Y - Point.Y) - (EdgeStart.Y - EdgeEnd.Y);
+ if ((Term1 < 0) == (Term2 <= 0)) Lcross++;
+ }
+ }
+
+ // Der Punkt befindet sich auf einer Kante, wenn die Anzahl der linken und rechten Überschneidungen nicht die gleiche Geradzahligkeit haben
+ if ((Rcross % 2 ) != (Lcross % 2 )) return EdgesBelongToPolygon;
+
+ // Der Punkt befindet sich genau dann strikt innerhalb des Polygons, wenn die Anzahl der Überschneidungen ungerade ist
+ if ((Rcross % 2) == 1) return true;
+ else return false;
+}
+
+bool BS_Polygon::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ Writer.Write(VertexCount);
+ for (int i = 0; i < VertexCount; ++i)
+ {
+ Writer.Write(Vertecies[i].X);
+ Writer.Write(Vertecies[i].Y);
+ }
+
+ return true;
+}
+
+bool BS_Polygon::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ int StoredVertexCount;
+ Reader.Read(StoredVertexCount);
+
+ std::vector<BS_Vertex> StoredVertecies(StoredVertexCount);
+ for (int i = 0; i < StoredVertexCount; ++i)
+ {
+ Reader.Read(StoredVertecies[i].X);
+ Reader.Read(StoredVertecies[i].Y);
+ }
+
+ Init(StoredVertexCount, &StoredVertecies[0]);
+
+ return Reader.IsGood();
+}
+
+// Schwerpunkt
+// -----------
+
+BS_Vertex BS_Polygon::GetCentroid() const
+{
+ return m_Centroid;
+}
+
+BS_Vertex BS_Polygon::ComputeCentroid() const
+{
+ // Flächeninhalt des Polygons berechnen.
+ int DoubleArea = 0;
+ for (int i = 0; i < VertexCount; ++i)
+ {
+ DoubleArea += Vertecies[i].X * Vertecies[i + 1].Y - Vertecies[i + 1].X * Vertecies[i].Y;
+ }
+
+ // Division durch 0 beim nächsten Schritt vermeiden.
+ if (DoubleArea == 0) return BS_Vertex();
+
+ // Schwerpunkt berechnen.
+ BS_Vertex Centroid;
+ for (int i = 0; i < VertexCount; ++i)
+ {
+ int Area = Vertecies[i].X * Vertecies[i + 1].Y - Vertecies[i + 1].X * Vertecies[i].Y;
+ Centroid.X += (Vertecies[i].X + Vertecies[i + 1].X) * Area;
+ Centroid.Y += (Vertecies[i].Y + Vertecies[i + 1].Y) * Area;
+ }
+ Centroid.X /= 3 * DoubleArea;
+ Centroid.Y /= 3 * DoubleArea;
+
+ return Centroid;
+}
diff --git a/engines/sword25/math/polygon.h b/engines/sword25/math/polygon.h
new file mode 100755
index 0000000000..68e2f4c05a
--- /dev/null
+++ b/engines/sword25/math/polygon.h
@@ -0,0 +1,252 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_POLYGON_H
+#define BS_POLYGON_H
+
+// Includes
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "vertex.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_Vertex;
+
+/**
+ @brief Eine Polygonklasse.
+*/
+class BS_Polygon : public BS_Persistable
+{
+public:
+ /**
+ @brief Erzeugt ein Objekt vom Typ #BS_Polygon, das 0 Vertecies enthält.
+
+ Mit der Methode Init() können dem Polygon später Vertecies hinzugefügt werden.
+ */
+ BS_Polygon();
+
+ /**
+ @brief Copy-Constructor
+ */
+ BS_Polygon(const BS_Polygon& Other);
+
+ /**
+ @brief Erstellt ein Polygon anhand persistierter Daten.
+ */
+ BS_Polygon(BS_InputPersistenceBlock & Reader);
+
+ /**
+ @brief Erzeugt ein Objekt vom Typ #BS_Polygon und ordnet ihm Vertecies zu.
+ @param VertexCount die Anzahl der Vertecies im Vertex Array.
+ @param Vertecies ein Array, das Objekte vom Typ BS_Vertex enthält, die die Vertecies des Polygons darstellen.
+ @remark Die Vertecies müssen ein nicht selbstüberschneidendes Polygon definieren.
+ Falls das Polygon selbstüberschneidend sein sollte wird ein leeres BS_Polygon Objekt erzeugt.
+ */
+ BS_Polygon(int VertexCount, const BS_Vertex* Vertecies);
+
+ /**
+ @brief Löscht das BS_Polygon Objekt.
+ */
+ virtual ~BS_Polygon();
+
+ /**
+ @brief Initialisiert das BS_Polygon mit einer Liste von Vertecies.
+
+ Die Vertecies müssen ein nicht selbstüberschneidendes Polygon definieren.
+ Es kann auch einem Polygon, welches bereits Vertecies enthält, mit einer neue Vertexliste initialisiert werden, dabei gehen die
+ alten Vertecies verloren.
+
+ @param VertexCount die Anzahl der Vertecies im Vertecies Array.
+ @param Vertecies ein Array, das Objekte vom Typ BS_Vertex enthält, die die Vertecies des Polygons darstellen.
+ @return Gibt false zurück, falls die Vertecies ein selbstüberschneidendes Polygon definiert haben.
+ In diesem Fall wird das Objekt nicht initialisiert.
+ */
+ bool Init(int VertexCount, const BS_Vertex* Vertecies);
+
+ //@{
+ /** @name Sondierende Methoden */
+
+ /**
+ @brief Überprüft, ob die Vertecies des Polygons im Uhrzeigersinn angeordnet sind.
+ @return Gibt true zurück, wenn die Vertecies des Polygons im Uhrzeigersinn angeordnet sind oder Koplanar sind.<br>
+ Gibt false zurück, wenn die Vertecies des Polygons entgegen dem Uhrzeigersinn angeordnet sind.
+ @remark Diese Methode gibt nur ein sinnvolles Ergebnis zurück, wenn das Polygon mindestens 3 Vertecies hat.
+ */
+ bool IsCW() const;
+
+ /**
+ @brief Überprüft, ob die Vertecies des Polygons entgegen dem Uhrzeigersinn angeordnet sind.
+ @return Gibt true zurück, wenn die Vertecies des Polygons entgegen dem Uhrzeigersinn angeordnet sind.<br>
+ Gibt false zurück, wenn die Vertecies des Polygons im Uhrzeigersinn angeordnet sind oder Koplanar sind.
+ @remark Diese Methode gibt nur ein sinnvolles Ergebnis zurück, wenn das Polygon mindestens 3 Vertecies hat.
+
+ */
+ bool IsCCW() const;
+
+ /**
+ @brief Überprüft, ob das Polygon konvex ist.
+ @return Gibt true zurück, wenn das Polygon konvex ist.<br>
+ Gibt false zurück, wenn das Polygon konkav ist.
+ @remark Diese Methode gibt nur ein sinnvolles Ergebnis zurück, wenn das Polygon mindestens 3 Vertecies hat.
+ */
+ bool IsConvex() const;
+
+ /**
+ @brief Überprüft, ob das Polygon konkav ist.
+ @return Gibt true zurück, wenn das Polygon konkav ist.<br>
+ Gibt false zurück, wenn das Polygon konvex ist.
+ @remark Diese Methode gibt nur ein sinnvolles Ergebnis zurück, wenn das Polygon mindestens 3 Vertecies hat.
+ */
+ bool IsConcave() const;
+
+ /**
+ @brief Überprüft, ob sich ein Punkt innerhalb des Polygons befindet.
+ @param Vertex ein Vertex, mit den Koordinaten des zu testenden Punktes.
+ @param BorderBelongsToPolygon gibt an, ob der Rand des Polygons als Teil des Polygons betrachtet werden soll.<br>
+ Der Standardwert ist true.
+ @return Gibt true zurück, wenn sich der Punkt innerhalb des Polygons befindet.<br>
+ Gibt false zurück, wenn sich der Punkt außerhalb des Polygons befindet.
+ */
+ bool IsPointInPolygon(const BS_Vertex& Vertex, bool BorderBelongsToPolygon = true) const;
+
+ /**
+ @brief Überprüft, ob sich ein Punkt innerhalb des Polygons befindet.
+ @param X die Position des Punktes auf der X-Achse.
+ @param Y die Position des Punktes auf der Y-Achse.
+ @param BorderBelongsToPolygon gibt an, ob der Rand des Polygons als Teil des Polygons betrachtet werden soll.<br>
+ Der Standardwert ist true.
+ @return Gibt true zurück, wenn sich der Punkt innerhalb des Polygons befindet.<br>
+ Gibt false zurück, wenn sich der Punkt außerhalb des Polygons befindet.
+ */
+ bool IsPointInPolygon(int X, int Y, bool BorderBelongsToPolygon = true) const;
+
+ /**
+ @brief Gibt den Schwerpunkt des Polygons zurück.
+ */
+ BS_Vertex GetCentroid() const;
+
+ // Rand gehört zum Polygon
+ // Polygon muss CW sein
+ bool IsLineInterior(const BS_Vertex & a, const BS_Vertex & b) const;
+ // Rand gehört nicht zum Polygon
+ // Polygon muss CW sein
+ bool IsLineExterior(const BS_Vertex & a, const BS_Vertex & b) const;
+
+ //@}
+
+ //@{
+ /** @name Manipulierende Methoden */
+
+ /**
+ @brief Stellt sicher, dass die Vertecies des Polygons im Uhrzeigersinn angeordnet sind.
+ */
+ void EnsureCWOrder();
+
+ /**
+ @brief Stellt sicher, dass die Vertecies des Polygons entgegen dem Uhrzeigersinn angeordnet sind.
+ */
+ void EnsureCCWOrder();
+
+ /**
+ @brief Kehrt die Reihenfolge der Vertecies um.
+ */
+ void ReverseVertexOrder();
+
+ /**
+ @brief Verschiebt das Polygon.
+ @param Delta das Vertex um das das Polygon verschoben werden soll.
+ */
+ void operator+=(const BS_Vertex& Delta);
+
+ //@}
+
+ /// Gibt die Anzahl an Vertecies im Vertecies-Array an.
+ int VertexCount;
+ /// Enthält die Vertecies des Polygons.
+ BS_Vertex* Vertecies;
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ bool m_IsCW;
+ bool m_IsConvex;
+ BS_Vertex m_Centroid;
+
+ /**
+ @brief Berechnet den Schwerpunkt des Polygons.
+ */
+ BS_Vertex ComputeCentroid() const;
+
+ /**
+ @brief Bestimmt wie die Vertecies des Polygon angeordnet sind.
+ @return Gibt true zurück, wenn die Vertecies im Uhrzeigersinn angeordnet sind, ansonsten false.
+ */
+ bool ComputeIsCW() const;
+
+ /**
+ @brief Bestimmt ob das Polygon Konvex oder Konkav ist.
+ @return Gibt true zurück, wenn das Polygon Konvex ist, ansonsten false.
+ */
+ bool ComputeIsConvex() const;
+
+ /**
+ @brief Berechnet das Kreuzprodukt dreier Vertecies.
+ @param V1 das erste Vertex
+ @param V2 des zweite Vertex
+ @param V3 das dritte Vertex
+ @return Gibt das Kreuzprodukt der drei Vertecies zurück.
+ @todo Diese Methode sollte an geeigneter Stelle in die BS_Vertex Klasse integriert werden.
+ */
+ int CrossProduct(const BS_Vertex& V1, const BS_Vertex& V2, const BS_Vertex& V3) const;
+
+ /**
+ @brief Berechnet des Skalarprodukt der beiden von drei Vertecies aufgespannten Vektoren.
+
+ Die Vektoren werden von V2 -> V1 und V2 -> V3 aufgespannt.
+
+ @param V1 das erste Vertex
+ @param V2 des zweite Vertex
+ @param V3 das dritte Vertex
+ @return Gibt das Skalarprodukt der drei Vertecies zurück.
+ @todo Diese Methode sollte an geeigneter Stelle in die BS_Vertex Klasse integriert werden.
+ */
+ int DotProduct(const BS_Vertex& V1, const BS_Vertex& V2, const BS_Vertex& V3) const;
+
+ /**
+ @brief Überprüft ob das Polygon selbstüberschneidend ist.
+ @return Gibt true zurück, wenn das Polygon selbstüberschneidend ist.<br>
+ Gibt false zurück, wenn das Polygon nicht selbstüberschneidend ist.
+ */
+ bool CheckForSelfIntersection() const;
+
+ /**
+ @brief Findet das Vertex des Polygons das am weitesten rechts unten liegt und gibt dessen Index im Vertex-Array zurück.
+ @return Gibt den Index des Vertex zurück das am weiteesten rechts unten liegt.<br>
+ Gibt -1 zurück, wenn die Vertexliste leer ist.
+ */
+ int FindLRVertexIndex() const;
+
+ bool IsLineInCone(int StartVertexIndex, const BS_Vertex & EndVertex, bool IncludeEdges) const;
+};
+
+#endif \ No newline at end of file
diff --git a/engines/sword25/math/rect.h b/engines/sword25/math/rect.h
new file mode 100755
index 0000000000..5edcf6b6af
--- /dev/null
+++ b/engines/sword25/math/rect.h
@@ -0,0 +1,298 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_RECT_H
+#define BS_RECT_H
+
+// Includes
+#include "kernel/common.h"
+#include "vertex.h"
+
+// Klassendefinition
+/**
+ @brief Diese Klasse beschreibt ein Rechteck und einige nützliche Operationen auf Rechtecken.
+*/
+class BS_Rect
+{
+public:
+ /// Das linke Extrem des Rechteckes.
+ int left;
+ /// Das obere Extrem des Rechteckes.
+ int top;
+ /// Das rechte Extrem des Rechteckes + 1.
+ int right;
+ /// Das untere Extrem des Rechteckes + 1.
+ int bottom;
+
+ /**
+ @brief Konstruktor, der alle Werte des Rechteckes mit 0 initialisiert.
+ */
+ BS_Rect()
+ {
+ left = 0;
+ top = 0;
+ right = 0;
+ bottom = 0;
+ }
+ /**
+ @brief Konstruktor, der das Rechteck mit den übergebenen Werten initialisiert.
+ @param left das linke Extrem des Rechteckes
+ @param top das obere Extrem des Rechteckes
+ @param right das rechte Extrem des Rechteckes + 1
+ @param bottom des untere Extrem des Rechteckes + 1
+ */
+ BS_Rect(int left, int top, int right, int bottom)
+ {
+ this->left = left;
+ this->top = top;
+ this->right = right;
+ this->bottom = bottom;
+ }
+ /**
+ @brief Verschiebt das Rechteck.
+ @param DeltaX der Wert um den das Rechteck auf der X-Achse verschoben werden soll.
+ @param DeltaY der Wert um den das Rechteck auf der Y-Achse verschoben werden soll.
+ */
+ void Move(int DeltaX, int DeltaY)
+ {
+ left += DeltaX;
+ right += DeltaX;
+ top += DeltaY;
+ bottom += DeltaY;
+ }
+ /**
+ @brief Testet ob sich zwei Rechtecke schneiden.
+ @return Gibt true zurück, wenn sich die Rechtecke schneiden.
+ */
+ bool DoesIntersect(const BS_Rect& Rect) const
+ {
+ int Dist;
+
+ // Intersektion auf der X-Achse
+ Dist = left - Rect.left;
+ if (Dist < 0)
+ {
+ Dist = abs(Dist);
+
+ // Schneiden sich die Rechtecke nicht auf der X-Achse, so schneiden sie sich gar nicht
+ if (Dist >= GetWidth())
+ return false;
+ }
+ else
+ {
+ // Schneiden sich die Rechtecke nicht auf der X-Achse, so schneiden sie sich gar nicht
+ if (Dist >= Rect.right - Rect.left)
+ return false;
+ }
+
+ // Intersektion auf der Y-Achse
+ Dist = top - Rect.top;
+ if (Dist < 0)
+ {
+ Dist = abs(Dist);
+
+ // Schneiden sich die Rechtecke nicht auf der Y-Achse, so schneiden sie sich gar nicht
+ if (Dist >= GetHeight())
+ return false;
+ }
+ else
+ {
+ // Schneiden sich die Rechtecke nicht auf der Y-Achse, so schneiden sie sich gar nicht
+ if (Dist >= Rect.bottom - Rect.top)
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ @brief Bildet den Durchschnitt zweier Rechtecke
+ @param Rect das Rechteck, dass mit dem Objekt geschnitter werden soll.
+ @param Result das Rechteck, dass das Ergebnisrechteck enthalten soll.
+ @return Gibt false zurück, falls Result undefiniert ist.<br>
+ Dies ist der Fall, wenn sich die Rechtecke nicht schneiden.
+ */
+ bool Intersect(const BS_Rect& Rect, BS_Rect& Result) const
+ {
+ int Dist;
+
+ // Intersektion auf der X-Achse
+ Dist = left - Rect.left;
+ if (Dist < 0)
+ {
+ Dist = abs(Dist);
+
+ // Schneiden sich die Rechtecke nicht auf der X-Achse, so schneiden sie sich gar nicht
+ if (Dist >= GetWidth())
+ {
+ Result = BS_Rect(0, 0, 0, 0);
+ return false;
+ }
+
+ // Die Abmessungen des Rect auf der X-Achse berechnen
+ Result.left = Rect.left;
+ }
+ else
+ {
+ // Schneiden sich die Rechtecke nicht auf der X-Achse, so schneiden sie sich gar nicht
+ if (Dist >= Rect.right - Rect.left)
+ {
+ Result = BS_Rect(0, 0, 0, 0);
+ return false;
+ }
+
+ // Die Abmessungen des Rect auf der X-Achse berechnen
+ Result.left = left;
+ }
+ Result.right = right < Rect.right ? right : Rect.right;
+
+ // Intersektion auf der Y-Achse
+ Dist = top - Rect.top;
+ if (Dist < 0)
+ {
+ Dist = abs(Dist);
+
+ // Schneiden sich die Rechtecke nicht auf der Y-Achse, so schneiden sie sich gar nicht
+ if (Dist >= GetHeight())
+ {
+ Result = BS_Rect(0, 0, 0, 0);
+ return false;
+ }
+
+ // Die Abmessungen des Rect auf der Y-Achse berechnen
+ Result.top = Rect.top;
+ }
+ else
+ {
+ // Schneiden sich die Rechtecke nicht auf der Y-Achse, so schneiden sie sich gar nicht
+ if (Dist >= Rect.bottom - Rect.top)
+ {
+ Result = BS_Rect(0, 0, 0, 0);
+ return false;
+ }
+
+ // Die Abmessungen des Rect auf der Y-Achse berechnen
+ Result.top = top;
+ }
+ Result.bottom = bottom < Rect.bottom ? bottom : Rect.bottom;
+
+ return true;
+ }
+ /**
+ @brief Bildet die Bounding-Box zweier Rechtecke.
+ @param Rect das Rechteck, dass mit dem Objekt verbunden werden soll.
+ @remark Das Ergebnis ist nicht ein Join in eigentlichen Sinne, sondern vielmehr die Bounding-Box um die Beiden
+ Rechtecke.
+ */
+ void Join(const BS_Rect& Rect, BS_Rect& Result) const
+ {
+ Result.left = left < Rect.left ? left : Rect.left;
+ Result.top = top < Rect.top ? top : Rect.top;
+ Result.right = right > Rect.right ? right : Rect.right;
+ Result.bottom = bottom > Rect.bottom ? bottom : Rect.bottom;
+ return;
+ }
+ /**
+ @brief Gibt die Breite des Rechteckes zurück.
+ @return Die Breite des Rechteckes.
+ */
+ int GetWidth() const
+ {
+ return right - left;
+ }
+ /**
+ @brief Gibt die Höhe des Rechteckes zurück.
+ @return Die Höhe des Rechteckes.
+ */
+ int GetHeight() const
+ {
+ return bottom - top;
+ }
+ /** @brief Gibt den Flächeninhalt des Rechteckes zurück.
+ @return Der Flächeninhalt des Rechteckes.
+ */
+ int GetArea() const
+ {
+ return GetWidth() * GetHeight();
+ }
+ /**
+ @brief Vergleichsoperator zum Überprüfen der Gleichheit zweier BS_Rect Objekte.
+ @return Gibt true zurück, wenn die Objekte die gleichen Werte haben, ansonsten false.
+ */
+ bool operator== (const BS_Rect& rhs)
+ {
+ return (left == rhs.left) &&
+ (top == rhs.top) &&
+ (right == rhs.right) &&
+ (bottom == rhs.bottom);
+ }
+ /**
+ @brief Vergleichsoperator zum Überprüfen der Ungleichheit zweier BS_Rect Objekte.
+ @return Gibt true zurück, wenn die Objekte ungleiche Werte haben, ansonsten false.
+ */
+ bool operator!= (const BS_Rect& rhs)
+ {
+ return !(*this == rhs);
+ }
+ /**
+ @brief Überprüft, ob das Objekt einen gültigen Zustand hat.
+ @return Gibt false zurück, wenn das Objekt einen ungültigen Zustand hat.
+ */
+ bool IsValid() const
+ {
+ if (left < right && top < bottom) return true;
+ return false;
+ }
+ /**
+ @brief Testet, ob sich ein Vertex innerhalb des Rechteckes befindet.
+ @param Vertex das Vertex, dass mit dem Rechteckes getestet werden soll
+ @return Gibt true zurück, wenn sich das Vertex innerhalb des Rechteckes befindet.<br>
+ Gibt false zurück, wenn sich das Vertex außerhalb des Rechteckes befindet.
+ */
+ bool IsPointInRect(const BS_Vertex& Vertex) const
+ {
+ if (Vertex.X >= left && Vertex.X < right &&
+ Vertex.Y >= top && Vertex.Y < bottom)
+ return true;
+ return false;
+ }
+ /**
+ @brief Testet, ob sich ein Punkt innerhalb des Rechteckes befindet.
+ @param X die Position des Punktes auf der X-Achse.
+ @param Y die Position des Punktes auf der Y-Achse.
+ @return Gibt true zurück, wenn sich der Punkt innerhalb des Rechteckes befindet.<br>
+ Gibt false zurück, wenn sich der Punkt außerhalb des Rechteckes befindet.
+ */
+ bool IsPointInRect(int X, int Y) const
+ {
+ return IsPointInRect(BS_Vertex(X, Y));
+ }
+ /**
+ @brief Testet, ob ein andere Rechteck komplett in den Rechteck enthalten ist.
+ @param OtherRect das zu testende Rechteck
+ @brief Gibt true zurück, wenn sich das andere Rechteck komplett im Rechteck enthalten ist, ansonsten false.
+ */
+ bool ContainsRect(const BS_Rect & OtherRect) const
+ {
+ return IsPointInRect(OtherRect.left, OtherRect.top) &&
+ IsPointInRect(OtherRect.right - 1, OtherRect.bottom - 1);
+ }
+};
+
+#endif \ No newline at end of file
diff --git a/engines/sword25/math/region.cpp b/engines/sword25/math/region.cpp
new file mode 100755
index 0000000000..3c7f3ef5dc
--- /dev/null
+++ b/engines/sword25/math/region.cpp
@@ -0,0 +1,412 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+
+#include "region.h"
+#include "walkregion.h"
+#include "regionregistry.h"
+
+#define BS_LOG_PREFIX "REGION"
+
+// Konstruktion/Destruktion
+// ------------------------
+
+BS_Region::BS_Region() : m_Valid(false), m_Type(RT_REGION)
+{
+ BS_RegionRegistry::GetInstance().RegisterObject(this);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Region::BS_Region(BS_InputPersistenceBlock & Reader, unsigned int Handle) : m_Valid(false), m_Type(RT_REGION)
+{
+ BS_RegionRegistry::GetInstance().RegisterObject(this, Handle);
+ Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_Region::Create(REGION_TYPE Type)
+{
+ BS_Region * RegionPtr;
+ switch (Type)
+ {
+ case RT_REGION:
+ RegionPtr = new BS_Region();
+ break;
+
+ case RT_WALKREGION:
+ RegionPtr = new BS_WalkRegion();
+ break;
+
+ default:
+ BS_ASSERT(true);
+ }
+
+ return BS_RegionRegistry::GetInstance().ResolvePtr(RegionPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_Region::Create(BS_InputPersistenceBlock & Reader, unsigned int Handle)
+{
+ // Typ einlesen.
+ unsigned int Type;
+ Reader.Read(Type);
+
+ // Je nach Typ ein neues BS_Region oder BS_WalkRegion Objekt erstellen.
+ BS_Region * RegionPtr;
+ if (Type == RT_REGION)
+ {
+ RegionPtr = new BS_Region(Reader, Handle);
+ }
+ else if (Type == RT_WALKREGION)
+ {
+ RegionPtr = new BS_WalkRegion(Reader, Handle);
+ }
+ else
+ {
+ BS_ASSERT(false);
+ }
+
+ return BS_RegionRegistry::GetInstance().ResolvePtr(RegionPtr);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Region::~BS_Region()
+{
+ BS_RegionRegistry::GetInstance().DeregisterObject(this);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Region::Init(const BS_Polygon& Contour, const std::vector<BS_Polygon>* pHoles)
+{
+ // Objektzustand zurücksetzen.
+ m_Valid = false;
+ m_Position = BS_Vertex(0, 0);
+ m_Polygons.clear();
+
+ // Genügend Platz für Kontur und Löcher im Polygon-Vektor reservieren
+ if (pHoles)
+ m_Polygons.reserve(1 + pHoles->size());
+ else
+ m_Polygons.reserve(1);
+
+ // Kontur an die erste Position im Polygon-Vektor kopieren
+ m_Polygons.push_back(BS_Polygon());
+ m_Polygons[0].Init(Contour.VertexCount, Contour.Vertecies);
+ // Sicherstellen, dass die Vertecies der Contour im Uhrzeigersinn angeordnet sind.
+ m_Polygons[0].EnsureCWOrder();
+
+ // Übergebene Lochpolygone an die folgenden Positionen im Polygon-Vektor kopieren
+ if (pHoles)
+ {
+ for (unsigned int i = 0; i< pHoles->size(); ++i)
+ {
+ m_Polygons.push_back(BS_Polygon());
+ m_Polygons[i + 1].Init((*pHoles)[i].VertexCount, (*pHoles)[i].Vertecies);
+ m_Polygons[i + 1].EnsureCWOrder();
+ }
+ }
+
+
+ // Bounding-Box initialisieren.
+ UpdateBoundingBox();
+
+ m_Valid = true;
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Region::UpdateBoundingBox()
+{
+ if (m_Polygons[0].VertexCount)
+ {
+ int MinX = m_Polygons[0].Vertecies[0].X;
+ int MaxX = m_Polygons[0].Vertecies[0].X;
+ int MinY = m_Polygons[0].Vertecies[0].Y;
+ int MaxY = m_Polygons[0].Vertecies[0].Y;
+
+ for (int i = 1; i < m_Polygons[0].VertexCount; i++)
+ {
+ if (m_Polygons[0].Vertecies[i].X < MinX) MinX = m_Polygons[0].Vertecies[i].X;
+ else if (m_Polygons[0].Vertecies[i].X > MaxX) MaxX = m_Polygons[0].Vertecies[i].X;
+ if (m_Polygons[0].Vertecies[i].Y < MinY) MinY = m_Polygons[0].Vertecies[i].Y;
+ else if (m_Polygons[0].Vertecies[i].Y > MaxY) MaxY = m_Polygons[0].Vertecies[i].Y;
+ }
+
+ m_BoundingBox = BS_Rect(MinX, MinY, MaxX + 1, MaxY + 1);
+ }
+}
+
+// Positionsänderungen
+// -------------------
+
+void BS_Region::SetPos(int X, int Y)
+{
+ // Unterschied zwischen alter und neuer Position berechnen.
+ BS_Vertex Delta(X - m_Position.X, Y - m_Position.Y);
+
+ // Neue Position im internen Zustand merken.
+ m_Position = BS_Vertex(X, Y);
+
+ // Alle Vertecies verschieben.
+ for (unsigned int i = 0; i < m_Polygons.size(); ++i)
+ {
+ m_Polygons[i] += Delta;
+ }
+
+ // Bounding-Box aktualisieren
+ UpdateBoundingBox();
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Region::SetPosX(int X)
+{
+ SetPos(X, m_Position.Y);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Region::SetPosY(int Y)
+{
+ SetPos(m_Position.X, Y);
+}
+
+// Punkt-Region Tests
+// ------------------
+
+bool BS_Region::IsPointInRegion(int X, int Y) const
+{
+ // Testen, ob der Punkt in der Bounding-Box ist.
+ if (m_BoundingBox.IsPointInRect(X, Y))
+ {
+ // Testen, ob der Punkt in der Contour ist.
+ if (m_Polygons[0].IsPointInPolygon(X, Y, true))
+ {
+ // Testen, ob der Punkt in einem Loch ist.
+ for (unsigned int i = 1; i < m_Polygons.size(); i++)
+ {
+ if (m_Polygons[i].IsPointInPolygon(X,Y, false))
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Region::IsPointInRegion(const BS_Vertex& Vertex) const
+{
+ return IsPointInRegion(Vertex.X, Vertex.Y);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Vertex BS_Region::FindClosestRegionPoint(const BS_Vertex& Point) const
+{
+ // Feststellen, ob sich der Punkt innerhalb eines Loches befindet, falls dies der Fall ist wird der dichteste Punkt am Rand des Loches gesucht
+ int PolygonIdx = 0;
+ {
+ for (unsigned int i = 1; i < m_Polygons.size(); ++i)
+ {
+ if (m_Polygons[i].IsPointInPolygon(Point))
+ {
+ PolygonIdx = i;
+ break;
+ }
+ }
+ }
+
+ const BS_Polygon & Polygon = m_Polygons[PolygonIdx];
+
+ BS_ASSERT(Polygon.VertexCount > 1);
+
+ // Für jede Linie des Polygons wird der Punkt berechnet, der dem übergebenen am nächsten ist.
+ // Der Punkt dieser Menge mit dem gerigsten Abstand zum übergebenen Punkt ist das Ergebnis.
+ BS_Vertex ClosestVertex = FindClosestPointOnLine(Polygon.Vertecies[0], Polygon.Vertecies[1], Point);
+ int ClosestVertexDistance2 = ClosestVertex.Distance(Point);
+ for (int i = 1; i < Polygon.VertexCount; ++i)
+ {
+ int j = (i + 1) % Polygon.VertexCount;
+
+ BS_Vertex CurVertex = FindClosestPointOnLine(Polygon.Vertecies[i], Polygon.Vertecies[j], Point);
+ if (CurVertex.Distance(Point) < ClosestVertexDistance2)
+ {
+ ClosestVertex = CurVertex;
+ ClosestVertexDistance2 = CurVertex.Distance(Point);
+ }
+ }
+
+ // Feststellen, ob der konstruierte Punkt wirklich von der Methode IsPointInRegion als innerhalb der Region liegend erkannt wird.
+ // Dies muss nicht so sein, da aufgrund von Rundungsfehlern am Rand der Polygone Ungenauigkeiten auftreten.
+ if (IsPointInRegion(ClosestVertex))
+ return ClosestVertex;
+ else
+ {
+ // Es wird versucht einen Punkt innerhalb der Region zu konstruieren indem 8 Punkte getestet werden, die in unmittelbarer Umgebung des
+ // berechneten Punktes liegen
+ if (IsPointInRegion(ClosestVertex + BS_Vertex(-2, -2)))
+ return ClosestVertex + BS_Vertex(-2, -2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(0, -2)))
+ return ClosestVertex + BS_Vertex(0, -2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(2, -2)))
+ return ClosestVertex + BS_Vertex(2, -2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(-2, 0)))
+ return ClosestVertex + BS_Vertex(-2, 0);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(0, 2)))
+ return ClosestVertex + BS_Vertex(0, 2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(-2, 2)))
+ return ClosestVertex + BS_Vertex(-2, 2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(-2, 0)))
+ return ClosestVertex + BS_Vertex(2, 2);
+ else if (IsPointInRegion(ClosestVertex + BS_Vertex(2, 2)))
+ return ClosestVertex + BS_Vertex(2, 2);
+
+ // Falls auch auf diese Weise kein Punkt gefunden werden konnte, der innerhalb der Region liegt wird das Vertex genommen, welches am
+ // nächst an dem Punkt liegt.
+ ClosestVertex = Polygon.Vertecies[0];
+ int ShortestVertexDistance2 = Polygon.Vertecies[0].Distance2(Point);
+ {
+ for (int i = 1; i < Polygon.VertexCount; i++)
+ {
+ int CurDistance2 = Polygon.Vertecies[i].Distance2(Point);
+ if (CurDistance2 < ShortestVertexDistance2)
+ {
+ ClosestVertex = Polygon.Vertecies[i];
+ ShortestVertexDistance2 = CurDistance2;
+ }
+ }
+ }
+ BS_LOG_WARNINGLN("Clostest vertex forced because edgepoint was outside region.");
+ return ClosestVertex;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Vertex BS_Region::FindClosestPointOnLine(const BS_Vertex & LineStart, const BS_Vertex & LineEnd, const BS_Vertex Point) const
+{
+ float Vector1X = static_cast<float>(Point.X - LineStart.X);
+ float Vector1Y = static_cast<float>(Point.Y - LineStart.Y);
+ float Vector2X = static_cast<float>(LineEnd.X - LineStart.X);
+ float Vector2Y = static_cast<float>(LineEnd.Y - LineStart.Y);
+ float Vector2Length = sqrtf(Vector2X * Vector2X + Vector2Y * Vector2Y);
+ Vector2X /= Vector2Length;
+ Vector2Y /= Vector2Length;
+ float Distance = sqrtf(static_cast<float>((LineStart.X - LineEnd.X) * (LineStart.X - LineEnd.X) +
+ (LineStart.Y - LineEnd.Y) * (LineStart.Y - LineEnd.Y)));
+ float Dot = Vector1X * Vector2X + Vector1Y * Vector2Y;
+
+ if (Dot <= 0) return LineStart;
+ if (Dot >= Distance) return LineEnd;
+
+ BS_Vertex Vector3(static_cast<int>(Vector2X * Dot + 0.5f), static_cast<int>(Vector2Y * Dot + 0.5f));
+ return LineStart + Vector3;
+}
+
+// -----------------------------------------------------------------------------
+// Sichtlinie
+// -----------------------------------------------------------------------------
+
+bool BS_Region::IsLineOfSight(const BS_Vertex & a, const BS_Vertex & b) const
+{
+ BS_ASSERT(m_Polygons.size());
+
+ // Die Linie muss innerhalb des Kontur-Polygons und ausserhalb aller Loch-Polygone sein
+ std::vector<BS_Polygon>::const_iterator iter = m_Polygons.begin();
+ if (!(*iter).IsLineInterior(a, b)) return false;
+ for (iter++; iter != m_Polygons.end(); iter++)
+ if (!(*iter).IsLineExterior(a, b)) return false;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_Region::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ Writer.Write(static_cast<unsigned int>(m_Type));
+ Writer.Write(m_Valid);
+ Writer.Write(m_Position.X);
+ Writer.Write(m_Position.Y);
+
+ Writer.Write(m_Polygons.size());
+ std::vector<BS_Polygon>::iterator It = m_Polygons.begin();
+ while (It != m_Polygons.end())
+ {
+ Result &= It->Persist(Writer);
+ ++It;
+ }
+
+ Writer.Write(m_BoundingBox.left);
+ Writer.Write(m_BoundingBox.top);
+ Writer.Write(m_BoundingBox.right);
+ Writer.Write(m_BoundingBox.bottom);
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_Region::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ Reader.Read(m_Valid);
+ Reader.Read(m_Position.X);
+ Reader.Read(m_Position.Y);
+
+ m_Polygons.clear();
+ unsigned int PolygonCount;
+ Reader.Read(PolygonCount);
+ for (unsigned int i = 0; i < PolygonCount; ++i)
+ {
+ m_Polygons.push_back(BS_Polygon(Reader));
+ }
+
+ Reader.Read(m_BoundingBox.left);
+ Reader.Read(m_BoundingBox.top);
+ Reader.Read(m_BoundingBox.right);
+ Reader.Read(m_BoundingBox.bottom);
+
+ return Reader.IsGood();
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Vertex BS_Region::GetCentroid() const
+{
+ if (m_Polygons.size() > 0)
+ return m_Polygons[0].GetCentroid();
+ return
+ BS_Vertex();
+}
diff --git a/engines/sword25/math/region.h b/engines/sword25/math/region.h
new file mode 100755
index 0000000000..7004d252eb
--- /dev/null
+++ b/engines/sword25/math/region.h
@@ -0,0 +1,222 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_REGION_H
+#define BS_REGION_H
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "vertex.h"
+#include "polygon.h"
+#include "rect.h"
+
+/**
+ @brief Diese Klasse ist die Basisklasse aller Regionen.
+
+ Mit der Methode IsValid() lässt sich abfragen, ob sich das Objekt in einem gültigen Zustand befindet.<br>
+ Sollte dies nicht der Fall sein, ist die Methode Init() die einzige Methode die aufgerufen werden darf.
+ Diese Klasse garantiert, dass die Vertecies der die Umriss- und die Lochpolygone im Uhrzeigersinn angeordnet sind, so dass auf den Polygonen
+ arbeitende Algorithmen nur für diese Anordnung implementiert werden müssen.
+*/
+class BS_Region : public BS_Persistable
+{
+protected:
+ /**
+ @brief Erzeugt ein uninitialisiertes #BS_Region Objekt.
+
+ Nach dem Erzeugen ist das Objekt noch ungültig (IsValid() gibt false zurück), allerdings kann das Objekt nachträglich über
+ einen Aufruf von Init() in einen gültigen Zustand versetzt werden.
+ */
+ BS_Region();
+
+ BS_Region(BS_InputPersistenceBlock & Reader, unsigned int Handle);
+
+public:
+ enum REGION_TYPE
+ {
+ RT_REGION,
+ RT_WALKREGION,
+ };
+
+ static unsigned int Create(REGION_TYPE Type);
+ static unsigned int Create(BS_InputPersistenceBlock & Reader, unsigned int Handle = 0);
+
+ virtual ~BS_Region();
+
+ /**
+ @brief Initialisiert ein BS_Region Objekt.
+ @param Contour ein Polygon das den Umriss der Region angibt.
+ @param pHoles ein Pointer auf einen Vector von Polygonen, die Löcher in der Region angeben.<br>
+ Falls die Region keine Löcher hat, muss NULL übergeben werden.<br>
+ Der Standardwert ist NULL.
+ @return Gibt true zurück, wenn die Initialisierung erfolgreich war.<br>
+ Gibt false zurück, wenn die Intialisierung fehlgeschlagen ist.
+ @remark Falls die Region bereits initialisiert war, wird der alte Zustand gelöscht.
+ */
+ virtual bool Init(const BS_Polygon& Contour, const std::vector<BS_Polygon>* pHoles = NULL);
+
+ //@{
+ /** @name Sondierende Methoden */
+
+ /**
+ @brief Gibt an, ob das Objekt in einem gültigen Zustand ist.
+ @return Gibt true zurück, wenn sich das Objekt in einem gültigen Zustand befindet.
+ Gibt false zurück, wenn sich das Objekt in einem ungültigen Zustand befindet.
+ @remark Ungültige Objekte können durch einen Aufruf von Init() in einen gültigen Zustand versetzt werden.
+ */
+ bool IsValid() const { return m_Valid; }
+
+ /**
+ @brief Gibt die Position der Region zurück.
+ */
+ const BS_Vertex& GetPosition() const { return m_Position; }
+
+ /**
+ @brief Gibt die Position des Region auf der X-Achse zurück.
+ */
+ int GetPosX() const { return m_Position.X; }
+
+ /**
+ @brief Gibt die Position des Region auf der Y-Achse zurück.
+ */
+ int GetPosY() const { return m_Position.Y; }
+
+ /**
+ @brief Gibt an, ob sich ein Punkt innerhalb der Region befindet.
+ @param Vertex ein Vertex, mit den Koordinaten des zu testenden Punktes.
+ @return Gibt true zurück, wenn sich der Punkt innerhalb der Region befindet.<br>
+ Gibt false zurück, wenn sich der Punkt außerhalb der Region befindet.
+ */
+ bool IsPointInRegion(const BS_Vertex& Vertex) const;
+
+ /**
+ @brief Gibt an, ob sich ein Punkt innerhalb der Region befindet.
+ @param X die Position des Punktes auf der X-Achse.
+ @param Y die Position des Punktes auf der Y-Achse.
+ @return Gibt true zurück, wenn sich der Punkt innerhalb der Region befindet.<br>
+ Gibt false zurück, wenn sich der Punkt außerhalb der Region befindet.
+ */
+ bool IsPointInRegion(int X, int Y) const;
+
+ /**
+ @brief Gibt das Umrisspolygon der Region zurück.
+ */
+ const BS_Polygon& GetContour() const { return m_Polygons[0]; }
+
+ /**
+ @brief Gibt die Anzahl der Lochpolygone in der Region zurück.
+ */
+ int GetHoleCount() const { return static_cast<int>(m_Polygons.size() - 1); }
+
+ /**
+ @brief Gibt ein bestimmtes Lochpolygon in der Region zurück.
+ @param i die Nummer des zurückzugebenen Loches.<br>
+ Dieser Wert muss zwischen 0 und GetHoleCount() - 1 liegen.
+ @return Gibt das gewünschte Lochpolygon zurück.
+ */
+ inline const BS_Polygon& GetHole(unsigned int i) const;
+
+ /**
+ @brief Findet für einen Punkt ausserhalb der Region den nächsten Punkt, der sich innerhalb der Region befindet.
+ @param Point der Punkt, der sich ausserhalb der Region befindet
+ @return Gibt den Punkt innerhalb der Region zurück, der den geringsten Abstand zum übergebenen Punkt hat.
+ @remark Diese Methode arbeitet nicht immer Pixelgenau. Man sollte sich also nicht darauf verlassen, dass es wirklich keine Punkt innerhalb der
+ Region gibt, der dichter am übergebenen Punkt liegt.
+ */
+ BS_Vertex FindClosestRegionPoint(const BS_Vertex& Point) const;
+
+ /**
+ @brief Gibt den Schwerpunkt des Umrisspolygons zurück.
+ */
+ BS_Vertex GetCentroid() const;
+
+ bool IsLineOfSight(const BS_Vertex & a, const BS_Vertex & b) const;
+
+ //@}
+
+ //@{
+ /** @name Manipulierende Methoden */
+
+ /**
+ @brief Setzt die Position der Region.
+ @param X die neue Position der Region auf der X-Achse.
+ @param Y die neue Position der Region auf der Y-Achse.
+ */
+ virtual void SetPos(int X, int Y);
+
+ /**
+ @brief Setzt die Position der Region auf der X-Achse.
+ @param X die neue Position der Region auf der X-Achse.
+ */
+ void SetPosX(int X);
+
+ /**
+ @brief Setzt die Position der Region auf der Y-Achse.
+ @param Y die neue Position der Region auf der Y-Achse.
+ */
+ void SetPosY(int Y);
+
+ //@}
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+protected:
+ /// Diese Variable gibt den Typ des Objektes an.
+ REGION_TYPE m_Type;
+ /// Diese Variable gibt an, ob der aktuelle Objektzustand gültig ist.
+ bool m_Valid;
+ /// Dieses Vertex gibt die Position der Region an.
+ BS_Vertex m_Position;
+ /// Dieser Vector enthält alle Polygone die die Region definieren. Das erste Element des Vectors ist die Kontur, alle weiteren sind die Löcher.
+ std::vector<BS_Polygon> m_Polygons;
+ /// Die Bounding-Box der Region.
+ BS_Rect m_BoundingBox;
+
+ /**
+ @brief Aktualisiert die Bounding-Box der Region.
+ */
+ void UpdateBoundingBox();
+
+ /**
+ @brief Findet den Punkt auf einer Linie, der einem anderen Punkt am nächsten ist.
+ @param LineStart der Startpunkt der Linie
+ @param LineEnd der Endpunkt der Linie
+ @param Point der Punkt, zu dem der nächste Punkt auf der Linie konstruiert werden soll.
+ @return Gibt den Punkt auf der Linie zurück, der dem übergebenen Punkt am nächsten ist.
+ */
+ BS_Vertex FindClosestPointOnLine(const BS_Vertex & LineStart, const BS_Vertex & LineEnd, const BS_Vertex Point) const;
+};
+
+
+// -----------------------------------------------------------------------------
+// Inlines
+// -----------------------------------------------------------------------------
+
+inline const BS_Polygon& BS_Region::GetHole(unsigned int i) const
+{
+ BS_ASSERT(i < m_Polygons.size() - 1);
+ return m_Polygons[i + 1];
+}
+
+#endif
diff --git a/engines/sword25/math/regionregistry.cpp b/engines/sword25/math/regionregistry.cpp
new file mode 100755
index 0000000000..af120ddd8b
--- /dev/null
+++ b/engines/sword25/math/regionregistry.cpp
@@ -0,0 +1,111 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "REGIONREGISTRY"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+#include "regionregistry.h"
+#include "region.h"
+
+// -----------------------------------------------------------------------------
+// Implementation
+// -----------------------------------------------------------------------------
+
+std::auto_ptr<BS_RegionRegistry> BS_RegionRegistry::m_InstancePtr;
+
+// -----------------------------------------------------------------------------
+
+void BS_RegionRegistry::LogErrorLn(const char * Message) const
+{
+ BS_LOG_ERRORLN(Message);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_RegionRegistry::LogWarningLn(const char * Message) const
+{
+ BS_LOG_WARNINGLN(Message);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RegionRegistry::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Das nächste zu vergebene Handle schreiben.
+ Writer.Write(m_NextHandle);
+
+ // Anzahl an BS_Regions schreiben.
+ Writer.Write(m_Handle2PtrMap.size());
+
+ // Alle BS_Regions persistieren.
+ HANDLE2PTR_MAP::const_iterator Iter = m_Handle2PtrMap.begin();
+ while (Iter != m_Handle2PtrMap.end())
+ {
+ // Handle persistieren.
+ Writer.Write(Iter->first);
+
+ // Objekt persistieren.
+ Result &= Iter->second->Persist(Writer);
+
+ ++Iter;
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_RegionRegistry::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Das nächste zu vergebene Handle wieder herstellen.
+ Reader.Read(m_NextHandle);
+
+ // Alle vorhandenen BS_Regions zerstören.
+ while (!m_Handle2PtrMap.empty()) delete m_Handle2PtrMap.begin()->second;
+
+ // Anzahl an BS_Regions einlesen.
+ unsigned int RegionCount;
+ Reader.Read(RegionCount);
+
+ // Alle gespeicherten BS_Regions wieder herstellen.
+ for (unsigned int i = 0; i < RegionCount; ++i)
+ {
+ // Handle lesen.
+ unsigned int Handle;
+ Reader.Read(Handle);
+
+ // BS_Region wieder herstellen.
+ Result &= BS_Region::Create(Reader, Handle) != 0;
+ }
+
+ return Reader.IsGood() && Result;
+}
diff --git a/engines/sword25/math/regionregistry.h b/engines/sword25/math/regionregistry.h
new file mode 100755
index 0000000000..fba6ba2cf6
--- /dev/null
+++ b/engines/sword25/math/regionregistry.h
@@ -0,0 +1,64 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_REGIONREGISTRY_H
+#define BS_REGIONREGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/persistable.h"
+#include "kernel/objectregistry.h"
+
+#include "kernel/memlog_off.h"
+#include <memory>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+class BS_Region;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_RegionRegistry : public BS_ObjectRegistry<BS_Region>, public BS_Persistable
+{
+public:
+ static BS_RegionRegistry & GetInstance()
+ {
+ if (!m_InstancePtr.get()) m_InstancePtr.reset(new BS_RegionRegistry);
+ return *m_InstancePtr.get();
+ }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ virtual void LogErrorLn(const char * Message) const;
+ virtual void LogWarningLn(const char * Message) const;
+
+ static std::auto_ptr<BS_RegionRegistry> m_InstancePtr;
+};
+
+#endif
diff --git a/engines/sword25/math/vertex.cpp b/engines/sword25/math/vertex.cpp
new file mode 100755
index 0000000000..ddf13e3d9d
--- /dev/null
+++ b/engines/sword25/math/vertex.cpp
@@ -0,0 +1,76 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include "vertex.h"
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lauxlib.h>
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Vertex & BS_Vertex::LuaVertexToVertex(lua_State * L, int StackIndex, BS_Vertex & Vertex)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass wir wirklich eine Tabelle betrachten
+ luaL_checktype(L, StackIndex, LUA_TTABLE);
+
+ // X Komponente auslesen
+ lua_pushstring(L, "X");
+ lua_gettable(L, StackIndex);
+ if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, StackIndex, "the X component has to be a number");
+ Vertex.X = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Y Komponente auslesen
+ lua_pushstring(L, "Y");
+ lua_gettable(L, StackIndex);
+ if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, StackIndex, "the Y component has to be a number");
+ Vertex.Y = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return Vertex;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_Vertex::VertexToLuaVertex(lua_State * L, const BS_Vertex & Vertex)
+{
+ // Neue Tabelle erstellen
+ lua_newtable(L);
+
+ // X-Wert in die Tabelle schreiben
+ lua_pushstring(L, "X");
+ lua_pushnumber(L, Vertex.X);
+ lua_settable(L, -3);
+
+ // Y-Wert in die Tabelle schreiben
+ lua_pushstring(L, "Y");
+ lua_pushnumber(L, Vertex.Y);
+ lua_settable(L, -3);
+}
diff --git a/engines/sword25/math/vertex.h b/engines/sword25/math/vertex.h
new file mode 100755
index 0000000000..3353f34f10
--- /dev/null
+++ b/engines/sword25/math/vertex.h
@@ -0,0 +1,142 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_Vertex
+ ---------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef _BS_VERTEX_H
+#define _BS_VERTEX_H
+
+// Includes
+#include <math.h>
+#include "../kernel/common.h"
+
+// Forward-Declarations
+struct lua_State;
+
+/**
+ @brief Ein 2D-Vertex.
+*/
+class BS_Vertex
+{
+public:
+ BS_Vertex() : X(0), Y(0) {};
+ BS_Vertex(int X, int Y) { this->X = X; this->Y = Y; }
+
+ int X;
+ int Y;
+
+ /**
+ @brief Vergleicht zwei Vertecies.
+ */
+ inline bool operator==(const BS_Vertex& rhs) const { if (X == rhs.X && Y == rhs.Y) return true; return false; }
+ /**
+ @brief Vergleicht zwei Vertecies.
+ */
+ inline bool operator!=(const BS_Vertex& rhs) const { if (X != rhs.X || Y != rhs.Y) return true; return false; }
+ /**
+ @brief Addiert ein Vertex zum Vertex.
+ */
+ inline void operator+=(const BS_Vertex& Delta) { X += Delta.X; Y += Delta.Y; }
+
+ /**
+ @brief Subtrahiert ein Vertex vom Vertex.
+ */
+ inline void operator-=(const BS_Vertex& Delta) { X -= Delta.X; Y -= Delta.Y; }
+
+ /**
+ @brief Addiert zwei Vertecies
+ */
+ inline BS_Vertex operator+(const BS_Vertex& Delta) const { return BS_Vertex(X + Delta.X, Y + Delta.Y); }
+
+ /**
+ @brief Subtrahiert zwei Vertecies
+ */
+ inline BS_Vertex operator-(const BS_Vertex& Delta) const { return BS_Vertex(X - Delta.X, Y - Delta.Y); }
+
+ /**
+ @brief Berechnet das Quadrat des Abstandes zweier Vertecies.
+ @param Vertex das Vertex zu dem der Abstand berechnet werden soll.
+ @return Gibt das Quadrat des Abstandes zwischen diesem Objekt und Vertex zurück.
+ @remark Falls nur Abstände verglichen werden sollen, sollte diese Methode benutzt werden, da sie schneller ist, als Distance().
+ */
+ inline int Distance2(const BS_Vertex& Vertex) const
+ {
+ return (X - Vertex.X) * (X - Vertex.X) + (Y - Vertex.Y) * (Y - Vertex.Y);
+ }
+
+ /**
+ @brief Berechnet den Abstand zweier Vertecies.
+ @param Vertex das Vertex zu dem der Abstand berechnet werden soll.
+ @return Gibt den Abstand zwischen diesem Objekt und Vertex zurück.
+ @remark Falls nur Abstände verglichen werden sollen, sollte diese Methode Distance2(), die das Quadrat des Abstandes berechnet.
+ Sie ist schneller.
+ */
+ inline int Distance(const BS_Vertex& Vertex) const
+ {
+ return (int)(sqrtf(static_cast<float>(Distance2(Vertex))) + 0.5);
+ }
+
+ /**
+ @brief Berechnet das Kreuzprodukt dieses Vertex mit einem weiteren Vertex. Hierbei werden die Vertecies als Vektoren aufgefasst.
+ @param Vertex das zweite Vertex
+ @return Gibt das Kreuzprodukt von diesem Vertex und dem Parameter Vertex zurück.
+ */
+ inline int ComputeCrossProduct(const BS_Vertex& Vertex) const
+ {
+ return X * Vertex.Y - Vertex.X * Y;
+ }
+
+ /**
+ @brief Berechnet das Skalarprodukt dieses Vertex mit einem weiteren Vertex. Hierbei werden die Vertecies als Vektoren aufgefasst.
+ @param Vertex das zweite Vertex
+ @return Gibt das Skalarprodukt von diesem Vertex und dem Parameter Vertex zurück.
+ */
+ inline int ComputeDotProduct(const BS_Vertex& Vertex) const
+ {
+ return X * Vertex.X + Y * Vertex.Y;
+ }
+
+ /**
+ @brief Berechnet den Winkel zwischen diesem Vertex und einem weiteren Vertex. Hierbei werden die Vertecies als Vektoren aufgefasst.
+ @param Vertex das zweite Vertex
+ @return Gibt den Winkel zwischen diesem Vertex und dem Parameter Vertex im Bogenmaß zurück.
+ */
+ inline float ComputeAngle(const BS_Vertex& Vertex) const
+ {
+ return atan2f(static_cast<float>(ComputeCrossProduct(Vertex)), static_cast<float>(ComputeDotProduct(Vertex)));
+ }
+
+ /**
+ @brief Berechnet die Länge des Vektors
+ */
+ inline float ComputeLength() const
+ {
+ return sqrtf(static_cast<float>(X * X + Y * Y));
+ }
+
+ static BS_Vertex & LuaVertexToVertex(lua_State * L, int StackIndex, BS_Vertex & Vertex);
+ static void VertexToLuaVertex(lua_State * L, const BS_Vertex & Vertex);
+};
+
+#endif
diff --git a/engines/sword25/math/walkregion.cpp b/engines/sword25/math/walkregion.cpp
new file mode 100755
index 0000000000..4d13a9b82e
--- /dev/null
+++ b/engines/sword25/math/walkregion.cpp
@@ -0,0 +1,432 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#include <list>
+#include <algorithm>
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "walkregion.h"
+#include "line.h"
+
+#define BS_LOG_PREFIX "WALKREGION"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+static const int INFINITY = INT_MAX;
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_WalkRegion::BS_WalkRegion()
+{
+ m_Type = RT_WALKREGION;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_WalkRegion::BS_WalkRegion(BS_InputPersistenceBlock &Reader, unsigned int Handle) :
+ BS_Region(Reader, Handle)
+{
+ m_Type = RT_WALKREGION;
+ Unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_WalkRegion::~BS_WalkRegion()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_WalkRegion::Init(const BS_Polygon & Contour, const std::vector<BS_Polygon> * pHoles)
+{
+ // Standard-Initialisierungen der Region vornehmen.
+ if (!BS_Region::Init(Contour, pHoles)) return false;
+
+ // Datenstrukturen fürs Pathfinding vorbereiten
+ InitNodeVector();
+ ComputeVisibilityMatrix();
+
+ // Erfolg signalisieren.
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_WalkRegion::QueryPath(BS_Vertex StartPoint, BS_Vertex EndPoint, BS_Path & Path)
+{
+ BS_ASSERT(Path.empty());
+
+ // Falls Start und Ziel identisch sind, muss trivialerweise kein Pfad gefunden werden.
+ if (StartPoint == EndPoint) return true;
+
+ // Sicherstellen, dass Start und Ziel gültig sind und neuen Start- und Zielpunkt finden, falls sie ausserhalb des Polygons liegen.
+ if (!CheckAndPrepareStartAndEnd(StartPoint, EndPoint)) return false;
+
+ // Wenn zwischen Start- und Endpunkt eine Sichtlinie besteht, muss kein Pathfindung durchgeführt werden und als Ergebnis wird die
+ // direkte Verbindungslinie zwischen Start- und Endpunkt zurückgegeben.
+ if (IsLineOfSight(StartPoint, EndPoint))
+ {
+ Path.push_back(StartPoint);
+ Path.push_back(EndPoint);
+ return true;
+ }
+
+ return FindPath(StartPoint, EndPoint, Path);
+}
+
+// -----------------------------------------------------------------------------
+
+struct DijkstraNode
+{
+ typedef std::vector<DijkstraNode> Container;
+ typedef Container::iterator Iter;
+ typedef Container::const_iterator ConstIter;
+
+ DijkstraNode() : Cost(INFINITY), Chosen(false) {};
+ ConstIter ParentIter;
+ int Cost;
+ bool Chosen;
+};
+
+static void InitDijkstraNodes(DijkstraNode::Container & DijkstraNodes, const BS_Region & Region, const BS_Vertex & Start, const std::vector<BS_Vertex> & Nodes)
+{
+ // Ausreichend Platz im Vector reservieren
+ DijkstraNodes.resize(Nodes.size());
+
+ // Alle Randknoten initialisieren, die vom Startknoten sichtbar sind
+ DijkstraNode::Iter DijkstraIter = DijkstraNodes.begin();
+ for (std::vector<BS_Vertex>::const_iterator NodesIter = Nodes.begin(); NodesIter != Nodes.end(); NodesIter++, DijkstraIter++)
+ {
+ (*DijkstraIter).ParentIter = DijkstraNodes.end();
+ if (Region.IsLineOfSight(*NodesIter, Start)) (*DijkstraIter).Cost = (*NodesIter).Distance(Start);
+ }
+ BS_ASSERT(DijkstraIter == DijkstraNodes.end());
+}
+
+static DijkstraNode::Iter ChooseClosestNode(DijkstraNode::Container & Nodes)
+{
+ DijkstraNode::Iter ClosestNodeInter = Nodes.end();
+ int MinCost = INFINITY;
+
+ for (DijkstraNode::Iter iter = Nodes.begin(); iter != Nodes.end(); iter++)
+ {
+ if (!(*iter).Chosen && (*iter).Cost < MinCost)
+ {
+ MinCost = (*iter).Cost;
+ ClosestNodeInter = iter;
+ }
+ }
+
+ return ClosestNodeInter;
+}
+
+static void RelaxNodes(DijkstraNode::Container & Nodes,
+ const std::vector< std::vector<int> > & VisibilityMatrix,
+ const DijkstraNode::ConstIter & CurNodeIter)
+{
+ // Alle Nachfolger vom aktuellen Knoten, die noch nicht gewählt wurden, werden in die Randknotenliste eingefügt und die Kosten werden
+ // aktualisiert, wenn ein kürzerer Pfad zu ihnen gefunden wurde.
+
+ int CurNodeIndex = CurNodeIter - Nodes.begin();
+ for (unsigned int i = 0; i < Nodes.size(); i++)
+ {
+ int Cost = VisibilityMatrix[CurNodeIndex][i];
+ if (!Nodes[i].Chosen && Cost != INFINITY)
+ {
+ int TotalCost = (*CurNodeIter).Cost + Cost;
+ if (TotalCost < Nodes[i].Cost)
+ {
+ Nodes[i].ParentIter = CurNodeIter;
+ Nodes[i].Cost = TotalCost;
+ }
+ }
+ }
+}
+
+static void RelaxEndPoint(const BS_Vertex & CurNodePos,
+ const DijkstraNode::ConstIter & CurNodeIter,
+ const BS_Vertex & EndPointPos,
+ DijkstraNode & EndPoint,
+ const BS_Region & Region)
+{
+ if (Region.IsLineOfSight(CurNodePos, EndPointPos))
+ {
+ int TotalCost = (*CurNodeIter).Cost + CurNodePos.Distance(EndPointPos);
+ if (TotalCost < EndPoint.Cost)
+ {
+ EndPoint.ParentIter = CurNodeIter;
+ EndPoint.Cost = TotalCost;
+ }
+ }
+}
+
+bool BS_WalkRegion::FindPath(const BS_Vertex & Start, const BS_Vertex & End, BS_Path & Path) const
+{
+ // Dies ist eine Implementation des Dijkstra-Algorithmus
+
+ // Randknotenliste initialisieren
+ DijkstraNode::Container DijkstraNodes;
+ InitDijkstraNodes(DijkstraNodes, *this, Start, m_Nodes);
+
+ // Der Endpunkt wird gesondert behandelt, da er im Sichtbarkeitsgraphen nicht vorhanden ist
+ DijkstraNode EndPoint;
+
+ // Da in jedem Durchgang ein Knoten aus der Knotenliste gewählt wird, und danach nie wieder gewählt werden kann, ist die maximale Anzahl der
+ // Schleifendurchläufe durch die Anzahl der Knoten begrenzt.
+ for (unsigned int i = 0; i < m_Nodes.size(); i++)
+ {
+ // Bestimme nächstgelegenen Knoten in der Randknotenliste
+ DijkstraNode::Iter NodeInter = ChooseClosestNode(DijkstraNodes);
+ (*NodeInter).Chosen = true;
+
+ // Falls kein freier Knoten mehr in der Randknotenliste vorhanden ist, gibt es keinen Weg vom Start- zum Endknoten.
+ // Dieser Fall sollte nie auftreten, da die Anzahl der Schleifendurchgänge begrenzt ist, aber sicher ist sicher.
+ if (NodeInter == DijkstraNodes.end()) return false;
+
+ // Wenn der Zielpunkt noch näher liegt als der nächte Punkt, ist die Suche beendet
+ if (EndPoint.Cost <= (*NodeInter).Cost)
+ {
+ // Ergebnispfad extrahieren
+
+ // Den Endpunkt in den Ergebnispfad einfügen
+ Path.push_back(End);
+
+ // Die Wegknoten in umgekehrter Reihenfolge ablaufen und in den Ergebnispfad einfügen
+ DijkstraNode::ConstIter CurNode = EndPoint.ParentIter;
+ while (CurNode != DijkstraNodes.end())
+ {
+ BS_ASSERT((*CurNode).Chosen);
+ Path.push_back(m_Nodes[CurNode - DijkstraNodes.begin()]);
+ CurNode = (*CurNode).ParentIter;
+ }
+
+ // Den Startpunkt in den Ergebnispfad einfügen
+ Path.push_back(Start);
+
+ // Die Knoten des Pfades müssen ungedreht werden, da sie in umgekehrter Reihenfolge extrahiert wurden.
+ // Diesen Schritt könnte man sich sparen, wenn man den Pfad vom Ende zum Anfang sucht.
+ std::reverse(Path.begin(), Path.end());
+
+ return true;
+ }
+
+ // Relaxation-Schritt für die Knoten des Graphen und für den Endknoten durchführen
+ RelaxNodes(DijkstraNodes, m_VisibilityMatrix, NodeInter);
+ RelaxEndPoint(m_Nodes[NodeInter - DijkstraNodes.begin()], NodeInter, End, EndPoint, *this);
+ }
+
+ // Falls die Schleife komplett durchlaufen wurde, wurden alle Knoten gewählt und es wurde trotzdem kein Pfad gefunden. Es existiert also keiner.
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_WalkRegion::InitNodeVector()
+{
+ // Knoten-Vector leeren.
+ m_Nodes.clear();
+
+ // Anzahl der Knoten bestimmen.
+ int NodeCount = 0;
+ {
+ for (unsigned int i = 0; i < m_Polygons.size(); i++)
+ NodeCount += m_Polygons[i].VertexCount;
+ }
+
+ // Knoten-Vector füllen
+ m_Nodes.reserve(NodeCount);
+ {
+ for (unsigned int j = 0; j < m_Polygons.size(); j++)
+ for (int i = 0; i < m_Polygons[j].VertexCount; i++)
+ m_Nodes.push_back(m_Polygons[j].Vertecies[i]);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_WalkRegion::ComputeVisibilityMatrix()
+{
+ // Sichtbarkeitsmatrix initialisieren
+ m_VisibilityMatrix = std::vector< std::vector <int> >(m_Nodes.size(), std::vector<int>(m_Nodes.size(), INFINITY));
+
+ // Sichtbarkeiten zwischen Vertecies berechnen und in die Sichbarkeitsmatrix eintragen.
+ for (unsigned int j = 0; j < m_Nodes.size(); ++j)
+ {
+ for (unsigned int i = j; i < m_Nodes.size(); ++i)
+ {
+ if (IsLineOfSight(m_Nodes[i], m_Nodes[j]))
+ {
+ // Wenn eine Sichtlinie besteht wird die Entfernung der Knoten eingetragen
+ int Distance = m_Nodes[i].Distance(m_Nodes[j]);
+ m_VisibilityMatrix[i][j] = Distance;
+ m_VisibilityMatrix[j][i] = Distance;
+ }
+ else
+ {
+ // Wenn keine Sichtlinie besteht wird die Entfernung "unendlich" eingetragen
+ m_VisibilityMatrix[i][j] = INFINITY;
+ m_VisibilityMatrix[j][i] = INFINITY;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_WalkRegion::CheckAndPrepareStartAndEnd(BS_Vertex & Start, BS_Vertex & End) const
+{
+ if (!IsPointInRegion(Start))
+ {
+ BS_Vertex NewStart = FindClosestRegionPoint(Start);
+
+ // Sicherstellen, dass der ermittelte Punkt wirklich innerhalb der Region liegt und Notfalls abbrechen.
+ if (!IsPointInRegion(NewStart))
+ {
+ BS_LOG_ERRORLN("Constructed startpoint ((%d,%d) from (%d,%d)) is not inside the region.",
+ NewStart.X, NewStart.Y,
+ Start.X, Start.Y);
+ return false;
+ }
+
+ Start = NewStart;
+ }
+
+ // Falls der Zielpunkt außerhalb der Region liegt, wird der nächste Punkt innerhalb der Region bestimmt und als Endpunkt benutzt.
+ if (!IsPointInRegion(End))
+ {
+ BS_Vertex NewEnd = FindClosestRegionPoint(End);
+
+ // Sicherstellen, dass der ermittelte Punkt wirklich innerhalb der Region liegt und Notfalls abbrechen.
+ if (!IsPointInRegion(NewEnd))
+ {
+ BS_LOG_ERRORLN("Constructed endpoint ((%d,%d) from (%d,%d)) is not inside the region.",
+ NewEnd.X, NewEnd.Y,
+ End.X, End.Y);
+ return false;
+ }
+
+ End = NewEnd;
+ }
+
+ // Erfolg signalisieren
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_WalkRegion::SetPos(int X, int Y)
+{
+ // Unterschied zwischen alter und neuer Position berechnen.
+ BS_Vertex Delta(X - m_Position.X, Y - m_Position.Y);
+
+ // Alle Nodes verschieben.
+ for (unsigned int i = 0; i < m_Nodes.size(); i++) m_Nodes[i] += Delta;
+
+ // Region verschieben
+ BS_Region::SetPos(X, Y);
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_WalkRegion::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ bool Result = true;
+
+ // Elternobjekt persistieren.
+ Result &= BS_Region::Persist(Writer);
+
+ // Knoten persistieren.
+ Writer.Write(m_Nodes.size());
+ std::vector<BS_Vertex>::const_iterator It = m_Nodes.begin();
+ while (It != m_Nodes.end())
+ {
+ Writer.Write(It->X);
+ Writer.Write(It->Y);
+ ++It;
+ }
+
+ // Sichtbarkeitsmatrix persistieren.
+ Writer.Write(m_VisibilityMatrix.size());
+ std::vector< std::vector<int> >::const_iterator RowIter = m_VisibilityMatrix.begin();
+ while (RowIter != m_VisibilityMatrix.end())
+ {
+ Writer.Write(RowIter->size());
+ std::vector<int>::const_iterator ColIter = RowIter->begin();
+ while (ColIter != RowIter->end())
+ {
+ Writer.Write(*ColIter);
+ ++ColIter;
+ }
+
+ ++RowIter;
+ }
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_WalkRegion::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ bool Result = true;
+
+ // Das Elternobjekt wurde schon über den Konstruktor von BS_Region geladen, daher müssen an dieser Stelle nur noch die zusätzlichen Daten von
+ // BS_WalkRegion geladen werden.
+
+ // Knoten laden.
+ unsigned int NodeCount;
+ Reader.Read(NodeCount);
+ m_Nodes.clear();
+ m_Nodes.resize(NodeCount);
+ std::vector<BS_Vertex>::iterator It = m_Nodes.begin();
+ while (It != m_Nodes.end())
+ {
+ Reader.Read(It->X);
+ Reader.Read(It->Y);
+ ++It;
+ }
+
+ // Sichtbarkeitsmatrix laden.
+ unsigned int RowCount;
+ Reader.Read(RowCount);
+ m_VisibilityMatrix.clear();
+ m_VisibilityMatrix.resize(RowCount);
+ std::vector< std::vector<int> >::iterator RowIter = m_VisibilityMatrix.begin();
+ while (RowIter != m_VisibilityMatrix.end())
+ {
+ unsigned int ColCount;
+ Reader.Read(ColCount);
+ RowIter->resize(ColCount);
+ std::vector<int>::iterator ColIter = RowIter->begin();
+ while (ColIter != RowIter->end())
+ {
+ Reader.Read(*ColIter);
+ ++ColIter;
+ }
+
+ ++RowIter;
+ }
+
+ return Result && Reader.IsGood();
+}
diff --git a/engines/sword25/math/walkregion.h b/engines/sword25/math/walkregion.h
new file mode 100755
index 0000000000..d9fa73d585
--- /dev/null
+++ b/engines/sword25/math/walkregion.h
@@ -0,0 +1,102 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_WALKREGION_H
+#define BS_WALKREGION_H
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "region.h"
+
+// -----------------------------------------------------------------------------
+// Typdefinitionen
+// -----------------------------------------------------------------------------
+
+typedef std::vector<BS_Vertex> BS_Path;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+/**
+ @brief Diese Klasse stellt die Region dar, in der sich der Hauptcharakter bewegen kann.
+*/
+class BS_WalkRegion : public BS_Region
+{
+ friend BS_Region;
+
+protected:
+ BS_WalkRegion();
+ BS_WalkRegion(BS_InputPersistenceBlock & Reader, unsigned int Handle);
+
+public:
+ virtual ~BS_WalkRegion();
+
+ virtual bool Init(const BS_Polygon & Contour, const std::vector<BS_Polygon> * pHoles = 0);
+
+ /**
+ @brief Ermittelt den kürzesten Weg zwischen zwei Punkten in der Region.
+
+ Diese Methode verlangt, dass der Startpunkt innerhalb der Region liegt. Der Endpunkt darf außerhalb der Region liegen. In diesem
+ Fall wählt die Methode als Endpunkt den Punkt innerhalb der Region, der am dichtesten am Endpunkt liegt.
+
+ @param X1 X-Koordinate des Startpunktes
+ @param Y1 Y-Koordinate des Startpunktes
+ @param X2 X-Koordinate des Zielpunktes
+ @param Y2 Y-Koordinate des Zielpunktes
+ @param Path ein leerer BS_Path, der den Ergebnispfad aufnehmen soll
+ @return Gibt false zurück, fall die Eingaben ungültig waren, ansonsten wird true zurückgegeben.
+ */
+ bool QueryPath(int X1, int Y1, int X2, int Y2, BS_Path & Path) { return QueryPath(BS_Vertex(X1, Y1), BS_Vertex(X2, Y2), Path); }
+
+ /**
+ @brief Ermittelt den kürzesten Weg zwischen zwei Punkten in der Region.
+
+ Diese Methode verlangt, dass der Startpunkt innerhalb der Region liegt. Der Endpunkt darf außerhalb der Region liegen. In diesem
+ Fall wählt die Methode als Endpunkt den Punkt innerhalb der Region, der am dichtesten am Endpunkt liegt.
+
+ @param StartPoint der Startpunkt
+ @param EndPoint der Endpunkt
+ @param Path ein leerer BS_Path, der den Ergebnispfad aufnehmen soll
+ @return Gibt false zurück, fall die Eingaben ungültig waren, ansonsten wird true zurückgegeben.
+ */
+ bool QueryPath(BS_Vertex StartPoint, BS_Vertex EndPoint, BS_Path & Path);
+
+ virtual void SetPos(int X, int Y);
+
+ const std::vector<BS_Vertex> & GetNodes() const { return m_Nodes; }
+ const std::vector< std::vector<int> > & GetVisibilityMatrix() const { return m_VisibilityMatrix; }
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ std::vector<BS_Vertex> m_Nodes;
+ std::vector< std::vector<int> > m_VisibilityMatrix;
+
+ void InitNodeVector();
+ void ComputeVisibilityMatrix();
+ bool CheckAndPrepareStartAndEnd(BS_Vertex & Start, BS_Vertex & End) const;
+ bool FindPath(const BS_Vertex & Start, const BS_Vertex & End, BS_Path & Path) const;
+};
+
+#endif
diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp
new file mode 100755
index 0000000000..ecee060d56
--- /dev/null
+++ b/engines/sword25/package/packagemanager.cpp
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "PACKAGEMANAGER"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "packagemanager.h"
+
+// -----------------------------------------------------------------------------
+
+BS_PackageManager::BS_PackageManager(BS_Kernel * pKernel) : BS_Service(pKernel)
+{
+ if (!_RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+} \ No newline at end of file
diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h
new file mode 100755
index 0000000000..67c97505c7
--- /dev/null
+++ b/engines/sword25/package/packagemanager.h
@@ -0,0 +1,188 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_PackageManager
+ -------------
+ Dies ist das Package Manager Interface, dass alle Methoden enthält, die ein Package Manager
+ implementieren muss.
+ Beim Package Manager ist folgendes zu beachten:
+ 1. es wird ein komplett neuer (virtueller) Verzeichnisbaum erstellt,
+ in den Packages und Verzeichnisse gemounted werden können.
+ 2. zum Trennen von Elementen eines Verzeichnisspfades wird '/' statt '\' verwendet.
+ 3. LoadDirectoryAsPackage sollte nur zum Testen benutzt werden. Im Final Release sollen sich
+ alle Dateien in echten Packages befinden.
+
+ Autor: Malte Thiesen, $author$
+*/
+
+#ifndef BS_PACKAGE_MANAGER_H
+#define BS_PACKAGE_MANAGER_H
+
+// Includes
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "kernel/service.h"
+
+// Klassendefinition
+/**
+ @brief Das Package Manager Interface
+
+ Beim Package Manager ist folgendes zu beachten:<br>
+ 1. es wird ein komplett neuer (virtueller) Verzeichnisbaum erstellt,
+ in den Packages und Verzeichnisse gemounted werden können.<br>
+ 2. zum Trennen von Elementen eines Verzeichnisspfades wird '/' statt '\' verwendet.<br>
+ 3. LoadDirectoryAsPackage sollte nur zum Testen benutzt werden. Im Final Release sollen sich
+ alle Dateien in echten Packages befinden.
+*/
+class BS_PackageManager : public BS_Service
+{
+public:
+ BS_PackageManager(BS_Kernel* pKernel);
+ virtual ~BS_PackageManager() {};
+
+ enum FILE_TYPES
+ {
+ FT_DIRECTORY = (1<<0),
+ FT_FILE = (1<<1)
+ };
+
+ /**
+ @brief Mit Instanzen dieses Objektes wird nach Dateien gesucht.
+
+ Objekte diesen Types wird mit BS_PackageManager::CreateSearch erzeugt.
+ */
+ class FileSearch
+ {
+ public:
+ virtual ~FileSearch(){};
+
+ /**
+ @brief Gibt den Dateinamen der aktuellen Datei zurück.
+ @return Gibt den Dateinamen der aktuellen Datei zurück.
+ */
+ virtual std::string GetCurFileName() = 0;
+ /**
+ @brief Gibt den Typ der aktuellen Datei zurück.
+ @return Gibt den Typ der aktuellen Datei zurück.<br>
+ Dieses ist entweder BS_PackageManager::FT_FILE oder BS_PackageManager::FT_DIRECTORY.
+ */
+ virtual unsigned int GetCurFileType() = 0;
+ /**
+ @brief Gibt die Größe der aktuellen Datei zurück.
+ @return Gibt die Größe der aktuellen Datei zurück.<br>
+ Bei Verzeichnissen ist dieser Wert immer 0.
+ */
+ virtual unsigned int GetCurFileSize() = 0;
+ // Sucht die nächste Datei
+ // Gibt false zurück, falls keine weitere Datei gefunden wurde
+ /**
+ @brief Sucht die nächste Datei.
+ @return Gibt false zurück, falls keine weitere Datei die Suchkriterien erfüllte.
+ */
+ virtual bool NextFile() = 0;
+ };
+
+ /**
+ @brief Mounted den Inhalt eines Packages in das angegebene Verzeichnis im virtuellen Verzeichnisbaum.
+ @param FileName der Dateiname des zu mountenden Packages
+ @param MountPosition der Verzeichnisname, unter dem das Package gemounted werden soll
+ @return Gibt true zurück falls das mounten erfolgreich war, andernfalls false.
+ */
+ virtual bool LoadPackage(const std::string& FileName, const std::string& MountPosition) = 0;
+ /**
+ @brief Mounted den Inhalt eines Verzeichnisses in das angegebene Verzeichnis im virtuellen Verzeichnisbaum.
+ @param DirectoryName der Name des zu mountenden Verzeichnisses
+ @param MountPosition der Verzeichnisname, unter dem das Verzeichnis gemounted werden soll
+ @return Gibt true zurück falls das mounten erfolgreich war, andernfalls false.
+ */
+ virtual bool LoadDirectoryAsPackage(const std::string& DirectoryName, const std::string& MountPosition) = 0;
+ /**
+ @brief Lädt eine Datei aus dem virtuellen Verzeichnisbaum.
+ @param FileName der Dateiname der zu ladenden Datei
+ @param pFileSize Pointer auf die Variable, die die Größe der geladenen Datei enthalten soll<br>
+ Der Standardwert ist NULL.
+ @return Gibt einen Pointer auf die Dateidaten zürück, oder NULL, wenn die Datei nicht geladen werden konnte.
+ @remark Es darf nicht vergessen werden, die Dateidaten nach Benutzung mit BE_DELETE_A freizugeben.
+ */
+ virtual void* GetFile(const std::string& FileName, unsigned int* pFileSize = NULL) = 0;
+ /**
+ @brief Gibt den Pfad zum aktuellen Verzeichnis zurück.
+ @return Gibt einen String zurück, der den Pfad zum aktuellen Verzeichnis enthält.<br>
+ Falls der Pfad nicht bestimmt werden konnte wird ein leerer String zurückgegeben.
+ @remark Zum Trennen von Pfadelementen wird "/" und nicht "\" verwendet.
+ */
+ virtual std::string GetCurrentDirectory() = 0;
+ /**
+ @brief Wechselt das aktuelle Verzeichnis.
+ @param Directory ein String der das Verzeichnis bezeichnet, in dass gewechselt werden soll.<br>
+ Die Pfadangabe darf relativ sein.
+ @return Gibt true zurück, falls der Vorgang erfolgreich war, ansonsten false.
+ @remark Zum Trennen von Pfadelementen wird "/" und nicht "\" verwendet.
+ */
+ virtual bool ChangeDirectory(const std::string& Directory) = 0;
+ /**
+ @brief Gibt den absoluten Pfad zu einer Datei im virtuellen Verzeichnisbaum zurück.
+ @param FileName der Dateiname der Datei, deren absoluter Pfad bestimmt werden soll.<br>
+ Diese Parameter kann sowohl relative als auch absolute Pfadangaben beinhalten.
+ @return Gibt einen String zurück, der den absoluten Pfad zur übergebenen Datei enthält.<br>
+ Falls der absolute Pfad nicht bestimmt werden konnte, wird ein leerer String zurückgegeben.
+ @remark Zum Trennen von Pfadelementen wird "/" und nicht "\" verwendet.
+ */
+ virtual std::string GetAbsolutePath(const std::string& FileName) = 0;
+ /**
+ @brief Erstellt ein BS_PackageManager::FileSearch Objekt mit dem Nach Dateien gesucht werden kann.
+ @param Filter gibt den Suchstring an. Dieser darf die Wildcards '*' und '?' enthalten.
+ @param Path gibt das Verzeichnis an, welches durchsucht werden soll.
+ @param TypeFilter ist eine Kombination der Flags BS_PackageManager::FT_DIRECTORY und BS_PackageManager::FT_FILE.<br>
+ Diese Flags geben an, ob nach Dateien oder Verzeichnissen oder beiden gesucht werden soll.<br>
+ Der Standardwert ist BS_PackageManager::FT_DIRECTORY | BS_PackageManager::FT_FILE.
+ @return Gibt einen Pointer auf ein BS_PackageManager::FileSearch Objekt zurück, oder NULL wenn keine Datei gefunden wurde.
+ @remark Nicht vergessen, das Objekt nach Benutzung mit delete freizugeben.
+ */
+ virtual FileSearch* CreateSearch(const std::string& Filter, const std::string& Path, unsigned int TypeFilter = FT_DIRECTORY | FT_FILE) = 0;
+
+ /**
+ * @brief Gibt die Dateigröße zurück.
+ * @param FileName die Datei.
+ * @return die Dateigröße. Im Falle eines Fehlers wird 0xffffffff zurückgegeben.
+ * @remarks Bei komprimierten Containern wird die unkomprimierte Größe zurückgegeben.
+ **/
+ virtual unsigned int GetFileSize(const std::string& FileName) = 0;
+
+ /**
+ @brief Gibt den Typ einer Datei zurück.
+ @param FileName der Dateiname
+ @return Gibt den Dateityp zurück (BS_PackageManager::FT_DIRECTORY oder BS_PackageManager::FT_FILE).<br>
+ Falls die Datei nicht gefunden wurde wird 0 zurückgegeben.
+ */
+ virtual unsigned int GetFileType(const std::string & FileName) = 0;
+
+ /**
+ @brief Bestimmt, ob eine Datei existiert.
+ @param FileName der Dateiname
+ @return Gibt true zurück, wenn die Datei existiert, ansonsten false.
+ */
+ virtual bool FileExists(const std::string & FileName) = 0;
+
+private:
+ bool _RegisterScriptBindings();
+};
+
+#endif
diff --git a/engines/sword25/package/packagemanager_script.cpp b/engines/sword25/package/packagemanager_script.cpp
new file mode 100755
index 0000000000..01b6692a4c
--- /dev/null
+++ b/engines/sword25/package/packagemanager_script.cpp
@@ -0,0 +1,247 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+
+#include "packagemanager.h"
+
+// -----------------------------------------------------------------------------
+
+static BS_PackageManager * GetPM()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_PackageManager * pPM = static_cast<BS_PackageManager *>(pKernel->GetService("package"));
+ BS_ASSERT(pPM);
+ return pPM;
+}
+
+// -----------------------------------------------------------------------------
+
+static int LoadPackage(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushbooleancpp(L, pPM->LoadPackage(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int LoadDirectoryAsPackage(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushbooleancpp(L, pPM->LoadDirectoryAsPackage(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetCurrentDirectory(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushstring(L, pPM->GetCurrentDirectory().c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ChangeDirectory(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushbooleancpp(L, pPM->ChangeDirectory(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetAbsolutePath(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushstring(L, pPM->GetAbsolutePath(luaL_checkstring(L, 1)).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetFileSize(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushnumber(L, pPM->GetFileSize(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetFileType(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ lua_pushnumber(L, pPM->GetFileType(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static void SplitSearchPath(const std::string & Path, std::string & Directory, std::string & Filter)
+{
+ std::string::size_type LastSlash = Path.rfind("/");
+ if (LastSlash == std::string::npos)
+ {
+ Directory = "";
+ Filter = Path;
+ }
+ else
+ {
+ Directory = Path.substr(0, LastSlash);
+ Filter = Path.substr(LastSlash + 1);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+static void DoSearch(lua_State * L, const std::string & Path, unsigned int Type)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ // Der Packagemanager-Service muss den Suchstring und den Pfad getrennt übergeben bekommen.
+ // Um die Benutzbarkeit zu verbessern sollen Skriptprogrammierer dieses als ein Pfad übergeben können.
+ // Daher muss der übergebene Pfad am letzten Slash aufgesplittet werden.
+ std::string Directory;
+ std::string Filter;
+ SplitSearchPath(Path, Directory, Filter);
+
+ // Ergebnistable auf dem Lua-Stack erstellen
+ lua_newtable(L);
+
+ // Suche durchführen und die Namen aller gefundenen Dateien in die Ergebnistabelle einfügen.
+ // Als Indizes werden fortlaufende Nummern verwandt.
+ unsigned int ResultNr = 1;
+ BS_PackageManager::FileSearch * pFS = pPM->CreateSearch(Filter, Directory, Type);
+ if (pFS)
+ {
+ do
+ {
+ lua_pushnumber(L, ResultNr);
+ lua_pushstring(L, pFS->GetCurFileName().c_str());
+ lua_settable(L, -3);
+ ResultNr++;
+ } while(pFS->NextFile());
+ }
+
+ delete(pFS);
+}
+
+// -----------------------------------------------------------------------------
+
+static int FindFiles(lua_State * L)
+{
+ DoSearch(L, luaL_checkstring(L, 1), BS_PackageManager::FT_FILE);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int FindDirectories(lua_State * L)
+{
+ DoSearch(L, luaL_checkstring(L, 1), BS_PackageManager::FT_DIRECTORY);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetFileAsString(lua_State * L)
+{
+ BS_PackageManager * pPM = GetPM();
+
+ unsigned int FileSize;
+ void * FileData = pPM->GetFile(luaL_checkstring(L, 1), &FileSize);
+ if (FileData)
+ {
+ lua_pushlstring(L, static_cast<char *>(FileData), FileSize);
+ delete FileData;
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int FileExists(lua_State * L)
+{
+ lua_pushbooleancpp(L, GetPM()->FileExists(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * PACKAGE_LIBRARY_NAME = "Package";
+
+static const luaL_reg PACKAGE_FUNCTIONS[] =
+{
+ "LoadPackage", LoadPackage,
+ "LoadDirectoryAsPackage", LoadDirectoryAsPackage,
+ "GetCurrentDirectory", GetCurrentDirectory,
+ "ChangeDirectory", ChangeDirectory,
+ "GetAbsolutePath", GetAbsolutePath,
+ "GetFileSize", GetFileSize,
+ "GetFileType", GetFileType,
+ "FindFiles", FindFiles,
+ "FindDirectories", FindDirectories,
+ "GetFileAsString", GetFileAsString,
+ "FileExists", FileExists,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_PackageManager::_RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/package/physfspackagemanager.cpp b/engines/sword25/package/physfspackagemanager.cpp
new file mode 100755
index 0000000000..70b9d98578
--- /dev/null
+++ b/engines/sword25/package/physfspackagemanager.cpp
@@ -0,0 +1,487 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "physfspackagemanager.h"
+#include "util/physfs/physfs.h"
+extern "C"
+{
+ #include "util/physfs/extras/globbing.h"
+};
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include <sstream>
+#include "kernel/memlog_on.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "PHYSFSPACKAGEMANAGER"
+
+// -----------------------------------------------------------------------------
+// Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * SafeGetLastError()
+ {
+ const char * ErrorMessage = PHYSFS_getLastError();
+ return ErrorMessage ? ErrorMessage : "unknown";
+ }
+
+ // -------------------------------------------------------------------------
+
+ void LogPhysfsError(const char * FunctionName)
+ {
+ BS_LOG_ERRORLN("%s() failed. Reason: %s.", FunctionName, SafeGetLastError());
+ }
+
+ // -------------------------------------------------------------------------
+
+ void LogPhysfsError(const char * FunctionName, const char * FileName)
+ {
+ BS_LOG_ERRORLN("%s() on file \"%s\" failed. Reason: %s.", FunctionName, FileName, SafeGetLastError());
+ }
+
+ // -------------------------------------------------------------------------
+
+ const char PATH_SEPARATOR = '/';
+ const char NAVIGATION_CHARACTER = '.';
+
+ // -------------------------------------------------------------------------
+
+ std::string RemoveRedundantPathSeparators(const std::string & Path)
+ {
+ std::string Result;
+
+ // Über alle Zeichen des Eingabepfades iterieren.
+ std::string::const_iterator It = Path.begin();
+ while (It != Path.end())
+ {
+ if (*It == PATH_SEPARATOR)
+ {
+ // Verzeichnistrenner gefunden.
+
+ // Folgen von Verzeichnistrennern überspringen.
+ while (It != Path.end() && *It == PATH_SEPARATOR) ++It;
+
+ // Einzelnen Verzeichnistrenner ausgeben, nur am Ende des Pfades wird kein Verzeichnistrenner mehr ausgegeben.
+ if (It != Path.end()) Result.push_back(PATH_SEPARATOR);
+ }
+ else
+ {
+ // Normales Zeichen gefunden, wird unverändert ausgegeben.
+ Result.push_back(*It);
+ ++It;
+ }
+ }
+
+ return Result;
+ }
+
+ // ---------------------------------------------------------------------
+
+ struct PathElement
+ {
+ public:
+ PathElement(std::string::const_iterator Begin, std::string::const_iterator End) : m_Begin(Begin), m_End(End) {}
+
+ std::string::const_iterator GetBegin() const { return m_Begin; }
+ std::string::const_iterator GetEnd() const { return m_End; }
+
+ private:
+ std::string::const_iterator m_Begin;
+ std::string::const_iterator m_End;
+ };
+
+ // -------------------------------------------------------------------------
+
+ std::string NormalizePath(const std::string & Path, const std::string & CurrentDirectory)
+ {
+ // Feststellen, ob der Pfad absolut (beginnt mit /) oder relativ ist und im relativen Fall dem Gesamtpfad das aktuelle Verzeichnis
+ // voranstellen.
+ std::string WholePath = (Path.size() >= 1 && Path[0] == PATH_SEPARATOR) ? "" : CurrentDirectory + PATH_SEPARATOR;
+
+ // Alle gedoppelten und nachfolgende Verzeichnistrenner aus dem übergebenen Pfad entfernen und den Gesamtpfad zusammensetzen.
+ // CurrentDirectory wird nicht auf diese Weise gesäubert. Es wird vorrausgesetzt, dass CurrentDirectory keine überflüssigen
+ // Verzeichnistrenner beinhaltet.
+ WholePath += RemoveRedundantPathSeparators(Path);
+
+ // Gesamtpfad parsen und in Einzelelemente aufteilen. Dabei werden Vorkommen von ".." und "." korrekt behandelt.
+ vector<PathElement> PathElements;
+ size_t SeparatorPos = 0;
+ while (SeparatorPos < WholePath.size())
+ {
+ // Nächsten Verzeichnistrenner finden.
+ size_t NextSeparatorPos = WholePath.find(PATH_SEPARATOR, SeparatorPos + 1);
+ if (NextSeparatorPos == std::string::npos) NextSeparatorPos = WholePath.size();
+
+ // Anfang und Ende vom Pfadelement berechnen.
+ std::string::const_iterator ElementBegin = WholePath.begin() + SeparatorPos + 1;
+ std::string::const_iterator ElementEnd = WholePath.begin() + NextSeparatorPos;
+
+ if (ElementEnd - ElementBegin == 2 &&
+ ElementBegin[0] == NAVIGATION_CHARACTER &&
+ ElementBegin[1] == NAVIGATION_CHARACTER)
+ {
+ // Element ist "..", daher wird das vorangegangene Pfadelement aus dem vector entfernt.
+ if (PathElements.size()) PathElements.pop_back();
+ }
+ else if (ElementEnd - ElementBegin == 1 &&
+ ElementBegin[0] == NAVIGATION_CHARACTER)
+ {
+ // Element ist ".", wir tun gar nichts.
+ }
+ else
+ {
+ // Normales Element in den vector einfügen.
+ PathElements.push_back(PathElement(WholePath.begin() + SeparatorPos + 1, WholePath.begin() + NextSeparatorPos));
+ }
+
+ SeparatorPos = NextSeparatorPos;
+ }
+
+ if (PathElements.size())
+ {
+ // Die einzelnen Pfadelemente werden durch Verzeichnistrenner getrennt aneinandergesetzt.
+ // Der so entstandene String wird als Ergebnis zurückgegeben.
+ ostringstream PathBuilder;
+ vector<PathElement>::const_iterator It = PathElements.begin();
+ while (It != PathElements.end())
+ {
+ PathBuilder << PATH_SEPARATOR << std::string(It->GetBegin(), It->GetEnd());
+ ++It;
+ }
+
+ return PathBuilder.str();
+ }
+ else
+ {
+ // Nach dem Parsen sind keine Pfadelemente mehr übrig geblieben, daher wird des Root-Verzeichnis zurückgegeben.
+ return std::string(1, PATH_SEPARATOR);
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // RAII-Klasse für PHYSFS-Filehandles.
+ // -------------------------------------------------------------------------
+
+ class PhysfsHandleHolder
+ {
+ public:
+ PhysfsHandleHolder(PHYSFS_File * Handle) : m_Handle(Handle) {}
+ ~PhysfsHandleHolder()
+ {
+ if (m_Handle)
+ {
+ if (!PHYSFS_close(m_Handle)) LogPhysfsError("PHYSFS_close");
+ }
+ }
+
+ PHYSFS_File * Get() { return m_Handle; }
+ PHYSFS_File * Release()
+ {
+ PHYSFS_File * Result = m_Handle;
+ m_Handle = 0;
+ return Result;
+ }
+
+ private:
+ PHYSFS_File * m_Handle;
+ };
+
+ // -------------------------------------------------------------------------
+ // RAII-Klasse für PHYSFS-Listen.
+ // -------------------------------------------------------------------------
+
+ template<typename T>
+ class PhysfsListHolder
+ {
+ public:
+ PhysfsListHolder(T List) : m_List(List) {};
+ ~PhysfsListHolder() { if (m_List) PHYSFS_freeList(m_List); }
+
+ T Get() { return m_List; }
+
+ private:
+ T m_List;
+ };
+
+ // -------------------------------------------------------------------------
+
+ PHYSFS_File * OpenFileAndGetSize(const std::string & FileName, const std::string & CurrentDirectory, unsigned int & FileSize)
+ {
+ // Datei öffnen.
+ PhysfsHandleHolder Handle(PHYSFS_openRead(NormalizePath(FileName, CurrentDirectory).c_str()));
+ if (!Handle.Get())
+ {
+ LogPhysfsError("PHYSFS_openRead", FileName.c_str());
+ return 0;
+ }
+
+ // Dateigröße bestimmen.
+ PHYSFS_sint64 LongFileSize = PHYSFS_fileLength(Handle.Get());
+ if (LongFileSize == -1)
+ {
+ BS_LOG_ERRORLN("Unable to determine filelength on PhysicsFS file \"%s\".", FileName.c_str());
+ return 0;
+ }
+ if (LongFileSize >= UINT_MAX)
+ {
+ BS_LOG_ERRORLN("File \"%s\" is too big.", FileName.c_str());
+ return 0;
+ }
+
+ // Rückgabewerte setzen.
+ FileSize = static_cast<unsigned int>(LongFileSize);
+ return Handle.Release();
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_PhysfsPackageManager::BS_PhysfsPackageManager(BS_Kernel * KernelPtr) :
+ BS_PackageManager(KernelPtr),
+ m_CurrentDirectory(1, PATH_SEPARATOR)
+{
+ if (!PHYSFS_init(0)) LogPhysfsError("PHYSFS_init");
+}
+
+// -----------------------------------------------------------------------------
+
+BS_PhysfsPackageManager::~BS_PhysfsPackageManager()
+{
+ if (!PHYSFS_deinit()) LogPhysfsError("PHYSFS_deinit");
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_PhysfsPackageManager_CreateObject(BS_Kernel * KernelPtr) { return new BS_PhysfsPackageManager(KernelPtr); }
+
+// -----------------------------------------------------------------------------
+
+bool BS_PhysfsPackageManager::LoadPackage(const std::string & FileName, const std::string & MountPosition)
+{
+ if (!PHYSFS_mount(FileName.c_str(), NormalizePath(MountPosition, m_CurrentDirectory).c_str(), 0))
+ {
+ BS_LOG_ERRORLN("Unable to mount file \"%s\" to \"%s\". Reason: %s.", FileName.c_str(), MountPosition.c_str(), SafeGetLastError());
+ return false;
+ }
+ else
+ {
+ BS_LOGLN("Package '%s' mounted as '%s'.", FileName.c_str(), MountPosition.c_str());
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PhysfsPackageManager::LoadDirectoryAsPackage(const std::string & DirectoryName, const std::string & MountPosition)
+{
+ if (!PHYSFS_mount(DirectoryName.c_str(), NormalizePath(MountPosition, m_CurrentDirectory).c_str(), 0))
+ {
+ BS_LOG_ERRORLN("Unable to mount directory \"%s\" to \"%s\". Reason: %s.", DirectoryName.c_str(), MountPosition.c_str(), SafeGetLastError());
+ return false;
+ }
+ else
+ {
+ BS_LOGLN("Directory '%s' mounted as '%s'.", DirectoryName.c_str(), MountPosition.c_str());
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void * BS_PhysfsPackageManager::GetFile(const std::string & FileName, unsigned int * FileSizePtr)
+{
+ // Datei öffnen und deren Größe bestimmen.
+ unsigned int FileSize;
+ PhysfsHandleHolder Handle(OpenFileAndGetSize(FileName, m_CurrentDirectory, FileSize));
+ if (!Handle.Get()) return 0;
+
+ // Falls gewünscht, die Größe der Datei zurückgeben.
+ if (FileSizePtr) *FileSizePtr = FileSize;
+
+ // Datei einlesen.
+ char * Buffer = new char[FileSize];
+ if (PHYSFS_read(Handle.Get(), Buffer, 1, FileSize) <= 0)
+ {
+ LogPhysfsError("PHYSFS_read", FileName.c_str());
+ delete [] Buffer;
+ return 0;
+ }
+
+ return Buffer;
+}
+
+// -----------------------------------------------------------------------------
+
+std::string BS_PhysfsPackageManager::GetCurrentDirectory()
+{
+ return m_CurrentDirectory;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PhysfsPackageManager::ChangeDirectory(const std::string & Directory)
+{
+ // Pfad normalisieren.
+ std::string CleanedDirectory = NormalizePath(Directory, m_CurrentDirectory);
+
+ // Interne Variable setzen, wenn das Verzeichnis tatsächlich existiert oder das Wurzelverzeichnis ist.
+ if (CleanedDirectory == std::string(1, PATH_SEPARATOR) || PHYSFS_isDirectory(CleanedDirectory.c_str()))
+ {
+ m_CurrentDirectory = CleanedDirectory;
+ return true;
+ }
+ // Fehler ausgeben, wenn das Verzeichnis nicht existiert.
+ else
+ {
+ BS_LOG_ERRORLN("Tried to change to non-existing directory \"%s\". Call is ignored", Directory.c_str());
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+std::string BS_PhysfsPackageManager::GetAbsolutePath(const std::string & FileName)
+{
+ return NormalizePath(FileName, m_CurrentDirectory);
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_PhysfsPackageManager::GetFileSize(const std::string & FileName)
+{
+ // Datei öffnen und deren Größe bestimmen.
+ unsigned int FileSize;
+ PhysfsHandleHolder Handle(OpenFileAndGetSize(FileName, m_CurrentDirectory, FileSize));
+ if (!Handle.Get()) return 0xffffffff;
+
+ // Größe der Datei zurückgeben.
+ return FileSize;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_PhysfsPackageManager::GetFileType(const std::string & FileName)
+{
+ std::string NormalizedPath = NormalizePath(FileName, m_CurrentDirectory);
+
+ if (PHYSFS_exists(NormalizedPath.c_str()))
+ {
+ return PHYSFS_isDirectory(NormalizedPath.c_str()) ? BS_PackageManager::FT_DIRECTORY : BS_PackageManager::FT_FILE;
+ }
+ else
+ {
+ BS_LOG_ERRORLN("Cannot determine type of non-existant file \"%s\".", NormalizedPath.c_str());
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_PhysfsPackageManager::FileExists(const std::string & FileName)
+{
+ std::string NormalizedPath = NormalizePath(FileName, m_CurrentDirectory);
+ return PHYSFS_exists(NormalizedPath.c_str()) != 0;
+}
+
+// -----------------------------------------------------------------------------
+// Dateien suchen
+// -----------------------------------------------------------------------------
+
+class PhysfsFileSearch : public BS_PackageManager::FileSearch
+{
+public:
+ // Path muss normalisiert sein.
+ PhysfsFileSearch(BS_PackageManager & PackageManager, const vector<std::string> & FoundFiles) :
+ m_PackageManager(PackageManager),
+ m_FoundFiles(FoundFiles),
+ m_FoundFilesIt(m_FoundFiles.begin())
+ {
+ }
+
+ virtual std::string GetCurFileName()
+ {
+ return *m_FoundFilesIt;
+ }
+
+ virtual unsigned int GetCurFileType()
+ {
+ return m_PackageManager.GetFileType(*m_FoundFilesIt);
+ }
+
+ virtual unsigned int GetCurFileSize()
+ {
+ return m_PackageManager.GetFileSize(*m_FoundFilesIt);
+ }
+
+ virtual bool NextFile()
+ {
+ ++m_FoundFilesIt;
+ return m_FoundFilesIt != m_FoundFiles.end();
+ }
+
+ BS_PackageManager & m_PackageManager;
+ vector<std::string> m_FoundFiles;
+ vector<std::string>::const_iterator m_FoundFilesIt;
+};
+
+// -----------------------------------------------------------------------------
+
+BS_PackageManager::FileSearch * BS_PhysfsPackageManager::CreateSearch(const std::string& Filter, const std::string& Path, unsigned int TypeFilter)
+{
+ std::string NormalizedPath = NormalizePath(Path, m_CurrentDirectory);
+
+ // Nach Wildcards gefilterte Ergebnisliste erstellen.
+ PhysfsListHolder<char **> FilesPtr(PHYSFSEXT_enumerateFilesWildcard(NormalizedPath.c_str(), Filter.c_str(), 1));
+
+ // Diese Liste muss nun wiederum nach den gewünschten Dateitype gefiltert werden. Das Ergebnis wird in einem vector gespeichert, der dann
+ // einem PhysfsFileSearch-Objekt übergeben wird.
+ vector<std::string> FoundFiles;
+ for (char ** CurFilePtr = FilesPtr.Get(); *CurFilePtr != 0; ++CurFilePtr)
+ {
+ // Vollständigen Pfad zur gefunden Datei konstruieren.
+ std::string FullFilePath = NormalizedPath + std::string(1, PATH_SEPARATOR) + *CurFilePtr;
+
+ // Feststellen, ob der Dateityp erwünscht ist und nur dann den Dateinamen dem Ergebnisvektor hinzufügen.
+ unsigned int FileType = GetFileType(FullFilePath);
+ if (FileType & TypeFilter) FoundFiles.push_back(FullFilePath);
+ }
+
+ // Falls überhaupt eine Datei gefunden wurde, wird ein FileSearch-Objekt zurückgegeben mit dem über die gefundenen Dateien iteriert werden kann.
+ // Anderenfalls wird 0 zurückgegeben.
+ if (FoundFiles.size())
+ {
+ return new PhysfsFileSearch(*this, FoundFiles);
+ }
+ else
+ {
+ return 0;
+ }
+}
diff --git a/engines/sword25/package/physfspackagemanager.h b/engines/sword25/package/physfspackagemanager.h
new file mode 100755
index 0000000000..236ec4fbbe
--- /dev/null
+++ b/engines/sword25/package/physfspackagemanager.h
@@ -0,0 +1,61 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_PHYSFS_PACKAGE_MANAGER_H
+#define BS_PHYSFS_PACKAGE_MANAGER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "packagemanager.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_PhysfsPackageManager : public BS_PackageManager
+{
+public:
+ BS_PhysfsPackageManager(BS_Kernel * KernelPtr);
+ virtual ~BS_PhysfsPackageManager();
+
+ virtual bool LoadPackage(const std::string & FileName, const std::string& MountPosition);
+ virtual bool LoadDirectoryAsPackage(const std::string & DirectoryName, const std::string& MountPosition);
+ virtual void* GetFile(const std::string& FileName, unsigned int * FileSizePtr = 0);
+ virtual std::string GetCurrentDirectory();
+ virtual bool ChangeDirectory(const std::string & Directory);
+ virtual std::string GetAbsolutePath(const std::string & FileName);
+ virtual FileSearch* CreateSearch(const std::string & Filter, const std::string& Path, unsigned int TypeFilter = FT_DIRECTORY | FT_FILE);
+ virtual unsigned int GetFileSize(const std::string & FileName);
+ virtual unsigned int GetFileType(const std::string & FileName);
+ virtual bool FileExists(const std::string & FileName);
+
+private:
+ std::string m_CurrentDirectory;
+};
+
+#endif
diff --git a/engines/sword25/script/lua_extensions.cpp b/engines/sword25/script/lua_extensions.cpp
new file mode 100755
index 0000000000..05f9c5db79
--- /dev/null
+++ b/engines/sword25/script/lua_extensions.cpp
@@ -0,0 +1,68 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "luascript.h"
+#include "luabindhelper.h"
+
+// -----------------------------------------------------------------------------
+
+static int Warning(lua_State * L)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ luaL_checkstring(L, 1);
+ luaL_where(L, 1);
+ lua_pushstring(L, "WARNING - ");
+ lua_pushvalue(L, 1);
+ lua_concat(L, 3);
+ BS_Log::Log("%s\n", luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg GLOBAL_FUNCTIONS[] =
+{
+ "warning", Warning,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::RegisterStandardLibExtensions()
+{
+ lua_State * L = m_State;
+ BS_ASSERT(m_State);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, "", GLOBAL_FUNCTIONS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/script/luabindhelper.cpp b/engines/sword25/script/luabindhelper.cpp
new file mode 100755
index 0000000000..eccc568989
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.cpp
@@ -0,0 +1,419 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/kernel.h"
+#include "luabindhelper.h"
+#include "luascript.h"
+#include <sstream>
+
+#define BS_LOG_PREFIX "LUABINDHELPER"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * METATABLES_TABLE_NAME = "__METATABLES";
+ const char * PERMANENTS_TABLE_NAME = "Permanents";
+
+ bool RegisterPermanent(lua_State * L, const std::string & Name)
+ {
+ // Eine C-Funktion muss auf dem Stack liegen.
+ if (!lua_iscfunction(L, -1)) return false;
+
+ // Sicherstellen, dass die Permanents-Tabelle oben auf dem Stack liegt.
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ if (lua_isnil(L, -1))
+ {
+ // Permanents-Tabelle existiert noch nicht, sie muss erstellt werden.
+
+ // Nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Permanents-Tabelle erstellen und eine zweite Referenz darauf auf den Stack legen.
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+
+ // Permanents-Tabelle in der Registry speichern. Die zweite Referenz verbleibt auf dem Stack um im Anschluss benutzt zu werden.
+ lua_setfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ }
+
+ // C-Funktion mit dem Namen als Index in der Permanents-Tabelle ablegen.
+ lua_insert(L, -2);
+ lua_setfield(L, -2, Name.c_str());
+
+ // Permanents-Tabelle vom Stack nehmen.
+ lua_pop(L, 1);
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddFunctionsToLib(lua_State * L, const std::string & LibName, const luaL_reg * Functions)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Wenn der Tabellenname leer ist, werden die Funktionen zum globalen Namensraum hinzugefügt.
+ if (LibName.size() == 0)
+ {
+ for (; Functions->name; ++Functions)
+ {
+ lua_pushstring(L, Functions->name);
+ lua_pushcclosure(L, Functions->func, 0);
+ lua_settable(L, LUA_GLOBALSINDEX);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Functions->name);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ RegisterPermanent(L, Functions->name);
+ }
+ }
+ // Wenn der Tabellenname nicht leer ist, werden die Funktionen zu dieser Tabelle hinzugefügt.
+ else
+ {
+ // Sicherstellen, dass die Library-Table existiert.
+ if (!_CreateTable(L, LibName)) return false;
+
+ // Die einzelnen Funktionen in der Table registrieren.
+ for (; Functions->name; ++Functions)
+ {
+ // Funktion registrieren.
+ lua_pushstring(L, Functions->name);
+ lua_pushcclosure(L, Functions->func, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Functions->name);
+ lua_gettable(L, -2);
+ RegisterPermanent(L, LibName + "." + Functions->name);
+ }
+
+ // Library-Table vom Lua-Stack nehmen.
+ lua_pop(L, 1);
+ }
+
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddConstantsToLib(lua_State * L, const std::string & LibName, const lua_constant_reg * Constants)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Wenn der Tabellenname leer ist, werden die Konstanten zum globalen Namensraum hinzugefügt.
+ if (LibName.size() == 0)
+ {
+ for (; Constants->Name; ++Constants)
+ {
+ lua_pushstring(L, Constants->Name);
+ lua_pushnumber(L, Constants->Value);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+ // Wenn der Tabellenname nicht leer ist, werden die Konstanten zu dieser Tabelle hinzugefügt.
+ else
+ {
+ // Sicherstellen, dass die Library-Table existiert.
+ if (!_CreateTable(L, LibName)) return false;
+
+ // Die einzelnen Konstanten in der Table registrieren
+ for (; Constants->Name; ++Constants)
+ {
+ lua_pushstring(L, Constants->Name);
+ lua_pushnumber(L, Constants->Value);
+ lua_settable(L, -3);
+ }
+
+ // Library-Tabelle vom Lua-Stack nehmen
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::AddMethodsToClass(lua_State * L, const std::string & ClassName, const luaL_reg * Methods)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Metatable auf den Lua-Stack laden
+ if (!GetMetatable(L, ClassName)) return false;
+
+ // Die einzelnen Methoden in der Metatable registrieren
+ for (; Methods->name; ++Methods)
+ {
+ lua_pushstring(L, Methods->name);
+ lua_pushcclosure(L, Methods->func, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, Methods->name);
+ lua_gettable(L, -2);
+ RegisterPermanent(L, ClassName + "." + Methods->name);
+ }
+
+ // Metatable vom Lua-Stack nehmen
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::SetClassGCHandler(lua_State * L, const std::string & ClassName, lua_CFunction GCHandler)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Metatable auf den Lua-Stack laden
+ if (!GetMetatable(L, ClassName)) return false;
+
+ // Den GC-Handler in die Metatable schreiben
+ lua_pushstring(L, "__gc");
+ lua_pushcclosure(L, GCHandler, 0);
+ lua_settable(L, -3);
+
+ // Funktion als permanent registrieren, damit sie beim Persistieren ignoriert wird.
+ lua_pushstring(L, "__gc");
+ lua_gettable(L, -2);
+ RegisterPermanent(L, ClassName + ".__gc");
+
+ // Metatable vom Lua-Stack nehmen
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ void PushMetatableTable(lua_State * L)
+ {
+ // Tabelle mit den Metatabellen auf den Stack legen.
+ lua_getglobal(L, METATABLES_TABLE_NAME);
+
+ // Wenn die Tabelle noch nicht existiert, muss sie erstellt werden.
+ if (lua_isnil(L, -1))
+ {
+ // nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen, in die globale Table eintragen und eine Referenz auf dem Stack lassen.
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, METATABLES_TABLE_NAME);
+ }
+ }
+}
+
+
+bool BS_LuaBindhelper::GetMetatable(lua_State * L, const std::string & TableName)
+{
+ // Tabelle mit den Metatabellen auf den Stack legen.
+ PushMetatableTable(L);
+
+ // Versuchen, die gewünschte Metatabelle auf den Stack zu legen. Wenn sie noch nicht existiert, muss sie erstellt werden.
+ lua_getfield(L, -1, TableName.c_str());
+ if (lua_isnil(L, -1))
+ {
+ // nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen.
+ lua_newtable(L);
+
+ // Das __index Feld der Metatabele zeigt auf die Metatabelle selbst.
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+
+ // Persistfeld auf true setzen. Dies sorgt dafür, dass Objekte mit dieser Metatabelle direkt gespeichert werden.
+ lua_pushbooleancpp(L, true);
+ lua_setfield(L, -2, "__persist");
+
+ // Metatabelle in die Tabelle für Metatabellen eintragen und eine Referenz auf dem Stack lassen.
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, TableName.c_str());
+ }
+
+ // Tabelle mit den Metatabellen vom Stack nehmen.
+ lua_remove(L, -2);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaBindhelper::_CreateTable(lua_State * L, const std::string & TableName)
+{
+ // Der Tabellenname wird an den Punkten auseinandergetrennt und jeweils die Untertabellen erstellt.
+ // Auf diese Weise können auch Tabellen mit Untertabellen erstellt werden (z.B. Foo.Bar).
+ std::string::size_type PartBegin = 0;
+ while (PartBegin <= TableName.size())
+ {
+ std::string::size_type PartEnd;
+ PartEnd = TableName.find(".", PartBegin);
+ if (PartEnd == std::string::npos) PartEnd = TableName.size();
+ std::string SubTableName = TableName.substr(PartBegin, PartEnd - PartBegin);
+
+ // Tabellen mit einen leeren String als Namen sind nicht zulässig.
+ if (SubTableName.size() == 0) return false;
+
+ // Überprüfen, ob die Tabelle mit dem Namen bereits existiert.
+ // Beim ersten Durchgang wird im globalen Namensbereich gesucht, bei späteren Durchgängen in der entsprechenden Elterntabelle auf dem Stack.
+ if (PartBegin == 0)
+ {
+ lua_pushstring(L, SubTableName.c_str());
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ }
+ else
+ {
+ lua_pushstring(L, SubTableName.c_str());
+ lua_gettable(L, -2);
+ if (!lua_isnil(L, -1)) lua_remove(L, -2);
+ }
+
+ // Wenn nicht, Table erstellen
+ if (lua_isnil(L, -1))
+ {
+ // nil-Wert vom Stack holen
+ lua_pop(L, 1);
+
+ // Neue Tabelle erstellen
+ lua_newtable(L);
+ lua_pushstring(L, SubTableName.c_str());
+ lua_pushvalue(L, -2);
+ if (PartBegin == 0)
+ lua_settable(L, LUA_GLOBALSINDEX);
+ else
+ {
+ lua_settable(L, -4);
+ lua_remove(L, -2);
+ }
+ }
+
+ PartBegin = PartEnd + 1;
+ }
+
+ return true;
+}
+
+namespace
+{
+ std::string GetLuaValueInfo(lua_State * L, int StackIndex)
+ {
+ switch (lua_type(L, StackIndex))
+ {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, StackIndex));
+ break;
+
+ case LUA_TSTRING:
+ lua_pushfstring(L, "\"%s\"", lua_tostring(L, StackIndex));
+ break;
+
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, StackIndex) ? "true" : "false"));
+ break;
+
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+
+ default:
+ lua_pushfstring(L, "%s: %p", luaL_typename(L, StackIndex), lua_topointer(L, StackIndex));
+ break;
+ }
+
+ std::string Result(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ return Result;
+ }
+}
+
+std::string BS_LuaBindhelper::StackDump(lua_State *L)
+{
+ std::ostringstream oss;
+
+ int i = lua_gettop(L);
+ oss << "------------------- Stack Dump -------------------\n";
+
+ while(i)
+ {
+ oss << i << ": " << GetLuaValueInfo(L, i) << "\n";
+ i--;
+ }
+
+ oss << "-------------- Stack Dump Finished ---------------\n";
+
+ return oss.str();
+}
+
+std::string BS_LuaBindhelper::TableDump(lua_State * L)
+{
+ std::ostringstream oss;
+
+ oss << "------------------- Table Dump -------------------\n";
+
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+ oss << GetLuaValueInfo(L, -2) << " : " << GetLuaValueInfo(L, -1) << "\n";
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+
+ oss << "-------------- Table Dump Finished ---------------\n";
+
+ return oss.str();
+}
diff --git a/engines/sword25/script/luabindhelper.h b/engines/sword25/script/luabindhelper.h
new file mode 100755
index 0000000000..c6a8b8e626
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.h
@@ -0,0 +1,107 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef LUABINDHELPER_H
+#define LUABINDHELPER_H
+
+#include "kernel/common.h"
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lauxlib.h>
+}
+
+#define lua_pushbooleancpp(L, b) (lua_pushboolean(L, b ? 1 : 0))
+#define lua_tobooleancpp(L, i) (lua_toboolean(L, i) == 0 ? false : true)
+
+struct lua_constant_reg
+{
+ const char * Name;
+ lua_Number Value;
+};
+
+class BS_LuaBindhelper
+{
+public:
+ /**
+ @brief Registriert eine Menge von Funktionen und fügt dieser einer Lua-Library hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Funktionen registriert werden sollen
+ @param LibName der Name der Library.<br>
+ Wenn dies ein Leerer String ist, werden die Funktionen zum globalen Namensraum hinzugefügt.
+ @param Functions ein Array das die Funktionspointer mit ihren Namen enthält.<br>
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddFunctionsToLib(lua_State * L, const std::string & LibName, const luaL_reg * Functions);
+
+ /**
+ @brief Fügt eine Menge von Konstanten einer Lua-Library hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Konstanten registriert werden sollen
+ @param LibName der Name der Library.<br>
+ Wenn dies ein Leerer String ist, werden die Konstanten zum globalen Namensraum hinzugefügt.
+ @param Constants ein Array das die Werte der Konstanten mit ihren Namen enthält.<br
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddConstantsToLib(lua_State * L, const std::string & LibName, const lua_constant_reg * Constants);
+
+ /**
+ @brief Fügt eine Menge von Methoden zu einer Lua-Klasse hinzu.
+ @param L ein Pointer auf die Lua-VM in der die Methoden registriert werden sollen
+ @param ClassName der Name der Metatable der Klasse.<br>
+ Wenn die Metatable noch nicht existiert, wird sie erstellt.
+ @param Methods ein Array das die Funktionspointer der Methoden mit ihren Namen enthält.<br>
+ Das Array muss mit dem Eintrag {0, 0} terminiert sein.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool AddMethodsToClass(lua_State * L, const std::string & ClassName, const luaL_reg * Methods);
+
+ /**
+ @brief Legt eine Funktion fest, die aufgerufen wird, wenn Exemplare einer bestimmten Lua-Klasse vom Garbage-Collecter gelöscht werden.
+ @param L ein Pointer auf die Lua-VM
+ @param ClassName der Name der Metatable der Klasse.<br>
+ Wenn die Metatable noch nicht existiert, wird sie erstellt.
+ @param GCHandler ein Funktionspointer auf die Funktion.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ static bool SetClassGCHandler(lua_State * L, const std::string & ClassName, lua_CFunction GCHandler);
+
+ /**
+ @brief Gibt einen String zurück, der einen Stackdump des Lua-Stacks enthält.
+
+ @param L ein Pointer auf die Lua-VM.
+ */
+ static std::string StackDump(lua_State * L);
+
+ /**
+ @brief Gibt einen String zurück, den den Inhalt einer Tabelle beschreibt.
+
+ @param L ein Pointer auf die Lua-VM.
+ @remark Auf dem Lua-Stack muss die Tabelle liegen, die ausgelesen werden soll.
+ */
+ static std::string TableDump(lua_State * L);
+
+ static bool GetMetatable(lua_State * L, const std::string & TableName);
+
+private:
+ static bool _CreateTable(lua_State * L, const std::string & TableName);
+};
+
+#endif
diff --git a/engines/sword25/script/luacallback.cpp b/engines/sword25/script/luacallback.cpp
new file mode 100755
index 0000000000..09103d5467
--- /dev/null
+++ b/engines/sword25/script/luacallback.cpp
@@ -0,0 +1,203 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "luacallback.h"
+#include "luabindhelper.h"
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lauxlib.h>
+}
+
+#define BS_LOG_PREFIX "LUA"
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * CALLBACKTABLE_NAME = "__CALLBACKS";
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaCallback::BS_LuaCallback(lua_State * L)
+{
+ // Callbacktabelle erstellen.
+ lua_newtable(L);
+ lua_setglobal(L, CALLBACKTABLE_NAME);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaCallback::~BS_LuaCallback()
+{
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::RegisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
+{
+ BS_ASSERT(lua_isfunction(L, -1));
+ EnsureObjectCallbackTableExists(L, ObjectHandle);
+
+ // Funktion in der Objekt-Callbacktabelle speichern.
+ lua_pushvalue(L, -2);
+ luaL_ref(L, -2);
+
+ // Funktion und Objekt-Callbacktabelle vom Stack poppen.
+ lua_pop(L, 2);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::UnregisterCallbackFunction(lua_State * L, unsigned int ObjectHandle)
+{
+ BS_ASSERT(lua_isfunction(L, -1));
+ EnsureObjectCallbackTableExists(L,ObjectHandle);
+
+ // Über alle Elemente der Objekt-Callbacktabelle iterieren und die Funktion daraus entfernen.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+
+ // Falls der Wert identisch mit dem Funktionsparameter ist, wird sie aus der Tabelle entfernt.
+ if (lua_equal(L, -1, -4))
+ {
+ lua_pushvalue(L, -2);
+ lua_pushnil(L);
+ lua_settable(L, -5);
+
+ // Die Funktion wurde gefunden, die Iteration kann abgebrochen werden.
+ lua_pop(L, 2);
+ break;
+ }
+ else
+ {
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+
+ // Funktion und Objekt-Callbacktabelle vom Stack poppen.
+ lua_pop(L, 2);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::RemoveAllObjectCallbacks(lua_State * L, unsigned int ObjectHandle)
+{
+ PushCallbackTable(L);
+
+ // Objekt-Callbacktabelle aus der Callbacktabelle entfernen.
+ lua_pushnumber(L, ObjectHandle);
+ lua_pushnil(L);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::InvokeCallbackFunctions(lua_State * L, unsigned int ObjectHandle)
+{
+ EnsureObjectCallbackTableExists(L, ObjectHandle);
+
+ // Über die Tabelle iterieren und alle Callbacks ausführen.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Der Wert des aktuellen Elementes liegt oben auf dem Stack, darunter der Index.
+
+ // Falls der Wert eine Funktion ist, wird sie ausgeführt.
+ if (lua_type(L, -1) == LUA_TFUNCTION)
+ {
+ // Pre-Funktion aufrufen.
+ // Abgeleitete Klassen könnten in dieser Funktion Parameter auf den Stack schieben.
+ // Der Rückgabewert gibt die Anzahl der Parameter zurück.
+ int ArgumentCount = PreFunctionInvokation(L);
+
+ // lua_pcall poppt die Funktion und die Parameter selber vom Stack.
+ if (lua_pcall(L, ArgumentCount, 0, 0) != 0)
+ {
+ // Ein Fehler ist aufgetreten.
+ BS_LOG_ERRORLN("An error occured executing a callback function: %s", lua_tostring(L, -1));
+
+ // Fehlernachricht vom Stack poppen.
+ lua_pop(L, 1);
+ }
+ }
+ else
+ {
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::EnsureObjectCallbackTableExists(lua_State * L, unsigned int ObjectHandle)
+{
+ PushObjectCallbackTable(L, ObjectHandle);
+
+ // Falls die Tabelle nil ist, muss sie zunächst erstellt werden.
+ if (lua_isnil(L, -1))
+ {
+ // Nil vom Stack poppen.
+ lua_pop(L, 1);
+
+ PushCallbackTable(L);
+
+ // Neue Tabelle unter dem Index ObjectHandle in der Callbacktabelle ablegen.
+ lua_newtable(L);
+ lua_pushnumber(L, ObjectHandle);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4);
+
+ // Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
+ lua_remove(L, -2);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::PushCallbackTable(lua_State * L)
+{
+ lua_getglobal(L, CALLBACKTABLE_NAME);
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaCallback::PushObjectCallbackTable(lua_State * L, unsigned int ObjectHandle)
+{
+ PushCallbackTable(L);
+
+ // Objekt-Callbacktabelle auf den Stack legen.
+ lua_pushnumber(L, ObjectHandle);
+ lua_gettable(L, -2);
+
+ // Callbacktabelle vom Stack entfernen, Objekt-Callbacktabelle aber dort lassen.
+ lua_remove(L, -2);
+}
diff --git a/engines/sword25/script/luacallback.h b/engines/sword25/script/luacallback.h
new file mode 100755
index 0000000000..dbf201b4ad
--- /dev/null
+++ b/engines/sword25/script/luacallback.h
@@ -0,0 +1,64 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_LUACALLBACK_H
+#define BS_LUACALLBACK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+struct lua_State;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class BS_LuaCallback
+{
+public:
+ BS_LuaCallback(lua_State * L);
+ virtual ~BS_LuaCallback();
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void RegisterCallbackFunction(lua_State * L, unsigned int ObjectHandle);
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void UnregisterCallbackFunction(lua_State * L, unsigned int ObjectHandle);
+
+ void RemoveAllObjectCallbacks(lua_State * L, unsigned int ObjectHandle);
+
+ void InvokeCallbackFunctions(lua_State * L, unsigned int ObjectHandle);
+
+protected:
+ virtual int PreFunctionInvokation(lua_State * L) { return 0; }
+
+private:
+ void EnsureObjectCallbackTableExists(lua_State * L,unsigned int ObjectHandle);
+ void PushCallbackTable(lua_State * L);
+ void PushObjectCallbackTable(lua_State * L, unsigned int ObjectHandle);
+};
+
+#endif
diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp
new file mode 100755
index 0000000000..dbefc338ce
--- /dev/null
+++ b/engines/sword25/script/luascript.cpp
@@ -0,0 +1,607 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "LUA"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+extern "C"
+{
+ #include <lua.h>
+ #include <lualib.h>
+ #include <lauxlib.h>
+ #include "util/pluto/pluto.h"
+}
+
+#include "package/packagemanager.h"
+#include "luascript.h"
+#include "luabindhelper.h"
+
+#include "kernel/outputpersistenceblock.h"
+#include "kernel/inputpersistenceblock.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include "kernel/memlog_on.h"
+
+using namespace std;
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_LuaScriptEngine::BS_LuaScriptEngine(BS_Kernel * KernelPtr) :
+ BS_ScriptEngine(KernelPtr),
+ m_State(0),
+ m_PcallErrorhandlerRegistryIndex(0)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_LuaScriptEngine::~BS_LuaScriptEngine()
+{
+ // Lua deinitialisieren
+ if (m_State) lua_close(m_State);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_LuaScriptEngine_CreateObject(BS_Kernel * KernelPtr) { return new BS_LuaScriptEngine(KernelPtr); }
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ int PanicCB(lua_State * L)
+ {
+ BS_LOG_ERRORLN("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::Init()
+{
+ // Lua-State intialisieren und Standardbibliotheken initialisieren
+ m_State = luaL_newstate();
+ if (!m_State || ! RegisterStandardLibs() || !RegisterStandardLibExtensions())
+ {
+ BS_LOG_ERRORLN("Lua could not be initialized.");
+ return false;
+ }
+
+ // Panic-Callbackfunktion registrieren.
+ lua_atpanic(m_State, PanicCB);
+
+ // Errorhandlerfunktion für lua_pcall-Aufrufe.
+ // Der untenstehende Code enthält eine lokale ErrorHandler-Funktion und gibt diese zurück.
+ const char ErrorHandlerCode[] =
+ "local function ErrorHandler(message) "
+ " return message .. '\\n' .. debug.traceback('', 2) "
+ "end "
+ "return ErrorHandler";
+
+ // Den Code compilieren.
+ if (luaL_loadbuffer(m_State, ErrorHandlerCode, strlen(ErrorHandlerCode), "PCALL ERRORHANDLER") != 0)
+ {
+ // Fehlernachricht ausgeben und Methode beenden.
+ BS_LOG_ERRORLN("Couldn't compile luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+ // Den Code ausführen, dies legt die Errorhandler-Funktion oben auf den Stack.
+ if (lua_pcall(m_State, 0, 1, 0) != 0)
+ {
+ // Fehlernachricht ausgeben und Methode beenden.
+ BS_LOG_ERRORLN("Couldn't prepare luaL_pcall errorhandler:\n%s", lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+
+ // Die Errorhandler-Funktion in der Lua-Registry ablegen und den Index merken.
+ m_PcallErrorhandlerRegistryIndex = luaL_ref(m_State, LUA_REGISTRYINDEX);
+
+ // Die Pluto Persistenz-Bibliothek initialisieren.
+ luaopen_pluto(m_State);
+ lua_pop(m_State, 1);
+
+ BS_LOGLN("Lua initialized.");
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteFile(const std::string & FileName)
+{
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(m_State);
+#endif
+
+ // Pointer auf den Packagemanager holen
+ BS_PackageManager * pPackage = static_cast<BS_PackageManager *>(BS_Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Datei einlesen
+ unsigned int FileSize;
+ char * FileData = static_cast<char *>(pPackage->GetFile(FileName, &FileSize));
+ if (!FileData)
+ {
+ BS_LOG_ERRORLN("Couldn't read \"%s\".", FileName.c_str());
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+ return false;
+ }
+
+ // Dateiinhalt ausführen
+ if (!ExecuteBuffer(FileData, FileSize, "@" + pPackage->GetAbsolutePath(FileName)))
+ {
+ // Dateipuffer freigeben
+ delete[] FileData;
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+ return false;
+ }
+
+ // Dateipuffer freigeben
+ delete[] FileData;
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(m_State));
+#endif
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteString(const std::string & Code)
+{
+ return ExecuteBuffer(Code.c_str(), Code.length(), "???");
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ void RemoveForbiddenFunctions(lua_State * L)
+ {
+ static const char * FORBIDDEN_FUNCTIONS[] =
+ {
+ "dofile",
+ 0
+ };
+
+ const char ** Iterator = FORBIDDEN_FUNCTIONS;
+ while (*Iterator)
+ {
+ lua_pushnil(L);
+ lua_setfield(L, LUA_GLOBALSINDEX, *Iterator);
+ ++Iterator;
+ }
+ }
+}
+
+bool BS_LuaScriptEngine::RegisterStandardLibs()
+{
+ luaL_openlibs(m_State);
+ RemoveForbiddenFunctions(m_State);
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::ExecuteBuffer(const char * Data, unsigned int Size, const std::string & Name) const
+{
+ // Puffer kompilieren
+ if (luaL_loadbuffer(m_State, Data, Size, Name.c_str()) != 0)
+ {
+ BS_LOG_ERRORLN("Couldn't compile \"%s\":\n%s", Name.c_str(), lua_tostring(m_State, -1));
+ lua_pop(m_State, 1);
+
+ return false;
+ }
+
+ // Error-Handler Funktion hinter der auszuführenden Funktion auf den Stack legen.
+ lua_rawgeti(m_State, LUA_REGISTRYINDEX, m_PcallErrorhandlerRegistryIndex);
+ lua_insert(m_State, -2);
+
+ // Pufferinhalt ausführen
+ if (lua_pcall(m_State, 0, 0, -2) != 0)
+ {
+ BS_LOG_ERRORLN("An error occured while executing \"%s\":\n%s.",
+ Name.c_str(),
+ lua_tostring(m_State, -1));
+ lua_pop(m_State, 2);
+
+ return false;
+ }
+
+ // Error-Handler Funktion vom Stack nehmen.
+ lua_pop(m_State, 1);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_LuaScriptEngine::SetCommandLine(const vector<string> & CommandLineParameters)
+{
+ lua_newtable(m_State);
+
+ for (size_t i = 0; i < CommandLineParameters.size(); ++i)
+ {
+ lua_pushnumber(m_State, i + 1);
+ lua_pushstring(m_State, CommandLineParameters[i].c_str());
+ lua_settable(m_State, -3);
+ }
+
+ lua_setglobal(m_State, "CommandLine");
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const char * PERMANENTS_TABLE_NAME = "Permanents";
+
+ // -------------------------------------------------------------------------
+
+ // Diese Array enthält die Namen der globalen Lua-Objekte, die nicht persistiert werden sollen.
+ const char * STANDARD_PERMANENTS[] =
+ {
+ "string",
+ "xpcall",
+ "package",
+ "tostring",
+ "print",
+ "os",
+ "unpack",
+ "require",
+ "getfenv",
+ "setmetatable",
+ "next",
+ "assert",
+ "tonumber",
+ "io",
+ "rawequal",
+ "collectgarbage",
+ "getmetatable",
+ "module",
+ "rawset",
+ "warning",
+ "math",
+ "debug",
+ "pcall",
+ "table",
+ "newproxy",
+ "type",
+ "coroutine",
+ "select",
+ "gcinfo",
+ "pairs",
+ "rawget",
+ "loadstring",
+ "ipairs",
+ "_VERSION",
+ "setfenv",
+ "load",
+ "error",
+ "loadfile",
+
+ "pairs_next",
+ "ipairs_next",
+ "pluto",
+ "Cfg",
+ "Translator",
+ "Persistence",
+ "CommandLine",
+ 0
+ };
+
+ // -------------------------------------------------------------------------
+
+ enum PERMANENT_TABLE_TYPE
+ {
+ PTT_PERSIST,
+ PTT_UNPERSIST,
+ };
+
+ // -------------------------------------------------------------------------
+
+ bool PushPermanentsTable(lua_State * L, PERMANENT_TABLE_TYPE TableType)
+ {
+ // Permanents-Tabelle erstellen.
+ lua_newtable(L);
+
+ // Alle Standard-Permanents in die Tabelle einfügen.
+ unsigned int Index = 0;
+ while (STANDARD_PERMANENTS[Index])
+ {
+ // Permanent auf den Stack legen, falls es nicht existiert, wird es einfach ignoriert.
+ lua_getglobal(L, STANDARD_PERMANENTS[Index]);
+ if (!lua_isnil(L, -1))
+ {
+ // Namen des Elementes als einzigartigen Wert auf den Stack legen.
+ lua_pushstring(L, STANDARD_PERMANENTS[Index]);
+
+ // Falls geladen wird, ist der Name der Index und das Objekt der Wert.
+ // In diesem Fall müssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ // Eintrag in der Tabelle vornehmen.
+ lua_settable(L, -3);
+ }
+ else
+ {
+ // nil von Stack poppen.
+ lua_pop(L, 1);
+ }
+
+ ++Index;
+ }
+
+ // Alle Registrierten C-Funktionen in die Tabelle einfügen.
+ // BS_LuaBindhelper legt in der Registry eine Tabelle an, in der alle registrierten C-Funktionen gespeichert werden.
+
+ // Tabelle mit den C-Permanents auf den Stack legen.
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+
+ if (!lua_isnil(L, -1))
+ {
+ // Über alle Elemente der Tabelle iterieren.
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Wert und Index auf dem Stack duplizieren und in der Reihenfolge vertauschen.
+ lua_pushvalue(L, -1);
+ lua_pushvalue(L, -3);
+
+ // Falls geladen wird, ist der Name der Index und das Objekt der Wert.
+ // In diesem Fall müssen also die Position von Name und Objekt auf dem Stack vertauscht werden.
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ // Eintrag in der Ergebnistabelle vornehmen.
+ lua_settable(L, -6);
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(L, 1);
+ }
+ }
+
+ // Tabelle mit den C-Permanents vom Stack poppen.
+ lua_pop(L, 1);
+
+ // coroutine.yield muss extra in die Permanents-Tabelle eingetragen werden, da inaktive Coroutinen diese C-Funktion auf dem Stack liegen
+ // haben.
+
+ // Funktion coroutine.yield auf den Stack legen.
+ lua_getglobal(L, "coroutine");
+ lua_pushstring(L, "yield");
+ lua_gettable(L, -2);
+
+ // coroutine.yield mit eigenem eindeutigen Wert in der Permanents-Tabelle ablegen.
+ lua_pushstring(L, "coroutine.yield");
+
+ if (TableType == PTT_UNPERSIST) lua_insert(L, -2);
+
+ lua_settable(L, -4);
+
+ // Tabelle coroutine vom Stack poppen.
+ lua_pop(L, 1);
+
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ int Chunkwriter(lua_State *L, const void* p, size_t sz, void* ud)
+ {
+ vector<unsigned char> & chunkData = *reinterpret_cast<vector<unsigned char> * >(ud);
+ const unsigned char * buffer = reinterpret_cast<const unsigned char *>(p);
+
+ while (sz--) chunkData.push_back(*buffer++);
+
+ return 1;
+ }
+}
+
+bool BS_LuaScriptEngine::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ // Den Lua-Stack leeren. pluto_persist() erwartet, dass der Stack bis aus seine Parameter leer ist.
+ lua_settop(m_State, 0);
+
+ // Garbage Collection erzwingen.
+ lua_gc(m_State, LUA_GCCOLLECT, 0);
+
+ // Permanents-Tabelle und die zu persistierende Tabelle auf den Stack legen.
+ // pluto_persist erwartet diese beiden Objekte auf dem Lua-Stack.
+ PushPermanentsTable(m_State, PTT_PERSIST);
+ lua_getglobal(m_State, "_G");
+
+ // Lua persistieren und die Daten in einem vector ablegen.
+ vector<unsigned char> chunkData;
+ pluto_persist(m_State, Chunkwriter, &chunkData);
+
+ // Persistenzdaten in den Writer schreiben.
+ Writer.Write(&chunkData[0], chunkData.size());
+
+ // Die beiden Tabellen vom Stack nehmen.
+ lua_pop(m_State, 2);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ // -------------------------------------------------------------------------
+
+ struct ChunkreaderData
+ {
+ void * BufferPtr;
+ size_t Size;
+ bool BufferReturned;
+ };
+
+ // -------------------------------------------------------------------------
+
+ const char * Chunkreader(lua_State *L, void *ud, size_t *sz)
+ {
+ ChunkreaderData & cd = *reinterpret_cast<ChunkreaderData *>(ud);
+
+ if (!cd.BufferReturned)
+ {
+ cd.BufferReturned = true;
+ *sz = cd.Size;
+ return reinterpret_cast<const char *>(cd.BufferPtr);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ // -------------------------------------------------------------------------
+
+ void ClearGlobalTable(lua_State * L, const char ** Exceptions)
+ {
+ // Über alle Elemente der globalen Tabelle iterieren.
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0)
+ {
+ // Jetzt liegen der Wert und der Index des aktuellen Elementes auf dem Stack.
+ // Der Wert interessiert uns nicht, daher wird er vom Stack gepoppt.
+ lua_pop(L, 1);
+
+ // Feststellen, ob das Element auf nil gesetzt , also aus der globalen Tabelle entfernt werden soll.
+ // Hierfür wird geprüft, ob das Elementname ein String ist und in der Liste der Ausnahmen vorkommt.
+ bool SetElementToNil = true;
+ if (lua_isstring(L, -1))
+ {
+ const char * IndexString = lua_tostring(L, -1);
+ const char ** ExceptionsWalker = Exceptions;
+ while (*ExceptionsWalker)
+ {
+ if (strcmp(IndexString, *ExceptionsWalker) == 0) SetElementToNil = false;
+ ++ExceptionsWalker;
+ }
+ }
+
+ // Wenn der obige Test ergeben hat, dass das Element entfernt werden soll, wird es entfernt indem der Wert auf nil gesetzt wird.
+ if (SetElementToNil)
+ {
+ lua_pushvalue(L, -1);
+ lua_pushnil(L);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+
+ // Globale Tabelle vom Stack nehmen.
+ lua_pop(L, 1);
+
+ // Garbage-Collection vornehmen, damit die entfernten Elemente alle gelöscht werden.
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_LuaScriptEngine::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ // Den Lua-Stack leeren. pluto_unpersist() erwartet, dass der Stack bis aus seine Parameter leer ist.
+ lua_settop(m_State, 0);
+
+ // Permanents Tabelle auf den Stack legen. Dies passiert schon an dieser Stelle, da zum Erstellen der Tabelle alle Permanents zugreifbar sein
+ // müssen. Dies ist nur am Anfang dieser Funktion der Fall, im Folgenden wird die globale Tabelle geleert.
+ PushPermanentsTable(m_State, PTT_UNPERSIST);
+
+ // Alle Elemente aus der globalen Tabelle mit Ausnhame von _G und __METATABLES entfernen.
+ // Danach wird eine Garbage Collection durchgeführt und somit alle von Lua verwalteten Objekte gelöscht.
+ // __METATABLES wird zunächst nicht entfernt, da die Metatables für die Finalizer der Objekte benötigt werden.
+ static const char * ClearExceptionsFirstPass[] =
+ {
+ "_G",
+ "__METATABLES",
+ 0
+ };
+ ClearGlobalTable(m_State, ClearExceptionsFirstPass);
+
+ // Im zweiten Durchgang werden auch die Metatables entfernt.
+ static const char * ClearExceptionsSecondPass[] =
+ {
+ "_G",
+ 0
+ };
+ ClearGlobalTable(m_State, ClearExceptionsSecondPass);
+
+ // Persistierte Lua-Daten einlesen.
+ vector<unsigned char> chunkData;
+ Reader.Read(chunkData);
+
+ // Chunk-Reader initialisieren. Er wird von pluto_unpersist benutzt um die benötigten Daten einzulesen.
+ ChunkreaderData cd;
+ cd.BufferPtr = &chunkData[0];
+ cd.Size = chunkData.size();
+ cd.BufferReturned = false;
+
+ pluto_unpersist(m_State, Chunkreader, &cd);
+
+ // Permanents-Tabelle vom Stack nehmen.
+ lua_remove(m_State, -2);
+
+ // Die eingelesenen Elemente in die globale Tabelle eintragen.
+ lua_pushnil(m_State);
+ while (lua_next(m_State, -2) != 0)
+ {
+ // Die Referenz auf die globale Tabelle (_G) darf nicht überschrieben werden, sonst tickt Lua total aus.
+ bool IsGlobalReference = lua_isstring(m_State, -2) && strcmp(lua_tostring(m_State, -2), "_G") == 0;
+ if (!IsGlobalReference)
+ {
+ lua_pushvalue(m_State, -2);
+ lua_pushvalue(m_State, -2);
+
+ lua_settable(m_State, LUA_GLOBALSINDEX);
+ }
+
+ // Wert vom Stack poppen. Der Index liegt dann oben für den nächsten Aufruf von lua_next().
+ lua_pop(m_State, 1);
+ }
+
+ // Tabelle mit den geladenen Daten vom Stack poppen.
+ lua_pop(m_State, 1);
+
+ // Garbage Collection erzwingen.
+ lua_gc(m_State, LUA_GCCOLLECT, 0);
+
+ return true;
+}
diff --git a/engines/sword25/script/luascript.h b/engines/sword25/script/luascript.h
new file mode 100755
index 0000000000..dd0500a710
--- /dev/null
+++ b/engines/sword25/script/luascript.h
@@ -0,0 +1,102 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef LUASCRIPT_H
+#define LUASCRIPT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "script.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+struct lua_State;
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_LuaScriptEngine : public BS_ScriptEngine
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_LuaScriptEngine(BS_Kernel * KernelPtr);
+ virtual ~BS_LuaScriptEngine();
+
+ /**
+ @brief Initialisiert die Scriptengine.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool Init();
+
+ /**
+ @brief Lädt eine Skriptdatei und führt diese aus.
+ @param FileName der Dateiname der Skriptdatei
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteFile(const std::string & FileName);
+
+ /**
+ @brief Führt einen String mit Skriptcode aus.
+ @param Code ein String der Skriptcode enthält.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteString(const std::string & Code);
+
+ /**
+ @brief Gibt einen Pointer auf das Hauptobjekt der Skriptsprache zurück.
+ @remark Durch die Benutzung dieser Methode wird die Kapselung der Sprache aufgehoben.
+ */
+ virtual void * GetScriptObject() { return m_State; }
+
+ /**
+ @brief Macht die Kommandozeilen-Parameter für die Skriptumgebung zugänglich.
+ @param CommandLineParameters ein string vector der alle Kommandozeilenparameter enthält.
+ @remark Auf welche Weise die Kommandozeilen-Parameter durch Skripte zugegriffen werden können hängt von der jeweiligen Implementierung ab.
+ */
+ virtual void SetCommandLine(const std::vector<std::string> & CommandLineParameters);
+
+ /**
+ @remark Der Lua-Stack wird durch diese Methode geleert.
+ */
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer);
+ /**
+ @remark Der Lua-Stack wird durch diese Methode geleert.
+ */
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ lua_State * m_State;
+ int m_PcallErrorhandlerRegistryIndex;
+
+ bool RegisterStandardLibs();
+ bool RegisterStandardLibExtensions();
+ bool ExecuteBuffer(const char * Data, unsigned int Size, const std::string & Name) const;
+};
+
+#endif
diff --git a/engines/sword25/script/script.h b/engines/sword25/script/script.h
new file mode 100755
index 0000000000..7fafac0f96
--- /dev/null
+++ b/engines/sword25/script/script.h
@@ -0,0 +1,99 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/service.h"
+#include "kernel/persistable.h"
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include <string>
+#include "kernel/memlog_on.h"
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class BS_Kernel;
+class BS_OutputPersistenceBlock;
+class BS_InputPersistenceBlock;
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class BS_ScriptEngine : public BS_Service, public BS_Persistable
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_ScriptEngine(BS_Kernel * KernelPtr) : BS_Service(KernelPtr) {};
+ virtual ~BS_ScriptEngine() {};
+
+ // -----------------------------------------------------------------------------
+ // DIESE METHODEN MÜSSEN VON DER SCRIPTENGINE IMPLEMENTIERT WERDEN
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Initialisiert die Scriptengine.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool Init() = 0;
+
+ /**
+ @brief Lädt eine Skriptdatei und führt diese aus.
+ @param FileName der Dateiname der Skriptdatei
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteFile(const std::string & FileName) = 0;
+
+ /**
+ @brief Führt einen String mit Skriptcode aus.
+ @param Code ein String der Skriptcode enthält.
+ @return Gibt true bei Erfolg zurück, ansonsten false.
+ */
+ virtual bool ExecuteString(const std::string & Code) = 0;
+
+ /**
+ @brief Gibt einen Pointer auf das Hauptobjekt der Skriptsprache zurück.
+ @remark Durch die Benutzung dieser Methode wird die Kapselung der Sprache vom Rest der Engine aufgehoben.
+ */
+ virtual void * GetScriptObject() = 0;
+
+ /**
+ @brief Macht die Kommandozeilen-Parameter für die Skriptumgebung zugänglich.
+ @param CommandLineParameters ein string vector der alle Kommandozeilenparameter enthält.
+ @remark Auf welche Weise die Kommandozeilen-Parameter durch Skripte zugegriffen werden können hängt von der jeweiligen Implementierung ab.
+ */
+ virtual void SetCommandLine(const std::vector<std::string> & CommandLineParameters) = 0;
+
+ virtual bool Persist(BS_OutputPersistenceBlock & Writer) = 0;
+ virtual bool Unpersist(BS_InputPersistenceBlock & Reader) = 0;
+};
+
+#endif
diff --git a/engines/sword25/sfx/fmodexchannel.cpp b/engines/sword25/sfx/fmodexchannel.cpp
new file mode 100755
index 0000000000..88ad9640c5
--- /dev/null
+++ b/engines/sword25/sfx/fmodexchannel.cpp
@@ -0,0 +1,299 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "FMODEXCHANNEL"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "fmod.h"
+#include "fmodexexception.h"
+#include "fmodexchannel.h"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_FMODExChannel::BS_FMODExChannel(FMOD_CHANNEL * ChannelPtr, FMOD_SOUND * SoundPtr) :
+ m_ChannelPtr(ChannelPtr),
+ m_SoundPtr(SoundPtr)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+BS_FMODExChannel::~BS_FMODExChannel()
+{
+ if (m_ChannelPtr) FMOD_Channel_Stop(m_ChannelPtr);
+ if (m_SoundPtr) FMOD_Sound_Release(m_SoundPtr);
+}
+
+// -----------------------------------------------------------------------------
+// FMOD Ex macht alle Kanäle ungültig, sobald sie nicht mehr abgespielt werden,
+// oder wenn der Kanal in der zwischenzeit neu vergeben wurde.
+// Dann führen alle Aufrufe von Funktionen dieser Kanäle zu dem Fehlern
+// FMOD_ERR_INVALID_HANDLE oder FMOD_ERR_CHANNEL_STOLEN
+// Dieses Soundsystem entfernt aber nur jeden Frame alle toten Kanäle. Daher
+// kann es vorkommen, dass an einem bereits toten Kanal Aufrufe getätigt werden.
+// Diese Fehler werden daher von den folgenden Methoden ignoriert.
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ bool IsImportantError(FMOD_RESULT Result)
+ {
+ return Result != FMOD_OK && Result != FMOD_ERR_INVALID_HANDLE && Result != FMOD_ERR_CHANNEL_STOLEN;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Setter
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetPaused(bool Paused)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetPaused(m_ChannelPtr, Paused ? 1 : 0);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetPaused()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetVolume(float Volume)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetVolume(m_ChannelPtr, Volume);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetVolume()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetPanning(float Panning)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetPan(m_ChannelPtr, Panning);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetPan()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetLoop(bool Loop)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetLoopCount(m_ChannelPtr, Loop ? -1 : 0);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetLoopCount()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetLoopPoints(unsigned int LoopStart, unsigned int LoopEnd)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetLoopPoints(m_ChannelPtr, LoopStart, FMOD_TIMEUNIT_PCM, LoopEnd, FMOD_TIMEUNIT_PCM);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetLoopPoints()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::SetPosition(unsigned int Position)
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_SetPosition(m_ChannelPtr, Position, FMOD_TIMEUNIT_PCM);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_SetPosition()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::Stop()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_RESULT Result = FMOD_Channel_Stop(m_ChannelPtr);
+ if (IsImportantError(Result))
+ {
+ BS_FMODExException("FMOD_Channel_Stop()", Result).Log();
+ return false;
+ }
+ else
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Getter
+// -----------------------------------------------------------------------------
+
+float BS_FMODExChannel::GetVolume()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ float Volume = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetVolume(m_ChannelPtr, &Volume);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetVolume()", Result).Log();
+
+ return Volume;
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_FMODExChannel::GetPanning()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ float Panning = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetPan(m_ChannelPtr, &Panning);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetPan()", Result).Log();
+
+ return Panning;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExChannel::GetPosition()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ unsigned int Position = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetPosition(m_ChannelPtr, &Position, FMOD_TIMEUNIT_PCM);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetPosition()", Result).Log();
+
+ return Position;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExChannel::GetTime()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ unsigned int Time = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetPosition(m_ChannelPtr, &Time, FMOD_TIMEUNIT_MS);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetPosition()", Result).Log();
+
+ return Time;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExChannel::GetLoopStart()
+{
+ BS_ASSERT(m_ChannelPtr);
+ unsigned int LoopStart = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetLoopPoints(m_ChannelPtr, &LoopStart, FMOD_TIMEUNIT_PCM, 0, FMOD_TIMEUNIT_PCM);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetLoopPoints()", Result).Log();
+
+ return LoopStart;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExChannel::GetLoopEnd()
+{
+ BS_ASSERT(m_ChannelPtr);
+ unsigned int LoopEnd = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetLoopPoints(m_ChannelPtr, 0, FMOD_TIMEUNIT_PCM, &LoopEnd, FMOD_TIMEUNIT_PCM);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetLoopPoints()", Result).Log();
+
+ return LoopEnd;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::IsLooping()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ int LoopCount = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetLoopCount(m_ChannelPtr, &LoopCount);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetLoopCount()", Result).Log();
+
+ return LoopCount == -1;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::IsPaused()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_BOOL Paused = 0;
+ FMOD_RESULT Result = FMOD_Channel_GetPaused(m_ChannelPtr, &Paused);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_GetPaused()", Result).Log();
+
+ return Paused != 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExChannel::IsPlaying()
+{
+ BS_ASSERT(m_ChannelPtr);
+
+ FMOD_BOOL Playing = 0;
+ FMOD_RESULT Result = FMOD_Channel_IsPlaying(m_ChannelPtr, &Playing);
+ if (IsImportantError(Result)) BS_FMODExException("FMOD_Channel_IsPlaying()", Result).Log();
+
+ return Playing != 0;
+}
diff --git a/engines/sword25/sfx/fmodexchannel.h b/engines/sword25/sfx/fmodexchannel.h
new file mode 100755
index 0000000000..9ebf674ff2
--- /dev/null
+++ b/engines/sword25/sfx/fmodexchannel.h
@@ -0,0 +1,69 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef FMODEXCHANNEL_H
+#define FMODEXCHANNEL_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+struct FMOD_CHANNEL;
+struct FMOD_SOUND;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FMODExChannel
+{
+public:
+ BS_FMODExChannel(FMOD_CHANNEL * ChannelPtr, FMOD_SOUND * SoundPtr);
+ virtual ~BS_FMODExChannel();
+
+ bool SetPaused(bool Paused);
+ bool SetVolume(float Volume);
+ bool SetPanning(float Panning);
+ bool SetLoop(bool Loop);
+ bool SetLoopPoints(unsigned int LoopStart, unsigned int LoopEnd);
+ bool SetPosition(unsigned int Position);
+ bool Stop();
+
+ float GetVolume();
+ float GetPanning();
+ unsigned int GetPosition();
+ unsigned int GetTime();
+ unsigned int GetLoopStart();
+ unsigned int GetLoopEnd();
+ bool IsLooping();
+ bool IsPaused();
+ bool IsPlaying();
+
+private:
+ FMOD_CHANNEL * m_ChannelPtr;
+ FMOD_SOUND * m_SoundPtr;
+};
+
+#endif
diff --git a/engines/sword25/sfx/fmodexexception.h b/engines/sword25/sfx/fmodexexception.h
new file mode 100755
index 0000000000..c0541f6d3c
--- /dev/null
+++ b/engines/sword25/sfx/fmodexexception.h
@@ -0,0 +1,51 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_FMODEXEXCEPTION_H
+#define BS_FMODEXEXCEPTION_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "fmod_errors.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FMODExException
+{
+public:
+ BS_FMODExException(const char * Function_, FMOD_RESULT Result_) :
+ Function(Function_),
+ Result(Result_) {}
+
+ const char * Function;
+ FMOD_RESULT Result;
+
+ void Log()
+ {
+ BS_LOG_ERROR("Call to %s failed.", Function);
+ BS_LOGLN(" FMOD error: %s(%d)", FMOD_ErrorString(Result), Result);
+ }
+};
+
+#endif
diff --git a/engines/sword25/sfx/fmodexresource.cpp b/engines/sword25/sfx/fmodexresource.cpp
new file mode 100755
index 0000000000..3e15dbb260
--- /dev/null
+++ b/engines/sword25/sfx/fmodexresource.cpp
@@ -0,0 +1,170 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "FMODEXRESOURCE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include <memory>
+#include "fmod.h"
+#include "fmodexexception.h"
+#include "fmodexchannel.h"
+#include "package/packagemanager.h"
+#include "fmodexresource.h"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const unsigned int MAX_SAMPLE_SIZE = 100 * 1024; // Die Dateigröße in Byte ab der ein Sound als Stream abgespielt wird
+}
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+
+BS_FMODExResource::BS_FMODExResource(const std::string& FileName, FMOD_SYSTEM * FMOD, bool & Success) :
+ m_SoundPtr(0),
+ m_SoundDataPtr(0),
+ BS_Resource(FileName, BS_Resource::TYPE_SOUND)
+{
+ BS_ASSERT(FMOD);
+
+ // Von Misserfolg ausgehen
+ Success = false;
+
+ // Pointer auf den Package-Manager bekommen
+ BS_PackageManager * PackagePtr = BS_Kernel::GetInstance()->GetPackage();
+ if (!PackagePtr)
+ {
+ BS_LOG_ERRORLN("Package manager not found.");
+ return;
+ }
+
+ // Datei laden
+ unsigned int FileSize;
+ char * FileDataPtr = (char*) PackagePtr->GetFile(GetFileName(), &FileSize);
+ if (!FileDataPtr)
+ {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", GetFileName().c_str());
+ return;
+ }
+
+ // Ob die Sounddatei als Sample oder als Stream behandelt wird, ist abhängig von der Dateigröße.
+ // Samples werden sofort intialisiert.
+ // Für Streams wird hingegen bei jedem Abspielen ein neuer Sound erstellt. Dieses Vorgehen ist notwendig, da FMOD Ex Samples beliebig oft
+ // gleichzeitig abspielen kann, Streams jedoch nur ein mal.
+ if (FileSize <= MAX_SAMPLE_SIZE)
+ {
+ FMOD_CREATESOUNDEXINFO ExInfo;
+ memset(&ExInfo, 0, sizeof(ExInfo));
+ ExInfo.cbsize = sizeof(ExInfo);
+ ExInfo.length = FileSize;
+
+ FMOD_RESULT Result = FMOD_System_CreateSound(FMOD, FileDataPtr,
+ FMOD_CREATESAMPLE | FMOD_OPENMEMORY | FMOD_SOFTWARE | FMOD_2D | FMOD_LOOP_NORMAL,
+ &ExInfo,
+ &m_SoundPtr);
+ if (Result != FMOD_OK) BS_FMODExException("FMOD_System_CreateSound()", Result).Log();
+
+ Success = Result == FMOD_OK;
+
+ delete FileDataPtr;
+ }
+ else
+ {
+ m_SoundDataPtr = FileDataPtr;
+ m_SoundDataSize = FileSize;
+
+ Success = true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+BS_FMODExResource::~BS_FMODExResource()
+{
+ // Sound freigeben, solange des Soundsystem noch läuft.
+ // Sollte das Soundsystem beendet worden sein müssen und können Sounds nicht mehr freigegeben werden.
+ if (m_SoundPtr && BS_Kernel::GetInstance()->GetService("sfx")) FMOD_Sound_Release(m_SoundPtr);
+ if (m_SoundDataPtr) delete [] m_SoundDataPtr;
+}
+
+// -----------------------------------------------------------------------------
+// Abspielen
+// -----------------------------------------------------------------------------
+
+BS_FMODExChannel * BS_FMODExResource::StartSound(FMOD_SYSTEM * FMOD)
+{
+ BS_ASSERT(FMOD);
+
+ FMOD_CHANNEL * NewChannelPtr;
+ FMOD_SOUND * NewSoundPtr = 0;
+
+ // Sample können sofort abgespielt werden.
+ if (m_SoundPtr)
+ {
+ FMOD_RESULT Result = FMOD_System_PlaySound(FMOD, FMOD_CHANNEL_FREE, m_SoundPtr, 1, &NewChannelPtr);
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_System_PlaySound()", Result).Log();
+ return 0;
+ }
+ }
+ // Für Streams muss ein neuer Sound erstellt werden.
+ else
+ {
+ FMOD_CREATESOUNDEXINFO ExInfo;
+ memset(&ExInfo, 0, sizeof(ExInfo));
+ ExInfo.cbsize = sizeof(ExInfo);
+ ExInfo.length = m_SoundDataSize;
+
+ FMOD_RESULT Result;
+ Result = FMOD_System_CreateSound(FMOD,
+ m_SoundDataPtr,
+ FMOD_CREATESTREAM | FMOD_OPENMEMORY_POINT | FMOD_SOFTWARE | FMOD_2D | FMOD_LOOP_NORMAL,
+ &ExInfo,
+ &NewSoundPtr);
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_System_CreateSound()", Result).Log();
+ return 0;
+ }
+
+ Result = FMOD_System_PlaySound(FMOD, FMOD_CHANNEL_FREE, NewSoundPtr, 1, &NewChannelPtr);
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_System_PlaySound()", Result).Log();
+ return 0;
+ }
+ }
+
+ // Der Channel und der Sound (bei Streams) werden an ein BS_FMODExChannel-Objekt übergeben.
+ // Dieses Sorgt auch dafür, dass Channel und Sound korrekt zerstört werden.
+ return new BS_FMODExChannel(NewChannelPtr, NewSoundPtr);
+}
diff --git a/engines/sword25/sfx/fmodexresource.h b/engines/sword25/sfx/fmodexresource.h
new file mode 100755
index 0000000000..43ff93e380
--- /dev/null
+++ b/engines/sword25/sfx/fmodexresource.h
@@ -0,0 +1,56 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef BS_FMODRESOURCE_H
+#define BS_FMODRESOURCE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/resource.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_FMODExChannel;
+struct FMOD_SOUND;
+struct FMOD_SYSTEM;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FMODExResource : public BS_Resource
+{
+public:
+ BS_FMODExResource(const std::string& FileName, FMOD_SYSTEM * FMOD, bool & Success);
+ virtual ~BS_FMODExResource();
+
+ BS_FMODExChannel * StartSound(FMOD_SYSTEM * FMOD);
+
+private:
+ FMOD_SOUND * m_SoundPtr;
+ char * m_SoundDataPtr;
+ unsigned int m_SoundDataSize;
+};
+
+#endif
diff --git a/engines/sword25/sfx/fmodexsound.cpp b/engines/sword25/sfx/fmodexsound.cpp
new file mode 100755
index 0000000000..346d55086b
--- /dev/null
+++ b/engines/sword25/sfx/fmodexsound.cpp
@@ -0,0 +1,889 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// Die von der Engine ausgegebenen Soundhandles werden intern auf FMOD Ex Handles gemapped.
+// Diese Handles sind nur solange gültig, wie der Sound spielt. Falls danach versucht wird manipulierend auf den Sound zuzugreifen,
+// schlägt dieses ohne Fehlermeldung fehl.
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "FMODEXSOUND"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "fmod.h"
+#include "fmod_errors.h"
+#include "fmodexexception.h"
+#include "fmodexchannel.h"
+#include "fmodexresource.h"
+#include "kernel/string.h"
+#include "kernel/inputpersistenceblock.h"
+#include "kernel/outputpersistenceblock.h"
+#include "package/packagemanager.h"
+#include "fmodexsound.h"
+
+// -----------------------------------------------------------------------------
+// Konstanten und lokale Funktionen
+// -----------------------------------------------------------------------------
+
+namespace
+{
+ const float DEFAULT_MUSIC_VOLUME = 1.0f;
+ const float DEFAULT_SPEECH_VOLUME = 1.0f;
+ const float DEFAULT_SFX_VOLUME = 1.0f;
+ const unsigned int SOUNDTYPE_COUNT = 3;
+ const unsigned int INVALID_SOUND_HANDLE = 0xffffffff;
+
+ // -------------------------------------------------------------------------
+
+ inline float NormalizePanning(float Panning)
+ {
+ bool Corrected = false;
+ float Result = Panning;
+ if (Result > 1.0f)
+ {
+ Result = 1.0f;
+ Corrected = true;
+ }
+ if (Result < -1.0f)
+ {
+ Result = -1.0f;
+ Corrected = true;
+ }
+
+ if (Corrected) BS_LOG_WARNINGLN("Tried to set an invalid panning value of %.2f. It was corrected to %.2f", Panning, Result);
+
+ return Result;
+ }
+
+ // -------------------------------------------------------------------------
+
+ inline float NormalizeVolume(float Volume)
+ {
+ bool Corrected = false;
+ float Result = Volume;
+ if (Result> 1.0f)
+ {
+ Result = 1.0f;
+ Corrected = true;
+ }
+ if (Result < 0.0f)
+ {
+ Result = 0.0f;
+ Corrected = true;
+ }
+
+ if (Corrected) BS_LOG_WARNINGLN("Tried to set an invalid volume value of %.2f. It was corrected to %.2f", Volume, Result);
+
+ return Result;
+ }
+
+ // -------------------------------------------------------------------------
+
+ inline FMOD_SOUND_FORMAT BitsPerSampleToFMODExSoundFormat(unsigned int BitsPerSample)
+ {
+ switch (BitsPerSample)
+ {
+ case 8: return FMOD_SOUND_FORMAT_PCM8;
+ case 16: return FMOD_SOUND_FORMAT_PCM16;
+ case 24: return FMOD_SOUND_FORMAT_PCM24;
+ case 32: return FMOD_SOUND_FORMAT_PCM32;
+ default: return FMOD_SOUND_FORMAT_NONE;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+BS_FMODExSound::BS_FMODExSound(BS_Kernel* pKernel) :
+ BS_SoundEngine(pKernel),
+ m_FMOD(0),
+ m_NextHandle(1)
+{
+ // Lautstärkeneinstellungen auf die Standardwerte setzen
+ m_Volumes[MUSIC] = DEFAULT_MUSIC_VOLUME;
+ m_Volumes[SPEECH] = DEFAULT_SPEECH_VOLUME;
+ m_Volumes[SFX] = DEFAULT_SFX_VOLUME;
+}
+
+// -----------------------------------------------------------------------------
+
+BS_FMODExSound::~BS_FMODExSound()
+{
+ // Alle noch spielenden Sounds stoppen und die Ressourcen freigeben
+ for (PSM_ITER it = m_PlayingSoundsMap.begin(); it != m_PlayingSoundsMap.end(); ++it)
+ {
+ if (it->second.ChannelPtr) delete it->second.ChannelPtr;
+ if (it->second.ResourcePtr) it->second.ResourcePtr->Release();
+ }
+
+ // FMOD Ex deinitialisieren
+ if (m_FMOD) FMOD_System_Release(m_FMOD);
+}
+
+// -----------------------------------------------------------------------------
+
+BS_Service * BS_FMODExSound_CreateObject(BS_Kernel* pKernel) { return new BS_FMODExSound(pKernel); }
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::Init(unsigned int SampleRate, unsigned int Channels)
+{
+ // Eine Warnung ausgeben, wenn dieser Service schon initialisiert wurde.
+ // Allerdings wird trotzdem true zurückgegeben, weil kein Fehler aufgetreten ist, der Service ist noch benutzbar.
+ if (m_FMOD)
+ {
+ BS_LOG_WARNINGLN("Tried to initialize again. Call ignored.");
+ return true;
+ }
+ else
+ {
+ try
+ {
+ // Die FMOD Ex mit den übergebenen Werte initialisieren
+ FMOD_RESULT Result = FMOD_System_Create(&m_FMOD);
+ if (Result != FMOD_OK) throw(BS_FMODExException("FMOD_System_Create()", Result));
+
+ Result = FMOD_System_SetSoftwareFormat(m_FMOD, SampleRate, FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR);
+ if (Result != FMOD_OK) throw(BS_FMODExException("FMOD_System_SetSoftwareFormat()", Result));
+
+ Result = FMOD_System_Init(m_FMOD, Channels, FMOD_INIT_NORMAL, 0);
+ if (Result != FMOD_OK) throw(BS_FMODExException("FMOD_System_Init()", Result));
+ }
+
+ catch(BS_FMODExException Ex)
+ {
+ Ex.Log();
+ BS_LOG_ERRORLN("FMOD Ex could not be initialized.");
+
+ if (m_FMOD)
+ {
+ FMOD_System_Release(m_FMOD);
+ m_FMOD = 0;
+ }
+
+ return false;
+ }
+
+ BS_LOGLN("FMOD Ex initialized. Sample rate: %d / Channels: %d", SampleRate, Channels);
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::Update()
+{
+ BS_ASSERT(m_FMOD);
+
+ FMOD_RESULT Result = FMOD_System_Update(m_FMOD);
+ if (Result != FMOD_OK) BS_FMODExException("FMOD_System_Update()", Result).Log();
+
+ RemoveInactiveSounds();
+}
+
+// -----------------------------------------------------------------------------
+// Sounds abspielen
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::PlaySound(const std::string& FileName,
+ SOUND_TYPES Type,
+ float Volume,
+ float Pan,
+ bool Loop,
+ int LoopStart, int LoopEnd,
+ unsigned int Layer)
+{
+ return PlaySoundInternal(FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer, 0, 0) != 0;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExSound::PlaySoundEx(const std::string& FileName,
+ SOUND_TYPES Type,
+ float Volume,
+ float Pan,
+ bool Loop,
+ int LoopStart, int LoopEnd,
+ unsigned int Layer)
+{
+ return PlaySoundInternal(FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer, 0, 0);
+}
+
+// -------------------------------------------------------------------------
+
+FMOD_RESULT F_CALLBACK BS_FMODExSound::FMODExDynamicSoundSetPosCallback(FMOD_SOUND *sound, int subsound, unsigned int position, FMOD_TIMEUNIT postype)
+{
+ // In dynamischen Sounds wird nicht gesprungen, daher tut dieses Funktion nichts.
+ return FMOD_OK;
+}
+
+// -------------------------------------------------------------------------
+
+FMOD_RESULT F_CALLBACK BS_FMODExSound::FMODExDynamicSoundReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen)
+{
+ // Handle auf das aktuelle Soundsystem holen, dies ist wohl dieses hier.
+ BS_FMODExSound * t = reinterpret_cast<BS_FMODExSound *>(BS_Kernel::GetInstance()->GetSfx());
+
+ // Handle auf den richtigen Sound holen, wurde als FMOD Ex Benutzerdaten gesetzt.
+ unsigned int Handle;
+ FMOD_RESULT Result = FMOD_Sound_GetUserData(sound, reinterpret_cast<void **>(&Handle));
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_Sound_GetUserData()", Result).Log();
+ return FMOD_OK;
+ }
+
+ // Sounddaten holen und Callbackfunktion aufrufen.
+ PlayingSoundData * PSD = t->GetPlayingSoundDataByHandle(Handle);
+ if (PSD) PSD->ReadCallback(PSD->UserData, data, datalen);
+
+ return FMOD_OK;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExSound::PlayDynamicSoundEx(DynamicSoundReadCallback ReadCallback,
+ void * UserData,
+ SOUND_TYPES Type,
+ unsigned int SampleRate,
+ unsigned int BitsPerSample,
+ unsigned int Channels,
+ float Volume,
+ float Pan,
+ unsigned int Layer)
+{
+ // Parameter überprüfen
+ if (BitsPerSampleToFMODExSoundFormat(BitsPerSample) == FMOD_SOUND_FORMAT_NONE)
+ {
+ BS_LOG_ERRORLN("Cannot create a dynamic sound with %d bits per sample.", BitsPerSample);
+ return 0;
+ }
+ if (Channels == 0 || Channels > 2)
+ {
+ BS_LOG_ERRORLN("Cannot create a dynamic sound with %d channels.", Channels);
+ return 0;
+ }
+
+ // Zu vergebendes Handle bestimmen
+ unsigned int Handle = m_NextHandle++;
+
+ // Sound in die Sound-Map eintragen mit all den Informationen, die wir bisher haben.
+ // Dies muss für dynamische Sounds so früh geschehen, da sofort nach dem Aufruf von FMOD_System_CreateSound der Callback aufgerufen wird um
+ // den Decode-Buffer zu füllen. Unser Callback liest den BS-Callback aus der Sound-Map. Wenn wir den Sound später hinzufügen würden, würde der
+ // BS-Callback zu diesem Sound nicht in der Map stehen und nicht aufgerufen werden können.
+ // Den fehlenden ChannelPtr tragen wir später in dieser Funktion ein.
+ m_PlayingSoundsMap[Handle] = PlayingSoundData(0, 0, Type, Layer, Volume, ReadCallback, UserData);
+
+ // Dynamischen FMOD Ex Sound erstellen
+ FMOD_CREATESOUNDEXINFO CreateSoundExInfo;
+ memset(&CreateSoundExInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+ CreateSoundExInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ CreateSoundExInfo.length = 0xffffffff;
+ CreateSoundExInfo.numchannels = Channels;
+ CreateSoundExInfo.defaultfrequency = SampleRate;
+ CreateSoundExInfo.format = BitsPerSampleToFMODExSoundFormat(BitsPerSample);
+ CreateSoundExInfo.pcmreadcallback = FMODExDynamicSoundReadCallback;
+ CreateSoundExInfo.pcmsetposcallback = FMODExDynamicSoundSetPosCallback;
+ CreateSoundExInfo.userdata = reinterpret_cast<void *>(Handle);
+
+ FMOD_SOUND * FMODExSoundPtr;
+ FMOD_RESULT Result = FMOD_System_CreateSound(m_FMOD,
+ 0,
+ FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_HARDWARE | FMOD_CREATESTREAM,
+ &CreateSoundExInfo,
+ &FMODExSoundPtr);
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_System_CreateSound() from PlayDynamicSoundEx()", Result).Log();
+ return 0;
+ }
+
+ // Neu erstellten Sound einem Kanal zuweisen
+ FMOD_CHANNEL * FMODExChannelPtr;
+ Result = FMOD_System_PlaySound(m_FMOD, FMOD_CHANNEL_FREE, FMODExSoundPtr, 1, &FMODExChannelPtr);
+ if (Result != FMOD_OK)
+ {
+ BS_FMODExException("FMOD_System_PlaySound() from PlayDynamicSoundEx()", Result).Log();
+ return 0;
+ }
+
+ // FMOD Ex Kanal an einen BS_FMODExChannel binden und abspielen
+ BS_FMODExChannel * ChannelPtr = new BS_FMODExChannel(FMODExChannelPtr, FMODExSoundPtr);
+ ChannelPtr->SetPaused(false);
+
+ // ChannelPtr in die PlayingSoundData-Struktur eintragen
+ PlayingSoundData * PSD = GetPlayingSoundDataByHandle(Handle);
+ if (PSD) PSD->ChannelPtr = ChannelPtr;
+
+ return Handle;
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExSound::PlaySoundInternal(const std::string& FileName,
+ SOUND_TYPES Type,
+ float Volume,
+ float Pan,
+ bool Loop,
+ int LoopStart, int LoopEnd,
+ unsigned int Layer,
+ unsigned int Position,
+ unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ BS_ASSERT(Type < SOUNDTYPE_COUNT);
+
+ // Resource anfordern
+ BS_Resource * ResourcePtr = BS_Kernel::GetInstance()->GetResourceManager()->RequestResource(FileName);
+ if (!ResourcePtr)
+ {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", FileName.c_str());
+ return 0;
+ }
+ if (ResourcePtr->GetType() != BS_Resource::TYPE_SOUND)
+ {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a sound.", FileName.c_str());
+ return 0;
+ }
+ BS_FMODExResource * SoundResourcePtr = static_cast<BS_FMODExResource *>(ResourcePtr);
+
+ // Sound im Pause-Modus starten
+ BS_FMODExChannel * ChannelPtr = SoundResourcePtr->StartSound(m_FMOD);
+
+ if (ChannelPtr)
+ {
+ try
+ {
+ // Falls der Sound gelooped wird, Loop-Points setzen
+ if (Loop)
+ {
+ // Bestimmen, welche Loop-Points benutzt werden. Falls ein Loop-Point als Parameter nicht spezifiziert wurde (Wert -1),
+ // wird der Loop-Point von FMOD Ex benutzt.
+ unsigned int RealLoopStart = (LoopStart > 0) ? LoopStart : ChannelPtr->GetLoopStart();
+ unsigned int RealLoopEnd = (LoopEnd > 0) ? LoopEnd : ChannelPtr->GetLoopEnd();
+
+ // Loop-Points auf Gültigkeit überprüfen
+ if (RealLoopStart > RealLoopEnd)
+ {
+ BS_LOG_ERRORLN("Loop start (%d) was placed after loop end (%d) for sound \"%s\".",
+ RealLoopStart, RealLoopEnd,
+ SoundResourcePtr->GetFileName().c_str());
+ throw(0);
+ }
+ if (RealLoopStart > ChannelPtr->GetLoopEnd())
+ {
+ BS_LOG_ERRORLN("Loop start (%d) was placed after end (%d) of sound \"%s\".",
+ RealLoopStart,
+ ChannelPtr->GetLoopEnd(),
+ SoundResourcePtr->GetFileName().c_str());
+ throw(0);
+ }
+ if (RealLoopEnd > ChannelPtr->GetLoopEnd())
+ {
+ BS_LOG_ERRORLN("Loop end (%d) was placed after end (%d) of sound \"%s\".",
+ RealLoopEnd,
+ ChannelPtr->GetLoopEnd(),
+ SoundResourcePtr->GetFileName().c_str());
+ throw(0);
+ }
+
+ // Loop-Points setzen
+ if (!ChannelPtr->SetLoopPoints(RealLoopStart, RealLoopEnd)) throw(0);
+ }
+
+ // Sound-Parameter gemäß der Übergabeparameter setzen
+ if (!ChannelPtr->SetVolume(NormalizeVolume(Volume) * m_Volumes[Type])) throw(0);
+ if (!ChannelPtr->SetPanning(NormalizePanning(Pan))) throw(0);
+ if (!ChannelPtr->SetLoop(Loop)) throw(0);
+ if (!ChannelPtr->SetPosition(Position)) throw(0);
+ }
+ catch (...)
+ {
+ delete ChannelPtr;
+ SoundResourcePtr->Release();
+ return 0;
+ }
+
+ unsigned int MyLoopStart = ChannelPtr->GetLoopStart();
+ unsigned int MyLoopEnd = ChannelPtr->GetLoopEnd();
+ ChannelPtr->SetLoopPoints(MyLoopStart, MyLoopEnd);
+
+ // Sound abspielen
+ ChannelPtr->SetPaused(false);
+
+ // Sound in die Sound-Map eintragen
+ unsigned int NewHandle = (Handle != 0) ? Handle : m_NextHandle++;
+ m_PlayingSoundsMap[NewHandle] = PlayingSoundData(SoundResourcePtr, ChannelPtr, Type, Layer, Volume);
+
+ return NewHandle;
+ }
+ else
+ {
+ SoundResourcePtr->Release();
+ return 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Sonstige Methoden
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::SetVolume(float Volume, SOUND_TYPES Type)
+{
+ BS_ASSERT(m_FMOD);
+ BS_ASSERT(Type < SOUNDTYPE_COUNT);
+ m_Volumes[Type] = NormalizeVolume(Volume);
+
+ // Alle Volumen der Sounds der Kategorie aktualisieren
+ PSM_CONST_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ const PlayingSoundData & PSD = it->second;
+ if (PSD.ChannelPtr && PSD.Type == Type) PSD.ChannelPtr->SetVolume(Volume * PSD.Volume);
+
+ ++it;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_FMODExSound::GetVolume(SOUND_TYPES Type)
+{
+ BS_ASSERT(m_FMOD);
+ BS_ASSERT(Type < SOUNDTYPE_COUNT);
+ return m_Volumes[Type];
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::PauseAll()
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle Sounds durchgehen und alle pausieren.
+ // Diese werden dann markiert, damit ResumeAll() feststellen kann, welche Sounds mit PauseAll() pausiert wurden.
+ // ResumeAll() setzt dann nur diejenigen fort, die nur über PauseAll() pausiert wurden.
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ PlayingSoundData & PSD = it->second;
+
+ if (PSD.ChannelPtr) PSD.ChannelPtr->SetPaused(true);
+ PSD.PausedGlobal = true;
+
+ ++it;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::ResumeAll()
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle Sounds durchgehen, die gloable Pause aufheben und diejenigen fortsetzen,
+ // die keine Pause mehr haben (weder explizit, über den Layer oder global).
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ PlayingSoundData & PSD = it->second;
+
+ if (PSD.PausedGlobal)
+ {
+ PSD.PausedGlobal = false;
+ if (PSD.ChannelPtr && !PSD.PausedLayer && !PSD.Paused) PSD.ChannelPtr->SetPaused(false);
+ }
+
+ ++it;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::PauseLayer(unsigned int Layer)
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle Sounds durchgehen und alle pausieren, die sich auf den angegebenen Layer befinden.
+ // Diese werden dann markiert, damit ResumeLayer() feststellen kann, welche Sounds mit PauseLayer() pausiert wurden.
+ // ResumeLayer() setzt dann nur diejenigen fort, die nur über PauseLayer() mit der entsprechenden Layer-Nummer pausiert wurden.
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ PlayingSoundData & PSD = it->second;
+
+ if (PSD.Layer == Layer)
+ {
+ if (PSD.ChannelPtr) PSD.ChannelPtr->SetPaused(true);
+ PSD.PausedLayer = true;
+ }
+
+ ++it;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::ResumeLayer(unsigned int Layer)
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle Sounds durchgehen, die Layer-Pause aufheben und diejenigen fortsetzen,
+ // die keine Pause mehr haben (weder explizit, über den Layer oder global).
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ PlayingSoundData & PSD = it->second;
+
+ if (PSD.PausedLayer && PSD.Layer == Layer)
+ {
+ PSD.PausedLayer = false;
+ if (PSD.ChannelPtr && !PSD.PausedGlobal && !PSD.Paused) PSD.ChannelPtr->SetPaused(false);
+ }
+
+ ++it;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Sound Setter
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::SetSoundVolume(unsigned int Handle, float Volume)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr) if (PSDPtr->ChannelPtr && PSDPtr->ChannelPtr->SetVolume(NormalizeVolume(Volume) * m_Volumes[PSDPtr->Type])) PSDPtr->Volume = Volume;
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::SetSoundPanning(unsigned int Handle, float Pan)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) PSDPtr->ChannelPtr->SetPanning(NormalizePanning(Pan));
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::PauseSound(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr)
+ {
+ PSDPtr->Paused = true;
+ if (PSDPtr->ChannelPtr) PSDPtr->ChannelPtr->SetPaused(true);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::ResumeSound(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr)
+ {
+ PSDPtr->Paused = false;
+ if (PSDPtr->ChannelPtr && !PSDPtr->PausedGlobal && !PSDPtr->PausedLayer) PSDPtr->ChannelPtr->SetPaused(false);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::StopSound(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) PSDPtr->ChannelPtr->Stop();
+}
+
+// -----------------------------------------------------------------------------
+// Sound Getter
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::IsSoundPaused(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) return PSDPtr->ChannelPtr->IsPaused();
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::IsSoundPlaying(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) return PSDPtr->ChannelPtr->IsPlaying();
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_FMODExSound::GetSoundVolume(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr) return PSDPtr->Volume;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_FMODExSound::GetSoundPanning(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) return PSDPtr->ChannelPtr->GetPanning();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+float BS_FMODExSound::GetSoundTime(unsigned int Handle)
+{
+ BS_ASSERT(m_FMOD);
+ PlayingSoundData * PSDPtr = GetPlayingSoundDataByHandle(Handle);
+ if (PSDPtr && PSDPtr->ChannelPtr) return static_cast<float>(PSDPtr->ChannelPtr->GetTime()) / 1000.0f;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+// Hilfsmethoden
+// -----------------------------------------------------------------------------
+
+void BS_FMODExSound::RemoveInactiveSounds()
+{
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ if (!it->second.ChannelPtr || !it->second.ChannelPtr->IsPlaying())
+ {
+ PlayingSoundData & PSD = it->second;
+
+ delete PSD.ChannelPtr;
+ if (PSD.ResourcePtr) PSD.ResourcePtr->Release();
+
+ it = m_PlayingSoundsMap.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ /*
+ static size_t lastActiveChannels = 0;
+ if (m_PlayingSoundsMap.size() != lastActiveChannels)
+ {
+ BS_LOGLN("Aktive Kanaele: %d", m_PlayingSoundsMap.size());
+ lastActiveChannels = m_PlayingSoundsMap.size();
+ }
+ */
+}
+
+// -----------------------------------------------------------------------------
+
+BS_FMODExSound::PlayingSoundData * BS_FMODExSound::GetPlayingSoundDataByHandle(unsigned int Handle)
+{
+ // Zum Soundhandle gehörige Daten in der Hash-Map finden
+ PSM_ITER it = m_PlayingSoundsMap.find(Handle);
+ // Falls die Daten nicht gefunden werden konnten, Fehler zurückgebene, ansonsten ein Pointer auf die Daten.
+ if (it == m_PlayingSoundsMap.end()) return 0;
+ return &((*it).second);
+}
+
+// -----------------------------------------------------------------------------
+
+unsigned int BS_FMODExSound::CountPlayingDynamicSounds()
+{
+ unsigned int Result = 0;
+ for (PSM_CONST_ITER it = m_PlayingSoundsMap.begin(); it != m_PlayingSoundsMap.end(); ++it) if (!it->second.ResourcePtr) ++Result;
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+// Ressourcen-Verwaltung
+// -----------------------------------------------------------------------------
+
+BS_Resource * BS_FMODExSound::LoadResource(const std::string& FileName)
+{
+ BS_ASSERT(m_FMOD);
+ BS_ASSERT(CanLoadResource(FileName));
+
+ bool Success;
+ BS_FMODExResource * ResourcePtr = new BS_FMODExResource(FileName, m_FMOD, Success);
+ if (Success)
+ return ResourcePtr;
+ else
+ {
+ delete ResourcePtr;
+ return 0;
+ }
+}
+bool BS_FMODExSound::CanLoadResource(const std::string& FileName)
+{
+ if (FileName.size() >= 4)
+ {
+ std::string Extension(FileName.end() - 4, FileName.end());
+ BS_String::ToLower(Extension);
+
+ return Extension == ".wav" ||
+ Extension == ".ogg" ||
+ Extension == ".mp3";
+ }
+ else
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::Persist(BS_OutputPersistenceBlock & Writer)
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle inaktiven Sounds entfernen, damit kein unnötiger Ballast gespeichert wird
+ RemoveInactiveSounds();
+
+ // Warnung ausgeben, wenn dynamische Sounds abgespielt werden
+ unsigned int PlayingDynamicSounds = CountPlayingDynamicSounds();
+ if (PlayingDynamicSounds) BS_LOG_WARNINGLN("There are currently dynamic sounds playing. These will not be persisted.");
+
+ // Nächstes Handle speichern
+ Writer.Write(m_NextHandle);
+
+ // Anzahl spielender (nicht dynamischer) Sounds speichern
+ Writer.Write(m_PlayingSoundsMap.size() - PlayingDynamicSounds);
+
+ // Informationen für jeden spielenden (nicht dynamischen) Sound speichern
+ PSM_CONST_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ const PlayingSoundData & PSD = it->second;
+
+ if (PSD.ResourcePtr)
+ {
+ // Handle speichern
+ Writer.Write(it->first);
+
+ // Soundeigenschaften speichern
+ Writer.Write(PSD.ResourcePtr->GetFileName());
+ Writer.Write(static_cast<unsigned int>(PSD.Type));
+ Writer.Write(PSD.Layer);
+
+ Writer.Write(PSD.Volume);
+ Writer.Write(PSD.ChannelPtr->GetPanning());
+ Writer.Write(PSD.ChannelPtr->IsLooping());
+ Writer.Write(PSD.ChannelPtr->GetLoopStart());
+ Writer.Write(PSD.ChannelPtr->GetLoopEnd());
+ Writer.Write(PSD.ChannelPtr->GetPosition());
+ Writer.Write(PSD.Paused);
+ Writer.Write(PSD.PausedLayer);
+ Writer.Write(PSD.PausedGlobal);
+ }
+
+ ++it;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool BS_FMODExSound::Unpersist(BS_InputPersistenceBlock & Reader)
+{
+ BS_ASSERT(m_FMOD);
+
+ // Alle Sounds stoppen
+ PSM_ITER it = m_PlayingSoundsMap.begin();
+ while (it != m_PlayingSoundsMap.end())
+ {
+ const PlayingSoundData & PSD = it->second;
+ if (PSD.ChannelPtr) delete PSD.ChannelPtr;
+ if (PSD.ResourcePtr) PSD.ResourcePtr->Release();
+ ++it;
+ }
+
+ // Sound-Map leeren
+ m_PlayingSoundsMap.clear();
+
+ // Nächstes Handle laden
+ Reader.Read(m_NextHandle);
+
+ // Soundanzahl einlesen
+ unsigned int SoundCount = 0;
+ Reader.Read(SoundCount);
+
+ // Informationen über jeden spielenden Sound einlesen und ihn mit den Parametern abspielen
+ for (unsigned int i = 0; i < SoundCount; ++i)
+ {
+ unsigned int Handle;
+ std::string FileName;
+ unsigned int Type;
+ unsigned int Layer;
+
+ float Volume;
+ float Pan;
+ bool Loop;
+ unsigned int LoopStart;
+ unsigned int LoopEnd;
+ unsigned int Position;
+ bool Paused;
+ bool PausedLayer;
+ bool PausedGlobal;
+
+ Reader.Read(Handle);
+ Reader.Read(FileName);
+ Reader.Read(Type);
+ Reader.Read(Layer);
+
+ Reader.Read(Volume);
+ Reader.Read(Pan);
+ Reader.Read(Loop);
+ Reader.Read(LoopStart);
+ Reader.Read(LoopEnd);
+ Reader.Read(Position);
+ Reader.Read(Paused);
+ Reader.Read(PausedLayer);
+ Reader.Read(PausedGlobal);
+
+ if (Reader.IsGood())
+ {
+ PlaySoundInternal(FileName, (SOUND_TYPES) Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer, Position, Handle);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return Reader.IsGood();
+}
diff --git a/engines/sword25/sfx/fmodexsound.h b/engines/sword25/sfx/fmodexsound.h
new file mode 100755
index 0000000000..4c0d8fc448
--- /dev/null
+++ b/engines/sword25/sfx/fmodexsound.h
@@ -0,0 +1,142 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#ifndef FMODEXSOUND_H
+#define FMODEXSOUND_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/memlog_off.h"
+#include <vector>
+#include <map>
+#include "kernel/memlog_on.h"
+
+#include "kernel/common.h"
+#include "kernel/hashmap.h"
+#include "soundengine.h"
+
+#include "fmod.h"
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class BS_FMODExChannel;
+struct FMOD_SYSTEM;
+struct FMOD_CHANNEL;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_FMODExSound : public BS_SoundEngine
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_FMODExSound(BS_Kernel* pKernel);
+ virtual ~BS_FMODExSound();
+
+ bool Init(unsigned int SampleRate, unsigned int Channels = 32);
+ void Update();
+ void SetVolume(float Volume, SOUND_TYPES Type);
+ float GetVolume(SOUND_TYPES Type);
+ void PauseAll();
+ void ResumeAll();
+ void PauseLayer(unsigned int Layer);
+ void ResumeLayer(unsigned int Layer);
+ bool PlaySound(const std::string& FileName, SOUND_TYPES Type, float Volume, float Pan, bool Loop, int LoopStart, int LoopEnd, unsigned int Layer);
+ unsigned int PlaySoundEx(const std::string& FileName, SOUND_TYPES Type, float Volume, float Pan, bool Loop, int LoopStart, int LoopEnd, unsigned int Layer);
+ unsigned int PlayDynamicSoundEx(DynamicSoundReadCallback ReadCallback, void * UserData, SOUND_TYPES Type, unsigned int SampleRate, unsigned int BitsPerSample, unsigned int Channels, float Volume = 1.0f, float Pan = 0.0f, unsigned int Layer = 0);
+
+ void SetSoundVolume(unsigned int Handle, float Volume);
+ void SetSoundPanning(unsigned int Handle, float Pan);
+ void PauseSound(unsigned int Handle);
+ void ResumeSound(unsigned int Handle);
+ void StopSound(unsigned int Handle);
+ bool IsSoundPaused(unsigned int Handle);
+ bool IsSoundPlaying(unsigned int Handle);
+ float GetSoundVolume(unsigned int Handle);
+ float GetSoundPanning(unsigned int Handle);
+ float GetSoundTime(unsigned int Handle);
+
+ BS_Resource * LoadResource(const std::string& FileName);
+ bool CanLoadResource(const std::string& FileName);
+
+ // -----------------------------------------------------------------------------
+ // Persistenz
+ // -----------------------------------------------------------------------------
+
+ bool Persist(BS_OutputPersistenceBlock & Writer);
+ bool Unpersist(BS_InputPersistenceBlock & Reader);
+
+private:
+ struct PlayingSoundData
+ {
+ PlayingSoundData() {};
+ PlayingSoundData(BS_Resource * ResourcePtr_, BS_FMODExChannel * ChannelPtr_, SOUND_TYPES Type_, unsigned int Layer_, float Volume_, DynamicSoundReadCallback ReadCallback_ = 0, void * UserData_ = 0) :
+ ResourcePtr(ResourcePtr_),
+ ChannelPtr(ChannelPtr_),
+ Type(Type_),
+ Layer(Layer_),
+ Volume(Volume_),
+ ReadCallback(ReadCallback_),
+ UserData(UserData_),
+ Paused(false),
+ PausedLayer(false),
+ PausedGlobal(false)
+ {}
+
+ BS_Resource * ResourcePtr;
+ BS_FMODExChannel * ChannelPtr;
+ SOUND_TYPES Type;
+ unsigned int Layer;
+ DynamicSoundReadCallback ReadCallback;
+ void * UserData;
+
+ float Volume;
+ bool Paused;
+ bool PausedLayer;
+ bool PausedGlobal;
+ };
+
+ typedef BS_Hashmap<unsigned int, PlayingSoundData> PSM;
+ typedef BS_Hashmap<unsigned int, PlayingSoundData>::iterator PSM_ITER;
+ typedef BS_Hashmap<unsigned int, PlayingSoundData>::const_iterator PSM_CONST_ITER;
+ PSM m_PlayingSoundsMap;
+
+ FMOD_SYSTEM * m_FMOD;
+ float m_Volumes[3];
+ unsigned int m_NextHandle;
+
+ void RemoveInactiveSounds();
+ PlayingSoundData * GetPlayingSoundDataByHandle(unsigned int Handle);
+ unsigned int PlaySoundInternal(const std::string& FileName, SOUND_TYPES Type, float Volume, float Pan, bool Loop, int LoopStart, int LoopEnd, unsigned int Layer, unsigned int Handle, unsigned int Position);
+ unsigned int CountPlayingDynamicSounds();
+
+ static FMOD_RESULT F_CALLBACK FMODExDynamicSoundSetPosCallback(FMOD_SOUND *sound, int subsound, unsigned int position, FMOD_TIMEUNIT postype);
+ static FMOD_RESULT F_CALLBACK FMODExDynamicSoundReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen);
+ static FMOD_RESULT F_CALLBACK DSPReadCallback(FMOD_DSP_STATE * dsp_state, float * inbuffer, float * outbuffer, unsigned int length, int inchannels, int outchannels);
+};
+
+#endif
diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp
new file mode 100755
index 0000000000..d12c1fb604
--- /dev/null
+++ b/engines/sword25/sfx/soundengine.cpp
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "SOUNDENGINE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "soundengine.h"
+
+// -----------------------------------------------------------------------------
+
+BS_SoundEngine::BS_SoundEngine(BS_Kernel * pKernel) : BS_ResourceService(pKernel)
+{
+ if (!_RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+} \ No newline at end of file
diff --git a/engines/sword25/sfx/soundengine.h b/engines/sword25/sfx/soundengine.h
new file mode 100755
index 0000000000..16442c2481
--- /dev/null
+++ b/engines/sword25/sfx/soundengine.h
@@ -0,0 +1,253 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+/*
+ BS_SoundEngine
+ -------------
+ Dies ist das Soundengine Interface, dass alle Methoden enthält, die eine Soundengine
+ implementieren muss.
+ Implementationen der Soundengine müssen von dieser Klasse abgeleitet werden.
+ Es gilt zu beachten, dass eine Soundengine eine Liste mit ALLEN geladenen Samplen enthalten
+ muss, und dass diese Samples beim Aufruf des Destruktors freigegeben werden.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef BS_SOUNDENGINE_H
+#define BS_SOUNDENGINE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/resservice.h"
+#include "kernel/persistable.h"
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class BS_SoundEngine : public BS_ResourceService, public BS_Persistable
+{
+public:
+ // -----------------------------------------------------------------------------
+ // Enums und Typen
+ // -----------------------------------------------------------------------------
+
+ enum SOUND_TYPES
+ {
+ MUSIC = 0,
+ SPEECH = 1,
+ SFX = 2
+ };
+
+ /**
+ @brief Die Callbackfunktion von PlayDynamicSoundEx
+ @param UserData Benutzerspezifizierter Pointer
+ @param Data Pointer auf den zu beschreibenden Puffer
+ @param DataLength Länge der zu schreibenden Daten in Byte
+ */
+ typedef void (*DynamicSoundReadCallback)(void * UserData, void * Data, unsigned int DataLength);
+
+ // -----------------------------------------------------------------------------
+ // Konstruktion / Destruktion
+ // -----------------------------------------------------------------------------
+
+ BS_SoundEngine(BS_Kernel* pKernel);
+ virtual ~BS_SoundEngine() {};
+
+ // --------------------------------------------------------------
+ // DIESE METHODEN MÜSSEN VON DER SOUNDENGINE IMPLEMENTIERT WERDEN
+ // --------------------------------------------------------------
+
+ /**
+ @brief Initialisiert die Sound-Engine
+ @param SampleRate Gibt die zu nutzende SampleRate an
+ @param Channels die maximale Anzahl der Kanäle.<br>
+ Der Standardwert ist 32.
+ @return Gibt bei Erfolg TRUE, ansonsten FALSE zurück
+ @remark Aufrufe an allen anderen Methoden dürfen erst erfolgen, wenn diese Methode erfolgreich aufgerufen wurde.
+ */
+ virtual bool Init(unsigned int SampleRate, unsigned int Channels = 32) = 0;
+
+ /**
+ @brief Führt einen "Tick" der Sound-Engine aus
+
+ Diese Methode sollte ein mal pro Frame aufgerufen werden. Sie dient dazu Implementationen der Sound-Engine zu ermöglichen,
+ die nicht in einem eigenen Thread laufen oder zusätzliche Verwaltungsaufgaben durchführen müssen.
+ */
+ virtual void Update() = 0;
+
+ /**
+ @brief Setzt die Standardlautstärke für die verschiedenen Soundtypen
+ @param Volume die Standardlautstärke (0 = aus, 1 = volle Lautstärke)
+ @param Type der Soundtyp dessen Lautstärke geändert werden soll
+ */
+ virtual void SetVolume(float Volume, SOUND_TYPES Type) = 0;
+
+ /**
+ @brief Gibt die Standardlautstärke der verschiedenen Soundtypen
+ @param Type der Soundtyp
+ @return Gibt die Standardlautstärke des übergebenen Soundtyps zurück (0 = aus, 1 = volle Laustärke)
+ */
+ virtual float GetVolume(SOUND_TYPES Type) = 0;
+
+ /**
+ @brief Pausiert alle Sounds die gerade spielen
+ */
+ virtual void PauseAll() = 0;
+
+ /**
+ @brief Setzt alle gestoppten Sounds fort
+ */
+ virtual void ResumeAll() = 0;
+
+ /**
+ @brief Pausiert alle Sounds eines bestimmten Sound-Layers
+ @param Layer ein Soundlayer
+ */
+ virtual void PauseLayer(unsigned int Layer) = 0;
+
+ /**
+ @brief Setzt alle Sounds eines Layers fort, die mit PauseLayer() zuvor gestoppt wurden
+ @param Layer ein Soundlayer
+ */
+ virtual void ResumeLayer(unsigned int Layer) = 0;
+
+
+ /**
+ @brief Spielt einen Sound ab
+ @param FileName der Dateiname des abzuspielenden Sounds
+ @param Type der Typ des Sounds
+ @param Volume die Lautstärke mit der der Soundabgespielt werden soll (0 = aus, 1 = volle Lautstärke)
+ @param Pan das Panning (-1 = ganz links, 1 = ganz rechts)
+ @param Loop gibt an ob der Sound geloopt werden soll
+ @param LoopStart gibt den Start-Looppoint an. Wenn ein Wert kleiner als 0 übergeben wird, wird der Start des Sounds benutzt.
+ @param LoopEnd gibt den End-Looppoint an. Wenn ein Wert kleiner als 0 übergeben wird, wird das Ende des Sounds benutzt.
+ @param Layer der Soundlayer
+ @return Gibt true zurück, wenn das Abspielen des Sounds eingeleitet werden konnte.
+ @remark Falls eine erweiterte Kontrolle über das Abspielen benötigt wird, z.B. das Ändern der Soundparameter
+ Volume und Panning während des Abspielvorgangens, sollte PlaySoundEx benutzt werden.
+ */
+ virtual bool PlaySound(const std::string& FileName, SOUND_TYPES Type, float Volume = 1.0f, float Pan = 0.0f, bool Loop = false, int LoopStart = -1, int LoopEnd = -1, unsigned int Layer = 0) = 0;
+
+ /**
+ @brief Spielt einen Sound ab
+ @param FileName der Dateiname des abzuspielenden Sounds
+ @param Type der Typ des Sounds
+ @param Volume die Lautstärke mit der der Soundabgespielt werden soll (0 = aus, 1 = volle Lautstärke)
+ @param Pan das Panning (-1 = ganz links, 1 = ganz rechts)
+ @param Loop gibt an ob der Sound geloopt werden soll
+ @param LoopStart gibt den Start-Looppoint an. Wenn ein Wert kleiner als 0 übergeben wird, wird der Start des Sounds benutzt.
+ @param LoopEnd gibt den End-Looppoint an. Wenn ein Wert kleiner als 0 übergeben wird, wird das Ende des Sounds benutzt.
+ @param Layer der Soundlayer
+ @return Gibt ein Handle auf den Sounds zurück. Mit diesem Handle kann der Sound während des Abspielens manipuliert werden.
+ @remark Falls eine erweiterte Kontrolle über das Abspielen benötigt wird, z.B. das Ändern der Soundparameter
+ Volume und Panning während des Abspielvorgangens, sollte PlaySoundEx benutzt werden.
+ */
+ virtual unsigned int PlaySoundEx(const std::string& FileName, SOUND_TYPES Type, float Volume = 1.0f, float Pan = 0.0f, bool Loop = false, int LoopStart = -1, int LoopEnd = -1, unsigned int Layer = 0) = 0;
+
+ /**
+ @brief Spielt einen zur Laufzeit generierten Sound ab
+ @param ReadCallback ein Pointer auf eine Callbackfunktion, die aufgerufen wird, wenn Sounddaten benötigt werden.<br>
+ Nähere Details zu dieser Funktion gibt es bei der Dokumentation von DynamicSoundReadCallback.
+ @param UserData ein Pointer auf benutzerdefinierte Daten. Diese werden der Callback-Funktion bei jedem Aufruf übergeben.<br>
+ Falls keine solche Daten benötigt werden, kann dieser Parameter auf 0 gesetzt werden.
+ @param Type der Typ des Sounds
+ @param SampleRate die Sample-Rate des Sounds
+ @param BitsPerSample die Größe eines Samples in Bits. Hiermit sind tatsächlich nur einzelne Sample gemeint, diese Angabe ist unabhängig von der Anzahl der Kanäle.<br>
+ Erlaubte Werte sind 8, 16, 24 und 32.
+ @param Channels die Anzahl der Kanäle.<br>
+ Erlaubte Werte sind 1 und 2.
+ @param Volume die Lautstärke mit der der Soundabgespielt werden soll (0 = aus, 1 = volle Lautstärke)
+ @param Pan das Panning (-1 = ganz links, 1 = ganz rechts)
+ @param Layer der Soundlayer
+ @return Gibt ein Handle auf den Sounds zurück. Mit diesem Handle kann der Sound während des Abspielens manipuliert werden.
+ @remark Dynamische Sounds können nicht persistiert werden.
+ */
+ virtual unsigned int PlayDynamicSoundEx(DynamicSoundReadCallback ReadCallback, void * UserData, SOUND_TYPES Type, unsigned int SampleRate, unsigned int BitsPerSample, unsigned int Channels, float Volume = 1.0f, float Pan = 0.0f, unsigned int Layer = 0) = 0;
+
+ /**
+ @brief Setzt die Lautstärke eines spielenden Sounds
+ @param Handle das Handle des Sounds
+ @param Volume die Lautstärke mit der der Soundabgespielt werden soll (0 = aus, 1 = volle Lautstärke)
+ */
+ virtual void SetSoundVolume(unsigned int Handle, float Volume) = 0;
+
+ /**
+ @brief Setzt das Panning eines spielenden Sounds
+ @param Handle das Handle des Sounds
+ @param Pan das Panning (-1 = ganz links, 1 = ganz rechts)
+ */
+ virtual void SetSoundPanning(unsigned int Handle, float Pan) = 0;
+
+ /**
+ @brief Pausiert einen Sound
+ @param Handle das Handle des Sounds
+ */
+ virtual void PauseSound(unsigned int Handle) = 0;
+
+ /**
+ @brief Setzt einen Sound fort
+ @param Handle das Handle des Sounds
+ */
+ virtual void ResumeSound(unsigned int Handle) = 0;
+
+ /**
+ @brief Stoppt einen Sound
+ @param Handle das Handle des Sounds
+ @remark Nach einem Aufruf dieser Methode ist das Handle ungültig und darf nicht mehr benutzt werden.
+ */
+ virtual void StopSound(unsigned int Handle) = 0;
+
+ /**
+ @brief Gibt zurück, ob ein Sound pausiert ist
+ @param Handle das Handle des Sounds
+ @return Gibt true zurück, wenn der Sound pausiert ist, ansonsten false.
+ */
+ virtual bool IsSoundPaused(unsigned int Handle) = 0;
+
+ /**
+ @brief Gibt zurück, ob ein Sound noch spielt
+ @param Handle das Handle des Sounds
+ @return Gibt true zurück, wenn der Sound noch spielt, ansonsten false.
+ */
+ virtual bool IsSoundPlaying(unsigned int Handle) = 0;
+
+ /**
+ @brief Gibt die Lautstärke eines spielenden Sounds zurück (0 = aus, 1 = volle Lautstärke)
+ */
+ virtual float GetSoundVolume(unsigned int Handle) = 0;
+
+ /**
+ @brief Gibt das Panning eines spielenden Sounds zurück (-1 = ganz links, 1 = ganz rechts)
+ */
+ virtual float GetSoundPanning(unsigned int Handle) = 0;
+
+ /**
+ @brief Gibt die Position innerhalb des abgespielten Sounds in Sekunden zurück
+ */
+ virtual float GetSoundTime(unsigned int Handle) = 0;
+
+private:
+ bool _RegisterScriptBindings();
+};
+
+#endif
diff --git a/engines/sword25/sfx/soundengine_script.cpp b/engines/sword25/sfx/soundengine_script.cpp
new file mode 100755
index 0000000000..9cc3fcb153
--- /dev/null
+++ b/engines/sword25/sfx/soundengine_script.cpp
@@ -0,0 +1,397 @@
+// -----------------------------------------------------------------------------
+// This file is part of Broken Sword 2.5
+// Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer
+//
+// Broken Sword 2.5 is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// Broken Sword 2.5 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Broken Sword 2.5; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "kernel/common.h"
+#include "kernel/kernel.h"
+#include "script/script.h"
+#include "script/luabindhelper.h"
+
+#include "soundengine.h"
+
+// -----------------------------------------------------------------------------
+
+static int Init(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ if (lua_gettop(L) == 0)
+ lua_pushbooleancpp(L, pSfx->Init(44100, 32));
+ else if (lua_gettop(L) == 1)
+ lua_pushbooleancpp(L, pSfx->Init(static_cast<unsigned int>(luaL_checknumber(L, 1)), 32));
+ else
+ lua_pushbooleancpp(L, pSfx->Init(static_cast<unsigned int>(luaL_checknumber(L, 1)), static_cast<unsigned int>(luaL_checknumber(L, 2))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Update(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->Update();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetVolume(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->SetVolume(static_cast<float>(luaL_checknumber(L, 1)),
+ static_cast<BS_SoundEngine::SOUND_TYPES>(static_cast<unsigned int>(luaL_checknumber(L, 2))));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetVolume(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->GetVolume(static_cast<BS_SoundEngine::SOUND_TYPES>(static_cast<unsigned int>(luaL_checknumber(L, 1)))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int PauseAll(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->PauseAll();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ResumeAll(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->ResumeAll();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int PauseLayer(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->PauseLayer(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ResumeLayer(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->ResumeLayer(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void ProcessPlayParams(lua_State * L, std::string & FileName, BS_SoundEngine::SOUND_TYPES & Type, float & Volume, float & Pan, bool & Loop, int & LoopStart, int & LoopEnd, unsigned int & Layer)
+{
+ FileName = luaL_checkstring(L, 1);
+
+ Type = static_cast<BS_SoundEngine::SOUND_TYPES>(static_cast<unsigned int>(luaL_checknumber(L, 2)));
+
+ if (lua_gettop(L) < 3 || lua_isnil(L, 3)) Volume = 1.0f;
+ else Volume = static_cast<float>(luaL_checknumber(L, 3));
+
+ if (lua_gettop(L) < 4 || lua_isnil(L, 4)) Pan = 0.0f;
+ else Pan = static_cast<float>(luaL_checknumber(L, 4));
+
+ if (lua_gettop(L) < 5 || lua_isnil(L, 5)) Loop = false;
+ else Loop = lua_tobooleancpp(L, 5);
+
+ if (lua_gettop(L) < 6 || lua_isnil(L, 6)) LoopStart = -1;
+ else LoopStart = static_cast<int>(luaL_checknumber(L, 6));
+
+ if (lua_gettop(L) < 7 || lua_isnil(L, 7)) LoopEnd = -1;
+ else LoopEnd = static_cast<int>(luaL_checknumber(L, 7));
+
+ if (lua_gettop(L) < 8 || lua_isnil(L, 8)) Layer = 0;
+ else Layer = static_cast<unsigned int>(luaL_checknumber(L, 8));
+}
+
+static int PlaySound(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ std::string FileName;
+ BS_SoundEngine::SOUND_TYPES Type;
+ float Volume;
+ float Pan;
+ bool Loop;
+ int LoopStart;
+ int LoopEnd;
+ unsigned int Layer;
+ ProcessPlayParams(L, FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer);
+
+ lua_pushbooleancpp(L, pSfx->PlaySound(FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer));
+
+ return 1;
+}
+
+static int PlaySoundEx(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ std::string FileName;
+ BS_SoundEngine::SOUND_TYPES Type;
+ float Volume;
+ float Pan;
+ bool Loop;
+ int LoopStart;
+ int LoopEnd;
+ unsigned int Layer;
+ ProcessPlayParams(L, FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer);
+
+ lua_pushnumber(L, pSfx->PlaySoundEx(FileName, Type, Volume, Pan, Loop, LoopStart, LoopEnd, Layer));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetSoundVolume(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->SetSoundVolume(static_cast<unsigned int>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetSoundPanning(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->SetSoundPanning(static_cast<unsigned int>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int PauseSound(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->PauseSound(static_cast<unsigned int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ResumeSound(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->ResumeSound(static_cast<unsigned int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StopSound(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->StopSound(static_cast<unsigned int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSoundPaused(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushbooleancpp(L, pSfx->IsSoundPaused(static_cast<unsigned int>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSoundPlaying(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushbooleancpp(L, pSfx->IsSoundPlaying(static_cast<unsigned int>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSoundVolume(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->GetSoundVolume(static_cast<unsigned int>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSoundPanning(lua_State * L)
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_SoundEngine * pSfx = static_cast<BS_SoundEngine *>(BS_Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->GetSoundPanning(static_cast<unsigned int>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char * SFX_LIBRARY_NAME = "Sfx";
+
+static const luaL_reg SFX_FUNCTIONS[] =
+{
+ "Init", Init,
+ "Update", Update,
+ "__SetVolume", SetVolume,
+ "__GetVolume", GetVolume,
+ "PauseAll", PauseAll,
+ "ResumeAll", ResumeAll,
+ "PauseLayer", PauseLayer,
+ "ResumeLayer", ResumeLayer,
+ "__PlaySound", PlaySound,
+ "__PlaySoundEx", PlaySoundEx,
+ "__SetSoundVolume", SetSoundVolume,
+ "__SetSoundPanning", SetSoundPanning,
+ "__PauseSound", PauseSound,
+ "__ResumeSound", ResumeSound,
+ "__StopSound", StopSound,
+ "__IsSoundPaused", IsSoundPaused,
+ "__IsSoundPlaying", IsSoundPlaying,
+ "__GetSoundVolume", GetSoundVolume,
+ "__GetSoundPanning", GetSoundPanning,
+ 0, 0,
+};
+
+static const lua_constant_reg SFX_CONSTANTS[] =
+{
+ "MUSIC", BS_SoundEngine::MUSIC,
+ "SPEECH", BS_SoundEngine::SPEECH,
+ "SFX", BS_SoundEngine::SFX,
+ 0, 0,
+};
+
+// -----------------------------------------------------------------------------
+
+bool BS_SoundEngine::_RegisterScriptBindings()
+{
+ BS_Kernel * pKernel = BS_Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ BS_ScriptEngine * pScript = static_cast<BS_ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State * L = static_cast<lua_State *>(pScript->GetScriptObject());
+ BS_ASSERT(L);
+
+ if (!BS_LuaBindhelper::AddFunctionsToLib(L, SFX_LIBRARY_NAME, SFX_FUNCTIONS)) return false;
+ if (!BS_LuaBindhelper::AddConstantsToLib(L, SFX_LIBRARY_NAME, SFX_CONSTANTS)) return false;
+
+ return true;
+}
diff --git a/engines/sword25/util/glsprites/glsprites.h b/engines/sword25/util/glsprites/glsprites.h
new file mode 100755
index 0000000000..28b00a7fe6
--- /dev/null
+++ b/engines/sword25/util/glsprites/glsprites.h
@@ -0,0 +1,108 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_GLSPRITES_H
+#define GLS_GLSPRITES_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Typedefs */
+typedef unsigned __int32 GLS_UInt32;
+typedef signed __int32 GLS_SInt32;
+typedef unsigned __int8 GLS_UInt8;
+typedef float GLS_Float;
+typedef enum { GLS_True = 1, GLS_False = 0 } GLS_Bool;
+
+/* Errors */
+typedef enum
+{
+ GLS_OK,
+ GLS_OUT_OF_MEMORY,
+ GLS_WINDOW_CREATION_FAILED,
+ GLS_WINDOW_PREPARATION_FAILED,
+ GLS_DISPLAY_MODE_CHANGE_FAILED,
+ GLS_WINDOW_WAS_CLOSED,
+ GLS_OPENGL_CONTEXT_CREATION_FAILED,
+ GLS_COULD_NOT_FLIP,
+ GLS_TEXTURE_CREATION_FAILED,
+ GLS_NO_WINDOW,
+ GLS_INVALID_SPRITE_OBJECT,
+ GLS_INVALID_SPRITE_DIMENSIONS,
+ GLS_NO_DRIVER_SUPPORT_FOR_VSYNC,
+ GLS_OPENGL_ERROR,
+ GLS_INVALID_DATA_DIMENSIONS,
+ GLS_INVALID_DATA_POINTER,
+ GLS_INVALID_SUB_IMAGE,
+} GLS_Result;
+
+/* GLS_Rect */
+typedef struct
+{
+ GLS_UInt32 x1;
+ GLS_UInt32 y1;
+ GLS_UInt32 x2;
+ GLS_UInt32 y2;
+} GLS_Rect;
+
+/* GLS_Color */
+typedef struct
+{
+ GLS_UInt8 r;
+ GLS_UInt8 g;
+ GLS_UInt8 b;
+ GLS_UInt8 a;
+} GLS_Color;
+
+/* GLS_Sprite */
+typedef void * GLS_Sprite;
+
+/* Functions */
+GLS_Result GLS_Init(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, const char * windowTitle, GLS_Bool allowCloseWindow);
+GLS_Result GLS_InitExternalWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, void * windowHandle);
+GLS_Result GLS_Quit();
+
+GLS_Result GLS_SetVSync(GLS_Bool status);
+GLS_Result GLS_IsVsync(GLS_Bool * pStatus);
+
+GLS_Result GLS_StartFrame();
+GLS_Result GLS_EndFrame();
+
+GLS_Result GLS_NewSprite(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, const void * data, GLS_Sprite * pSprite);
+GLS_Result GLS_DeleteSprite(GLS_Sprite sprite);
+
+GLS_Result GLS_SetSpriteData(GLS_Sprite sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride);
+
+GLS_Result GLS_Blit(GLS_Sprite sprite,
+ GLS_SInt32 x, GLS_SInt32 y,
+ const GLS_Rect * subImage,
+ const GLS_Color * color,
+ GLS_Bool flipH, GLS_Bool flipV,
+ GLS_Float scaleX, GLS_Float scaleY);
+
+GLS_Result GLS_GetWindowHandle(void ** pWindowHandle);
+
+const char * GLS_ResultString(GLS_Result ResultCode);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/core.c b/engines/sword25/util/glsprites/internal/core.c
new file mode 100755
index 0000000000..deb1dcbd3d
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/core.c
@@ -0,0 +1,212 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include "glinclude.h"
+#include "core.h"
+#include "glswindow.h"
+
+
+/* --------------------------------------------------------------------------
+ GLOBALS
+ -------------------------------------------------------------------------- */
+
+GLS_OGLCaps GLS_TheOGLCaps;
+static GLS_Window * window = 0;
+static GLS_Bool vsyncActive = GLS_False;
+typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)( int );
+static PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
+
+
+/* -------------------------------------------------------------------------- */
+
+void InitGL(GLS_UInt32 width, GLS_UInt32 height)
+{
+ if (height == 0) height = 1;
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, height, 0, 1, -1);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glShadeModel(GL_SMOOTH);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void InitGLCaps(GLS_OGLCaps * OGLCaps)
+{
+ if (strstr(glGetString(GL_EXTENSIONS), "GL_ARB_texture_rectangle") != 0 ||
+ strstr(glGetString(GL_EXTENSIONS), "GL_EXT_texture_rectangle") != 0 ||
+ strstr(glGetString(GL_EXTENSIONS), "GL_NV_texture_rectangle") != 0)
+ {
+ OGLCaps->textureRectanglesSupported = GLS_True;
+ glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &(OGLCaps->maxTextureRectangleSize));
+ }
+ else
+ OGLCaps->textureRectanglesSupported = GLS_False;
+
+ if (strstr(glGetString(GL_EXTENSIONS), "WGL_EXT_swap_control") != 0)
+ {
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC) wglGetProcAddress("wglSwapIntervalEXT");
+ }
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &(OGLCaps->maxTextureSize));
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_Init(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, const char * windowTitle, GLS_Bool allowCloseWindow)
+{
+ GLS_Result Result;
+
+ Result = GLS_CreateGLWindow(width, height, fullscreen, windowTitle, allowCloseWindow, &window);
+ if (Result != GLS_OK) return Result;
+ InitGL(width, height);
+ InitGLCaps(&GLS_TheOGLCaps);
+
+ return GLS_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_InitExternalWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, void * windowHandle)
+{
+ GLS_Result Result;
+
+ Result = GLS_CreateGLExternalWindow(width, height, fullscreen, windowHandle, &window);
+ if (Result != GLS_OK) return Result;
+ InitGL(width, height);
+ InitGLCaps(&GLS_TheOGLCaps);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_Quit()
+{
+ if (window) GLS_CloseGLWindow(window);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_StartFrame()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+ return GLS_ProcessWindowMessages(window);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_EndFrame()
+{
+ if (GLS_WindowFlip(window))
+ return GLS_OK;
+ else
+ return GLS_COULD_NOT_FLIP;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_GetWindowHandle(void ** pWindowHandle)
+{
+ if (window)
+ {
+ *pWindowHandle = window->hwnd;
+ return GLS_OK;
+ }
+ else
+ {
+ return GLS_NO_WINDOW;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_SetVSync(GLS_Bool status)
+{
+ if (wglSwapIntervalEXT)
+ {
+ if (wglSwapIntervalEXT(status ? 1 : 0))
+ {
+ vsyncActive = status;
+ return GLS_OK;
+ }
+ else
+ return GLS_OPENGL_ERROR;
+ }
+ else
+ return GLS_NO_DRIVER_SUPPORT_FOR_VSYNC;
+}
+
+GLS_Result GLS_IsVsync(GLS_Bool * pStatus)
+{
+ if (wglSwapIntervalEXT)
+ {
+ *pStatus = vsyncActive;
+ return GLS_OK;
+ }
+ else
+ return GLS_NO_DRIVER_SUPPORT_FOR_VSYNC;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+#define GLRS(ERR) case GLS_##ERR: return #ERR;
+const char * GLS_ResultString(GLS_Result ResultCode)
+{
+ switch (ResultCode)
+ {
+ GLRS(OK)
+ GLRS(OUT_OF_MEMORY)
+ GLRS(WINDOW_CREATION_FAILED)
+ GLRS(WINDOW_PREPARATION_FAILED)
+ GLRS(DISPLAY_MODE_CHANGE_FAILED)
+ GLRS(WINDOW_WAS_CLOSED)
+ GLRS(OPENGL_CONTEXT_CREATION_FAILED)
+ GLRS(COULD_NOT_FLIP)
+ GLRS(TEXTURE_CREATION_FAILED)
+ GLRS(NO_WINDOW)
+ GLRS(INVALID_SPRITE_OBJECT)
+ GLRS(INVALID_SPRITE_DIMENSIONS)
+ GLRS(NO_DRIVER_SUPPORT_FOR_VSYNC)
+ GLRS(OPENGL_ERROR)
+ GLRS(INVALID_DATA_DIMENSIONS)
+ GLRS(INVALID_DATA_POINTER)
+ GLRS(INVALID_SUB_IMAGE)
+ default:
+ return "unknown result code";
+ }
+}
+#undef GLRS
diff --git a/engines/sword25/util/glsprites/internal/core.h b/engines/sword25/util/glsprites/internal/core.h
new file mode 100755
index 0000000000..9baa3e1c98
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/core.h
@@ -0,0 +1,40 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_CORE_H
+#define GLS_CORE_H
+
+#include "../glsprites.h"
+
+/* GL_ARB_texture_rectangle / GL_EXT_texture_rectangle / GL_NV_texture_rectangle */
+#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
+
+typedef struct
+{
+ GLS_UInt32 maxTextureSize;
+ GLS_Bool textureRectanglesSupported;
+ GLS_UInt32 maxTextureRectangleSize;
+} GLS_OGLCaps;
+
+extern GLS_OGLCaps GLS_TheOGLCaps;
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/glinclude.h b/engines/sword25/util/glsprites/internal/glinclude.h
new file mode 100755
index 0000000000..0ac11cccb2
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/glinclude.h
@@ -0,0 +1,36 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_GL_INCLUDE_H
+#define GLS_GL_INCLUDE_H
+
+/* This header is included by all GLS files that need OpenGL. */
+
+#ifdef WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+#endif
+
+#include <GL/gl.h>
+
+#ifndef GL_CLAMP_TO_EDGE
+ #define GL_CLAMP_TO_EDGE 0x812F
+#endif
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/glswindow.c b/engines/sword25/util/glsprites/internal/glswindow.c
new file mode 100755
index 0000000000..dcb8424d73
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/glswindow.c
@@ -0,0 +1,449 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <malloc.h>
+
+#include "glswindow.h"
+
+
+/* --------------------------------------------------------------------------
+ CONSTANTS
+ -------------------------------------------------------------------------- */
+
+static const char * WINDOW_CLASSNAME = "GLsprites class";
+static const GLS_UInt32 BITS_PER_PIXEL = 32;
+
+
+/* -------------------------------------------------------------------------- */
+
+static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_SYSCOMMAND:
+ {
+ switch (wParam)
+ {
+ case SC_SCREENSAVE:
+ case SC_MONITORPOWER:
+ return 0;
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ {
+ if (GetWindowLong(hWnd, GWL_USERDATA)) PostQuitMessage(0);
+ return 0;
+ }
+ }
+
+ return DefWindowProc(hWnd,uMsg,wParam,lParam);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Bool RegisterWindowClass()
+{
+ WNDCLASS wc;
+
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "OpenGL";
+
+ if (RegisterClass(&wc))
+ return GLS_True;
+ else
+ return GLS_False;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void CenterWindow(RECT * pWR, int * pX, int * pY)
+{
+ int screenX, screenY;
+
+ screenX = GetSystemMetrics(SM_CXSCREEN);
+ screenY = GetSystemMetrics(SM_CYSCREEN);
+
+ if (screenX == 0 || screenY == 0)
+ {
+ *pX = 0;
+ *pY = 0;
+ }
+ else
+ {
+ *pX = (screenX - (pWR->right - pWR->left)) / 2;
+ *pY = (screenY - (pWR->bottom - pWR->top)) / 2;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Bool CreateTheWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, const char * windowTitle, GLS_Bool allowCloseWindow, HWND * pHwnd)
+{
+ DWORD style;
+ DWORD exStyle;
+ RECT windowRect;
+ int x, y;
+
+ if (fullscreen)
+ {
+ exStyle = WS_EX_APPWINDOW;
+ style = WS_POPUP;
+ }
+ else
+ {
+ exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ style = WS_CAPTION;
+ if (allowCloseWindow) style |= WS_SYSMENU;
+ }
+
+ windowRect.left = 0;
+ windowRect.right = width;
+ windowRect.top= 0;
+ windowRect.bottom = height;
+ if (!AdjustWindowRectEx(&windowRect, style, FALSE, exStyle)) return GLS_False;
+
+ if (fullscreen)
+ {
+ x = 0;
+ y = 0;
+ }
+ else
+ CenterWindow(&windowRect, &x, &y);
+
+ *pHwnd = CreateWindowEx(
+ exStyle,
+ "OpenGL",
+ windowTitle,
+ style | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE,
+ x, y,
+ windowRect.right - windowRect.left,
+ windowRect.bottom - windowRect.top,
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL);
+
+ if (!*pHwnd) return GLS_False;
+
+ SetWindowLong(*pHwnd, GWL_USERDATA, (allowCloseWindow == GLS_True) ? 1 : 0);
+
+ return GLS_True;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void SetWindowClientSize(HWND hwnd, int cx, int cy)
+{
+ HMENU hmenu = GetMenu(hwnd);
+ RECT rcWindow = { 0, 0, cx, cy };
+
+ /*
+ * First convert the client rectangle to a window rectangle the
+ * menu-wrap-agnostic way.
+ */
+ AdjustWindowRectEx(&rcWindow, GetWindowLong(hwnd, GWL_STYLE), hmenu != NULL, GetWindowLong(hwnd, GWL_EXSTYLE));
+
+ /*
+ * If there is a menu, then check how much wrapping occurs
+ * when we set a window to the width specified by AdjustWindowRect
+ * and an infinite amount of height. An infinite height allows
+ * us to see every single menu wrap.
+ */
+ if (hmenu) {
+ RECT rcTemp = rcWindow;
+ rcTemp.bottom = 0x7FFF; /* "Infinite" height */
+ SendMessage(hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rcTemp);
+
+ /*
+ * Adjust our previous calculation to compensate for menu
+ * wrapping.
+ */
+ rcWindow.bottom += rcTemp.top;
+ }
+
+ SetWindowPos(hwnd, NULL, 0, 0, rcWindow.right - rcWindow.left,
+ rcWindow.bottom - rcWindow.top, SWP_NOMOVE | SWP_NOZORDER);
+
+}
+
+/* ---------------------------------------------------------------------------- */
+
+static GLS_Bool PrepareWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, HWND hwnd)
+{
+ if (fullscreen)
+ {
+ SetWindowLong(hwnd, GWL_STYLE, WS_POPUP);
+ SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TOPMOST);
+ SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ }
+
+ SetWindowClientSize(hwnd, width, height);
+ ShowWindow(hwnd, SW_SHOW);
+
+ return GLS_True;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Bool ChangeDisplayMode(GLS_UInt32 width, GLS_UInt32 height)
+{
+ DEVMODE dmScreenSettings;
+ memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
+ dmScreenSettings.dmSize = sizeof(dmScreenSettings);
+ dmScreenSettings.dmPelsWidth = width;
+ dmScreenSettings.dmPelsHeight = height;
+ dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL;
+ dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+ if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL)
+ return GLS_True;
+ else
+ return GLS_False;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Bool CreateOpenGLContext(HWND hwnd, HGLRC * pHrc)
+{
+ HDC hdc;
+ int pixelformat;
+ PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ PFD_DRAW_TO_WINDOW |
+ PFD_SUPPORT_OPENGL |
+ PFD_DOUBLEBUFFER,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0,
+ 0,
+ 0, 0, 0, 0,
+ 0,
+ 0,
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ hdc = GetDC(hwnd);
+ if (!hdc) return GLS_False;
+
+ pixelformat = ChoosePixelFormat(hdc, &pfd);
+ if (!pixelformat)
+ {
+ ReleaseDC(hwnd, hdc);
+ return GLS_False;
+ }
+
+ if (!SetPixelFormat(hdc, pixelformat, &pfd))
+ {
+ ReleaseDC(hwnd, hdc);
+ return GLS_False;
+ }
+
+ *pHrc = wglCreateContext(hdc);
+ if (!*pHrc)
+ {
+ ReleaseDC(hwnd, hdc);
+ return GLS_False;
+ }
+
+ if (!wglMakeCurrent(hdc, *pHrc))
+ {
+ wglDeleteContext(*pHrc);
+ *pHrc = 0;
+ ReleaseDC(hwnd, hdc);
+ return GLS_False;
+ }
+
+ ReleaseDC(hwnd, hdc);
+ return GLS_True;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_CreateGLWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, const char * windowTitle, GLS_Bool allowCloseWindow, GLS_Window ** pWindow)
+{
+ *pWindow = (GLS_Window *) malloc(sizeof(GLS_Window));
+ if (!*pWindow) return GLS_OUT_OF_MEMORY;
+
+ (*pWindow)->hrc = 0;
+ (*pWindow)->hwnd = 0;
+ (*pWindow)->fullscreen = GLS_False;
+ (*pWindow)->ownHwnd = GLS_True;
+
+ if (!RegisterWindowClass())
+ {
+ GLS_CloseGLWindow(*pWindow);
+ return GLS_WINDOW_CREATION_FAILED;
+ }
+
+ if (!CreateTheWindow(width, height, fullscreen, windowTitle, allowCloseWindow, &((*pWindow)->hwnd)))
+ {
+ GLS_CloseGLWindow(*pWindow);
+ return GLS_WINDOW_CREATION_FAILED;
+ }
+
+ if (fullscreen)
+ {
+ if (!ChangeDisplayMode(width, height))
+ {
+ return GLS_DISPLAY_MODE_CHANGE_FAILED;
+ }
+ (*pWindow)->fullscreen = GLS_True;
+ }
+
+ if (!CreateOpenGLContext((*pWindow)->hwnd, &((*pWindow)->hrc)))
+ {
+ GLS_CloseGLWindow(*pWindow);
+ return GLS_OPENGL_CONTEXT_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_CreateGLExternalWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, void * windowHandle, GLS_Window ** pWindow)
+{
+ *pWindow = (GLS_Window *) malloc(sizeof(GLS_Window));
+ if (!*pWindow) return GLS_OUT_OF_MEMORY;
+
+ (*pWindow)->hrc = 0;
+ (*pWindow)->hwnd = windowHandle;
+ (*pWindow)->fullscreen = GLS_False;
+ (*pWindow)->ownHwnd = GLS_False;
+
+ if (!PrepareWindow(width, height, fullscreen, (*pWindow)->hwnd))
+ {
+ GLS_CloseGLWindow(*pWindow);
+ return GLS_WINDOW_PREPARATION_FAILED;
+ }
+
+ if (fullscreen)
+ {
+ if (!ChangeDisplayMode(width, height))
+ {
+ return GLS_DISPLAY_MODE_CHANGE_FAILED;
+ }
+ (*pWindow)->fullscreen = GLS_True;
+ }
+
+ if (!CreateOpenGLContext((*pWindow)->hwnd, &((*pWindow)->hrc)))
+ {
+ GLS_CloseGLWindow(*pWindow);
+ return GLS_OPENGL_CONTEXT_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_CloseGLWindow(GLS_Window * window)
+{
+ if (window)
+ {
+ if (window->hrc)
+ {
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(window->hrc);
+ }
+
+ if (window->fullscreen) ChangeDisplaySettings(NULL, 0);
+ if (window->ownHwnd)
+ {
+ if (window->hwnd) DestroyWindow(window->hwnd);
+ UnregisterClass(WINDOW_CLASSNAME, GetModuleHandle(NULL));
+ }
+
+ free(window);
+ }
+
+ return GLS_True;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_ProcessWindowMessages(GLS_Window * window)
+{
+ MSG msg;
+ GLS_Result Result = GLS_OK;
+
+ if (window->ownHwnd)
+ {
+ while (PeekMessage(&msg, window->hwnd, 0, 0, PM_REMOVE))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ Result = GLS_WINDOW_WAS_CLOSED;
+ }
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ return Result;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_WindowFlip(GLS_Window * window)
+{
+ HDC hdc;
+
+ hdc = GetDC(window->hwnd);
+ if (!hdc) return GLS_False;
+
+ if (SwapBuffers(hdc))
+ {
+ ReleaseDC(window->hwnd, hdc);
+ return GLS_True;
+ }
+ else
+ {
+ ReleaseDC(window->hwnd, hdc);
+ return GLS_False;
+ }
+}
diff --git a/engines/sword25/util/glsprites/internal/glswindow.h b/engines/sword25/util/glsprites/internal/glswindow.h
new file mode 100755
index 0000000000..1311513e45
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/glswindow.h
@@ -0,0 +1,42 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_WINDOW_H
+#define GLS_WINDOW_H
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "../glsprites.h"
+
+typedef struct
+{
+ HWND hwnd;
+ HGLRC hrc;
+ GLS_Bool fullscreen;
+ GLS_Bool ownHwnd;
+} GLS_Window;
+
+GLS_Result GLS_CreateGLWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, const char * windowTitle, GLS_Bool allowCloseWindow, GLS_Window ** pWindow);
+GLS_Result GLS_CreateGLExternalWindow(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool fullscreen, void * windowHandle, GLS_Window ** pWindow);
+GLS_Result GLS_CloseGLWindow(GLS_Window * window);
+GLS_Result GLS_ProcessWindowMessages(GLS_Window * window);
+GLS_Result GLS_WindowFlip(GLS_Window * window);
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/sprite.c b/engines/sword25/util/glsprites/internal/sprite.c
new file mode 100755
index 0000000000..261ce207e6
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite.c
@@ -0,0 +1,156 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+
+#include <malloc.h>
+
+#include "glinclude.h"
+#include "util.h"
+#include "core.h"
+#include "sprite.h"
+#include "sprite_rectangle.h"
+#include "sprite_pow2.h"
+#include "sprite_tiled.h"
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ /* These first three members are present at the start of each sprite type.
+ Therefore it is safe to cast all sprite to this. */
+ GLS_spriteFunctionTable * functionTable;
+ GLS_UInt32 width;
+ GLS_UInt32 height;
+} spriteBase;
+
+/* -------------------------------------------------------------------------- */
+
+#define MAX_WASTED_TEXTURE_MEMORY_PERCENT 30
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_NewSprite(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite)
+{
+ //* The sprite needs to be at least 1x1 pixel in size */
+ if (width == 0 || height == 0) return GLS_INVALID_SPRITE_DIMENSIONS;
+
+ /* If both dimensions are powers of 2 a power of 2 texture will be used for the sprite */
+ if (GLS_IsPowerOf2(width) && GLS_IsPowerOf2(height)) return GLS_NewSpritePow2(width, height, useAlphachannel, data, pSprite);
+
+ /* Use TEXTURE_RECTANGLE_ARB for non power of 2 dimensions whenever possible */
+ if (GLS_TheOGLCaps.textureRectanglesSupported &&
+ GLS_TheOGLCaps.maxTextureRectangleSize >= width &&
+ GLS_TheOGLCaps.maxTextureRectangleSize >= height)
+ return GLS_NewSpriteRectangle(width, height, useAlphachannel, data, pSprite);
+
+ /* Use a power of 2 texture if TEXTURE_RECTANGLE_ARB is not possible, the image is small enough and the amount of wasted texture memory
+ is not too big */
+ if ((width <= GLS_TheOGLCaps.maxTextureSize && height <= GLS_TheOGLCaps.maxTextureSize) &&
+ (GLS_NextPowerOf2(width) * GLS_NextPowerOf2(height) * 100) / (width * height) <= MAX_WASTED_TEXTURE_MEMORY_PERCENT)
+ return GLS_NewSpritePow2(width, height, useAlphachannel, data, pSprite);
+
+ /* Otherwise use tiled power of 2 textures */
+ return GLS_NewSpriteTiled(width, height, useAlphachannel, data, pSprite);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_DeleteSprite(GLS_Sprite * sprite)
+{
+ /* Poor mans polymorphism. */
+ spriteBase * s = (spriteBase *) sprite;
+ return s->functionTable->Delete(sprite);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_Blit(GLS_Sprite * sprite,
+ GLS_SInt32 x, GLS_SInt32 y,
+ const GLS_Rect * subImage,
+ const GLS_Color * color,
+ GLS_Bool flipH, GLS_Bool flipV,
+ GLS_Float scaleX, GLS_Float scaleY)
+{
+ spriteBase * s = (spriteBase *) sprite;
+ GLS_Rect localSubImage;
+ const GLS_Rect * subImagePtr;
+ GLS_Result result;
+
+ /* Check parameters for validity. */
+ if (subImage && (subImage->x2 > s->width || subImage->y2 > s->height)) return GLS_INVALID_SUB_IMAGE;
+
+ /* If no subimage was supplied, create one that covers the whole sprite. */
+ if (subImage)
+ subImagePtr = subImage;
+ else
+ {
+ localSubImage.x1 = 0;
+ localSubImage.y1 = 0;
+ localSubImage.x2 = s->width;
+ localSubImage.y2 = s->height;
+ subImagePtr = &localSubImage;
+ }
+
+ /* Modify the Modelview-Matrix to reflect the sprites position, flip settings, and scale. */
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glTranslatef((GLfloat) x, (GLfloat) y, 0);
+ if (flipH == GLS_True || flipV == GLS_True || scaleX != 1.0f || scaleY != 1.0f)
+ {
+ glScalef(scaleX * (flipH == GLS_True ? -1.0f : 1.0f), scaleY * (flipV == GLS_True ? -1.0f : 1.0f), 1.0f);
+ glTranslatef(flipH == GLS_True ? (subImagePtr->x2 - subImagePtr->x1) * -1.0f : 0.0f,
+ flipV == GLS_True ? (subImagePtr->y2 - subImagePtr->y1) * -1.0f : 0.0f, 0.0f);
+ }
+
+ /* Set the color to be used. Use white if no color was provided. */
+ if (color)
+ glColor4ub(color->r, color->g, color->b, color->a);
+ else
+ glColor4ub(255, 255, 255, 255);
+
+ /* Display the sprite by calling the Blit() function of the specific sprite type. */
+ result = s->functionTable->Blit(sprite, subImagePtr);
+
+ /* Restore the old matrix. */
+ glPopMatrix();
+
+ return result;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_SetSpriteData(GLS_Sprite sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride)
+{
+ spriteBase * s = (spriteBase *) sprite;
+
+ /* Check parameters for validity. */
+ if (width == 0 || height == 0 || width > s->width || height > s->height) return GLS_INVALID_DATA_DIMENSIONS;
+ if (data == 0) return GLS_INVALID_DATA_POINTER;
+
+ /* Poor mans polymorphism. */
+ return s->functionTable->SetData(sprite, width, height, data, stride);
+}
diff --git a/engines/sword25/util/glsprites/internal/sprite.h b/engines/sword25/util/glsprites/internal/sprite.h
new file mode 100755
index 0000000000..8cb7fd41e7
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite.h
@@ -0,0 +1,40 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_SPRITE_H
+#define GLS_SPRITE_H
+
+#include "../glsprites.h"
+
+typedef enum
+{
+ ST_POW2,
+ ST_TILED,
+ ST_RECTANGLE,
+} GLS_spriteType;
+
+typedef struct
+{
+ GLS_spriteType type;
+ GLS_Result (*Delete)(GLS_Sprite *);
+ GLS_Result (*Blit)(GLS_Sprite *, const GLS_Rect *);
+ GLS_Result (*SetData)(GLS_Sprite *, GLS_UInt32, GLS_UInt32, const void *, GLS_UInt32);
+} GLS_spriteFunctionTable;
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/sprite_pow2.c b/engines/sword25/util/glsprites/internal/sprite_pow2.c
new file mode 100755
index 0000000000..8b4ebe18fe
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_pow2.c
@@ -0,0 +1,170 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include <malloc.h>
+
+#include "glinclude.h"
+#include "util.h"
+#include "sprite.h"
+#include "sprite_pow2.h"
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite *);
+static GLS_Result Blit(GLS_Sprite *, const GLS_Rect *);
+static GLS_Result SetData(GLS_Sprite *, GLS_UInt32, GLS_UInt32, const void *, GLS_UInt32);
+static GLS_spriteFunctionTable pow2FunctionTable = { ST_POW2, Delete, Blit, SetData };
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ /* These first three members have to be present at the start of each sprite type. */
+ GLS_spriteFunctionTable * functionTable;
+ GLS_UInt32 width;
+ GLS_UInt32 height;
+
+ GLS_UInt32 widthPow2;
+ GLS_UInt32 heightPow2;
+ GLint textureID;
+} spritePow2;
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_NewSpritePow2(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite)
+{
+ GLS_UInt32 widthPow2 = GLS_NextPowerOf2(width);
+ GLS_UInt32 heightPow2 = GLS_NextPowerOf2(height);
+
+ /* Allocate memory for sprite object */
+ spritePow2 * sprite = (spritePow2 *) malloc(sizeof(spritePow2));
+ if (!sprite) return GLS_OUT_OF_MEMORY;
+
+ /* Initialize sprite object */
+ sprite->functionTable = &pow2FunctionTable;
+ sprite->textureID = 0;
+ sprite->width = width;
+ sprite->height = height;
+ sprite->widthPow2 = widthPow2;
+ sprite->heightPow2 = heightPow2;
+
+ /* Create OpenGL texture */
+ glGenTextures(1, &(sprite->textureID));
+ glBindTexture(GL_TEXTURE_2D, sprite->textureID);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, useAlphachannel ? GL_RGBA8 : GL_RGB8, widthPow2, heightPow2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ /* Fill the texture with the supplied pixel data.
+ We don't use glTexImage2D for this, because the created sprite might not have power of 2 dimensions and therefore the
+ supplied pixeldata wouldn't cover the whole texture. */
+ if (data) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ *pSprite = (GLS_Sprite) sprite;
+
+ if (glGetError())
+ {
+ Delete(*pSprite);
+ return GLS_TEXTURE_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite * sprite)
+{
+ spritePow2 * s = (spritePow2 *) sprite;
+
+ if (s->textureID) glDeleteTextures(1, &s->textureID);
+ free(s);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Blit(GLS_Sprite * sprite,
+ const GLS_Rect * subImage)
+{
+ GLfloat width, height;
+ GLfloat texX1, texY1, texX2, texY2;
+
+ spritePow2 * s = (spritePow2 *) sprite;
+
+ width = (float) (subImage->x2 - subImage->x1);
+ height = (float) (subImage->y2 - subImage->y1);
+
+ texX1 = (GLfloat) subImage->x1 / (GLfloat) s->widthPow2;
+ texY1 = (GLfloat) subImage->y1 / (GLfloat) s->heightPow2;
+ texX2 = (GLfloat) subImage->x2 / (GLfloat) s->widthPow2;
+ texY2 = (GLfloat) subImage->y2 / (GLfloat) s->heightPow2;
+
+ glBindTexture(GL_TEXTURE_2D, s->textureID);
+
+ glEnable(GL_TEXTURE_2D);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(texX1, texY1);
+ glVertex2f(0.0f, 0.0f);
+
+ glTexCoord2f(texX2, texY1);
+ glVertex2f(width, 0.0f);
+
+ glTexCoord2f(texX2, texY2);
+ glVertex2f(width, height);
+
+ glTexCoord2f(texX1, texY2);
+ glVertex2f(0.0f, height);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result SetData(GLS_Sprite * sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride)
+{
+ spritePow2 * s = (spritePow2 *) sprite;
+
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width + stride);
+
+ glBindTexture(GL_TEXTURE_2D, s->textureID);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ glPopClientAttrib();
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
diff --git a/engines/sword25/util/glsprites/internal/sprite_pow2.h b/engines/sword25/util/glsprites/internal/sprite_pow2.h
new file mode 100755
index 0000000000..bbce39c4dc
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_pow2.h
@@ -0,0 +1,27 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_SPRITE_POW2_H
+#define GLS_SPRITE_POW2_H
+
+#include "../glsprites.h"
+
+GLS_Result GLS_NewSpritePow2(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite);
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/sprite_rectangle.c b/engines/sword25/util/glsprites/internal/sprite_rectangle.c
new file mode 100755
index 0000000000..546634eaac
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_rectangle.c
@@ -0,0 +1,164 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include <malloc.h>
+
+#include "glinclude.h"
+#include "core.h"
+#include "sprite.h"
+#include "sprite_rectangle.h"
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite *);
+static GLS_Result Blit(GLS_Sprite *, const GLS_Rect *);
+static GLS_Result SetData(GLS_Sprite *, GLS_UInt32, GLS_UInt32, const void *, GLS_UInt32);
+static GLS_spriteFunctionTable rectangleFunctionTable = { ST_RECTANGLE, Delete, Blit, SetData };
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ /* These first three members have to be present at the start of each sprite type. */
+ GLS_spriteFunctionTable * functionTable;
+ GLS_UInt32 width;
+ GLS_UInt32 height;
+
+ GLint textureID;
+} spriteRectangle;
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_NewSpriteRectangle(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite)
+{
+ /* Allocate memory for sprite object */
+ spriteRectangle * sprite = (spriteRectangle *) malloc(sizeof(spriteRectangle));
+ if (!sprite) return GLS_OUT_OF_MEMORY;
+
+ /* Initialize sprite object */
+ sprite->functionTable = &rectangleFunctionTable;
+ sprite->textureID = 0;
+ sprite->width = width;
+ sprite->height = height;
+
+ /* Create OpenGL texture */
+ glGenTextures(1, &(sprite->textureID));
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, sprite->textureID);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, useAlphachannel ? GL_RGBA8 : GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ *pSprite = (GLS_Sprite) sprite;
+
+ /* The unbind here is not really necessary but some GL drivers (e.g. GeForce 4 488 Go) screw up subsequent GL_TEXTURE_2D
+ render operations if a GL_TEXTURE_RECTANGLE_ARB texture is still bound. */
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+
+ if (glGetError())
+ {
+ Delete(*pSprite);
+ return GLS_TEXTURE_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite * sprite)
+{
+ spriteRectangle * s = (spriteRectangle *) sprite;
+
+ if (s->textureID) glDeleteTextures(1, &s->textureID);
+ free(s);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Blit(GLS_Sprite * sprite,
+ const GLS_Rect * subImage)
+{
+ GLfloat width;
+ GLfloat height;
+
+ spriteRectangle * s = (spriteRectangle *) sprite;
+
+ width = (float)(subImage->x2 - subImage->x1);
+ height = (float)(subImage->y2 - subImage->y1);
+
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s->textureID);
+
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+
+ glBegin(GL_QUADS);
+ glTexCoord2i(subImage->x1, subImage->y1);
+ glVertex2f(0.0f, 0.0f);
+
+ glTexCoord2i(subImage->x2, subImage->y1);
+ glVertex2f(width, 0.0f);
+
+ glTexCoord2i(subImage->x2, subImage->y2);
+ glVertex2f(width, height);
+
+ glTexCoord2i(subImage->x1, subImage->y2);
+ glVertex2f(0.0f, height);
+ glEnd();
+
+ /* The unbind here is not really necessary but some GL drivers (e.g. GeForce 4 488 Go) screw up subsequent GL_TEXTURE_2D
+ render operations if a GL_TEXTURE_RECTANGLE_ARB texture is still bound. */
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+
+ glDisable(GL_TEXTURE_RECTANGLE_ARB);
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result SetData(GLS_Sprite * sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride)
+{
+ spriteRectangle * s = (spriteRectangle *) sprite;
+
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width + stride);
+
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, s->textureID);
+ glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ /* The unbind here is not really necessary but some GL drivers (e.g. GeForce 4 488 Go) screw up subsequent GL_TEXTURE_2D
+ render operations if a GL_TEXTURE_RECTANGLE_ARB texture is still bound. */
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
+
+ glPopClientAttrib();
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
diff --git a/engines/sword25/util/glsprites/internal/sprite_rectangle.h b/engines/sword25/util/glsprites/internal/sprite_rectangle.h
new file mode 100755
index 0000000000..65bdf6a3ef
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_rectangle.h
@@ -0,0 +1,27 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_SPRITE_RECTANGLE_H
+#define GLS_SPRITE_RECTANGLE_H
+
+#include "../glsprites.h"
+
+GLS_Result GLS_NewSpriteRectangle(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite);
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/sprite_tiled.c b/engines/sword25/util/glsprites/internal/sprite_tiled.c
new file mode 100755
index 0000000000..82435a638e
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_tiled.c
@@ -0,0 +1,370 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include <malloc.h>
+
+#include "glinclude.h"
+#include "core.h"
+#include "util.h"
+#include "sprite.h"
+#include "sprite_tiled.h"
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite *);
+static GLS_Result Blit(GLS_Sprite *, const GLS_Rect *);
+static GLS_Result SetData(GLS_Sprite *, GLS_UInt32, GLS_UInt32, const void *, GLS_UInt32);
+static GLS_spriteFunctionTable tiledFunctionTable = { ST_TILED, Delete, Blit, SetData };
+
+/* -------------------------------------------------------------------------- */
+
+#define MIN_TEXTURE_SIZE_LOG2 6
+#define MIN_TEXTURE_SIZE (1 << MIN_TEXTURE_SIZE_LOG2)
+
+#define MAX_POSSIBLE_TEXTURE_SIZE_LOG2 31
+#define MAX_POSSIBLE_TEXTURE_SIZE (1 << MAX_POSSIBLE_TEXTURE_SIZE_LOG2)
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GLint textureID;
+ GLfloat x;
+ GLfloat y;
+ GLfloat width;
+ GLfloat height;
+} tile;
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct
+{
+ /* These first three members have to be present at the start of each sprite type. */
+ GLS_spriteFunctionTable * functionTable;
+ GLS_UInt32 width;
+ GLS_UInt32 height;
+
+ GLS_UInt32 widthSubdivisionCount;
+ GLS_UInt32 heightSubdivisionCount;
+ tile tiles[1]; /* Has to be last member. */
+} spriteTiled;
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_UInt32 CalculateSubdivisions(GLS_UInt32 value, GLS_UInt32 maxTextureSize, GLS_UInt32 maxTextureSizeLog2,
+ GLS_UInt32 subdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1])
+{
+ /* This function takes a value and determines how it best can be divided up into pieces of power of 2 length.
+ The resulting parts are at least MIN_TEXTURE_SIZE long and at most maxTextureSize long.
+ If we do this for both width and height of a sprite, we can determine how this sprite can be split up into
+ smaller power of 2 textures using a simple two dimensional loop.
+
+ This returns the number of subdivisions as the return value.
+ The sizes of the different pieces are encoded in the subdivisions array. The indicies into the array determine the
+ power of 2 and the values in the array the number of pieces with that size.
+ E.g. if the array contains the value 3 at index 4 this means you have 3 pieces of size 16 (2^4 = 16).
+ */
+
+ GLS_UInt32 subdivisionCount = 0;
+ GLS_UInt32 maxSizeCount;
+ GLS_UInt32 counter = 0;
+
+ /* First determine how many pieces of maximal size we can fill and write this into the result array.
+ Update the value and the subdivision count accordingly. */
+ maxSizeCount = value / maxTextureSize;
+ subdivisions[maxTextureSizeLog2] = maxSizeCount;
+ subdivisionCount += maxSizeCount;
+ value -= maxSizeCount * maxTextureSize;
+
+ /* The remaining value is rounded up to a multiple of the minimal texture size. */
+ value = ((value + MIN_TEXTURE_SIZE - 1) / MIN_TEXTURE_SIZE) * MIN_TEXTURE_SIZE;
+
+ /* Determine the remaining pieces by using the binary representation of the value.
+ If a bit is set, we need to add a piece of the according size. */
+ while (value)
+ {
+ if (value & 1)
+ {
+ ++subdivisions[counter];
+ ++subdivisionCount;
+ }
+
+ ++counter;
+ value >>= 1;
+ }
+
+ return subdivisionCount;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Result GLS_NewSpriteTiled(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite)
+{
+ GLS_UInt32 maxTextureSize, maxTextureSizeLog2;
+ GLS_UInt32 widthSubdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1] = { 0 };
+ GLS_UInt32 widthSubdivisionCount;
+ GLS_UInt32 heightSubdivisions[MAX_POSSIBLE_TEXTURE_SIZE_LOG2 + 1] = { 0 };
+ GLS_UInt32 heightSubdivisionCount;
+ GLS_UInt32 subdivisonX, subdivisionY, pixelX = 0, pixelY = 0, i;
+ spriteTiled * sprite;
+ tile * curTile;
+
+ /* Determine the maximal texture size to be used and make sure it is a power of two. */
+ maxTextureSize = GLS_TheOGLCaps.maxTextureSize > MAX_POSSIBLE_TEXTURE_SIZE ? MAX_POSSIBLE_TEXTURE_SIZE : GLS_TheOGLCaps.maxTextureSize;
+ maxTextureSize = GLS_IsPowerOf2(maxTextureSize) ? maxTextureSize : GLS_NextPowerOf2(maxTextureSize >> 1);
+
+ /* Determine the log2 of the maximal texture size. */
+ maxTextureSizeLog2 = GLS_Log2(maxTextureSize);
+
+ /* Determine the subdivisions along the width and the height. */
+ widthSubdivisionCount = CalculateSubdivisions(width, maxTextureSize, maxTextureSizeLog2, widthSubdivisions);
+ heightSubdivisionCount = CalculateSubdivisions(height, maxTextureSize, maxTextureSizeLog2, heightSubdivisions);
+
+ /* Allocate memory for sprite object */
+ sprite = (spriteTiled *) calloc(sizeof(spriteTiled) + sizeof(tile) * (widthSubdivisionCount * heightSubdivisionCount - 1), 1);
+ if (!sprite) return GLS_OUT_OF_MEMORY;
+
+ /* Initialize sprite object. */
+ sprite->functionTable = &tiledFunctionTable;
+ sprite->width = width;
+ sprite->height = height;
+ sprite->widthSubdivisionCount = widthSubdivisionCount;
+ sprite->heightSubdivisionCount = heightSubdivisionCount;
+
+ /* Create the textures. */
+ curTile = &sprite->tiles[0];
+ for (subdivisionY = MAX_POSSIBLE_TEXTURE_SIZE_LOG2;; --subdivisionY)
+ {
+ while (heightSubdivisions[subdivisionY]--)
+ {
+ for (subdivisonX = MAX_POSSIBLE_TEXTURE_SIZE_LOG2;; --subdivisonX)
+ {
+ for (i = 0; i < widthSubdivisions[subdivisonX]; ++i)
+ {
+ /* Initialize tile. Store as GLfloat to avoid unnecessary integer conversions when drawing. */
+ curTile->x = (GLfloat) pixelX;
+ curTile->y = (GLfloat) pixelY;
+ curTile->width = (GLfloat) (1 << subdivisonX);
+ curTile->height = (GLfloat) (1 << subdivisionY);
+
+ /* Create OpenGL texture */
+ glGenTextures(1, &(curTile->textureID));
+ glBindTexture(GL_TEXTURE_2D, curTile->textureID);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, useAlphachannel ? GL_RGBA8 : GL_RGB8, 1 << subdivisonX, 1 << subdivisionY, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+ /* Advance to the next empty tile. */
+ pixelX += 1 << subdivisonX;
+ ++curTile;
+ }
+
+ /* Test for loop end. Prevents underflow of unsigned integer. */
+ if (subdivisonX == 0) break;
+ }
+
+ /* Enter next row. */
+ pixelX = 0;
+ pixelY += 1 << subdivisionY;
+ }
+
+ /* Test for loop end. Prevents underflow of unsigned integer. */
+ if (subdivisionY == 0) break;
+ }
+
+ *pSprite = (GLS_Sprite) sprite;
+
+ /* Intialize the sprite with the supplied pixel data (if any). */
+ if (data) SetData(*pSprite, width, height, data, 0);
+
+ /* If an error occured the sprite is deleted and the function returns with an error. */
+ if (glGetError())
+ {
+ Delete(*pSprite);
+ return GLS_TEXTURE_CREATION_FAILED;
+ }
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Delete(GLS_Sprite * sprite)
+{
+ spriteTiled * s = (spriteTiled *) sprite;
+ GLS_UInt32 tileCount = s->widthSubdivisionCount * s->heightSubdivisionCount;
+ GLS_UInt32 i;
+
+ /* Delete each texture. */
+ for (i = 0; i < tileCount; ++i)
+ {
+ if (s->tiles[i].textureID) glDeleteTextures(1, &s->tiles[i].textureID);
+ }
+
+ /* Free the memory used by the sprite. */
+ free(s);
+
+ return GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result Blit(GLS_Sprite * sprite,
+ const GLS_Rect * subImage)
+{
+ GLfloat width, height;
+ GLfloat texX1, texY1, texX2, texY2;
+ GLS_UInt32 tileIndex = 0;
+ GLS_UInt32 tileX, tileY;
+ GLfloat tileRectX1, tileRectY1;
+ GLfloat tileRectX2, tileRectY2;
+ GLfloat tileWidth, tileHeight;
+ GLfloat lastHeight = 0;
+ GLS_GLfloatRect tileRect, resultRect;
+ GLfloat x = 0.0f, y = 0.0f;
+ GLS_GLfloatRect subImageF = { (GLfloat) subImage->x1, (GLfloat) subImage->y1, (GLfloat) subImage->x2, (GLfloat) subImage->y2 };
+
+ spriteTiled * s = (spriteTiled *) sprite;
+
+ /* Determine width and height of the subimage to display. */
+ width = (float)(subImage->x2 - subImage->x1);
+ height = (float)(subImage->y2 - subImage->y1);
+
+ glEnable(GL_TEXTURE_2D);
+
+ /* Draw all required tiles. */
+ for (tileY = 0; tileY < s->heightSubdivisionCount; ++tileY)
+ {
+ for (tileX = 0; tileX < s->widthSubdivisionCount; ++tileX)
+ {
+ tileRect.x1 = s->tiles[tileIndex].x;
+ tileRect.y1 = s->tiles[tileIndex].y;
+ tileRect.x2 = tileRect.x1 + s->tiles[tileIndex].width;
+ tileRect.y2 = tileRect.y1 + s->tiles[tileIndex].height;
+
+ if (GLS_IntersectGLfloatRects(&tileRect, &subImageF, &resultRect))
+ {
+ tileRectX1 = resultRect.x1;
+ tileRectY1 = resultRect.y1;
+ tileRectX2 = resultRect.x2;
+ tileRectY2 = resultRect.y2;
+
+ glBindTexture(GL_TEXTURE_2D, s->tiles[tileIndex].textureID);
+
+ texX1 = (tileRectX1 - s->tiles[tileIndex].x) / (GLfloat) s->tiles[tileIndex].width;
+ texY1 = (tileRectY1 - s->tiles[tileIndex].y) / (GLfloat) s->tiles[tileIndex].height;
+ texX2 = (GLfloat) (tileRectX2 - s->tiles[tileIndex].x) / (GLfloat) s->tiles[tileIndex].width;
+ texY2 = (GLfloat) (tileRectY2 - s->tiles[tileIndex].y) / (GLfloat) s->tiles[tileIndex].height;
+
+ tileWidth = resultRect.x2 - resultRect.x1;
+ tileHeight = resultRect.y2 - resultRect.y1;
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(texX1, texY1);
+ glVertex2f(x, y);
+
+ glTexCoord2f(texX2, texY1);
+ glVertex2f(x + tileWidth, y);
+
+ glTexCoord2f(texX2, texY2);
+ glVertex2f(x + tileWidth, y + tileHeight);
+
+ glTexCoord2f(texX1, texY2);
+ glVertex2f(x, y + tileHeight);
+ glEnd();
+
+ x += tileWidth;
+ lastHeight = tileHeight;
+ }
+
+ ++tileIndex;
+ }
+
+ x = 0;
+ y += lastHeight;
+ }
+
+ glDisable(GL_TEXTURE_2D);
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+static GLS_Result SetData(GLS_Sprite * sprite, GLS_UInt32 width, GLS_UInt32 height, const void * data, GLS_UInt32 stride)
+{
+ spriteTiled * s = (spriteTiled *) sprite;
+ GLS_UInt32 x = 0, y = 0, tileIndex = 0;
+ GLS_UInt32 tileX, tileY;
+ GLS_UInt32 copyWidth, copyHeight;
+ const GLS_UInt32 * pixelData = (const GLS_UInt32 *) data;
+
+ /* Set the source image row length and push the original value that it can be restored later. */
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, width + stride);
+
+ /* Loop through all tiles width first. */
+ for (tileY = 0; tileY < s->heightSubdivisionCount; ++tileY)
+ {
+ /* Is there enough pixel data to reach the next tile? */
+ if (height > y)
+ {
+ for (tileX = 0; tileX < s->widthSubdivisionCount; ++tileX)
+ {
+ /* Is the enough pixel data to reach the next tile? */
+ if (width > x)
+ {
+ /* Determine the amount of pixels to be copied to the current tile.
+ They might either cover the whole tile, or just part of the tile if not enough
+ pixel data is left. */
+ copyWidth = GLS_MIN(width - x, (GLS_UInt32) s->tiles[tileIndex].width);
+ copyHeight = GLS_MIN(height - y, (GLS_UInt32) s->tiles[tileIndex].height);
+
+ /* Copy the pixels. */
+ glBindTexture(GL_TEXTURE_2D, s->tiles[tileIndex].textureID);
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, copyWidth, copyHeight, GL_RGBA, GL_UNSIGNED_BYTE,
+ &pixelData[x + y * (width + stride)]);
+ }
+
+ /* Update the x position inside the pixel data and advance to the next tile. */
+ x += (GLS_UInt32) s->tiles[tileIndex].width;
+ ++tileIndex;
+ }
+ }
+
+ /* Restet the x position inside the pixel data (we have reached another row) and update the y position. */
+ x = 0;
+ y += (GLS_UInt32) s->tiles[tileIndex - 1].height;
+ }
+
+ glPopClientAttrib();
+
+ return glGetError() ? GLS_OPENGL_ERROR : GLS_OK;
+}
diff --git a/engines/sword25/util/glsprites/internal/sprite_tiled.h b/engines/sword25/util/glsprites/internal/sprite_tiled.h
new file mode 100755
index 0000000000..8fb0d83ba7
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/sprite_tiled.h
@@ -0,0 +1,41 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_SPRITE_TILED_H
+#define GLS_SPRITE_TILED_H
+
+#include "../glsprites.h"
+
+GLS_Result GLS_NewSpriteTiled(GLS_UInt32 width, GLS_UInt32 height, GLS_Bool useAlphachannel, void * data, GLS_Sprite ** pSprite);
+GLS_Result GLS_DeleteSpriteTiled(GLS_Sprite * sprite);
+
+GLS_Result GLS_BlitTiled(GLS_Sprite * sprite,
+ GLS_SInt32 x, GLS_SInt32 y,
+ const GLS_Rect * subImage,
+ const GLS_Color * color,
+ GLS_Bool flipH, GLS_Bool flipV,
+ GLS_Float scaleX, GLS_Float scaleY);
+
+GLS_Result GLS_SetSpriteDataTiled(GLS_Sprite * sprite,
+ GLS_UInt32 width, GLS_UInt32 height,
+ GLS_Bool useAlphachannel,
+ const void * data,
+ GLS_UInt32 stride);
+
+#endif
diff --git a/engines/sword25/util/glsprites/internal/util.c b/engines/sword25/util/glsprites/internal/util.c
new file mode 100755
index 0000000000..590f7e3459
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/util.c
@@ -0,0 +1,77 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+/* --------------------------------------------------------------------------
+ INCLUDES
+ -------------------------------------------------------------------------- */
+
+#include "util.h"
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Bool GLS_IsPowerOf2(GLS_UInt32 value)
+{
+ return !(value & (value - 1)) && value;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+GLS_UInt32 GLS_NextPowerOf2(GLS_UInt32 value)
+{
+ --value;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ ++value;
+
+ return value;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_UInt32 GLS_Log2(GLS_UInt32 value)
+{
+ GLS_UInt32 result = 0;
+ while (value >>= 1) ++result;
+ return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+GLS_Bool GLS_IntersectGLfloatRects(const GLS_GLfloatRect * r1, const GLS_GLfloatRect * r2, GLS_GLfloatRect * result)
+{
+ GLS_Bool rectsIntersect = (r1->x1 >= r2->x2 ||
+ r1->x2 <= r2->x1 ||
+ r1->y1 >= r2->y2 ||
+ r1->y2 <= r2->y1) ? GLS_False : GLS_True;
+
+ if(rectsIntersect)
+ {
+ result->x1 = GLS_MAX(r1->x1, r2->x1);
+ result->y1 = GLS_MAX(r1->y1, r2->y1);
+ result->x2 = GLS_MIN(r1->x2, r2->x2);
+ result->y2 = GLS_MIN(r1->y2, r2->y2);
+ }
+
+ return rectsIntersect;
+}
diff --git a/engines/sword25/util/glsprites/internal/util.h b/engines/sword25/util/glsprites/internal/util.h
new file mode 100755
index 0000000000..cdd69e2953
--- /dev/null
+++ b/engines/sword25/util/glsprites/internal/util.h
@@ -0,0 +1,42 @@
+/******************************************************************************/
+/* This file is part of Broken Sword 2.5 */
+/* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdörfer */
+/* */
+/* Broken Sword 2.5 is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Broken Sword 2.5 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 General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Broken Sword 2.5; if not, write to the Free Software */
+/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+/******************************************************************************/
+
+#ifndef GLS_UTIL_H
+#define GLS_UTIL_H
+
+#include "../glsprites.h"
+#include "glinclude.h"
+
+typedef struct
+{
+ GLfloat x1;
+ GLfloat y1;
+ GLfloat x2;
+ GLfloat y2;
+} GLS_GLfloatRect;
+
+GLS_Bool GLS_IsPowerOf2(GLS_UInt32 value);
+GLS_UInt32 GLS_NextPowerOf2(GLS_UInt32 value);
+GLS_UInt32 GLS_Log2(GLS_UInt32 value);
+GLS_Bool GLS_IntersectGLfloatRects(const GLS_GLfloatRect * r1, const GLS_GLfloatRect * r2, GLS_GLfloatRect * result);
+
+#define GLS_MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define GLS_MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+#endif
diff --git a/engines/sword25/util/lua/COPYRIGHT b/engines/sword25/util/lua/COPYRIGHT
new file mode 100755
index 0000000000..3a53e741e0
--- /dev/null
+++ b/engines/sword25/util/lua/COPYRIGHT
@@ -0,0 +1,34 @@
+Lua License
+-----------
+
+Lua is licensed under the terms of the MIT license reproduced below.
+This means that Lua is free software and can be used for both academic
+and commercial purposes at absolutely no cost.
+
+For details and rationale, see http://www.lua.org/license.html .
+
+===============================================================================
+
+Copyright (C) 1994-2008 Lua.org, PUC-Rio.
+
+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.
+
+===============================================================================
+
+(end of COPYRIGHT)
diff --git a/engines/sword25/util/lua/HISTORY b/engines/sword25/util/lua/HISTORY
new file mode 100755
index 0000000000..ce0c95bc69
--- /dev/null
+++ b/engines/sword25/util/lua/HISTORY
@@ -0,0 +1,183 @@
+HISTORY for Lua 5.1
+
+* Changes from version 5.0 to 5.1
+ -------------------------------
+ Language:
+ + new module system.
+ + new semantics for control variables of fors.
+ + new semantics for setn/getn.
+ + new syntax/semantics for varargs.
+ + new long strings and comments.
+ + new `mod' operator (`%')
+ + new length operator #t
+ + metatables for all types
+ API:
+ + new functions: lua_createtable, lua_get(set)field, lua_push(to)integer.
+ + user supplies memory allocator (lua_open becomes lua_newstate).
+ + luaopen_* functions must be called through Lua.
+ Implementation:
+ + new configuration scheme via luaconf.h.
+ + incremental garbage collection.
+ + better handling of end-of-line in the lexer.
+ + fully reentrant parser (new Lua function `load')
+ + better support for 64-bit machines.
+ + native loadlib support for Mac OS X.
+ + standard distribution in only one library (lualib.a merged into lua.a)
+
+* Changes from version 4.0 to 5.0
+ -------------------------------
+ Language:
+ + lexical scoping.
+ + Lua coroutines.
+ + standard libraries now packaged in tables.
+ + tags replaced by metatables and tag methods replaced by metamethods,
+ stored in metatables.
+ + proper tail calls.
+ + each function can have its own global table, which can be shared.
+ + new __newindex metamethod, called when we insert a new key into a table.
+ + new block comments: --[[ ... ]].
+ + new generic for.
+ + new weak tables.
+ + new boolean type.
+ + new syntax "local function".
+ + (f()) returns the first value returned by f.
+ + {f()} fills a table with all values returned by f.
+ + \n ignored in [[\n .
+ + fixed and-or priorities.
+ + more general syntax for function definition (e.g. function a.x.y:f()...end).
+ + more general syntax for function calls (e.g. (print or write)(9)).
+ + new functions (time/date, tmpfile, unpack, require, load*, etc.).
+ API:
+ + chunks are loaded by using lua_load; new luaL_loadfile and luaL_loadbuffer.
+ + introduced lightweight userdata, a simple "void*" without a metatable.
+ + new error handling protocol: the core no longer prints error messages;
+ all errors are reported to the caller on the stack.
+ + new lua_atpanic for host cleanup.
+ + new, signal-safe, hook scheme.
+ Implementation:
+ + new license: MIT.
+ + new, faster, register-based virtual machine.
+ + support for external multithreading and coroutines.
+ + new and consistent error message format.
+ + the core no longer needs "stdio.h" for anything (except for a single
+ use of sprintf to convert numbers to strings).
+ + lua.c now runs the environment variable LUA_INIT, if present. It can
+ be "@filename", to run a file, or the chunk itself.
+ + support for user extensions in lua.c.
+ sample implementation given for command line editing.
+ + new dynamic loading library, active by default on several platforms.
+ + safe garbage-collector metamethods.
+ + precompiled bytecodes checked for integrity (secure binary dostring).
+ + strings are fully aligned.
+ + position capture in string.find.
+ + read('*l') can read lines with embedded zeros.
+
+* Changes from version 3.2 to 4.0
+ -------------------------------
+ Language:
+ + new "break" and "for" statements (both numerical and for tables).
+ + uniform treatment of globals: globals are now stored in a Lua table.
+ + improved error messages.
+ + no more '$debug': full speed *and* full debug information.
+ + new read form: read(N) for next N bytes.
+ + general read patterns now deprecated.
+ (still available with -DCOMPAT_READPATTERNS.)
+ + all return values are passed as arguments for the last function
+ (old semantics still available with -DLUA_COMPAT_ARGRET)
+ + garbage collection tag methods for tables now deprecated.
+ + there is now only one tag method for order.
+ API:
+ + New API: fully re-entrant, simpler, and more efficient.
+ + New debug API.
+ Implementation:
+ + faster than ever: cleaner virtual machine and new hashing algorithm.
+ + non-recursive garbage-collector algorithm.
+ + reduced memory usage for programs with many strings.
+ + improved treatment for memory allocation errors.
+ + improved support for 16-bit machines (we hope).
+ + code now compiles unmodified as both ANSI C and C++.
+ + numbers in bases other than 10 are converted using strtoul.
+ + new -f option in Lua to support #! scripts.
+ + luac can now combine text and binaries.
+
+* Changes from version 3.1 to 3.2
+ -------------------------------
+ + redirected all output in Lua's core to _ERRORMESSAGE and _ALERT.
+ + increased limit on the number of constants and globals per function
+ (from 2^16 to 2^24).
+ + debugging info (lua_debug and hooks) moved into lua_state and new API
+ functions provided to get and set this info.
+ + new debug lib gives full debugging access within Lua.
+ + new table functions "foreachi", "sort", "tinsert", "tremove", "getn".
+ + new io functions "flush", "seek".
+
+* Changes from version 3.0 to 3.1
+ -------------------------------
+ + NEW FEATURE: anonymous functions with closures (via "upvalues").
+ + new syntax:
+ - local variables in chunks.
+ - better scope control with DO block END.
+ - constructors can now be also written: { record-part; list-part }.
+ - more general syntax for function calls and lvalues, e.g.:
+ f(x).y=1
+ o:f(x,y):g(z)
+ f"string" is sugar for f("string")
+ + strings may now contain arbitrary binary data (e.g., embedded zeros).
+ + major code re-organization and clean-up; reduced module interdependecies.
+ + no arbitrary limits on the total number of constants and globals.
+ + support for multiple global contexts.
+ + better syntax error messages.
+ + new traversal functions "foreach" and "foreachvar".
+ + the default for numbers is now double.
+ changing it to use floats or longs is easy.
+ + complete debug information stored in pre-compiled chunks.
+ + sample interpreter now prompts user when run interactively, and also
+ handles control-C interruptions gracefully.
+
+* Changes from version 2.5 to 3.0
+ -------------------------------
+ + NEW CONCEPT: "tag methods".
+ Tag methods replace fallbacks as the meta-mechanism for extending the
+ semantics of Lua. Whereas fallbacks had a global nature, tag methods
+ work on objects having the same tag (e.g., groups of tables).
+ Existing code that uses fallbacks should work without change.
+ + new, general syntax for constructors {[exp] = exp, ... }.
+ + support for handling variable number of arguments in functions (varargs).
+ + support for conditional compilation ($if ... $else ... $end).
+ + cleaner semantics in API simplifies host code.
+ + better support for writing libraries (auxlib.h).
+ + better type checking and error messages in the standard library.
+ + luac can now also undump.
+
+* Changes from version 2.4 to 2.5
+ -------------------------------
+ + io and string libraries are now based on pattern matching;
+ the old libraries are still available for compatibility
+ + dofile and dostring can now return values (via return statement)
+ + better support for 16- and 64-bit machines
+ + expanded documentation, with more examples
+
+* Changes from version 2.2 to 2.4
+ -------------------------------
+ + external compiler creates portable binary files that can be loaded faster
+ + interface for debugging and profiling
+ + new "getglobal" fallback
+ + new functions for handling references to Lua objects
+ + new functions in standard lib
+ + only one copy of each string is stored
+ + expanded documentation, with more examples
+
+* Changes from version 2.1 to 2.2
+ -------------------------------
+ + functions now may be declared with any "lvalue" as a name
+ + garbage collection of functions
+ + support for pipes
+
+* Changes from version 1.1 to 2.1
+ -------------------------------
+ + object-oriented support
+ + fallbacks
+ + simplified syntax for tables
+ + many internal improvements
+
+(end of HISTORY)
diff --git a/engines/sword25/util/lua/INSTALL b/engines/sword25/util/lua/INSTALL
new file mode 100755
index 0000000000..17eb8aee8c
--- /dev/null
+++ b/engines/sword25/util/lua/INSTALL
@@ -0,0 +1,99 @@
+INSTALL for Lua 5.1
+
+* Building Lua
+ ------------
+ Lua is built in the src directory, but the build process can be
+ controlled from the top-level Makefile.
+
+ Building Lua on Unix systems should be very easy. First do "make" and
+ see if your platform is listed. If so, just do "make xxx", where xxx
+ is your platform name. The platforms currently supported are:
+ aix ansi bsd freebsd generic linux macosx mingw posix solaris
+
+ If your platform is not listed, try the closest one or posix, generic,
+ ansi, in this order.
+
+ See below for customization instructions and for instructions on how
+ to build with other Windows compilers.
+
+ If you want to check that Lua has been built correctly, do "make test"
+ after building Lua. Also, have a look at the example programs in test.
+
+* Installing Lua
+ --------------
+ Once you have built Lua, you may want to install it in an official
+ place in your system. In this case, do "make install". The official
+ place and the way to install files are defined in Makefile. You must
+ have the right permissions to install files.
+
+ If you want to build and install Lua in one step, do "make xxx install",
+ where xxx is your platform name.
+
+ If you want to install Lua locally, then do "make local". This will
+ create directories bin, include, lib, man, and install Lua there as
+ follows:
+
+ bin: lua luac
+ include: lua.h luaconf.h lualib.h lauxlib.h lua.hpp
+ lib: liblua.a
+ man/man1: lua.1 luac.1
+
+ These are the only directories you need for development.
+
+ There are man pages for lua and luac, in both nroff and html, and a
+ reference manual in html in doc, some sample code in test, and some
+ useful stuff in etc. You don't need these directories for development.
+
+ If you want to install Lua locally, but in some other directory, do
+ "make install INSTALL_TOP=xxx", where xxx is your chosen directory.
+
+ See below for instructions for Windows and other systems.
+
+* Customization
+ -------------
+ Three things can be customized by editing a file:
+ - Where and how to install Lua -- edit Makefile.
+ - How to build Lua -- edit src/Makefile.
+ - Lua features -- edit src/luaconf.h.
+
+ You don't actually need to edit the Makefiles because you may set the
+ relevant variables when invoking make.
+
+ On the other hand, if you need to select some Lua features, you'll need
+ to edit src/luaconf.h. The edited file will be the one installed, and
+ it will be used by any Lua clients that you build, to ensure consistency.
+
+ We strongly recommend that you enable dynamic loading. This is done
+ automatically for all platforms listed above that have this feature
+ (and also Windows). See src/luaconf.h and also src/Makefile.
+
+* Building Lua on Windows and other systems
+ -----------------------------------------
+ If you're not using the usual Unix tools, then the instructions for
+ building Lua depend on the compiler you use. You'll need to create
+ projects (or whatever your compiler uses) for building the library,
+ the interpreter, and the compiler, as follows:
+
+ library: lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c
+ lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c
+ ltable.c ltm.c lundump.c lvm.c lzio.c
+ lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c
+ ltablib.c lstrlib.c loadlib.c linit.c
+
+ interpreter: library, lua.c
+
+ compiler: library, luac.c print.c
+
+ If you use Visual Studio .NET, you can use etc/luavs.bat in its
+ "Command Prompt".
+
+ If all you want is to build the Lua interpreter, you may put all .c files
+ in a single project, except for luac.c and print.c. Or just use etc/all.c.
+
+ To use Lua as a library in your own programs, you'll need to know how to
+ create and use libraries with your compiler.
+
+ As mentioned above, you may edit luaconf.h to select some features before
+ building Lua.
+
+(end of INSTALL)
diff --git a/engines/sword25/util/lua/Makefile b/engines/sword25/util/lua/Makefile
new file mode 100755
index 0000000000..fec201156a
--- /dev/null
+++ b/engines/sword25/util/lua/Makefile
@@ -0,0 +1,120 @@
+# makefile for installing Lua
+# see INSTALL for installation instructions
+# see src/Makefile and src/luaconf.h for further customization
+
+# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
+
+# Your platform. See PLATS for possible values.
+PLAT= none
+
+# Where to install. The installation starts in the src directory, so take care
+# if INSTALL_TOP is not an absolute path. (Man pages are installed from the
+# doc directory.) You may want to make these paths consistent with LUA_ROOT,
+# LUA_LDIR, and LUA_CDIR in luaconf.h (and also with etc/lua.pc).
+#
+INSTALL_TOP= /usr/local
+INSTALL_BIN= $(INSTALL_TOP)/bin
+INSTALL_INC= $(INSTALL_TOP)/include
+INSTALL_LIB= $(INSTALL_TOP)/lib
+INSTALL_MAN= $(INSTALL_TOP)/man/man1
+INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
+INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
+
+# How to install. If you don't have "install" (unlikely) then get install-sh at
+# http://dev.w3.org/cvsweb/libwww/config/install-sh
+# or use cp instead.
+INSTALL_EXEC= $(INSTALL) -p -m 0755
+INSTALL_DATA= $(INSTALL) -p -m 0644
+
+# Utilities.
+INSTALL= install
+MKDIR= mkdir
+
+# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
+
+# Convenience platforms targets.
+PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
+
+# What to install.
+TO_BIN= lua luac
+TO_INC= lua.h luaconf.h lualib.h lauxlib.h ../etc/lua.hpp
+TO_LIB= liblua.a
+TO_MAN= lua.1 luac.1
+
+# Lua version and release.
+V= 5.1
+R= 5.1.3
+
+all: $(PLAT)
+
+$(PLATS) clean:
+ cd src && $(MAKE) $@
+
+test: dummy
+ src/lua test/hello.lua
+
+install: dummy
+ cd src && $(MKDIR) -p $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
+ cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
+ cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
+ cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
+ cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
+
+local:
+ $(MAKE) install INSTALL_TOP=..
+
+none:
+ @echo "Please do"
+ @echo " make PLATFORM"
+ @echo "where PLATFORM is one of these:"
+ @echo " $(PLATS)"
+ @echo "See INSTALL for complete instructions."
+
+# make may get confused with test/ and INSTALL in a case-insensitive OS
+dummy:
+
+# echo config parameters
+echo:
+ @echo ""
+ @echo "These are the parameters currently set in src/Makefile to build Lua $R:"
+ @echo ""
+ @cd src && $(MAKE) -s echo
+ @echo ""
+ @echo "These are the parameters currently set in Makefile to install Lua $R:"
+ @echo ""
+ @echo "PLAT = $(PLAT)"
+ @echo "INSTALL_TOP = $(INSTALL_TOP)"
+ @echo "INSTALL_BIN = $(INSTALL_BIN)"
+ @echo "INSTALL_INC = $(INSTALL_INC)"
+ @echo "INSTALL_LIB = $(INSTALL_LIB)"
+ @echo "INSTALL_MAN = $(INSTALL_MAN)"
+ @echo "INSTALL_LMOD = $(INSTALL_LMOD)"
+ @echo "INSTALL_CMOD = $(INSTALL_CMOD)"
+ @echo "INSTALL_EXEC = $(INSTALL_EXEC)"
+ @echo "INSTALL_DATA = $(INSTALL_DATA)"
+ @echo ""
+ @echo "See also src/luaconf.h ."
+ @echo ""
+
+# echo private config parameters
+pecho:
+ @echo "V = $(V)"
+ @echo "R = $(R)"
+ @echo "TO_BIN = $(TO_BIN)"
+ @echo "TO_INC = $(TO_INC)"
+ @echo "TO_LIB = $(TO_LIB)"
+ @echo "TO_MAN = $(TO_MAN)"
+
+# echo config parameters as Lua code
+# uncomment the last sed expression if you want nil instead of empty strings
+lecho:
+ @echo "-- installation parameters for Lua $R"
+ @echo "VERSION = '$V'"
+ @echo "RELEASE = '$R'"
+ @$(MAKE) echo | grep = | sed -e 's/= /= "/' -e 's/$$/"/' #-e 's/""/nil/'
+ @echo "-- EOF"
+
+# list targets that do not create files (but not all makes understand .PHONY)
+.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho
+
+# (end of Makefile)
diff --git a/engines/sword25/util/lua/README b/engines/sword25/util/lua/README
new file mode 100755
index 0000000000..11b4dff70e
--- /dev/null
+++ b/engines/sword25/util/lua/README
@@ -0,0 +1,37 @@
+README for Lua 5.1
+
+See INSTALL for installation instructions.
+See HISTORY for a summary of changes since the last released version.
+
+* What is Lua?
+ ------------
+ Lua is a powerful, light-weight programming language designed for extending
+ applications. Lua is also frequently used as a general-purpose, stand-alone
+ language. Lua is free software.
+
+ For complete information, visit Lua's web site at http://www.lua.org/ .
+ For an executive summary, see http://www.lua.org/about.html .
+
+ Lua has been used in many different projects around the world.
+ For a short list, see http://www.lua.org/uses.html .
+
+* Availability
+ ------------
+ Lua is freely available for both academic and commercial purposes.
+ See COPYRIGHT and http://www.lua.org/license.html for details.
+ Lua can be downloaded at http://www.lua.org/download.html .
+
+* Installation
+ ------------
+ Lua is implemented in pure ANSI C, and compiles unmodified in all known
+ platforms that have an ANSI C compiler. In most Unix-like platforms, simply
+ do "make" with a suitable target. See INSTALL for detailed instructions.
+
+* Origin
+ ------
+ Lua is developed at Lua.org, a laboratory of the Department of Computer
+ Science of PUC-Rio (the Pontifical Catholic University of Rio de Janeiro
+ in Brazil).
+ For more information about the authors, see http://www.lua.org/authors.html .
+
+(end of README)
diff --git a/engines/sword25/util/lua/doc/amazon.gif b/engines/sword25/util/lua/doc/amazon.gif
new file mode 100755
index 0000000000..f2586d5765
--- /dev/null
+++ b/engines/sword25/util/lua/doc/amazon.gif
Binary files differ
diff --git a/engines/sword25/util/lua/doc/contents.html b/engines/sword25/util/lua/doc/contents.html
new file mode 100755
index 0000000000..8e58e18ca9
--- /dev/null
+++ b/engines/sword25/util/lua/doc/contents.html
@@ -0,0 +1,499 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.1 Reference Manual - contents</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE TYPE="text/css">
+ul {
+ list-style-type: none ;
+ list-style-position: outside ;
+}
+</STYLE>
+</HEAD>
+
+<BODY>
+
+<HR>
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="" BORDER=0></A>
+Lua 5.1 Reference Manual
+</H1>
+
+This is an online version of
+<BLOCKQUOTE>
+<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
+<IMG SRC="cover.png" ALT="" TITLE="buy from Amazon" BORDER=1 ALIGN="left" HSPACE=12>
+</A>
+<B>Lua 5.1 Reference Manual</B>
+<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes
+<BR>Lua.org, August 2006
+<BR>ISBN 85-903798-3-3
+<BR><A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
+<IMG SRC="amazon.gif" ALT="[Buy from Amazon]" BORDER=0></A>
+<BR CLEAR="all">
+</BLOCKQUOTE>
+<P>
+
+Buy a copy of this book and
+<A HREF="http://www.lua.org/donations.html">help to support</A>
+the Lua project.
+<P>
+
+The reference manual is the official definition of the Lua language.
+For a complete introduction to Lua programming, see the book
+<A HREF="http://www.lua.org/docs.html#books">Programming in Lua</A>.
+<P>
+
+<A HREF="manual.html">start</A>
+&middot;
+<A HREF="#contents">contents</A>
+&middot;
+<A HREF="#index">index</A>
+&middot;
+<A HREF="http://www.lua.org/manual/5.1/pt/">português</A>
+&middot;
+<A HREF="http://www.lua.org/manual/5.1/es/">español</A>
+<HR>
+<SMALL>
+Copyright &copy; 2006-2008 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<a href="http://www.lua.org/license.html#5">Lua license</a>.
+</SMALL>
+<P>
+
+<H2><A NAME="contents">Contents</A></H2>
+<UL style="padding: 0">
+<LI><A HREF="manual.html">1 - Introduction</A>
+<P>
+<LI><A HREF="manual.html#2">2 - The Language</A>
+<UL>
+<LI><A HREF="manual.html#2.1">2.1 - Lexical Conventions</A>
+<LI><A HREF="manual.html#2.2">2.2 - Values and Types</A>
+<UL>
+<LI><A HREF="manual.html#2.2.1">2.2.1 - Coercion</A>
+</UL>
+<LI><A HREF="manual.html#2.3">2.3 - Variables</A>
+<LI><A HREF="manual.html#2.4">2.4 - Statements</A>
+<UL>
+<LI><A HREF="manual.html#2.4.1">2.4.1 - Chunks</A>
+<LI><A HREF="manual.html#2.4.2">2.4.2 - Blocks</A>
+<LI><A HREF="manual.html#2.4.3">2.4.3 - Assignment</A>
+<LI><A HREF="manual.html#2.4.4">2.4.4 - Control Structures</A>
+<LI><A HREF="manual.html#2.4.5">2.4.5 - For Statement</A>
+<LI><A HREF="manual.html#2.4.6">2.4.6 - Function Calls as Statements</A>
+<LI><A HREF="manual.html#2.4.7">2.4.7 - Local Declarations</A>
+</UL>
+<LI><A HREF="manual.html#2.5">2.5 - Expressions</A>
+<UL>
+<LI><A HREF="manual.html#2.5.1">2.5.1 - Arithmetic Operators</A>
+<LI><A HREF="manual.html#2.5.2">2.5.2 - Relational Operators</A>
+<LI><A HREF="manual.html#2.5.3">2.5.3 - Logical Operators</A>
+<LI><A HREF="manual.html#2.5.4">2.5.4 - Concatenation</A>
+<LI><A HREF="manual.html#2.5.5">2.5.5 - The Length Operator</A>
+<LI><A HREF="manual.html#2.5.6">2.5.6 - Precedence</A>
+<LI><A HREF="manual.html#2.5.7">2.5.7 - Table Constructors</A>
+<LI><A HREF="manual.html#2.5.8">2.5.8 - Function Calls</A>
+<LI><A HREF="manual.html#2.5.9">2.5.9 - Function Definitions</A>
+</UL>
+<LI><A HREF="manual.html#2.6">2.6 - Visibility Rules</A>
+<LI><A HREF="manual.html#2.7">2.7 - Error Handling</A>
+<LI><A HREF="manual.html#2.8">2.8 - Metatables</A>
+<LI><A HREF="manual.html#2.9">2.9 - Environments</A>
+<LI><A HREF="manual.html#2.10">2.10 - Garbage Collection</A>
+<UL>
+<LI><A HREF="manual.html#2.10.1">2.10.1 - Garbage-Collection Metamethods</A>
+<LI><A HREF="manual.html#2.10.2">2.10.2 - Weak Tables</A>
+</UL>
+<LI><A HREF="manual.html#2.11">2.11 - Coroutines</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#3">3 - The Application Program Interface</A>
+<UL>
+<LI><A HREF="manual.html#3.1">3.1 - The Stack</A>
+<LI><A HREF="manual.html#3.2">3.2 - Stack Size</A>
+<LI><A HREF="manual.html#3.3">3.3 - Pseudo-Indices</A>
+<LI><A HREF="manual.html#3.4">3.4 - C Closures</A>
+<LI><A HREF="manual.html#3.5">3.5 - Registry</A>
+<LI><A HREF="manual.html#3.6">3.6 - Error Handling in C</A>
+<LI><A HREF="manual.html#3.7">3.7 - Functions and Types</A>
+<LI><A HREF="manual.html#3.8">3.8 - The Debug Interface</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#4">4 - The Auxiliary Library</A>
+<UL>
+<LI><A HREF="manual.html#4.1">4.1 - Functions and Types</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#5">5 - Standard Libraries</A>
+<UL>
+<LI><A HREF="manual.html#5.1">5.1 - Basic Functions</A>
+<LI><A HREF="manual.html#5.2">5.2 - Coroutine Manipulation</A>
+<LI><A HREF="manual.html#5.3">5.3 - Modules</A>
+<LI><A HREF="manual.html#5.4">5.4 - String Manipulation</A>
+<UL>
+<LI><A HREF="manual.html#5.4.1">5.4.1 - Patterns</A>
+</UL>
+<LI><A HREF="manual.html#5.5">5.5 - Table Manipulation</A>
+<LI><A HREF="manual.html#5.6">5.6 - Mathematical Functions</A>
+<LI><A HREF="manual.html#5.7">5.7 - Input and Output Facilities</A>
+<LI><A HREF="manual.html#5.8">5.8 - Operating System Facilities</A>
+<LI><A HREF="manual.html#5.9">5.9 - The Debug Library</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#6">6 - Lua Stand-alone</A>
+<P>
+<LI><A HREF="manual.html#7">7 - Incompatibilities with the Previous Version</A>
+<UL>
+<LI><A HREF="manual.html#7.1">7.1 - Changes in the Language</A>
+<LI><A HREF="manual.html#7.2">7.2 - Changes in the Libraries</A>
+<LI><A HREF="manual.html#7.3">7.3 - Changes in the API</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#8">8 - The Complete Syntax of Lua</A>
+</UL>
+
+<H2><A NAME="index">Index</A></H2>
+<TABLE WIDTH="100%">
+<TR VALIGN="top">
+<TD>
+<H3><A NAME="functions">Lua functions</A></H3>
+<A HREF="manual.html#pdf-_G">_G</A><BR>
+<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
+<A HREF="manual.html#pdf-assert">assert</A><BR>
+<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
+<A HREF="manual.html#pdf-dofile">dofile</A><BR>
+<A HREF="manual.html#pdf-error">error</A><BR>
+<A HREF="manual.html#pdf-getfenv">getfenv</A><BR>
+<A HREF="manual.html#pdf-getmetatable">getmetatable</A><BR>
+<A HREF="manual.html#pdf-ipairs">ipairs</A><BR>
+<A HREF="manual.html#pdf-load">load</A><BR>
+<A HREF="manual.html#pdf-loadfile">loadfile</A><BR>
+<A HREF="manual.html#pdf-loadstring">loadstring</A><BR>
+<A HREF="manual.html#pdf-module">module</A><BR>
+<A HREF="manual.html#pdf-next">next</A><BR>
+<A HREF="manual.html#pdf-pairs">pairs</A><BR>
+<A HREF="manual.html#pdf-pcall">pcall</A><BR>
+<A HREF="manual.html#pdf-print">print</A><BR>
+<A HREF="manual.html#pdf-rawequal">rawequal</A><BR>
+<A HREF="manual.html#pdf-rawget">rawget</A><BR>
+<A HREF="manual.html#pdf-rawset">rawset</A><BR>
+<A HREF="manual.html#pdf-require">require</A><BR>
+<A HREF="manual.html#pdf-select">select</A><BR>
+<A HREF="manual.html#pdf-setfenv">setfenv</A><BR>
+<A HREF="manual.html#pdf-setmetatable">setmetatable</A><BR>
+<A HREF="manual.html#pdf-tonumber">tonumber</A><BR>
+<A HREF="manual.html#pdf-tostring">tostring</A><BR>
+<A HREF="manual.html#pdf-type">type</A><BR>
+<A HREF="manual.html#pdf-unpack">unpack</A><BR>
+<A HREF="manual.html#pdf-xpcall">xpcall</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-coroutine.create">coroutine.create</A><BR>
+<A HREF="manual.html#pdf-coroutine.resume">coroutine.resume</A><BR>
+<A HREF="manual.html#pdf-coroutine.running">coroutine.running</A><BR>
+<A HREF="manual.html#pdf-coroutine.status">coroutine.status</A><BR>
+<A HREF="manual.html#pdf-coroutine.wrap">coroutine.wrap</A><BR>
+<A HREF="manual.html#pdf-coroutine.yield">coroutine.yield</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-debug.debug">debug.debug</A><BR>
+<A HREF="manual.html#pdf-debug.getfenv">debug.getfenv</A><BR>
+<A HREF="manual.html#pdf-debug.gethook">debug.gethook</A><BR>
+<A HREF="manual.html#pdf-debug.getinfo">debug.getinfo</A><BR>
+<A HREF="manual.html#pdf-debug.getlocal">debug.getlocal</A><BR>
+<A HREF="manual.html#pdf-debug.getmetatable">debug.getmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
+<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.setfenv">debug.setfenv</A><BR>
+<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
+<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
+<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.setupvalue">debug.setupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.traceback">debug.traceback</A><BR>
+
+</TD>
+<TD>
+<H3>&nbsp;</H3>
+<A HREF="manual.html#pdf-file:close">file:close</A><BR>
+<A HREF="manual.html#pdf-file:flush">file:flush</A><BR>
+<A HREF="manual.html#pdf-file:lines">file:lines</A><BR>
+<A HREF="manual.html#pdf-file:read">file:read</A><BR>
+<A HREF="manual.html#pdf-file:seek">file:seek</A><BR>
+<A HREF="manual.html#pdf-file:setvbuf">file:setvbuf</A><BR>
+<A HREF="manual.html#pdf-file:write">file:write</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-io.close">io.close</A><BR>
+<A HREF="manual.html#pdf-io.flush">io.flush</A><BR>
+<A HREF="manual.html#pdf-io.input">io.input</A><BR>
+<A HREF="manual.html#pdf-io.lines">io.lines</A><BR>
+<A HREF="manual.html#pdf-io.open">io.open</A><BR>
+<A HREF="manual.html#pdf-io.output">io.output</A><BR>
+<A HREF="manual.html#pdf-io.popen">io.popen</A><BR>
+<A HREF="manual.html#pdf-io.read">io.read</A><BR>
+<A HREF="manual.html#pdf-io.stderr">io.stderr</A><BR>
+<A HREF="manual.html#pdf-io.stdin">io.stdin</A><BR>
+<A HREF="manual.html#pdf-io.stdout">io.stdout</A><BR>
+<A HREF="manual.html#pdf-io.tmpfile">io.tmpfile</A><BR>
+<A HREF="manual.html#pdf-io.type">io.type</A><BR>
+<A HREF="manual.html#pdf-io.write">io.write</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-math.abs">math.abs</A><BR>
+<A HREF="manual.html#pdf-math.acos">math.acos</A><BR>
+<A HREF="manual.html#pdf-math.asin">math.asin</A><BR>
+<A HREF="manual.html#pdf-math.atan">math.atan</A><BR>
+<A HREF="manual.html#pdf-math.atan2">math.atan2</A><BR>
+<A HREF="manual.html#pdf-math.ceil">math.ceil</A><BR>
+<A HREF="manual.html#pdf-math.cos">math.cos</A><BR>
+<A HREF="manual.html#pdf-math.cosh">math.cosh</A><BR>
+<A HREF="manual.html#pdf-math.deg">math.deg</A><BR>
+<A HREF="manual.html#pdf-math.exp">math.exp</A><BR>
+<A HREF="manual.html#pdf-math.floor">math.floor</A><BR>
+<A HREF="manual.html#pdf-math.fmod">math.fmod</A><BR>
+<A HREF="manual.html#pdf-math.frexp">math.frexp</A><BR>
+<A HREF="manual.html#pdf-math.huge">math.huge</A><BR>
+<A HREF="manual.html#pdf-math.ldexp">math.ldexp</A><BR>
+<A HREF="manual.html#pdf-math.log">math.log</A><BR>
+<A HREF="manual.html#pdf-math.log10">math.log10</A><BR>
+<A HREF="manual.html#pdf-math.max">math.max</A><BR>
+<A HREF="manual.html#pdf-math.min">math.min</A><BR>
+<A HREF="manual.html#pdf-math.modf">math.modf</A><BR>
+<A HREF="manual.html#pdf-math.pi">math.pi</A><BR>
+<A HREF="manual.html#pdf-math.pow">math.pow</A><BR>
+<A HREF="manual.html#pdf-math.rad">math.rad</A><BR>
+<A HREF="manual.html#pdf-math.random">math.random</A><BR>
+<A HREF="manual.html#pdf-math.randomseed">math.randomseed</A><BR>
+<A HREF="manual.html#pdf-math.sin">math.sin</A><BR>
+<A HREF="manual.html#pdf-math.sinh">math.sinh</A><BR>
+<A HREF="manual.html#pdf-math.sqrt">math.sqrt</A><BR>
+<A HREF="manual.html#pdf-math.tan">math.tan</A><BR>
+<A HREF="manual.html#pdf-math.tanh">math.tanh</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-os.clock">os.clock</A><BR>
+<A HREF="manual.html#pdf-os.date">os.date</A><BR>
+<A HREF="manual.html#pdf-os.difftime">os.difftime</A><BR>
+<A HREF="manual.html#pdf-os.execute">os.execute</A><BR>
+<A HREF="manual.html#pdf-os.exit">os.exit</A><BR>
+<A HREF="manual.html#pdf-os.getenv">os.getenv</A><BR>
+<A HREF="manual.html#pdf-os.remove">os.remove</A><BR>
+<A HREF="manual.html#pdf-os.rename">os.rename</A><BR>
+<A HREF="manual.html#pdf-os.setlocale">os.setlocale</A><BR>
+<A HREF="manual.html#pdf-os.time">os.time</A><BR>
+<A HREF="manual.html#pdf-os.tmpname">os.tmpname</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-package.cpath">package.cpath</A><BR>
+<A HREF="manual.html#pdf-package.loaded">package.loaded</A><BR>
+<A HREF="manual.html#pdf-package.loaders">package.loaders</A><BR>
+<A HREF="manual.html#pdf-package.loadlib">package.loadlib</A><BR>
+<A HREF="manual.html#pdf-package.path">package.path</A><BR>
+<A HREF="manual.html#pdf-package.preload">package.preload</A><BR>
+<A HREF="manual.html#pdf-package.seeall">package.seeall</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-string.byte">string.byte</A><BR>
+<A HREF="manual.html#pdf-string.char">string.char</A><BR>
+<A HREF="manual.html#pdf-string.dump">string.dump</A><BR>
+<A HREF="manual.html#pdf-string.find">string.find</A><BR>
+<A HREF="manual.html#pdf-string.format">string.format</A><BR>
+<A HREF="manual.html#pdf-string.gmatch">string.gmatch</A><BR>
+<A HREF="manual.html#pdf-string.gsub">string.gsub</A><BR>
+<A HREF="manual.html#pdf-string.len">string.len</A><BR>
+<A HREF="manual.html#pdf-string.lower">string.lower</A><BR>
+<A HREF="manual.html#pdf-string.match">string.match</A><BR>
+<A HREF="manual.html#pdf-string.rep">string.rep</A><BR>
+<A HREF="manual.html#pdf-string.reverse">string.reverse</A><BR>
+<A HREF="manual.html#pdf-string.sub">string.sub</A><BR>
+<A HREF="manual.html#pdf-string.upper">string.upper</A><BR>
+<P>
+
+<A HREF="manual.html#pdf-table.concat">table.concat</A><BR>
+<A HREF="manual.html#pdf-table.insert">table.insert</A><BR>
+<A HREF="manual.html#pdf-table.maxn">table.maxn</A><BR>
+<A HREF="manual.html#pdf-table.remove">table.remove</A><BR>
+<A HREF="manual.html#pdf-table.sort">table.sort</A><BR>
+
+</TD>
+<TD>
+<H3>C API</H3>
+<A HREF="manual.html#lua_Alloc">lua_Alloc</A><BR>
+<A HREF="manual.html#lua_CFunction">lua_CFunction</A><BR>
+<A HREF="manual.html#lua_Debug">lua_Debug</A><BR>
+<A HREF="manual.html#lua_Hook">lua_Hook</A><BR>
+<A HREF="manual.html#lua_Integer">lua_Integer</A><BR>
+<A HREF="manual.html#lua_Number">lua_Number</A><BR>
+<A HREF="manual.html#lua_Reader">lua_Reader</A><BR>
+<A HREF="manual.html#lua_State">lua_State</A><BR>
+<A HREF="manual.html#lua_Writer">lua_Writer</A><BR>
+<P>
+
+<A HREF="manual.html#lua_atpanic">lua_atpanic</A><BR>
+<A HREF="manual.html#lua_call">lua_call</A><BR>
+<A HREF="manual.html#lua_checkstack">lua_checkstack</A><BR>
+<A HREF="manual.html#lua_close">lua_close</A><BR>
+<A HREF="manual.html#lua_concat">lua_concat</A><BR>
+<A HREF="manual.html#lua_cpcall">lua_cpcall</A><BR>
+<A HREF="manual.html#lua_createtable">lua_createtable</A><BR>
+<A HREF="manual.html#lua_dump">lua_dump</A><BR>
+<A HREF="manual.html#lua_equal">lua_equal</A><BR>
+<A HREF="manual.html#lua_error">lua_error</A><BR>
+<A HREF="manual.html#lua_gc">lua_gc</A><BR>
+<A HREF="manual.html#lua_getallocf">lua_getallocf</A><BR>
+<A HREF="manual.html#lua_getfenv">lua_getfenv</A><BR>
+<A HREF="manual.html#lua_getfield">lua_getfield</A><BR>
+<A HREF="manual.html#lua_getglobal">lua_getglobal</A><BR>
+<A HREF="manual.html#lua_gethook">lua_gethook</A><BR>
+<A HREF="manual.html#lua_gethookcount">lua_gethookcount</A><BR>
+<A HREF="manual.html#lua_gethookmask">lua_gethookmask</A><BR>
+<A HREF="manual.html#lua_getinfo">lua_getinfo</A><BR>
+<A HREF="manual.html#lua_getlocal">lua_getlocal</A><BR>
+<A HREF="manual.html#lua_getmetatable">lua_getmetatable</A><BR>
+<A HREF="manual.html#lua_getstack">lua_getstack</A><BR>
+<A HREF="manual.html#lua_gettable">lua_gettable</A><BR>
+<A HREF="manual.html#lua_gettop">lua_gettop</A><BR>
+<A HREF="manual.html#lua_getupvalue">lua_getupvalue</A><BR>
+<A HREF="manual.html#lua_insert">lua_insert</A><BR>
+<A HREF="manual.html#lua_isboolean">lua_isboolean</A><BR>
+<A HREF="manual.html#lua_iscfunction">lua_iscfunction</A><BR>
+<A HREF="manual.html#lua_isfunction">lua_isfunction</A><BR>
+<A HREF="manual.html#lua_islightuserdata">lua_islightuserdata</A><BR>
+<A HREF="manual.html#lua_isnil">lua_isnil</A><BR>
+<A HREF="manual.html#lua_isnone">lua_isnone</A><BR>
+<A HREF="manual.html#lua_isnoneornil">lua_isnoneornil</A><BR>
+<A HREF="manual.html#lua_isnumber">lua_isnumber</A><BR>
+<A HREF="manual.html#lua_isstring">lua_isstring</A><BR>
+<A HREF="manual.html#lua_istable">lua_istable</A><BR>
+<A HREF="manual.html#lua_isthread">lua_isthread</A><BR>
+<A HREF="manual.html#lua_isuserdata">lua_isuserdata</A><BR>
+<A HREF="manual.html#lua_lessthan">lua_lessthan</A><BR>
+<A HREF="manual.html#lua_load">lua_load</A><BR>
+<A HREF="manual.html#lua_newstate">lua_newstate</A><BR>
+<A HREF="manual.html#lua_newtable">lua_newtable</A><BR>
+<A HREF="manual.html#lua_newthread">lua_newthread</A><BR>
+<A HREF="manual.html#lua_newuserdata">lua_newuserdata</A><BR>
+<A HREF="manual.html#lua_next">lua_next</A><BR>
+<A HREF="manual.html#lua_objlen">lua_objlen</A><BR>
+<A HREF="manual.html#lua_pcall">lua_pcall</A><BR>
+<A HREF="manual.html#lua_pop">lua_pop</A><BR>
+<A HREF="manual.html#lua_pushboolean">lua_pushboolean</A><BR>
+<A HREF="manual.html#lua_pushcclosure">lua_pushcclosure</A><BR>
+<A HREF="manual.html#lua_pushcfunction">lua_pushcfunction</A><BR>
+<A HREF="manual.html#lua_pushfstring">lua_pushfstring</A><BR>
+<A HREF="manual.html#lua_pushinteger">lua_pushinteger</A><BR>
+<A HREF="manual.html#lua_pushlightuserdata">lua_pushlightuserdata</A><BR>
+<A HREF="manual.html#lua_pushliteral">lua_pushliteral</A><BR>
+<A HREF="manual.html#lua_pushlstring">lua_pushlstring</A><BR>
+<A HREF="manual.html#lua_pushnil">lua_pushnil</A><BR>
+<A HREF="manual.html#lua_pushnumber">lua_pushnumber</A><BR>
+<A HREF="manual.html#lua_pushstring">lua_pushstring</A><BR>
+<A HREF="manual.html#lua_pushthread">lua_pushthread</A><BR>
+<A HREF="manual.html#lua_pushvalue">lua_pushvalue</A><BR>
+<A HREF="manual.html#lua_pushvfstring">lua_pushvfstring</A><BR>
+<A HREF="manual.html#lua_rawequal">lua_rawequal</A><BR>
+<A HREF="manual.html#lua_rawget">lua_rawget</A><BR>
+<A HREF="manual.html#lua_rawgeti">lua_rawgeti</A><BR>
+<A HREF="manual.html#lua_rawset">lua_rawset</A><BR>
+<A HREF="manual.html#lua_rawseti">lua_rawseti</A><BR>
+<A HREF="manual.html#lua_register">lua_register</A><BR>
+<A HREF="manual.html#lua_remove">lua_remove</A><BR>
+<A HREF="manual.html#lua_replace">lua_replace</A><BR>
+<A HREF="manual.html#lua_resume">lua_resume</A><BR>
+<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
+<A HREF="manual.html#lua_setfenv">lua_setfenv</A><BR>
+<A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
+<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
+<A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
+<A HREF="manual.html#lua_setlocal">lua_setlocal</A><BR>
+<A HREF="manual.html#lua_setmetatable">lua_setmetatable</A><BR>
+<A HREF="manual.html#lua_settable">lua_settable</A><BR>
+<A HREF="manual.html#lua_settop">lua_settop</A><BR>
+<A HREF="manual.html#lua_setupvalue">lua_setupvalue</A><BR>
+<A HREF="manual.html#lua_status">lua_status</A><BR>
+<A HREF="manual.html#lua_toboolean">lua_toboolean</A><BR>
+<A HREF="manual.html#lua_tocfunction">lua_tocfunction</A><BR>
+<A HREF="manual.html#lua_tointeger">lua_tointeger</A><BR>
+<A HREF="manual.html#lua_tolstring">lua_tolstring</A><BR>
+<A HREF="manual.html#lua_tonumber">lua_tonumber</A><BR>
+<A HREF="manual.html#lua_topointer">lua_topointer</A><BR>
+<A HREF="manual.html#lua_tostring">lua_tostring</A><BR>
+<A HREF="manual.html#lua_tothread">lua_tothread</A><BR>
+<A HREF="manual.html#lua_touserdata">lua_touserdata</A><BR>
+<A HREF="manual.html#lua_type">lua_type</A><BR>
+<A HREF="manual.html#lua_typename">lua_typename</A><BR>
+<A HREF="manual.html#lua_upvalueindex">lua_upvalueindex</A><BR>
+<A HREF="manual.html#lua_xmove">lua_xmove</A><BR>
+<A HREF="manual.html#lua_yield">lua_yield</A><BR>
+
+</TD>
+<TD>
+<H3>auxiliary library</H3>
+<A HREF="manual.html#luaL_Buffer">luaL_Buffer</A><BR>
+<A HREF="manual.html#luaL_Reg">luaL_Reg</A><BR>
+<P>
+
+<A HREF="manual.html#luaL_addchar">luaL_addchar</A><BR>
+<A HREF="manual.html#luaL_addlstring">luaL_addlstring</A><BR>
+<A HREF="manual.html#luaL_addsize">luaL_addsize</A><BR>
+<A HREF="manual.html#luaL_addstring">luaL_addstring</A><BR>
+<A HREF="manual.html#luaL_addvalue">luaL_addvalue</A><BR>
+<A HREF="manual.html#luaL_argcheck">luaL_argcheck</A><BR>
+<A HREF="manual.html#luaL_argerror">luaL_argerror</A><BR>
+<A HREF="manual.html#luaL_buffinit">luaL_buffinit</A><BR>
+<A HREF="manual.html#luaL_callmeta">luaL_callmeta</A><BR>
+<A HREF="manual.html#luaL_checkany">luaL_checkany</A><BR>
+<A HREF="manual.html#luaL_checkint">luaL_checkint</A><BR>
+<A HREF="manual.html#luaL_checkinteger">luaL_checkinteger</A><BR>
+<A HREF="manual.html#luaL_checklong">luaL_checklong</A><BR>
+<A HREF="manual.html#luaL_checklstring">luaL_checklstring</A><BR>
+<A HREF="manual.html#luaL_checknumber">luaL_checknumber</A><BR>
+<A HREF="manual.html#luaL_checkoption">luaL_checkoption</A><BR>
+<A HREF="manual.html#luaL_checkstack">luaL_checkstack</A><BR>
+<A HREF="manual.html#luaL_checkstring">luaL_checkstring</A><BR>
+<A HREF="manual.html#luaL_checktype">luaL_checktype</A><BR>
+<A HREF="manual.html#luaL_checkudata">luaL_checkudata</A><BR>
+<A HREF="manual.html#luaL_dofile">luaL_dofile</A><BR>
+<A HREF="manual.html#luaL_dostring">luaL_dostring</A><BR>
+<A HREF="manual.html#luaL_error">luaL_error</A><BR>
+<A HREF="manual.html#luaL_getmetafield">luaL_getmetafield</A><BR>
+<A HREF="manual.html#luaL_getmetatable">luaL_getmetatable</A><BR>
+<A HREF="manual.html#luaL_gsub">luaL_gsub</A><BR>
+<A HREF="manual.html#luaL_loadbuffer">luaL_loadbuffer</A><BR>
+<A HREF="manual.html#luaL_loadfile">luaL_loadfile</A><BR>
+<A HREF="manual.html#luaL_loadstring">luaL_loadstring</A><BR>
+<A HREF="manual.html#luaL_newmetatable">luaL_newmetatable</A><BR>
+<A HREF="manual.html#luaL_newstate">luaL_newstate</A><BR>
+<A HREF="manual.html#luaL_openlibs">luaL_openlibs</A><BR>
+<A HREF="manual.html#luaL_optint">luaL_optint</A><BR>
+<A HREF="manual.html#luaL_optinteger">luaL_optinteger</A><BR>
+<A HREF="manual.html#luaL_optlong">luaL_optlong</A><BR>
+<A HREF="manual.html#luaL_optlstring">luaL_optlstring</A><BR>
+<A HREF="manual.html#luaL_optnumber">luaL_optnumber</A><BR>
+<A HREF="manual.html#luaL_optstring">luaL_optstring</A><BR>
+<A HREF="manual.html#luaL_prepbuffer">luaL_prepbuffer</A><BR>
+<A HREF="manual.html#luaL_pushresult">luaL_pushresult</A><BR>
+<A HREF="manual.html#luaL_ref">luaL_ref</A><BR>
+<A HREF="manual.html#luaL_register">luaL_register</A><BR>
+<A HREF="manual.html#luaL_typename">luaL_typename</A><BR>
+<A HREF="manual.html#luaL_typerror">luaL_typerror</A><BR>
+<A HREF="manual.html#luaL_unref">luaL_unref</A><BR>
+<A HREF="manual.html#luaL_where">luaL_where</A><BR>
+
+</TD>
+</TR>
+</TABLE>
+<P>
+
+<HR>
+<SMALL>
+Last update:
+Sat Jan 19 13:24:29 BRST 2008
+</SMALL>
+<!--
+Last change: revised for Lua 5.1.3
+-->
+
+</BODY>
+</HTML>
diff --git a/engines/sword25/util/lua/doc/cover.png b/engines/sword25/util/lua/doc/cover.png
new file mode 100755
index 0000000000..2dbb198123
--- /dev/null
+++ b/engines/sword25/util/lua/doc/cover.png
Binary files differ
diff --git a/engines/sword25/util/lua/doc/logo.gif b/engines/sword25/util/lua/doc/logo.gif
new file mode 100755
index 0000000000..2f5e4ac2e7
--- /dev/null
+++ b/engines/sword25/util/lua/doc/logo.gif
Binary files differ
diff --git a/engines/sword25/util/lua/doc/lua.1 b/engines/sword25/util/lua/doc/lua.1
new file mode 100755
index 0000000000..24809cc6c1
--- /dev/null
+++ b/engines/sword25/util/lua/doc/lua.1
@@ -0,0 +1,163 @@
+.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $
+.TH LUA 1 "$Date: 2006/01/06 16:03:34 $"
+.SH NAME
+lua \- Lua interpreter
+.SH SYNOPSIS
+.B lua
+[
+.I options
+]
+[
+.I script
+[
+.I args
+]
+]
+.SH DESCRIPTION
+.B lua
+is the stand-alone Lua interpreter.
+It loads and executes Lua programs,
+either in textual source form or
+in precompiled binary form.
+(Precompiled binaries are output by
+.BR luac ,
+the Lua compiler.)
+.B lua
+can be used as a batch interpreter and also interactively.
+.LP
+The given
+.I options
+(see below)
+are executed and then
+the Lua program in file
+.I script
+is loaded and executed.
+The given
+.I args
+are available to
+.I script
+as strings in a global table named
+.BR arg .
+If these arguments contain spaces or other characters special to the shell,
+then they should be quoted
+(but note that the quotes will be removed by the shell).
+The arguments in
+.B arg
+start at 0,
+which contains the string
+.RI ' script '.
+The index of the last argument is stored in
+.BR arg.n .
+The arguments given in the command line before
+.IR script ,
+including the name of the interpreter,
+are available in negative indices in
+.BR arg .
+.LP
+At the very start,
+before even handling the command line,
+.B lua
+executes the contents of the environment variable
+.BR LUA_INIT ,
+if it is defined.
+If the value of
+.B LUA_INIT
+is of the form
+.RI '@ filename ',
+then
+.I filename
+is executed.
+Otherwise, the string is assumed to be a Lua statement and is executed.
+.LP
+Options start with
+.B '\-'
+and are described below.
+You can use
+.B "'\--'"
+to signal the end of options.
+.LP
+If no arguments are given,
+then
+.B "\-v \-i"
+is assumed when the standard input is a terminal;
+otherwise,
+.B "\-"
+is assumed.
+.LP
+In interactive mode,
+.B lua
+prompts the user,
+reads lines from the standard input,
+and executes them as they are read.
+If a line does not contain a complete statement,
+then a secondary prompt is displayed and
+lines are read until a complete statement is formed or
+a syntax error is found.
+So, one way to interrupt the reading of an incomplete statement is
+to force a syntax error:
+adding a
+.B ';'
+in the middle of a statement is a sure way of forcing a syntax error
+(except inside multiline strings and comments; these must be closed explicitly).
+If a line starts with
+.BR '=' ,
+then
+.B lua
+displays the values of all the expressions in the remainder of the
+line. The expressions must be separated by commas.
+The primary prompt is the value of the global variable
+.BR _PROMPT ,
+if this value is a string;
+otherwise, the default prompt is used.
+Similarly, the secondary prompt is the value of the global variable
+.BR _PROMPT2 .
+So,
+to change the prompts,
+set the corresponding variable to a string of your choice.
+You can do that after calling the interpreter
+or on the command line
+(but in this case you have to be careful with quotes
+if the prompt string contains a space; otherwise you may confuse the shell.)
+The default prompts are "> " and ">> ".
+.SH OPTIONS
+.TP
+.B \-
+load and execute the standard input as a file,
+that is,
+not interactively,
+even when the standard input is a terminal.
+.TP
+.BI \-e " stat"
+execute statement
+.IR stat .
+You need to quote
+.I stat
+if it contains spaces, quotes,
+or other characters special to the shell.
+.TP
+.B \-i
+enter interactive mode after
+.I script
+is executed.
+.TP
+.BI \-l " name"
+call
+.BI require(' name ')
+before executing
+.IR script .
+Typically used to load libraries.
+.TP
+.B \-v
+show version information.
+.SH "SEE ALSO"
+.BR luac (1)
+.br
+http://www.lua.org/
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH AUTHORS
+R. Ierusalimschy,
+L. H. de Figueiredo,
+and
+W. Celes
+.\" EOF
diff --git a/engines/sword25/util/lua/doc/lua.css b/engines/sword25/util/lua/doc/lua.css
new file mode 100755
index 0000000000..039cf11698
--- /dev/null
+++ b/engines/sword25/util/lua/doc/lua.css
@@ -0,0 +1,41 @@
+body {
+ color: #000000 ;
+ background-color: #FFFFFF ;
+ font-family: sans-serif ;
+ text-align: justify ;
+ margin-right: 20px ;
+ margin-left: 20px ;
+}
+
+h1, h2, h3, h4 {
+ font-weight: normal ;
+ font-style: italic ;
+}
+
+a:link {
+ color: #000080 ;
+ background-color: inherit ;
+ text-decoration: none ;
+}
+
+a:visited {
+ background-color: inherit ;
+ text-decoration: none ;
+}
+
+a:link:hover, a:visited:hover {
+ color: #000080 ;
+ background-color: #E0E0FF ;
+}
+
+a:link:active, a:visited:active {
+ color: #FF0000 ;
+}
+
+hr {
+ border: 0 ;
+ height: 1px ;
+ color: #a0a0a0 ;
+ background-color: #a0a0a0 ;
+}
+
diff --git a/engines/sword25/util/lua/doc/lua.html b/engines/sword25/util/lua/doc/lua.html
new file mode 100755
index 0000000000..1d435ab029
--- /dev/null
+++ b/engines/sword25/util/lua/doc/lua.html
@@ -0,0 +1,172 @@
+<!-- $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ -->
+<HTML>
+<HEAD>
+<TITLE>LUA man page</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+</HEAD>
+
+<BODY BGCOLOR="#FFFFFF">
+
+<H2>NAME</H2>
+lua - Lua interpreter
+<H2>SYNOPSIS</H2>
+<B>lua</B>
+[
+<I>options</I>
+]
+[
+<I>script</I>
+[
+<I>args</I>
+]
+]
+<H2>DESCRIPTION</H2>
+<B>lua</B>
+is the stand-alone Lua interpreter.
+It loads and executes Lua programs,
+either in textual source form or
+in precompiled binary form.
+(Precompiled binaries are output by
+<B>luac</B>,
+the Lua compiler.)
+<B>lua</B>
+can be used as a batch interpreter and also interactively.
+<P>
+The given
+<I>options</I>
+(see below)
+are executed and then
+the Lua program in file
+<I>script</I>
+is loaded and executed.
+The given
+<I>args</I>
+are available to
+<I>script</I>
+as strings in a global table named
+<B>arg</B>.
+If these arguments contain spaces or other characters special to the shell,
+then they should be quoted
+(but note that the quotes will be removed by the shell).
+The arguments in
+<B>arg</B>
+start at 0,
+which contains the string
+'<I>script</I>'.
+The index of the last argument is stored in
+<B>arg.n</B>.
+The arguments given in the command line before
+<I>script</I>,
+including the name of the interpreter,
+are available in negative indices in
+<B>arg</B>.
+<P>
+At the very start,
+before even handling the command line,
+<B>lua</B>
+executes the contents of the environment variable
+<B>LUA_INIT</B>,
+if it is defined.
+If the value of
+<B>LUA_INIT</B>
+is of the form
+'@<I>filename</I>',
+then
+<I>filename</I>
+is executed.
+Otherwise, the string is assumed to be a Lua statement and is executed.
+<P>
+Options start with
+<B>'-'</B>
+and are described below.
+You can use
+<B>'--'</B>
+to signal the end of options.
+<P>
+If no arguments are given,
+then
+<B>"-v -i"</B>
+is assumed when the standard input is a terminal;
+otherwise,
+<B>"-"</B>
+is assumed.
+<P>
+In interactive mode,
+<B>lua</B>
+prompts the user,
+reads lines from the standard input,
+and executes them as they are read.
+If a line does not contain a complete statement,
+then a secondary prompt is displayed and
+lines are read until a complete statement is formed or
+a syntax error is found.
+So, one way to interrupt the reading of an incomplete statement is
+to force a syntax error:
+adding a
+<B>';'</B>
+in the middle of a statement is a sure way of forcing a syntax error
+(except inside multiline strings and comments; these must be closed explicitly).
+If a line starts with
+<B>'='</B>,
+then
+<B>lua</B>
+displays the values of all the expressions in the remainder of the
+line. The expressions must be separated by commas.
+The primary prompt is the value of the global variable
+<B>_PROMPT</B>,
+if this value is a string;
+otherwise, the default prompt is used.
+Similarly, the secondary prompt is the value of the global variable
+<B>_PROMPT2</B>.
+So,
+to change the prompts,
+set the corresponding variable to a string of your choice.
+You can do that after calling the interpreter
+or on the command line
+(but in this case you have to be careful with quotes
+if the prompt string contains a space; otherwise you may confuse the shell.)
+The default prompts are "&gt; " and "&gt;&gt; ".
+<H2>OPTIONS</H2>
+<P>
+<B>-</B>
+load and execute the standard input as a file,
+that is,
+not interactively,
+even when the standard input is a terminal.
+<P>
+<B>-e </B><I>stat</I>
+execute statement
+<I>stat</I>.
+You need to quote
+<I>stat </I>
+if it contains spaces, quotes,
+or other characters special to the shell.
+<P>
+<B>-i</B>
+enter interactive mode after
+<I>script</I>
+is executed.
+<P>
+<B>-l </B><I>name</I>
+call
+<B>require</B>('<I>name</I>')
+before executing
+<I>script</I>.
+Typically used to load libraries.
+<P>
+<B>-v</B>
+show version information.
+<H2>SEE ALSO</H2>
+<B>luac</B>(1)
+<BR>
+<A HREF="http://www.lua.org/">http://www.lua.org/</A>
+<H2>DIAGNOSTICS</H2>
+Error messages should be self explanatory.
+<H2>AUTHORS</H2>
+R. Ierusalimschy,
+L. H. de Figueiredo,
+and
+W. Celes
+<!-- EOF -->
+</BODY>
+</HTML>
diff --git a/engines/sword25/util/lua/doc/luac.1 b/engines/sword25/util/lua/doc/luac.1
new file mode 100755
index 0000000000..d8146782df
--- /dev/null
+++ b/engines/sword25/util/lua/doc/luac.1
@@ -0,0 +1,136 @@
+.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $
+.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $"
+.SH NAME
+luac \- Lua compiler
+.SH SYNOPSIS
+.B luac
+[
+.I options
+] [
+.I filenames
+]
+.SH DESCRIPTION
+.B luac
+is the Lua compiler.
+It translates programs written in the Lua programming language
+into binary files that can be later loaded and executed.
+.LP
+The main advantages of precompiling chunks are:
+faster loading,
+protecting source code from accidental user changes,
+and
+off-line syntax checking.
+.LP
+Pre-compiling does not imply faster execution
+because in Lua chunks are always compiled into bytecodes before being executed.
+.B luac
+simply allows those bytecodes to be saved in a file for later execution.
+.LP
+Pre-compiled chunks are not necessarily smaller than the corresponding source.
+The main goal in pre-compiling is faster loading.
+.LP
+The binary files created by
+.B luac
+are portable only among architectures with the same word size and byte order.
+.LP
+.B luac
+produces a single output file containing the bytecodes
+for all source files given.
+By default,
+the output file is named
+.BR luac.out ,
+but you can change this with the
+.B \-o
+option.
+.LP
+In the command line,
+you can mix
+text files containing Lua source and
+binary files containing precompiled chunks.
+This is useful to combine several precompiled chunks,
+even from different (but compatible) platforms,
+into a single precompiled chunk.
+.LP
+You can use
+.B "'\-'"
+to indicate the standard input as a source file
+and
+.B "'\--'"
+to signal the end of options
+(that is,
+all remaining arguments will be treated as files even if they start with
+.BR "'\-'" ).
+.LP
+The internal format of the binary files produced by
+.B luac
+is likely to change when a new version of Lua is released.
+So,
+save the source files of all Lua programs that you precompile.
+.LP
+.SH OPTIONS
+Options must be separate.
+.TP
+.B \-l
+produce a listing of the compiled bytecode for Lua's virtual machine.
+Listing bytecodes is useful to learn about Lua's virtual machine.
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and lists its contents.
+.TP
+.BI \-o " file"
+output to
+.IR file ,
+instead of the default
+.BR luac.out .
+(You can use
+.B "'\-'"
+for standard output,
+but not on platforms that open standard output in text mode.)
+The output file may be a source file because
+all files are loaded before the output file is written.
+Be careful not to overwrite precious files.
+.TP
+.B \-p
+load files but do not generate any output file.
+Used mainly for syntax checking and for testing precompiled chunks:
+corrupted files will probably generate errors when loaded.
+Lua always performs a thorough integrity test on precompiled chunks.
+Bytecode that passes this test is completely safe,
+in the sense that it will not break the interpreter.
+However,
+there is no guarantee that such code does anything sensible.
+(None can be given, because the halting problem is unsolvable.)
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and tests its contents.
+No messages are displayed if the file passes the integrity test.
+.TP
+.B \-s
+strip debug information before writing the output file.
+This saves some space in very large chunks,
+but if errors occur when running a stripped chunk,
+then the error messages may not contain the full information they usually do.
+For instance,
+line numbers and names of local variables are lost.
+.TP
+.B \-v
+show version information.
+.SH FILES
+.TP 15
+.B luac.out
+default output file
+.SH "SEE ALSO"
+.BR lua (1)
+.br
+http://www.lua.org/
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH AUTHORS
+L. H. de Figueiredo,
+R. Ierusalimschy and
+W. Celes
+.\" EOF
diff --git a/engines/sword25/util/lua/doc/luac.html b/engines/sword25/util/lua/doc/luac.html
new file mode 100755
index 0000000000..179ffe8288
--- /dev/null
+++ b/engines/sword25/util/lua/doc/luac.html
@@ -0,0 +1,145 @@
+<!-- $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ -->
+<HTML>
+<HEAD>
+<TITLE>LUAC man page</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+</HEAD>
+
+<BODY BGCOLOR="#FFFFFF">
+
+<H2>NAME</H2>
+luac - Lua compiler
+<H2>SYNOPSIS</H2>
+<B>luac</B>
+[
+<I>options</I>
+] [
+<I>filenames</I>
+]
+<H2>DESCRIPTION</H2>
+<B>luac</B>
+is the Lua compiler.
+It translates programs written in the Lua programming language
+into binary files that can be later loaded and executed.
+<P>
+The main advantages of precompiling chunks are:
+faster loading,
+protecting source code from accidental user changes,
+and
+off-line syntax checking.
+<P>
+Precompiling does not imply faster execution
+because in Lua chunks are always compiled into bytecodes before being executed.
+<B>luac</B>
+simply allows those bytecodes to be saved in a file for later execution.
+<P>
+Precompiled chunks are not necessarily smaller than the corresponding source.
+The main goal in precompiling is faster loading.
+<P>
+The binary files created by
+<B>luac</B>
+are portable only among architectures with the same word size and byte order.
+<P>
+<B>luac</B>
+produces a single output file containing the bytecodes
+for all source files given.
+By default,
+the output file is named
+<B>luac.out</B>,
+but you can change this with the
+<B>-o</B>
+option.
+<P>
+In the command line,
+you can mix
+text files containing Lua source and
+binary files containing precompiled chunks.
+This is useful because several precompiled chunks,
+even from different (but compatible) platforms,
+can be combined into a single precompiled chunk.
+<P>
+You can use
+<B>'-'</B>
+to indicate the standard input as a source file
+and
+<B>'--'</B>
+to signal the end of options
+(that is,
+all remaining arguments will be treated as files even if they start with
+<B>'-'</B>).
+<P>
+The internal format of the binary files produced by
+<B>luac</B>
+is likely to change when a new version of Lua is released.
+So,
+save the source files of all Lua programs that you precompile.
+<P>
+<H2>OPTIONS</H2>
+Options must be separate.
+<P>
+<B>-l</B>
+produce a listing of the compiled bytecode for Lua's virtual machine.
+Listing bytecodes is useful to learn about Lua's virtual machine.
+If no files are given, then
+<B>luac</B>
+loads
+<B>luac.out</B>
+and lists its contents.
+<P>
+<B>-o </B><I>file</I>
+output to
+<I>file</I>,
+instead of the default
+<B>luac.out</B>.
+(You can use
+<B>'-'</B>
+for standard output,
+but not on platforms that open standard output in text mode.)
+The output file may be a source file because
+all files are loaded before the output file is written.
+Be careful not to overwrite precious files.
+<P>
+<B>-p</B>
+load files but do not generate any output file.
+Used mainly for syntax checking and for testing precompiled chunks:
+corrupted files will probably generate errors when loaded.
+Lua always performs a thorough integrity test on precompiled chunks.
+Bytecode that passes this test is completely safe,
+in the sense that it will not break the interpreter.
+However,
+there is no guarantee that such code does anything sensible.
+(None can be given, because the halting problem is unsolvable.)
+If no files are given, then
+<B>luac</B>
+loads
+<B>luac.out</B>
+and tests its contents.
+No messages are displayed if the file passes the integrity test.
+<P>
+<B>-s</B>
+strip debug information before writing the output file.
+This saves some space in very large chunks,
+but if errors occur when running a stripped chunk,
+then the error messages may not contain the full information they usually do.
+For instance,
+line numbers and names of local variables are lost.
+<P>
+<B>-v</B>
+show version information.
+<H2>FILES</H2>
+<P>
+<B>luac.out</B>
+default output file
+<H2>SEE ALSO</H2>
+<B>lua</B>(1)
+<BR>
+<A HREF="http://www.lua.org/">http://www.lua.org/</A>
+<H2>DIAGNOSTICS</H2>
+Error messages should be self explanatory.
+<H2>AUTHORS</H2>
+L. H. de Figueiredo,
+R. Ierusalimschy and
+W. Celes
+<!-- EOF -->
+</BODY>
+</HTML>
diff --git a/engines/sword25/util/lua/doc/manual.css b/engines/sword25/util/lua/doc/manual.css
new file mode 100755
index 0000000000..eed5afd9ee
--- /dev/null
+++ b/engines/sword25/util/lua/doc/manual.css
@@ -0,0 +1,13 @@
+h3 code {
+ font-family: inherit ;
+}
+
+pre {
+ font-size: 105% ;
+}
+
+span.apii {
+ float: right ;
+ font-family: inherit ;
+}
+
diff --git a/engines/sword25/util/lua/doc/manual.html b/engines/sword25/util/lua/doc/manual.html
new file mode 100755
index 0000000000..b125c13d88
--- /dev/null
+++ b/engines/sword25/util/lua/doc/manual.html
@@ -0,0 +1,8764 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+<title>Lua 5.1 Reference Manual</title>
+<link rel="stylesheet" href="lua.css">
+<link rel="stylesheet" href="manual.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+</head>
+
+<body>
+
+<hr>
+<h1>
+<a href="http://www.lua.org/"><img src="logo.gif" alt="" border="0"></a>
+Lua 5.1 Reference Manual
+</h1>
+
+by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
+<p>
+<small>
+Copyright &copy; 2006-2008 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<a href="http://www.lua.org/license.html#5">Lua license</a>.
+</small>
+<hr>
+<p>
+
+<a href="contents.html#contents">contents</A>
+&middot;
+<a href="contents.html#index">index</A>
+
+<!-- ====================================================================== -->
+<p>
+
+<!-- $Id: manual.of,v 1.45 2008/01/19 00:17:30 roberto Exp $ -->
+
+
+
+
+<h1>1 - <a name="1">Introduction</a></h1>
+
+<p>
+Lua is an extension programming language designed to support
+general procedural programming with data description
+facilities.
+It also offers good support for object-oriented programming,
+functional programming, and data-driven programming.
+Lua is intended to be used as a powerful, light-weight
+scripting language for any program that needs one.
+Lua is implemented as a library, written in <em>clean</em> C
+(that is, in the common subset of ANSI&nbsp;C and C++).
+
+
+<p>
+Being an extension language, Lua has no notion of a "main" program:
+it only works <em>embedded</em> in a host client,
+called the <em>embedding program</em> or simply the <em>host</em>.
+This host program can invoke functions to execute a piece of Lua code,
+can write and read Lua variables,
+and can register C&nbsp;functions to be called by Lua code.
+Through the use of C&nbsp;functions, Lua can be augmented to cope with
+a wide range of different domains,
+thus creating customized programming languages sharing a syntactical framework.
+The Lua distribution includes a sample host program called <code>lua</code>,
+which uses the Lua library to offer a complete, stand-alone Lua interpreter.
+
+
+<p>
+Lua is free software,
+and is provided as usual with no guarantees,
+as stated in its license.
+The implementation described in this manual is available
+at Lua's official web site, <code>www.lua.org</code>.
+
+
+<p>
+Like any other reference manual,
+this document is dry in places.
+For a discussion of the decisions behind the design of Lua,
+see the technical papers available at Lua's web site.
+For a detailed introduction to programming in Lua,
+see Roberto's book, <em>Programming in Lua (Second Edition)</em>.
+
+
+
+<h1>2 - <a name="2">The Language</a></h1>
+
+<p>
+This section describes the lexis, the syntax, and the semantics of Lua.
+In other words,
+this section describes
+which tokens are valid,
+how they can be combined,
+and what their combinations mean.
+
+
+<p>
+The language constructs will be explained using the usual extended BNF notation,
+in which
+{<em>a</em>}&nbsp;means&nbsp;0 or more <em>a</em>'s, and
+[<em>a</em>]&nbsp;means an optional <em>a</em>.
+Non-terminals are shown like non-terminal,
+keywords are shown like <b>kword</b>,
+and other terminal symbols are shown like `<b>=</b>&acute;.
+The complete syntax of Lua can be found at the end of this manual.
+
+
+
+<h2>2.1 - <a name="2.1">Lexical Conventions</a></h2>
+
+<p>
+<em>Names</em>
+(also called <em>identifiers</em>)
+in Lua can be any string of letters,
+digits, and underscores,
+not beginning with a digit.
+This coincides with the definition of names in most languages.
+(The definition of letter depends on the current locale:
+any character considered alphabetic by the current locale
+can be used in an identifier.)
+Identifiers are used to name variables and table fields.
+
+
+<p>
+The following <em>keywords</em> are reserved
+and cannot be used as names:
+
+
+<pre>
+ and break do else elseif
+ end false for function if
+ in local nil not or
+ repeat return then true until while
+</pre>
+
+<p>
+Lua is a case-sensitive language:
+<code>and</code> is a reserved word, but <code>And</code> and <code>AND</code>
+are two different, valid names.
+As a convention, names starting with an underscore followed by
+uppercase letters (such as <code>_VERSION</code>)
+are reserved for internal global variables used by Lua.
+
+
+<p>
+The following strings denote other tokens:
+
+<pre>
+ + - * / % ^ #
+ == ~= &lt;= &gt;= &lt; &gt; =
+ ( ) { } [ ]
+ ; : , . .. ...
+</pre>
+
+<p>
+<em>Literal strings</em>
+can be delimited by matching single or double quotes,
+and can contain the following C-like escape sequences:
+'<code>\a</code>' (bell),
+'<code>\b</code>' (backspace),
+'<code>\f</code>' (form feed),
+'<code>\n</code>' (newline),
+'<code>\r</code>' (carriage return),
+'<code>\t</code>' (horizontal tab),
+'<code>\v</code>' (vertical tab),
+'<code>\\</code>' (backslash),
+'<code>\"</code>' (quotation mark [double quote]),
+and '<code>\'</code>' (apostrophe [single quote]).
+Moreover, a backslash followed by a real newline
+results in a newline in the string.
+A character in a string may also be specified by its numerical value
+using the escape sequence <code>\<em>ddd</em></code>,
+where <em>ddd</em> is a sequence of up to three decimal digits.
+(Note that if a numerical escape is to be followed by a digit,
+it must be expressed using exactly three digits.)
+Strings in Lua may contain any 8-bit value, including embedded zeros,
+which can be specified as '<code>\0</code>'.
+
+
+<p>
+To put a double (single) quote, a newline, a backslash,
+a carriage return,
+or an embedded zero
+inside a literal string enclosed by double (single) quotes
+you must use an escape sequence.
+Any other character may be directly inserted into the literal.
+(Some control characters may cause problems for the file system,
+but Lua has no problem with them.)
+
+
+<p>
+Literal strings can also be defined using a long format
+enclosed by <em>long brackets</em>.
+We define an <em>opening long bracket of level <em>n</em></em> as an opening
+square bracket followed by <em>n</em> equal signs followed by another
+opening square bracket.
+So, an opening long bracket of level&nbsp;0 is written as <code>[[</code>,
+an opening long bracket of level&nbsp;1 is written as <code>[=[</code>,
+and so on.
+A <em>closing long bracket</em> is defined similarly;
+for instance, a closing long bracket of level&nbsp;4 is written as <code>]====]</code>.
+A long string starts with an opening long bracket of any level and
+ends at the first closing long bracket of the same level.
+Literals in this bracketed form may run for several lines,
+do not interpret any escape sequences,
+and ignore long brackets of any other level.
+They may contain anything except a closing bracket of the proper level.
+
+
+<p>
+For convenience,
+when the opening long bracket is immediately followed by a newline,
+the newline is not included in the string.
+As an example, in a system using ASCII
+(in which '<code>a</code>' is coded as&nbsp;97,
+newline is coded as&nbsp;10, and '<code>1</code>' is coded as&nbsp;49),
+the five literals below denote the same string:
+
+<pre>
+ a = 'alo\n123"'
+ a = "alo\n123\""
+ a = '\97lo\10\04923"'
+ a = [[alo
+ 123"]]
+ a = [==[
+ alo
+ 123"]==]
+</pre>
+
+<p>
+A <em>numerical constant</em> may be written with an optional decimal part
+and an optional decimal exponent.
+Lua also accepts integer hexadecimal constants,
+by prefixing them with <code>0x</code>.
+Examples of valid numerical constants are
+
+<pre>
+ 3 3.0 3.1416 314.16e-2 0.31416E1 0xff 0x56
+</pre>
+
+<p>
+A <em>comment</em> starts with a double hyphen (<code>--</code>)
+anywhere outside a string.
+If the text immediately after <code>--</code> is not an opening long bracket,
+the comment is a <em>short comment</em>,
+which runs until the end of the line.
+Otherwise, it is a <em>long comment</em>,
+which runs until the corresponding closing long bracket.
+Long comments are frequently used to disable code temporarily.
+
+
+
+
+
+<h2>2.2 - <a name="2.2">Values and Types</a></h2>
+
+<p>
+Lua is a <em>dynamically typed language</em>.
+This means that
+variables do not have types; only values do.
+There are no type definitions in the language.
+All values carry their own type.
+
+
+<p>
+All values in Lua are <em>first-class values</em>.
+This means that all values can be stored in variables,
+passed as arguments to other functions, and returned as results.
+
+
+<p>
+There are eight basic types in Lua:
+<em>nil</em>, <em>boolean</em>, <em>number</em>,
+<em>string</em>, <em>function</em>, <em>userdata</em>,
+<em>thread</em>, and <em>table</em>.
+<em>Nil</em> is the type of the value <b>nil</b>,
+whose main property is to be different from any other value;
+it usually represents the absence of a useful value.
+<em>Boolean</em> is the type of the values <b>false</b> and <b>true</b>.
+Both <b>nil</b> and <b>false</b> make a condition false;
+any other value makes it true.
+<em>Number</em> represents real (double-precision floating-point) numbers.
+(It is easy to build Lua interpreters that use other
+internal representations for numbers,
+such as single-precision float or long integers;
+see file <code>luaconf.h</code>.)
+<em>String</em> represents arrays of characters.
+
+Lua is 8-bit clean:
+strings may contain any 8-bit character,
+including embedded zeros ('<code>\0</code>') (see <a href="#2.1">&sect;2.1</a>).
+
+
+<p>
+Lua can call (and manipulate) functions written in Lua and
+functions written in C
+(see <a href="#2.5.8">&sect;2.5.8</a>).
+
+
+<p>
+The type <em>userdata</em> is provided to allow arbitrary C&nbsp;data to
+be stored in Lua variables.
+This type corresponds to a block of raw memory
+and has no pre-defined operations in Lua,
+except assignment and identity test.
+However, by using <em>metatables</em>,
+the programmer can define operations for userdata values
+(see <a href="#2.8">&sect;2.8</a>).
+Userdata values cannot be created or modified in Lua,
+only through the C&nbsp;API.
+This guarantees the integrity of data owned by the host program.
+
+
+<p>
+The type <em>thread</em> represents independent threads of execution
+and it is used to implement coroutines (see <a href="#2.11">&sect;2.11</a>).
+Do not confuse Lua threads with operating-system threads.
+Lua supports coroutines on all systems,
+even those that do not support threads.
+
+
+<p>
+The type <em>table</em> implements associative arrays,
+that is, arrays that can be indexed not only with numbers,
+but with any value (except <b>nil</b>).
+Tables can be <em>heterogeneous</em>;
+that is, they can contain values of all types (except <b>nil</b>).
+Tables are the sole data structuring mechanism in Lua;
+they may be used to represent ordinary arrays,
+symbol tables, sets, records, graphs, trees, etc.
+To represent records, Lua uses the field name as an index.
+The language supports this representation by
+providing <code>a.name</code> as syntactic sugar for <code>a["name"]</code>.
+There are several convenient ways to create tables in Lua
+(see <a href="#2.5.7">&sect;2.5.7</a>).
+
+
+<p>
+Like indices,
+the value of a table field can be of any type (except <b>nil</b>).
+In particular,
+because functions are first-class values,
+table fields may contain functions.
+Thus tables may also carry <em>methods</em> (see <a href="#2.5.9">&sect;2.5.9</a>).
+
+
+<p>
+Tables, functions, threads, and (full) userdata values are <em>objects</em>:
+variables do not actually <em>contain</em> these values,
+only <em>references</em> to them.
+Assignment, parameter passing, and function returns
+always manipulate references to such values;
+these operations do not imply any kind of copy.
+
+
+<p>
+The library function <a href="#pdf-type"><code>type</code></a> returns a string describing the type
+of a given value.
+
+
+
+<h3>2.2.1 - <a name="2.2.1">Coercion</a></h3>
+
+<p>
+Lua provides automatic conversion between
+string and number values at run time.
+Any arithmetic operation applied to a string tries to convert
+this string to a number, following the usual conversion rules.
+Conversely, whenever a number is used where a string is expected,
+the number is converted to a string, in a reasonable format.
+For complete control over how numbers are converted to strings,
+use the <code>format</code> function from the string library
+(see <a href="#pdf-string.format"><code>string.format</code></a>).
+
+
+
+
+
+
+
+<h2>2.3 - <a name="2.3">Variables</a></h2>
+
+<p>
+Variables are places that store values.
+
+There are three kinds of variables in Lua:
+global variables, local variables, and table fields.
+
+
+<p>
+A single name can denote a global variable or a local variable
+(or a function's formal parameter,
+which is a particular kind of local variable):
+
+<pre>
+ var ::= Name
+</pre><p>
+Name denotes identifiers, as defined in <a href="#2.1">&sect;2.1</a>.
+
+
+<p>
+Any variable is assumed to be global unless explicitly declared
+as a local (see <a href="#2.4.7">&sect;2.4.7</a>).
+Local variables are <em>lexically scoped</em>:
+local variables can be freely accessed by functions
+defined inside their scope (see <a href="#2.6">&sect;2.6</a>).
+
+
+<p>
+Before the first assignment to a variable, its value is <b>nil</b>.
+
+
+<p>
+Square brackets are used to index a table:
+
+<pre>
+ var ::= prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute;
+</pre><p>
+The meaning of accesses to global variables
+and table fields can be changed via metatables.
+An access to an indexed variable <code>t[i]</code> is equivalent to
+a call <code>gettable_event(t,i)</code>.
+(See <a href="#2.8">&sect;2.8</a> for a complete description of the
+<code>gettable_event</code> function.
+This function is not defined or callable in Lua.
+We use it here only for explanatory purposes.)
+
+
+<p>
+The syntax <code>var.Name</code> is just syntactic sugar for
+<code>var["Name"]</code>:
+
+<pre>
+ var ::= prefixexp `<b>.</b>&acute; Name
+</pre>
+
+<p>
+All global variables live as fields in ordinary Lua tables,
+called <em>environment tables</em> or simply
+<em>environments</em> (see <a href="#2.9">&sect;2.9</a>).
+Each function has its own reference to an environment,
+so that all global variables in this function
+will refer to this environment table.
+When a function is created,
+it inherits the environment from the function that created it.
+To get the environment table of a Lua function,
+you call <a href="#pdf-getfenv"><code>getfenv</code></a>.
+To replace it,
+you call <a href="#pdf-setfenv"><code>setfenv</code></a>.
+(You can only manipulate the environment of C&nbsp;functions
+through the debug library; (see <a href="#5.9">&sect;5.9</a>).)
+
+
+<p>
+An access to a global variable <code>x</code>
+is equivalent to <code>_env.x</code>,
+which in turn is equivalent to
+
+<pre>
+ gettable_event(_env, "x")
+</pre><p>
+where <code>_env</code> is the environment of the running function.
+(See <a href="#2.8">&sect;2.8</a> for a complete description of the
+<code>gettable_event</code> function.
+This function is not defined or callable in Lua.
+Similarly, the <code>_env</code> variable is not defined in Lua.
+We use them here only for explanatory purposes.)
+
+
+
+
+
+<h2>2.4 - <a name="2.4">Statements</a></h2>
+
+<p>
+Lua supports an almost conventional set of statements,
+similar to those in Pascal or C.
+This set includes
+assignment, control structures, function calls,
+and variable declarations.
+
+
+
+<h3>2.4.1 - <a name="2.4.1">Chunks</a></h3>
+
+<p>
+The unit of execution of Lua is called a <em>chunk</em>.
+A chunk is simply a sequence of statements,
+which are executed sequentially.
+Each statement can be optionally followed by a semicolon:
+
+<pre>
+ chunk ::= {stat [`<b>;</b>&acute;]}
+</pre><p>
+There are no empty statements and thus '<code>;;</code>' is not legal.
+
+
+<p>
+Lua handles a chunk as the body of an anonymous function
+with a variable number of arguments
+(see <a href="#2.5.9">&sect;2.5.9</a>).
+As such, chunks can define local variables,
+receive arguments, and return values.
+
+
+<p>
+A chunk may be stored in a file or in a string inside the host program.
+When a chunk is executed, first it is pre-compiled into instructions for
+a virtual machine,
+and then the compiled code is executed
+by an interpreter for the virtual machine.
+
+
+<p>
+Chunks may also be pre-compiled into binary form;
+see program <code>luac</code> for details.
+Programs in source and compiled forms are interchangeable;
+Lua automatically detects the file type and acts accordingly.
+
+
+
+
+
+
+<h3>2.4.2 - <a name="2.4.2">Blocks</a></h3><p>
+A block is a list of statements;
+syntactically, a block is the same as a chunk:
+
+<pre>
+ block ::= chunk
+</pre>
+
+<p>
+A block may be explicitly delimited to produce a single statement:
+
+<pre>
+ stat ::= <b>do</b> block <b>end</b>
+</pre><p>
+Explicit blocks are useful
+to control the scope of variable declarations.
+Explicit blocks are also sometimes used to
+add a <b>return</b> or <b>break</b> statement in the middle
+of another block (see <a href="#2.4.4">&sect;2.4.4</a>).
+
+
+
+
+
+<h3>2.4.3 - <a name="2.4.3">Assignment</a></h3>
+
+<p>
+Lua allows multiple assignment.
+Therefore, the syntax for assignment
+defines a list of variables on the left side
+and a list of expressions on the right side.
+The elements in both lists are separated by commas:
+
+<pre>
+ stat ::= varlist `<b>=</b>&acute; explist
+ varlist ::= var {`<b>,</b>&acute; var}
+ explist ::= exp {`<b>,</b>&acute; exp}
+</pre><p>
+Expressions are discussed in <a href="#2.5">&sect;2.5</a>.
+
+
+<p>
+Before the assignment,
+the list of values is <em>adjusted</em> to the length of
+the list of variables.
+If there are more values than needed,
+the excess values are thrown away.
+If there are fewer values than needed,
+the list is extended with as many <b>nil</b>'s as needed.
+If the list of expressions ends with a function call,
+then all values returned by this call enter in the list of values,
+before the adjustment
+(except when the call is enclosed in parentheses; see <a href="#2.5">&sect;2.5</a>).
+
+
+<p>
+The assignment statement first evaluates all its expressions
+and only then are the assignments performed.
+Thus the code
+
+<pre>
+ i = 3
+ i, a[i] = i+1, 20
+</pre><p>
+sets <code>a[3]</code> to 20, without affecting <code>a[4]</code>
+because the <code>i</code> in <code>a[i]</code> is evaluated (to 3)
+before it is assigned&nbsp;4.
+Similarly, the line
+
+<pre>
+ x, y = y, x
+</pre><p>
+exchanges the values of <code>x</code> and <code>y</code>.
+
+
+<p>
+The meaning of assignments to global variables
+and table fields can be changed via metatables.
+An assignment to an indexed variable <code>t[i] = val</code> is equivalent to
+<code>settable_event(t,i,val)</code>.
+(See <a href="#2.8">&sect;2.8</a> for a complete description of the
+<code>settable_event</code> function.
+This function is not defined or callable in Lua.
+We use it here only for explanatory purposes.)
+
+
+<p>
+An assignment to a global variable <code>x = val</code>
+is equivalent to the assignment
+<code>_env.x = val</code>,
+which in turn is equivalent to
+
+<pre>
+ settable_event(_env, "x", val)
+</pre><p>
+where <code>_env</code> is the environment of the running function.
+(The <code>_env</code> variable is not defined in Lua.
+We use it here only for explanatory purposes.)
+
+
+
+
+
+<h3>2.4.4 - <a name="2.4.4">Control Structures</a></h3><p>
+The control structures
+<b>if</b>, <b>while</b>, and <b>repeat</b> have the usual meaning and
+familiar syntax:
+
+
+
+
+<pre>
+ stat ::= <b>while</b> exp <b>do</b> block <b>end</b>
+ stat ::= <b>repeat</b> block <b>until</b> exp
+ stat ::= <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b>
+</pre><p>
+Lua also has a <b>for</b> statement, in two flavors (see <a href="#2.4.5">&sect;2.4.5</a>).
+
+
+<p>
+The condition expression of a
+control structure may return any value.
+Both <b>false</b> and <b>nil</b> are considered false.
+All values different from <b>nil</b> and <b>false</b> are considered true
+(in particular, the number 0 and the empty string are also true).
+
+
+<p>
+In the <b>repeat</b>&ndash;<b>until</b> loop,
+the inner block does not end at the <b>until</b> keyword,
+but only after the condition.
+So, the condition can refer to local variables
+declared inside the loop block.
+
+
+<p>
+The <b>return</b> statement is used to return values
+from a function or a chunk (which is just a function).
+
+Functions and chunks may return more than one value,
+so the syntax for the <b>return</b> statement is
+
+<pre>
+ stat ::= <b>return</b> [explist]
+</pre>
+
+<p>
+The <b>break</b> statement is used to terminate the execution of a
+<b>while</b>, <b>repeat</b>, or <b>for</b> loop,
+skipping to the next statement after the loop:
+
+
+<pre>
+ stat ::= <b>break</b>
+</pre><p>
+A <b>break</b> ends the innermost enclosing loop.
+
+
+<p>
+The <b>return</b> and <b>break</b>
+statements can only be written as the <em>last</em> statement of a block.
+If it is really necessary to <b>return</b> or <b>break</b> in the
+middle of a block,
+then an explicit inner block can be used,
+as in the idioms
+<code>do return end</code> and <code>do break end</code>,
+because now <b>return</b> and <b>break</b> are the last statements in
+their (inner) blocks.
+
+
+
+
+
+<h3>2.4.5 - <a name="2.4.5">For Statement</a></h3>
+
+<p>
+
+The <b>for</b> statement has two forms:
+one numeric and one generic.
+
+
+<p>
+The numeric <b>for</b> loop repeats a block of code while a
+control variable runs through an arithmetic progression.
+It has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b>
+</pre><p>
+The <em>block</em> is repeated for <em>name</em> starting at the value of
+the first <em>exp</em>, until it passes the second <em>exp</em> by steps of the
+third <em>exp</em>.
+More precisely, a <b>for</b> statement like
+
+<pre>
+ for v = <em>e1</em>, <em>e2</em>, <em>e3</em> do <em>block</em> end
+</pre><p>
+is equivalent to the code:
+
+<pre>
+ do
+ local <em>var</em>, <em>limit</em>, <em>step</em> = tonumber(<em>e1</em>), tonumber(<em>e2</em>), tonumber(<em>e3</em>)
+ if not (<em>var</em> and <em>limit</em> and <em>step</em>) then error() end
+ while (<em>step</em> &gt; 0 and <em>var</em> &lt;= <em>limit</em>) or (<em>step</em> &lt;= 0 and <em>var</em> &gt;= <em>limit</em>) do
+ local v = <em>var</em>
+ <em>block</em>
+ <em>var</em> = <em>var</em> + <em>step</em>
+ end
+ end
+</pre><p>
+Note the following:
+
+<ul>
+
+<li>
+All three control expressions are evaluated only once,
+before the loop starts.
+They must all result in numbers.
+</li>
+
+<li>
+<code><em>var</em></code>, <code><em>limit</em></code>, and <code><em>step</em></code> are invisible variables.
+The names are here for explanatory purposes only.
+</li>
+
+<li>
+If the third expression (the step) is absent,
+then a step of&nbsp;1 is used.
+</li>
+
+<li>
+You can use <b>break</b> to exit a <b>for</b> loop.
+</li>
+
+<li>
+The loop variable <code>v</code> is local to the loop;
+you cannot use its value after the <b>for</b> ends or is broken.
+If you need this value,
+assign it to another variable before breaking or exiting the loop.
+</li>
+
+</ul>
+
+<p>
+The generic <b>for</b> statement works over functions,
+called <em>iterators</em>.
+On each iteration, the iterator function is called to produce a new value,
+stopping when this new value is <b>nil</b>.
+The generic <b>for</b> loop has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b>
+ namelist ::= Name {`<b>,</b>&acute; Name}
+</pre><p>
+A <b>for</b> statement like
+
+<pre>
+ for <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> in <em>explist</em> do <em>block</em> end
+</pre><p>
+is equivalent to the code:
+
+<pre>
+ do
+ local <em>f</em>, <em>s</em>, <em>var</em> = <em>explist</em>
+ while true do
+ local <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> = <em>f</em>(<em>s</em>, <em>var</em>)
+ <em>var</em> = <em>var_1</em>
+ if <em>var</em> == nil then break end
+ <em>block</em>
+ end
+ end
+</pre><p>
+Note the following:
+
+<ul>
+
+<li>
+<code><em>explist</em></code> is evaluated only once.
+Its results are an <em>iterator</em> function,
+a <em>state</em>,
+and an initial value for the first <em>iterator variable</em>.
+</li>
+
+<li>
+<code><em>f</em></code>, <code><em>s</em></code>, and <code><em>var</em></code> are invisible variables.
+The names are here for explanatory purposes only.
+</li>
+
+<li>
+You can use <b>break</b> to exit a <b>for</b> loop.
+</li>
+
+<li>
+The loop variables <code><em>var_i</em></code> are local to the loop;
+you cannot use their values after the <b>for</b> ends.
+If you need these values,
+then assign them to other variables before breaking or exiting the loop.
+</li>
+
+</ul>
+
+
+
+
+<h3>2.4.6 - <a name="2.4.6">Function Calls as Statements</a></h3><p>
+To allow possible side-effects,
+function calls can be executed as statements:
+
+<pre>
+ stat ::= functioncall
+</pre><p>
+In this case, all returned values are thrown away.
+Function calls are explained in <a href="#2.5.8">&sect;2.5.8</a>.
+
+
+
+
+
+<h3>2.4.7 - <a name="2.4.7">Local Declarations</a></h3><p>
+Local variables may be declared anywhere inside a block.
+The declaration may include an initial assignment:
+
+<pre>
+ stat ::= <b>local</b> namelist [`<b>=</b>&acute; explist]
+</pre><p>
+If present, an initial assignment has the same semantics
+of a multiple assignment (see <a href="#2.4.3">&sect;2.4.3</a>).
+Otherwise, all variables are initialized with <b>nil</b>.
+
+
+<p>
+A chunk is also a block (see <a href="#2.4.1">&sect;2.4.1</a>),
+and so local variables can be declared in a chunk outside any explicit block.
+The scope of such local variables extends until the end of the chunk.
+
+
+<p>
+The visibility rules for local variables are explained in <a href="#2.6">&sect;2.6</a>.
+
+
+
+
+
+
+
+<h2>2.5 - <a name="2.5">Expressions</a></h2>
+
+<p>
+The basic expressions in Lua are the following:
+
+<pre>
+ exp ::= prefixexp
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b>
+ exp ::= Number
+ exp ::= String
+ exp ::= function
+ exp ::= tableconstructor
+ exp ::= `<b>...</b>&acute;
+ exp ::= exp binop exp
+ exp ::= unop exp
+ prefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;
+</pre>
+
+<p>
+Numbers and literal strings are explained in <a href="#2.1">&sect;2.1</a>;
+variables are explained in <a href="#2.3">&sect;2.3</a>;
+function definitions are explained in <a href="#2.5.9">&sect;2.5.9</a>;
+function calls are explained in <a href="#2.5.8">&sect;2.5.8</a>;
+table constructors are explained in <a href="#2.5.7">&sect;2.5.7</a>.
+Vararg expressions,
+denoted by three dots ('<code>...</code>'), can only be used when
+directly inside a vararg function;
+they are explained in <a href="#2.5.9">&sect;2.5.9</a>.
+
+
+<p>
+Binary operators comprise arithmetic operators (see <a href="#2.5.1">&sect;2.5.1</a>),
+relational operators (see <a href="#2.5.2">&sect;2.5.2</a>), logical operators (see <a href="#2.5.3">&sect;2.5.3</a>),
+and the concatenation operator (see <a href="#2.5.4">&sect;2.5.4</a>).
+Unary operators comprise the unary minus (see <a href="#2.5.1">&sect;2.5.1</a>),
+the unary <b>not</b> (see <a href="#2.5.3">&sect;2.5.3</a>),
+and the unary <em>length operator</em> (see <a href="#2.5.5">&sect;2.5.5</a>).
+
+
+<p>
+Both function calls and vararg expressions may result in multiple values.
+If the expression is used as a statement (see <a href="#2.4.6">&sect;2.4.6</a>)
+(only possible for function calls),
+then its return list is adjusted to zero elements,
+thus discarding all returned values.
+If the expression is used as the last (or the only) element
+of a list of expressions,
+then no adjustment is made
+(unless the call is enclosed in parentheses).
+In all other contexts,
+Lua adjusts the result list to one element,
+discarding all values except the first one.
+
+
+<p>
+Here are some examples:
+
+<pre>
+ f() -- adjusted to 0 results
+ g(f(), x) -- f() is adjusted to 1 result
+ g(x, f()) -- g gets x plus all results from f()
+ a,b,c = f(), x -- f() is adjusted to 1 result (c gets nil)
+ a,b = ... -- a gets the first vararg parameter, b gets
+ -- the second (both a and b may get nil if there
+ -- is no corresponding vararg parameter)
+
+ a,b,c = x, f() -- f() is adjusted to 2 results
+ a,b,c = f() -- f() is adjusted to 3 results
+ return f() -- returns all results from f()
+ return ... -- returns all received vararg parameters
+ return x,y,f() -- returns x, y, and all results from f()
+ {f()} -- creates a list with all results from f()
+ {...} -- creates a list with all vararg parameters
+ {f(), nil} -- f() is adjusted to 1 result
+</pre>
+
+<p>
+An expression enclosed in parentheses always results in only one value.
+Thus,
+<code>(f(x,y,z))</code> is always a single value,
+even if <code>f</code> returns several values.
+(The value of <code>(f(x,y,z))</code> is the first value returned by <code>f</code>
+or <b>nil</b> if <code>f</code> does not return any values.)
+
+
+
+<h3>2.5.1 - <a name="2.5.1">Arithmetic Operators</a></h3><p>
+Lua supports the usual arithmetic operators:
+the binary <code>+</code> (addition),
+<code>-</code> (subtraction), <code>*</code> (multiplication),
+<code>/</code> (division), <code>%</code> (modulo), and <code>^</code> (exponentiation);
+and unary <code>-</code> (negation).
+If the operands are numbers, or strings that can be converted to
+numbers (see <a href="#2.2.1">&sect;2.2.1</a>),
+then all operations have the usual meaning.
+Exponentiation works for any exponent.
+For instance, <code>x^(-0.5)</code> computes the inverse of the square root of <code>x</code>.
+Modulo is defined as
+
+<pre>
+ a % b == a - math.floor(a/b)*b
+</pre><p>
+That is, it is the remainder of a division that rounds
+the quotient towards minus infinity.
+
+
+
+
+
+<h3>2.5.2 - <a name="2.5.2">Relational Operators</a></h3><p>
+The relational operators in Lua are
+
+<pre>
+ == ~= &lt; &gt; &lt;= &gt;=
+</pre><p>
+These operators always result in <b>false</b> or <b>true</b>.
+
+
+<p>
+Equality (<code>==</code>) first compares the type of its operands.
+If the types are different, then the result is <b>false</b>.
+Otherwise, the values of the operands are compared.
+Numbers and strings are compared in the usual way.
+Objects (tables, userdata, threads, and functions)
+are compared by <em>reference</em>:
+two objects are considered equal only if they are the <em>same</em> object.
+Every time you create a new object
+(a table, userdata, thread, or function),
+this new object is different from any previously existing object.
+
+
+<p>
+You can change the way that Lua compares tables and userdata
+by using the "eq" metamethod (see <a href="#2.8">&sect;2.8</a>).
+
+
+<p>
+The conversion rules of <a href="#2.2.1">&sect;2.2.1</a>
+<em>do not</em> apply to equality comparisons.
+Thus, <code>"0"==0</code> evaluates to <b>false</b>,
+and <code>t[0]</code> and <code>t["0"]</code> denote different
+entries in a table.
+
+
+<p>
+The operator <code>~=</code> is exactly the negation of equality (<code>==</code>).
+
+
+<p>
+The order operators work as follows.
+If both arguments are numbers, then they are compared as such.
+Otherwise, if both arguments are strings,
+then their values are compared according to the current locale.
+Otherwise, Lua tries to call the "lt" or the "le"
+metamethod (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<h3>2.5.3 - <a name="2.5.3">Logical Operators</a></h3><p>
+The logical operators in Lua are
+<b>and</b>, <b>or</b>, and <b>not</b>.
+Like the control structures (see <a href="#2.4.4">&sect;2.4.4</a>),
+all logical operators consider both <b>false</b> and <b>nil</b> as false
+and anything else as true.
+
+
+<p>
+The negation operator <b>not</b> always returns <b>false</b> or <b>true</b>.
+The conjunction operator <b>and</b> returns its first argument
+if this value is <b>false</b> or <b>nil</b>;
+otherwise, <b>and</b> returns its second argument.
+The disjunction operator <b>or</b> returns its first argument
+if this value is different from <b>nil</b> and <b>false</b>;
+otherwise, <b>or</b> returns its second argument.
+Both <b>and</b> and <b>or</b> use short-cut evaluation;
+that is,
+the second operand is evaluated only if necessary.
+Here are some examples:
+
+<pre>
+ 10 or 20 --&gt; 10
+ 10 or error() --&gt; 10
+ nil or "a" --&gt; "a"
+ nil and 10 --&gt; nil
+ false and error() --&gt; false
+ false and nil --&gt; false
+ false or nil --&gt; nil
+ 10 and 20 --&gt; 20
+</pre><p>
+(In this manual,
+--> indicates the result of the preceding expression.)
+
+
+
+
+
+<h3>2.5.4 - <a name="2.5.4">Concatenation</a></h3><p>
+The string concatenation operator in Lua is
+denoted by two dots ('<code>..</code>').
+If both operands are strings or numbers, then they are converted to
+strings according to the rules mentioned in <a href="#2.2.1">&sect;2.2.1</a>.
+Otherwise, the "concat" metamethod is called (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<h3>2.5.5 - <a name="2.5.5">The Length Operator</a></h3>
+
+<p>
+The length operator is denoted by the unary operator <code>#</code>.
+The length of a string is its number of bytes
+(that is, the usual meaning of string length when each
+character is one byte).
+
+
+<p>
+The length of a table <code>t</code> is defined to be any
+integer index <code>n</code>
+such that <code>t[n]</code> is not <b>nil</b> and <code>t[n+1]</code> is <b>nil</b>;
+moreover, if <code>t[1]</code> is <b>nil</b>, <code>n</code> may be zero.
+For a regular array, with non-nil values from 1 to a given <code>n</code>,
+its length is exactly that <code>n</code>,
+the index of its last value.
+If the array has "holes"
+(that is, <b>nil</b> values between other non-nil values),
+then <code>#t</code> may be any of the indices that
+directly precedes a <b>nil</b> value
+(that is, it may consider any such <b>nil</b> value as the end of
+the array).
+
+
+
+
+
+<h3>2.5.6 - <a name="2.5.6">Precedence</a></h3><p>
+Operator precedence in Lua follows the table below,
+from lower to higher priority:
+
+<pre>
+ or
+ and
+ &lt; &gt; &lt;= &gt;= ~= ==
+ ..
+ + -
+ * / %
+ not # - (unary)
+ ^
+</pre><p>
+As usual,
+you can use parentheses to change the precedences of an expression.
+The concatenation ('<code>..</code>') and exponentiation ('<code>^</code>')
+operators are right associative.
+All other binary operators are left associative.
+
+
+
+
+
+<h3>2.5.7 - <a name="2.5.7">Table Constructors</a></h3><p>
+Table constructors are expressions that create tables.
+Every time a constructor is evaluated, a new table is created.
+Constructors can be used to create empty tables,
+or to create a table and initialize some of its fields.
+The general syntax for constructors is
+
+<pre>
+ tableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;
+ fieldlist ::= field {fieldsep field} [fieldsep]
+ field ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp
+ fieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;
+</pre>
+
+<p>
+Each field of the form <code>[exp1] = exp2</code> adds to the new table an entry
+with key <code>exp1</code> and value <code>exp2</code>.
+A field of the form <code>name = exp</code> is equivalent to
+<code>["name"] = exp</code>.
+Finally, fields of the form <code>exp</code> are equivalent to
+<code>[i] = exp</code>, where <code>i</code> are consecutive numerical integers,
+starting with 1.
+Fields in the other formats do not affect this counting.
+For example,
+
+<pre>
+ a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
+</pre><p>
+is equivalent to
+
+<pre>
+ do
+ local t = {}
+ t[f(1)] = g
+ t[1] = "x" -- 1st exp
+ t[2] = "y" -- 2nd exp
+ t.x = 1 -- t["x"] = 1
+ t[3] = f(x) -- 3rd exp
+ t[30] = 23
+ t[4] = 45 -- 4th exp
+ a = t
+ end
+</pre>
+
+<p>
+If the last field in the list has the form <code>exp</code>
+and the expression is a function call or a vararg expression,
+then all values returned by this expression enter the list consecutively
+(see <a href="#2.5.8">&sect;2.5.8</a>).
+To avoid this,
+enclose the function call (or the vararg expression)
+in parentheses (see <a href="#2.5">&sect;2.5</a>).
+
+
+<p>
+The field list may have an optional trailing separator,
+as a convenience for machine-generated code.
+
+
+
+
+
+<h3>2.5.8 - <a name="2.5.8">Function Calls</a></h3><p>
+A function call in Lua has the following syntax:
+
+<pre>
+ functioncall ::= prefixexp args
+</pre><p>
+In a function call,
+first prefixexp and args are evaluated.
+If the value of prefixexp has type <em>function</em>,
+then this function is called
+with the given arguments.
+Otherwise, the prefixexp "call" metamethod is called,
+having as first parameter the value of prefixexp,
+followed by the original call arguments
+(see <a href="#2.8">&sect;2.8</a>).
+
+
+<p>
+The form
+
+<pre>
+ functioncall ::= prefixexp `<b>:</b>&acute; Name args
+</pre><p>
+can be used to call "methods".
+A call <code>v:name(<em>args</em>)</code>
+is syntactic sugar for <code>v.name(v,<em>args</em>)</code>,
+except that <code>v</code> is evaluated only once.
+
+
+<p>
+Arguments have the following syntax:
+
+<pre>
+ args ::= `<b>(</b>&acute; [explist] `<b>)</b>&acute;
+ args ::= tableconstructor
+ args ::= String
+</pre><p>
+All argument expressions are evaluated before the call.
+A call of the form <code>f{<em>fields</em>}</code> is
+syntactic sugar for <code>f({<em>fields</em>})</code>;
+that is, the argument list is a single new table.
+A call of the form <code>f'<em>string</em>'</code>
+(or <code>f"<em>string</em>"</code> or <code>f[[<em>string</em>]]</code>)
+is syntactic sugar for <code>f('<em>string</em>')</code>;
+that is, the argument list is a single literal string.
+
+
+<p>
+As an exception to the free-format syntax of Lua,
+you cannot put a line break before the '<code>(</code>' in a function call.
+This restriction avoids some ambiguities in the language.
+If you write
+
+<pre>
+ a = f
+ (g).x(a)
+</pre><p>
+Lua would see that as a single statement, <code>a = f(g).x(a)</code>.
+So, if you want two statements, you must add a semi-colon between them.
+If you actually want to call <code>f</code>,
+you must remove the line break before <code>(g)</code>.
+
+
+<p>
+A call of the form <code>return</code> <em>functioncall</em> is called
+a <em>tail call</em>.
+Lua implements <em>proper tail calls</em>
+(or <em>proper tail recursion</em>):
+in a tail call,
+the called function reuses the stack entry of the calling function.
+Therefore, there is no limit on the number of nested tail calls that
+a program can execute.
+However, a tail call erases any debug information about the
+calling function.
+Note that a tail call only happens with a particular syntax,
+where the <b>return</b> has one single function call as argument;
+this syntax makes the calling function return exactly
+the returns of the called function.
+So, none of the following examples are tail calls:
+
+<pre>
+ return (f(x)) -- results adjusted to 1
+ return 2 * f(x)
+ return x, f(x) -- additional results
+ f(x); return -- results discarded
+ return x or f(x) -- results adjusted to 1
+</pre>
+
+
+
+
+<h3>2.5.9 - <a name="2.5.9">Function Definitions</a></h3>
+
+<p>
+The syntax for function definition is
+
+<pre>
+ function ::= <b>function</b> funcbody
+ funcbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>
+</pre>
+
+<p>
+The following syntactic sugar simplifies function definitions:
+
+<pre>
+ stat ::= <b>function</b> funcname funcbody
+ stat ::= <b>local</b> <b>function</b> Name funcbody
+ funcname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]
+</pre><p>
+The statement
+
+<pre>
+ function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ function t.a.b.c.f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ t.a.b.c.f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ local function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ local f; f = function () <em>body</em> end
+</pre><p>
+<em>not</em> to
+
+<pre>
+ local f = function () <em>body</em> end
+</pre><p>
+(This only makes a difference when the body of the function
+contains references to <code>f</code>.)
+
+
+<p>
+A function definition is an executable expression,
+whose value has type <em>function</em>.
+When Lua pre-compiles a chunk,
+all its function bodies are pre-compiled too.
+Then, whenever Lua executes the function definition,
+the function is <em>instantiated</em> (or <em>closed</em>).
+This function instance (or <em>closure</em>)
+is the final value of the expression.
+Different instances of the same function
+may refer to different external local variables
+and may have different environment tables.
+
+
+<p>
+Parameters act as local variables that are
+initialized with the argument values:
+
+<pre>
+ parlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;
+</pre><p>
+When a function is called,
+the list of arguments is adjusted to
+the length of the list of parameters,
+unless the function is a variadic or <em>vararg function</em>,
+which is
+indicated by three dots ('<code>...</code>') at the end of its parameter list.
+A vararg function does not adjust its argument list;
+instead, it collects all extra arguments and supplies them
+to the function through a <em>vararg expression</em>,
+which is also written as three dots.
+The value of this expression is a list of all actual extra arguments,
+similar to a function with multiple results.
+If a vararg expression is used inside another expression
+or in the middle of a list of expressions,
+then its return list is adjusted to one element.
+If the expression is used as the last element of a list of expressions,
+then no adjustment is made
+(unless the call is enclosed in parentheses).
+
+
+<p>
+As an example, consider the following definitions:
+
+<pre>
+ function f(a, b) end
+ function g(a, b, ...) end
+ function r() return 1,2,3 end
+</pre><p>
+Then, we have the following mapping from arguments to parameters and
+to the vararg expression:
+
+<pre>
+ CALL PARAMETERS
+
+ f(3) a=3, b=nil
+ f(3, 4) a=3, b=4
+ f(3, 4, 5) a=3, b=4
+ f(r(), 10) a=1, b=10
+ f(r()) a=1, b=2
+
+ g(3) a=3, b=nil, ... --&gt; (nothing)
+ g(3, 4) a=3, b=4, ... --&gt; (nothing)
+ g(3, 4, 5, 8) a=3, b=4, ... --&gt; 5 8
+ g(5, r()) a=5, b=1, ... --&gt; 2 3
+</pre>
+
+<p>
+Results are returned using the <b>return</b> statement (see <a href="#2.4.4">&sect;2.4.4</a>).
+If control reaches the end of a function
+without encountering a <b>return</b> statement,
+then the function returns with no results.
+
+
+<p>
+The <em>colon</em> syntax
+is used for defining <em>methods</em>,
+that is, functions that have an implicit extra parameter <code>self</code>.
+Thus, the statement
+
+<pre>
+ function t.a.b.c:f (<em>params</em>) <em>body</em> end
+</pre><p>
+is syntactic sugar for
+
+<pre>
+ t.a.b.c.f = function (self, <em>params</em>) <em>body</em> end
+</pre>
+
+
+
+
+
+
+<h2>2.6 - <a name="2.6">Visibility Rules</a></h2>
+
+<p>
+
+Lua is a lexically scoped language.
+The scope of variables begins at the first statement <em>after</em>
+their declaration and lasts until the end of the innermost block that
+includes the declaration.
+Consider the following example:
+
+<pre>
+ x = 10 -- global variable
+ do -- new block
+ local x = x -- new 'x', with value 10
+ print(x) --&gt; 10
+ x = x+1
+ do -- another block
+ local x = x+1 -- another 'x'
+ print(x) --&gt; 12
+ end
+ print(x) --&gt; 11
+ end
+ print(x) --&gt; 10 (the global one)
+</pre>
+
+<p>
+Notice that, in a declaration like <code>local x = x</code>,
+the new <code>x</code> being declared is not in scope yet,
+and so the second <code>x</code> refers to the outside variable.
+
+
+<p>
+Because of the lexical scoping rules,
+local variables can be freely accessed by functions
+defined inside their scope.
+A local variable used by an inner function is called
+an <em>upvalue</em>, or <em>external local variable</em>,
+inside the inner function.
+
+
+<p>
+Notice that each execution of a <b>local</b> statement
+defines new local variables.
+Consider the following example:
+
+<pre>
+ a = {}
+ local x = 20
+ for i=1,10 do
+ local y = 0
+ a[i] = function () y=y+1; return x+y end
+ end
+</pre><p>
+The loop creates ten closures
+(that is, ten instances of the anonymous function).
+Each of these closures uses a different <code>y</code> variable,
+while all of them share the same <code>x</code>.
+
+
+
+
+
+<h2>2.7 - <a name="2.7">Error Handling</a></h2>
+
+<p>
+Because Lua is an embedded extension language,
+all Lua actions start from C&nbsp;code in the host program
+calling a function from the Lua library (see <a href="#lua_pcall"><code>lua_pcall</code></a>).
+Whenever an error occurs during Lua compilation or execution,
+control returns to C,
+which can take appropriate measures
+(such as printing an error message).
+
+
+<p>
+Lua code can explicitly generate an error by calling the
+<a href="#pdf-error"><code>error</code></a> function.
+If you need to catch errors in Lua,
+you can use the <a href="#pdf-pcall"><code>pcall</code></a> function.
+
+
+
+
+
+<h2>2.8 - <a name="2.8">Metatables</a></h2>
+
+<p>
+Every value in Lua may have a <em>metatable</em>.
+This <em>metatable</em> is an ordinary Lua table
+that defines the behavior of the original value
+under certain special operations.
+You can change several aspects of the behavior
+of operations over a value by setting specific fields in its metatable.
+For instance, when a non-numeric value is the operand of an addition,
+Lua checks for a function in the field <code>"__add"</code> in its metatable.
+If it finds one,
+Lua calls this function to perform the addition.
+
+
+<p>
+We call the keys in a metatable <em>events</em>
+and the values <em>metamethods</em>.
+In the previous example, the event is <code>"add"</code>
+and the metamethod is the function that performs the addition.
+
+
+<p>
+You can query the metatable of any value
+through the <a href="#pdf-getmetatable"><code>getmetatable</code></a> function.
+
+
+<p>
+You can replace the metatable of tables
+through the <a href="#pdf-setmetatable"><code>setmetatable</code></a>
+function.
+You cannot change the metatable of other types from Lua
+(except using the debug library);
+you must use the C&nbsp;API for that.
+
+
+<p>
+Tables and full userdata have individual metatables
+(although multiple tables and userdata can share their metatables);
+values of all other types share one single metatable per type.
+So, there is one single metatable for all numbers,
+one for all strings, etc.
+
+
+<p>
+A metatable may control how an object behaves in arithmetic operations,
+order comparisons, concatenation, length operation, and indexing.
+A metatable can also define a function to be called when a userdata
+is garbage collected.
+For each of these operations Lua associates a specific key
+called an <em>event</em>.
+When Lua performs one of these operations over a value,
+it checks whether this value has a metatable with the corresponding event.
+If so, the value associated with that key (the metamethod)
+controls how Lua will perform the operation.
+
+
+<p>
+Metatables control the operations listed next.
+Each operation is identified by its corresponding name.
+The key for each operation is a string with its name prefixed by
+two underscores, '<code>__</code>';
+for instance, the key for operation "add" is the
+string <code>"__add"</code>.
+The semantics of these operations is better explained by a Lua function
+describing how the interpreter executes the operation.
+
+
+<p>
+The code shown here in Lua is only illustrative;
+the real behavior is hard coded in the interpreter
+and it is much more efficient than this simulation.
+All functions used in these descriptions
+(<a href="#pdf-rawget"><code>rawget</code></a>, <a href="#pdf-tonumber"><code>tonumber</code></a>, etc.)
+are described in <a href="#5.1">&sect;5.1</a>.
+In particular, to retrieve the metamethod of a given object,
+we use the expression
+
+<pre>
+ metatable(obj)[event]
+</pre><p>
+This should be read as
+
+<pre>
+ rawget(getmetatable(obj) or {}, event)
+</pre><p>
+
+That is, the access to a metamethod does not invoke other metamethods,
+and the access to objects with no metatables does not fail
+(it simply results in <b>nil</b>).
+
+
+
+<ul>
+
+<li><b>"add":</b>
+the <code>+</code> operation.
+
+
+
+<p>
+The function <code>getbinhandler</code> below defines how Lua chooses a handler
+for a binary operation.
+First, Lua tries the first operand.
+If its type does not define a handler for the operation,
+then Lua tries the second operand.
+
+<pre>
+ function getbinhandler (op1, op2, event)
+ return metatable(op1)[event] or metatable(op2)[event]
+ end
+</pre><p>
+By using this function,
+the behavior of the <code>op1 + op2</code> is
+
+<pre>
+ function add_event (op1, op2)
+ local o1, o2 = tonumber(op1), tonumber(op2)
+ if o1 and o2 then -- both operands are numeric?
+ return o1 + o2 -- '+' here is the primitive 'add'
+ else -- at least one of the operands is not numeric
+ local h = getbinhandler(op1, op2, "__add")
+ if h then
+ -- call the handler with both operands
+ return (h(op1, op2))
+ else -- no handler available: default behavior
+ error(&middot;&middot;&middot;)
+ end
+ end
+ end
+</pre><p>
+</li>
+
+<li><b>"sub":</b>
+the <code>-</code> operation.
+
+Behavior similar to the "add" operation.
+</li>
+
+<li><b>"mul":</b>
+the <code>*</code> operation.
+
+Behavior similar to the "add" operation.
+</li>
+
+<li><b>"div":</b>
+the <code>/</code> operation.
+
+Behavior similar to the "add" operation.
+</li>
+
+<li><b>"mod":</b>
+the <code>%</code> operation.
+
+Behavior similar to the "add" operation,
+with the operation
+<code>o1 - floor(o1/o2)*o2</code> as the primitive operation.
+</li>
+
+<li><b>"pow":</b>
+the <code>^</code> (exponentiation) operation.
+
+Behavior similar to the "add" operation,
+with the function <code>pow</code> (from the C&nbsp;math library)
+as the primitive operation.
+</li>
+
+<li><b>"unm":</b>
+the unary <code>-</code> operation.
+
+
+<pre>
+ function unm_event (op)
+ local o = tonumber(op)
+ if o then -- operand is numeric?
+ return -o -- '-' here is the primitive 'unm'
+ else -- the operand is not numeric.
+ -- Try to get a handler from the operand
+ local h = metatable(op).__unm
+ if h then
+ -- call the handler with the operand
+ return (h(op))
+ else -- no handler available: default behavior
+ error(&middot;&middot;&middot;)
+ end
+ end
+ end
+</pre><p>
+</li>
+
+<li><b>"concat":</b>
+the <code>..</code> (concatenation) operation.
+
+
+<pre>
+ function concat_event (op1, op2)
+ if (type(op1) == "string" or type(op1) == "number") and
+ (type(op2) == "string" or type(op2) == "number") then
+ return op1 .. op2 -- primitive string concatenation
+ else
+ local h = getbinhandler(op1, op2, "__concat")
+ if h then
+ return (h(op1, op2))
+ else
+ error(&middot;&middot;&middot;)
+ end
+ end
+ end
+</pre><p>
+</li>
+
+<li><b>"len":</b>
+the <code>#</code> operation.
+
+
+<pre>
+ function len_event (op)
+ if type(op) == "string" then
+ return strlen(op) -- primitive string length
+ elseif type(op) == "table" then
+ return #op -- primitive table length
+ else
+ local h = metatable(op).__len
+ if h then
+ -- call the handler with the operand
+ return (h(op))
+ else -- no handler available: default behavior
+ error(&middot;&middot;&middot;)
+ end
+ end
+ end
+</pre><p>
+See <a href="#2.5.5">&sect;2.5.5</a> for a description of the length of a table.
+</li>
+
+<li><b>"eq":</b>
+the <code>==</code> operation.
+
+The function <code>getcomphandler</code> defines how Lua chooses a metamethod
+for comparison operators.
+A metamethod only is selected when both objects
+being compared have the same type
+and the same metamethod for the selected operation.
+
+<pre>
+ function getcomphandler (op1, op2, event)
+ if type(op1) ~= type(op2) then return nil end
+ local mm1 = metatable(op1)[event]
+ local mm2 = metatable(op2)[event]
+ if mm1 == mm2 then return mm1 else return nil end
+ end
+</pre><p>
+The "eq" event is defined as follows:
+
+<pre>
+ function eq_event (op1, op2)
+ if type(op1) ~= type(op2) then -- different types?
+ return false -- different objects
+ end
+ if op1 == op2 then -- primitive equal?
+ return true -- objects are equal
+ end
+ -- try metamethod
+ local h = getcomphandler(op1, op2, "__eq")
+ if h then
+ return (h(op1, op2))
+ else
+ return false
+ end
+ end
+</pre><p>
+<code>a ~= b</code> is equivalent to <code>not (a == b)</code>.
+</li>
+
+<li><b>"lt":</b>
+the <code>&lt;</code> operation.
+
+
+<pre>
+ function lt_event (op1, op2)
+ if type(op1) == "number" and type(op2) == "number" then
+ return op1 &lt; op2 -- numeric comparison
+ elseif type(op1) == "string" and type(op2) == "string" then
+ return op1 &lt; op2 -- lexicographic comparison
+ else
+ local h = getcomphandler(op1, op2, "__lt")
+ if h then
+ return (h(op1, op2))
+ else
+ error(&middot;&middot;&middot;);
+ end
+ end
+ end
+</pre><p>
+<code>a &gt; b</code> is equivalent to <code>b &lt; a</code>.
+</li>
+
+<li><b>"le":</b>
+the <code>&lt;=</code> operation.
+
+
+<pre>
+ function le_event (op1, op2)
+ if type(op1) == "number" and type(op2) == "number" then
+ return op1 &lt;= op2 -- numeric comparison
+ elseif type(op1) == "string" and type(op2) == "string" then
+ return op1 &lt;= op2 -- lexicographic comparison
+ else
+ local h = getcomphandler(op1, op2, "__le")
+ if h then
+ return (h(op1, op2))
+ else
+ h = getcomphandler(op1, op2, "__lt")
+ if h then
+ return not h(op2, op1)
+ else
+ error(&middot;&middot;&middot;);
+ end
+ end
+ end
+ end
+</pre><p>
+<code>a &gt;= b</code> is equivalent to <code>b &lt;= a</code>.
+Note that, in the absence of a "le" metamethod,
+Lua tries the "lt", assuming that <code>a &lt;= b</code> is
+equivalent to <code>not (b &lt; a)</code>.
+</li>
+
+<li><b>"index":</b>
+The indexing access <code>table[key]</code>.
+
+
+<pre>
+ function gettable_event (table, key)
+ local h
+ if type(table) == "table" then
+ local v = rawget(table, key)
+ if v ~= nil then return v end
+ h = metatable(table).__index
+ if h == nil then return nil end
+ else
+ h = metatable(table).__index
+ if h == nil then
+ error(&middot;&middot;&middot;);
+ end
+ end
+ if type(h) == "function" then
+ return (h(table, key)) -- call the handler
+ else return h[key] -- or repeat operation on it
+ end
+ end
+</pre><p>
+</li>
+
+<li><b>"newindex":</b>
+The indexing assignment <code>table[key] = value</code>.
+
+
+<pre>
+ function settable_event (table, key, value)
+ local h
+ if type(table) == "table" then
+ local v = rawget(table, key)
+ if v ~= nil then rawset(table, key, value); return end
+ h = metatable(table).__newindex
+ if h == nil then rawset(table, key, value); return end
+ else
+ h = metatable(table).__newindex
+ if h == nil then
+ error(&middot;&middot;&middot;);
+ end
+ end
+ if type(h) == "function" then
+ h(table, key,value) -- call the handler
+ else h[key] = value -- or repeat operation on it
+ end
+ end
+</pre><p>
+</li>
+
+<li><b>"call":</b>
+called when Lua calls a value.
+
+
+<pre>
+ function function_event (func, ...)
+ if type(func) == "function" then
+ return func(...) -- primitive call
+ else
+ local h = metatable(func).__call
+ if h then
+ return h(func, ...)
+ else
+ error(&middot;&middot;&middot;)
+ end
+ end
+ end
+</pre><p>
+</li>
+
+</ul>
+
+
+
+
+<h2>2.9 - <a name="2.9">Environments</a></h2>
+
+<p>
+Besides metatables,
+objects of types thread, function, and userdata
+have another table associated with them,
+called their <em>environment</em>.
+Like metatables, environments are regular tables and
+multiple objects can share the same environment.
+
+
+<p>
+Environments associated with userdata have no meaning for Lua.
+It is only a convenience feature for programmers to associate a table to
+a userdata.
+
+
+<p>
+Environments associated with threads are called
+<em>global environments</em>.
+They are used as the default environment for their threads and
+non-nested functions created by the thread
+(through <a href="#pdf-loadfile"><code>loadfile</code></a>, <a href="#pdf-loadstring"><code>loadstring</code></a> or <a href="#pdf-load"><code>load</code></a>)
+and can be directly accessed by C&nbsp;code (see <a href="#3.3">&sect;3.3</a>).
+
+
+<p>
+Environments associated with C&nbsp;functions can be directly
+accessed by C&nbsp;code (see <a href="#3.3">&sect;3.3</a>).
+They are used as the default environment for other C&nbsp;functions
+created by the function.
+
+
+<p>
+Environments associated with Lua functions are used to resolve
+all accesses to global variables within the function (see <a href="#2.3">&sect;2.3</a>).
+They are used as the default environment for other Lua functions
+created by the function.
+
+
+<p>
+You can change the environment of a Lua function or the
+running thread by calling <a href="#pdf-setfenv"><code>setfenv</code></a>.
+You can get the environment of a Lua function or the running thread
+by calling <a href="#pdf-getfenv"><code>getfenv</code></a>.
+To manipulate the environment of other objects
+(userdata, C&nbsp;functions, other threads) you must
+use the C&nbsp;API.
+
+
+
+
+
+<h2>2.10 - <a name="2.10">Garbage Collection</a></h2>
+
+<p>
+Lua performs automatic memory management.
+This means that
+you have to worry neither about allocating memory for new objects
+nor about freeing it when the objects are no longer needed.
+Lua manages memory automatically by running
+a <em>garbage collector</em> from time to time
+to collect all <em>dead objects</em>
+(that is, these objects that are no longer accessible from Lua).
+All objects in Lua are subject to automatic management:
+tables, userdata, functions, threads, and strings.
+
+
+<p>
+Lua implements an incremental mark-and-sweep collector.
+It uses two numbers to control its garbage-collection cycles:
+the <em>garbage-collector pause</em> and
+the <em>garbage-collector step multiplier</em>.
+
+
+<p>
+The garbage-collector pause
+controls how long the collector waits before starting a new cycle.
+Larger values make the collector less aggressive.
+Values smaller than 1 mean the collector will not wait to
+start a new cycle.
+A value of 2 means that the collector waits for the total memory in use
+to double before starting a new cycle.
+
+
+<p>
+The step multiplier
+controls the relative speed of the collector relative to
+memory allocation.
+Larger values make the collector more aggressive but also increase
+the size of each incremental step.
+Values smaller than 1 make the collector too slow and
+may result in the collector never finishing a cycle.
+The default, 2, means that the collector runs at "twice"
+the speed of memory allocation.
+
+
+<p>
+You can change these numbers by calling <a href="#lua_gc"><code>lua_gc</code></a> in C
+or <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> in Lua.
+Both get percentage points as arguments
+(so an argument of 100 means a real value of 1).
+With these functions you can also control
+the collector directly (e.g., stop and restart it).
+
+
+
+<h3>2.10.1 - <a name="2.10.1">Garbage-Collection Metamethods</a></h3>
+
+<p>
+Using the C&nbsp;API,
+you can set garbage-collector metamethods for userdata (see <a href="#2.8">&sect;2.8</a>).
+These metamethods are also called <em>finalizers</em>.
+Finalizers allow you to coordinate Lua's garbage collection
+with external resource management
+(such as closing files, network or database connections,
+or freeing your own memory).
+
+
+<p>
+Garbage userdata with a field <code>__gc</code> in their metatables are not
+collected immediately by the garbage collector.
+Instead, Lua puts them in a list.
+After the collection,
+Lua does the equivalent of the following function
+for each userdata in that list:
+
+<pre>
+ function gc_event (udata)
+ local h = metatable(udata).__gc
+ if h then
+ h(udata)
+ end
+ end
+</pre>
+
+<p>
+At the end of each garbage-collection cycle,
+the finalizers for userdata are called in <em>reverse</em>
+order of their creation,
+among those collected in that cycle.
+That is, the first finalizer to be called is the one associated
+with the userdata created last in the program.
+The userdata itself is freed only in the next garbage-collection cycle.
+
+
+
+
+
+<h3>2.10.2 - <a name="2.10.2">Weak Tables</a></h3>
+
+<p>
+A <em>weak table</em> is a table whose elements are
+<em>weak references</em>.
+A weak reference is ignored by the garbage collector.
+In other words,
+if the only references to an object are weak references,
+then the garbage collector will collect this object.
+
+
+<p>
+A weak table can have weak keys, weak values, or both.
+A table with weak keys allows the collection of its keys,
+but prevents the collection of its values.
+A table with both weak keys and weak values allows the collection of
+both keys and values.
+In any case, if either the key or the value is collected,
+the whole pair is removed from the table.
+The weakness of a table is controlled by the
+<code>__mode</code> field of its metatable.
+If the <code>__mode</code> field is a string containing the character&nbsp;'<code>k</code>',
+the keys in the table are weak.
+If <code>__mode</code> contains '<code>v</code>',
+the values in the table are weak.
+
+
+<p>
+After you use a table as a metatable,
+you should not change the value of its field <code>__mode</code>.
+Otherwise, the weak behavior of the tables controlled by this
+metatable is undefined.
+
+
+
+
+
+
+
+<h2>2.11 - <a name="2.11">Coroutines</a></h2>
+
+<p>
+Lua supports coroutines,
+also called <em>collaborative multithreading</em>.
+A coroutine in Lua represents an independent thread of execution.
+Unlike threads in multithread systems, however,
+a coroutine only suspends its execution by explicitly calling
+a yield function.
+
+
+<p>
+You create a coroutine with a call to <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>.
+Its sole argument is a function
+that is the main function of the coroutine.
+The <code>create</code> function only creates a new coroutine and
+returns a handle to it (an object of type <em>thread</em>);
+it does not start the coroutine execution.
+
+
+<p>
+When you first call <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+passing as its first argument
+the thread returned by <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the coroutine starts its execution,
+at the first line of its main function.
+Extra arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> are passed on
+to the coroutine main function.
+After the coroutine starts running,
+it runs until it terminates or <em>yields</em>.
+
+
+<p>
+A coroutine can terminate its execution in two ways:
+normally, when its main function returns
+(explicitly or implicitly, after the last instruction);
+and abnormally, if there is an unprotected error.
+In the first case, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>true</b>,
+plus any values returned by the coroutine main function.
+In case of errors, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>false</b>
+plus an error message.
+
+
+<p>
+A coroutine yields by calling <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+When a coroutine yields,
+the corresponding <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns immediately,
+even if the yield happens inside nested function calls
+(that is, not in the main function,
+but in a function directly or indirectly called by the main function).
+In the case of a yield, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> also returns <b>true</b>,
+plus any values passed to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+The next time you resume the same coroutine,
+it continues its execution from the point where it yielded,
+with the call to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a> returning any extra
+arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+
+
+<p>
+Like <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> function also creates a coroutine,
+but instead of returning the coroutine itself,
+it returns a function that, when called, resumes the coroutine.
+Any arguments passed to this function
+go as extra arguments to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+<a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> returns all the values returned by <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+except the first one (the boolean error code).
+Unlike <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+<a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> does not catch errors;
+any error is propagated to the caller.
+
+
+<p>
+As an example,
+consider the following code:
+
+<pre>
+ function foo (a)
+ print("foo", a)
+ return coroutine.yield(2*a)
+ end
+
+ co = coroutine.create(function (a,b)
+ print("co-body", a, b)
+ local r = foo(a+1)
+ print("co-body", r)
+ local r, s = coroutine.yield(a+b, a-b)
+ print("co-body", r, s)
+ return b, "end"
+ end)
+
+ print("main", coroutine.resume(co, 1, 10))
+ print("main", coroutine.resume(co, "r"))
+ print("main", coroutine.resume(co, "x", "y"))
+ print("main", coroutine.resume(co, "x", "y"))
+</pre><p>
+When you run it, it produces the following output:
+
+<pre>
+ co-body 1 10
+ foo 2
+
+ main true 4
+ co-body r
+ main true 11 -9
+ co-body x y
+ main true 10 end
+ main false cannot resume dead coroutine
+</pre>
+
+
+
+
+<h1>3 - <a name="3">The Application Program Interface</a></h1>
+
+<p>
+
+This section describes the C&nbsp;API for Lua, that is,
+the set of C&nbsp;functions available to the host program to communicate
+with Lua.
+All API functions and related types and constants
+are declared in the header file <a name="pdf-lua.h"><code>lua.h</code></a>.
+
+
+<p>
+Even when we use the term "function",
+any facility in the API may be provided as a macro instead.
+All such macros use each of their arguments exactly once
+(except for the first argument, which is always a Lua state),
+and so do not generate any hidden side-effects.
+
+
+<p>
+As in most C&nbsp;libraries,
+the Lua API functions do not check their arguments for validity or consistency.
+However, you can change this behavior by compiling Lua
+with a proper definition for the macro <a name="pdf-luai_apicheck"><code>luai_apicheck</code></a>,
+in file <code>luaconf.h</code>.
+
+
+
+<h2>3.1 - <a name="3.1">The Stack</a></h2>
+
+<p>
+Lua uses a <em>virtual stack</em> to pass values to and from C.
+Each element in this stack represents a Lua value
+(<b>nil</b>, number, string, etc.).
+
+
+<p>
+Whenever Lua calls C, the called function gets a new stack,
+which is independent of previous stacks and of stacks of
+C&nbsp;functions that are still active.
+This stack initially contains any arguments to the C&nbsp;function
+and it is where the C&nbsp;function pushes its results
+to be returned to the caller (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+For convenience,
+most query operations in the API do not follow a strict stack discipline.
+Instead, they can refer to any element in the stack
+by using an <em>index</em>:
+A positive index represents an <em>absolute</em> stack position
+(starting at&nbsp;1);
+a negative index represents an <em>offset</em> relative to the top of the stack.
+More specifically, if the stack has <em>n</em> elements,
+then index&nbsp;1 represents the first element
+(that is, the element that was pushed onto the stack first)
+and
+index&nbsp;<em>n</em> represents the last element;
+index&nbsp;-1 also represents the last element
+(that is, the element at the&nbsp;top)
+and index <em>-n</em> represents the first element.
+We say that an index is <em>valid</em>
+if it lies between&nbsp;1 and the stack top
+(that is, if <code>1 &le; abs(index) &le; top</code>).
+
+
+
+
+
+
+<h2>3.2 - <a name="3.2">Stack Size</a></h2>
+
+<p>
+When you interact with Lua API,
+you are responsible for ensuring consistency.
+In particular,
+<em>you are responsible for controlling stack overflow</em>.
+You can use the function <a href="#lua_checkstack"><code>lua_checkstack</code></a>
+to grow the stack size.
+
+
+<p>
+Whenever Lua calls C,
+it ensures that at least <a name="pdf-LUA_MINSTACK"><code>LUA_MINSTACK</code></a> stack positions are available.
+<code>LUA_MINSTACK</code> is defined as 20,
+so that usually you do not have to worry about stack space
+unless your code has loops pushing elements onto the stack.
+
+
+<p>
+Most query functions accept as indices any value inside the
+available stack space, that is, indices up to the maximum stack size
+you have set through <a href="#lua_checkstack"><code>lua_checkstack</code></a>.
+Such indices are called <em>acceptable indices</em>.
+More formally, we define an <em>acceptable index</em>
+as follows:
+
+<pre>
+ (index &lt; 0 &amp;&amp; abs(index) &lt;= top) ||
+ (index &gt; 0 &amp;&amp; index &lt;= stackspace)
+</pre><p>
+Note that 0 is never an acceptable index.
+
+
+
+
+
+<h2>3.3 - <a name="3.3">Pseudo-Indices</a></h2>
+
+<p>
+Unless otherwise noted,
+any function that accepts valid indices can also be called with
+<em>pseudo-indices</em>,
+which represent some Lua values that are accessible to C&nbsp;code
+but which are not in the stack.
+Pseudo-indices are used to access the thread environment,
+the function environment,
+the registry,
+and the upvalues of a C&nbsp;function (see <a href="#3.4">&sect;3.4</a>).
+
+
+<p>
+The thread environment (where global variables live) is
+always at pseudo-index <a name="pdf-LUA_GLOBALSINDEX"><code>LUA_GLOBALSINDEX</code></a>.
+The environment of the running C&nbsp;function is always
+at pseudo-index <a name="pdf-LUA_ENVIRONINDEX"><code>LUA_ENVIRONINDEX</code></a>.
+
+
+<p>
+To access and change the value of global variables,
+you can use regular table operations over an environment table.
+For instance, to access the value of a global variable, do
+
+<pre>
+ lua_getfield(L, LUA_GLOBALSINDEX, varname);
+</pre>
+
+
+
+
+<h2>3.4 - <a name="3.4">C Closures</a></h2>
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+thus creating a <em>C&nbsp;closure</em>;
+these values are called <em>upvalues</em> and are
+accessible to the function whenever it is called
+(see <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>).
+
+
+<p>
+Whenever a C&nbsp;function is called,
+its upvalues are located at specific pseudo-indices.
+These pseudo-indices are produced by the macro
+<a name="lua_upvalueindex"><code>lua_upvalueindex</code></a>.
+The first value associated with a function is at position
+<code>lua_upvalueindex(1)</code>, and so on.
+Any access to <code>lua_upvalueindex(<em>n</em>)</code>,
+where <em>n</em> is greater than the number of upvalues of the
+current function,
+produces an acceptable (but invalid) index.
+
+
+
+
+
+<h2>3.5 - <a name="3.5">Registry</a></h2>
+
+<p>
+Lua provides a <em>registry</em>,
+a pre-defined table that can be used by any C&nbsp;code to
+store whatever Lua value it needs to store.
+This table is always located at pseudo-index
+<a name="pdf-LUA_REGISTRYINDEX"><code>LUA_REGISTRYINDEX</code></a>.
+Any C&nbsp;library can store data into this table,
+but it should take care to choose keys different from those used
+by other libraries, to avoid collisions.
+Typically, you should use as key a string containing your library name
+or a light userdata with the address of a C&nbsp;object in your code.
+
+
+<p>
+The integer keys in the registry are used by the reference mechanism,
+implemented by the auxiliary library,
+and therefore should not be used for other purposes.
+
+
+
+
+
+<h2>3.6 - <a name="3.6">Error Handling in C</a></h2>
+
+<p>
+Internally, Lua uses the C <code>longjmp</code> facility to handle errors.
+(You can also choose to use exceptions if you use C++;
+see file <code>luaconf.h</code>.)
+When Lua faces any error
+(such as memory allocation errors, type errors, syntax errors,
+and runtime errors)
+it <em>raises</em> an error;
+that is, it does a long jump.
+A <em>protected environment</em> uses <code>setjmp</code>
+to set a recover point;
+any error jumps to the most recent active recover point.
+
+
+<p>
+Most functions in the API may throw an error,
+for instance due to a memory allocation error.
+The documentation for each function indicates whether
+it can throw errors.
+
+
+<p>
+Inside a C&nbsp;function you can throw an error by calling <a href="#lua_error"><code>lua_error</code></a>.
+
+
+
+
+
+<h2>3.7 - <a name="3.7">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the C&nbsp;API in
+alphabetical order.
+Each function has an indicator like this:
+<span class="apii">[-o, +p, <em>x</em>]</span>
+
+
+<p>
+The first field, <code>o</code>,
+is how many elements the function pops from the stack.
+The second field, <code>p</code>,
+is how many elements the function pushes onto the stack.
+(Any function always pushes its results after popping its arguments.)
+A field in the form <code>x|y</code> means the function may push (or pop)
+<code>x</code> or <code>y</code> elements,
+depending on the situation;
+an interrogation mark '<code>?</code>' means that
+we cannot know how many elements the function pops/pushes
+by looking only at its arguments
+(e.g., they may depend on what is on the stack).
+The third field, <code>x</code>,
+tells whether the function may throw errors:
+'<code>-</code>' means the function never throws any error;
+'<code>m</code>' means the function may throw an error
+only due to not enough memory;
+'<code>e</code>' means the function may throw other kinds of errors;
+'<code>v</code>' means the function may throw an error on purpose.
+
+
+
+<hr><h3><a name="lua_Alloc"><code>lua_Alloc</code></a></h3>
+<pre>typedef void * (*lua_Alloc) (void *ud,
+ void *ptr,
+ size_t osize,
+ size_t nsize);</pre>
+
+<p>
+The type of the memory-allocation function used by Lua states.
+The allocator function must provide a
+functionality similar to <code>realloc</code>,
+but not exactly the same.
+Its arguments are
+<code>ud</code>, an opaque pointer passed to <a href="#lua_newstate"><code>lua_newstate</code></a>;
+<code>ptr</code>, a pointer to the block being allocated/reallocated/freed;
+<code>osize</code>, the original size of the block;
+<code>nsize</code>, the new size of the block.
+<code>ptr</code> is <code>NULL</code> if and only if <code>osize</code> is zero.
+When <code>nsize</code> is zero, the allocator must return <code>NULL</code>;
+if <code>osize</code> is not zero,
+it should free the block pointed to by <code>ptr</code>.
+When <code>nsize</code> is not zero, the allocator returns <code>NULL</code>
+if and only if it cannot fill the request.
+When <code>nsize</code> is not zero and <code>osize</code> is zero,
+the allocator should behave like <code>malloc</code>.
+When <code>nsize</code> and <code>osize</code> are not zero,
+the allocator behaves like <code>realloc</code>.
+Lua assumes that the allocator never fails when
+<code>osize &gt;= nsize</code>.
+
+
+<p>
+Here is a simple implementation for the allocator function.
+It is used in the auxiliary library by <a href="#luaL_newstate"><code>luaL_newstate</code></a>.
+
+<pre>
+ static void *l_alloc (void *ud, void *ptr, size_t osize,
+ size_t nsize) {
+ (void)ud; (void)osize; /* not used */
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+ }
+</pre><p>
+This code assumes
+that <code>free(NULL)</code> has no effect and that
+<code>realloc(NULL, size)</code> is equivalent to <code>malloc(size)</code>.
+ANSI&nbsp;C ensures both behaviors.
+
+
+
+
+
+<hr><h3><a name="lua_atpanic"><code>lua_atpanic</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);</pre>
+
+<p>
+Sets a new panic function and returns the old one.
+
+
+<p>
+If an error happens outside any protected environment,
+Lua calls a <em>panic function</em>
+and then calls <code>exit(EXIT_FAILURE)</code>,
+thus exiting the host application.
+Your panic function may avoid this exit by
+never returning (e.g., doing a long jump).
+
+
+<p>
+The panic function can access the error message at the top of the stack.
+
+
+
+
+
+<hr><h3><a name="lua_call"><code>lua_call</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +nresults, <em>e</em>]</span>
+<pre>void lua_call (lua_State *L, int nargs, int nresults);</pre>
+
+<p>
+Calls a function.
+
+
+<p>
+To call a function you must use the following protocol:
+first, the function to be called is pushed onto the stack;
+then, the arguments to the function are pushed
+in direct order;
+that is, the first argument is pushed first.
+Finally you call <a href="#lua_call"><code>lua_call</code></a>;
+<code>nargs</code> is the number of arguments that you pushed onto the stack.
+All arguments and the function value are popped from the stack
+when the function is called.
+The function results are pushed onto the stack when the function returns.
+The number of results is adjusted to <code>nresults</code>,
+unless <code>nresults</code> is <a name="pdf-LUA_MULTRET"><code>LUA_MULTRET</code></a>.
+In this case, <em>all</em> results from the function are pushed.
+Lua takes care that the returned values fit into the stack space.
+The function results are pushed onto the stack in direct order
+(the first result is pushed first),
+so that after the call the last result is on the top of the stack.
+
+
+<p>
+Any error inside the called function is propagated upwards
+(with a <code>longjmp</code>).
+
+
+<p>
+The following example shows how the host program may do the
+equivalent to this Lua code:
+
+<pre>
+ a = f("how", t.x, 14)
+</pre><p>
+Here it is in&nbsp;C:
+
+<pre>
+ lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
+ lua_pushstring(L, "how"); /* 1st argument */
+ lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
+ lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
+ lua_remove(L, -2); /* remove 't' from the stack */
+ lua_pushinteger(L, 14); /* 3rd argument */
+ lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
+ lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */
+</pre><p>
+Note that the code above is "balanced":
+at its end, the stack is back to its original configuration.
+This is considered good programming practice.
+
+
+
+
+
+<hr><h3><a name="lua_CFunction"><code>lua_CFunction</code></a></h3>
+<pre>typedef int (*lua_CFunction) (lua_State *L);</pre>
+
+<p>
+Type for C&nbsp;functions.
+
+
+<p>
+In order to communicate properly with Lua,
+a C&nbsp;function must use the following protocol,
+which defines the way parameters and results are passed:
+a C&nbsp;function receives its arguments from Lua in its stack
+in direct order (the first argument is pushed first).
+So, when the function starts,
+<code>lua_gettop(L)</code> returns the number of arguments received by the function.
+The first argument (if any) is at index 1
+and its last argument is at index <code>lua_gettop(L)</code>.
+To return values to Lua, a C&nbsp;function just pushes them onto the stack,
+in direct order (the first result is pushed first),
+and returns the number of results.
+Any other value in the stack below the results will be properly
+discarded by Lua.
+Like a Lua function, a C&nbsp;function called by Lua can also return
+many results.
+
+
+<p>
+As an example, the following function receives a variable number
+of numerical arguments and returns their average and sum:
+
+<pre>
+ static int foo (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number sum = 0;
+ int i;
+ for (i = 1; i &lt;= n; i++) {
+ if (!lua_isnumber(L, i)) {
+ lua_pushstring(L, "incorrect argument");
+ lua_error(L);
+ }
+ sum += lua_tonumber(L, i);
+ }
+ lua_pushnumber(L, sum/n); /* first result */
+ lua_pushnumber(L, sum); /* second result */
+ return 2; /* number of results */
+ }
+</pre>
+
+
+
+
+<hr><h3><a name="lua_checkstack"><code>lua_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>int lua_checkstack (lua_State *L, int extra);</pre>
+
+<p>
+Ensures that there are at least <code>extra</code> free stack slots in the stack.
+It returns false if it cannot grow the stack to that size.
+This function never shrinks the stack;
+if the stack is already larger than the new size,
+it is left unchanged.
+
+
+
+
+
+<hr><h3><a name="lua_close"><code>lua_close</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>void lua_close (lua_State *L);</pre>
+
+<p>
+Destroys all objects in the given Lua state
+(calling the corresponding garbage-collection metamethods, if any)
+and frees all dynamic memory used by this state.
+On several platforms, you may not need to call this function,
+because all resources are naturally released when the host program ends.
+On the other hand, long-running programs,
+such as a daemon or a web server,
+might need to release states as soon as they are not needed,
+to avoid growing too large.
+
+
+
+
+
+<hr><h3><a name="lua_concat"><code>lua_concat</code></a></h3><p>
+<span class="apii">[-n, +1, <em>e</em>]</span>
+<pre>void lua_concat (lua_State *L, int n);</pre>
+
+<p>
+Concatenates the <code>n</code> values at the top of the stack,
+pops them, and leaves the result at the top.
+If <code>n</code>&nbsp;is&nbsp;1, the result is the single value on the stack
+(that is, the function does nothing);
+if <code>n</code> is 0, the result is the empty string.
+Concatenation is performed following the usual semantics of Lua
+(see <a href="#2.5.4">&sect;2.5.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_cpcall"><code>lua_cpcall</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>-</em>]</span>
+<pre>int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);</pre>
+
+<p>
+Calls the C&nbsp;function <code>func</code> in protected mode.
+<code>func</code> starts with only one element in its stack,
+a light userdata containing <code>ud</code>.
+In case of errors,
+<a href="#lua_cpcall"><code>lua_cpcall</code></a> returns the same error codes as <a href="#lua_pcall"><code>lua_pcall</code></a>,
+plus the error object on the top of the stack;
+otherwise, it returns zero, and does not change the stack.
+All values returned by <code>func</code> are discarded.
+
+
+
+
+
+<hr><h3><a name="lua_createtable"><code>lua_createtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_createtable (lua_State *L, int narr, int nrec);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+The new table has space pre-allocated
+for <code>narr</code> array elements and <code>nrec</code> non-array elements.
+This pre-allocation is useful when you know exactly how many elements
+the table will have.
+Otherwise you can use the function <a href="#lua_newtable"><code>lua_newtable</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_dump"><code>lua_dump</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>int lua_dump (lua_State *L, lua_Writer writer, void *data);</pre>
+
+<p>
+Dumps a function as a binary chunk.
+Receives a Lua function on the top of the stack
+and produces a binary chunk that,
+if loaded again,
+results in a function equivalent to the one dumped.
+As it produces parts of the chunk,
+<a href="#lua_dump"><code>lua_dump</code></a> calls function <code>writer</code> (see <a href="#lua_Writer"><code>lua_Writer</code></a>)
+with the given <code>data</code>
+to write them.
+
+
+<p>
+The value returned is the error code returned by the last
+call to the writer;
+0&nbsp;means no errors.
+
+
+<p>
+This function does not pop the Lua function from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_equal"><code>lua_equal</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>int lua_equal (lua_State *L, int index1, int index2);</pre>
+
+<p>
+Returns 1 if the two values in acceptable indices <code>index1</code> and
+<code>index2</code> are equal,
+following the semantics of the Lua <code>==</code> operator
+(that is, may call metamethods).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices is non valid.
+
+
+
+
+
+<hr><h3><a name="lua_error"><code>lua_error</code></a></h3><p>
+<span class="apii">[-1, +0, <em>v</em>]</span>
+<pre>int lua_error (lua_State *L);</pre>
+
+<p>
+Generates a Lua error.
+The error message (which can actually be a Lua value of any type)
+must be on the stack top.
+This function does a long jump,
+and therefore never returns.
+(see <a href="#luaL_error"><code>luaL_error</code></a>).
+
+
+
+
+
+<hr><h3><a name="lua_gc"><code>lua_gc</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>int lua_gc (lua_State *L, int what, int data);</pre>
+
+<p>
+Controls the garbage collector.
+
+
+<p>
+This function performs several tasks,
+according to the value of the parameter <code>what</code>:
+
+<ul>
+
+<li><b><code>LUA_GCSTOP</code>:</b>
+stops the garbage collector.
+</li>
+
+<li><b><code>LUA_GCRESTART</code>:</b>
+restarts the garbage collector.
+</li>
+
+<li><b><code>LUA_GCCOLLECT</code>:</b>
+performs a full garbage-collection cycle.
+</li>
+
+<li><b><code>LUA_GCCOUNT</code>:</b>
+returns the current amount of memory (in Kbytes) in use by Lua.
+</li>
+
+<li><b><code>LUA_GCCOUNTB</code>:</b>
+returns the remainder of dividing the current amount of bytes of
+memory in use by Lua by 1024.
+</li>
+
+<li><b><code>LUA_GCSTEP</code>:</b>
+performs an incremental step of garbage collection.
+The step "size" is controlled by <code>data</code>
+(larger values mean more steps) in a non-specified way.
+If you want to control the step size
+you must experimentally tune the value of <code>data</code>.
+The function returns 1 if the step finished a
+garbage-collection cycle.
+</li>
+
+<li><b><code>LUA_GCSETPAUSE</code>:</b>
+sets <code>data</code>/100 as the new value
+for the <em>pause</em> of the collector (see <a href="#2.10">&sect;2.10</a>).
+The function returns the previous value of the pause.
+</li>
+
+<li><b><code>LUA_GCSETSTEPMUL</code>:</b>
+sets <code>data</code>/100 as the new value for the <em>step multiplier</em> of
+the collector (see <a href="#2.10">&sect;2.10</a>).
+The function returns the previous value of the step multiplier.
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_getallocf"><code>lua_getallocf</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_Alloc lua_getallocf (lua_State *L, void **ud);</pre>
+
+<p>
+Returns the memory-allocation function of a given state.
+If <code>ud</code> is not <code>NULL</code>, Lua stores in <code>*ud</code> the
+opaque pointer passed to <a href="#lua_newstate"><code>lua_newstate</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_getfenv"><code>lua_getfenv</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_getfenv (lua_State *L, int index);</pre>
+
+<p>
+Pushes onto the stack the environment table of
+the value at the given index.
+
+
+
+
+
+<hr><h3><a name="lua_getfield"><code>lua_getfield</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void lua_getfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given valid index.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<hr><h3><a name="lua_getglobal"><code>lua_getglobal</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void lua_getglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pushes onto the stack the value of the global <code>name</code>.
+It is defined as a macro:
+
+<pre>
+ #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
+</pre>
+
+
+
+
+<hr><h3><a name="lua_getmetatable"><code>lua_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>-</em>]</span>
+<pre>int lua_getmetatable (lua_State *L, int index);</pre>
+
+<p>
+Pushes onto the stack the metatable of the value at the given
+acceptable index.
+If the index is not valid,
+or if the value does not have a metatable,
+the function returns&nbsp;0 and pushes nothing on the stack.
+
+
+
+
+
+<hr><h3><a name="lua_gettable"><code>lua_gettable</code></a></h3><p>
+<span class="apii">[-1, +1, <em>e</em>]</span>
+<pre>void lua_gettable (lua_State *L, int index);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given valid index
+and <code>k</code> is the value at the top of the stack.
+
+
+<p>
+This function pops the key from the stack
+(putting the resulting value in its place).
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<hr><h3><a name="lua_gettop"><code>lua_gettop</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_gettop (lua_State *L);</pre>
+
+<p>
+Returns the index of the top element in the stack.
+Because indices start at&nbsp;1,
+this result is equal to the number of elements in the stack
+(and so 0&nbsp;means an empty stack).
+
+
+
+
+
+<hr><h3><a name="lua_insert"><code>lua_insert</code></a></h3><p>
+<span class="apii">[-1, +1, <em>-</em>]</span>
+<pre>void lua_insert (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given valid index,
+shifting up the elements above this index to open space.
+Cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_Integer"><code>lua_Integer</code></a></h3>
+<pre>typedef ptrdiff_t lua_Integer;</pre>
+
+<p>
+The type used by the Lua API to represent integral values.
+
+
+<p>
+By default it is a <code>ptrdiff_t</code>,
+which is usually the largest signed integral type the machine handles
+"comfortably".
+
+
+
+
+
+<hr><h3><a name="lua_isboolean"><code>lua_isboolean</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isboolean (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index has type boolean,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_iscfunction"><code>lua_iscfunction</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_iscfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a C&nbsp;function,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isfunction"><code>lua_isfunction</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a function
+(either C or Lua), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_islightuserdata"><code>lua_islightuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_islightuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a light userdata,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnil"><code>lua_isnil</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isnil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnone"><code>lua_isnone</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isnone (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given acceptable index is not valid
+(that is, it refers to an element outside the current stack),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnoneornil"><code>lua_isnoneornil</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isnoneornil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given acceptable index is not valid
+(that is, it refers to an element outside the current stack)
+or if the value at this index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnumber"><code>lua_isnumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isnumber (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a number
+or a string convertible to a number,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isstring"><code>lua_isstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isstring (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a string
+or a number (which is always convertible to a string),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_istable"><code>lua_istable</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_istable (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a table,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isthread"><code>lua_isthread</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isthread (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a thread,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isuserdata"><code>lua_isuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_isuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given acceptable index is a userdata
+(either full or light), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_lessthan"><code>lua_lessthan</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>int lua_lessthan (lua_State *L, int index1, int index2);</pre>
+
+<p>
+Returns 1 if the value at acceptable index <code>index1</code> is smaller
+than the value at acceptable index <code>index2</code>,
+following the semantics of the Lua <code>&lt;</code> operator
+(that is, may call metamethods).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices is non valid.
+
+
+
+
+
+<hr><h3><a name="lua_load"><code>lua_load</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>int lua_load (lua_State *L,
+ lua_Reader reader,
+ void *data,
+ const char *chunkname);</pre>
+
+<p>
+Loads a Lua chunk.
+If there are no errors,
+<a href="#lua_load"><code>lua_load</code></a> pushes the compiled chunk as a Lua
+function on top of the stack.
+Otherwise, it pushes an error message.
+The return values of <a href="#lua_load"><code>lua_load</code></a> are:
+
+<ul>
+
+<li><b>0:</b> no errors;</li>
+
+<li><b><a name="pdf-LUA_ERRSYNTAX"><code>LUA_ERRSYNTAX</code></a>:</b>
+syntax error during pre-compilation;</li>
+
+<li><b><a href="#pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>:</b>
+memory allocation error.</li>
+
+</ul>
+
+<p>
+This function only loads a chunk;
+it does not run it.
+
+
+<p>
+<a href="#lua_load"><code>lua_load</code></a> automatically detects whether the chunk is text or binary,
+and loads it accordingly (see program <code>luac</code>).
+
+
+<p>
+The <a href="#lua_load"><code>lua_load</code></a> function uses a user-supplied <code>reader</code> function
+to read the chunk (see <a href="#lua_Reader"><code>lua_Reader</code></a>).
+The <code>data</code> argument is an opaque value passed to the reader function.
+
+
+<p>
+The <code>chunkname</code> argument gives a name to the chunk,
+which is used for error messages and in debug information (see <a href="#3.8">&sect;3.8</a>).
+
+
+
+
+
+<hr><h3><a name="lua_newstate"><code>lua_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_State *lua_newstate (lua_Alloc f, void *ud);</pre>
+
+<p>
+Creates a new, independent state.
+Returns <code>NULL</code> if cannot create the state
+(due to lack of memory).
+The argument <code>f</code> is the allocator function;
+Lua does all memory allocation for this state through this function.
+The second argument, <code>ud</code>, is an opaque pointer that Lua
+simply passes to the allocator in every call.
+
+
+
+
+
+<hr><h3><a name="lua_newtable"><code>lua_newtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_newtable (lua_State *L);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+It is equivalent to <code>lua_createtable(L, 0, 0)</code>.
+
+
+
+
+
+<hr><h3><a name="lua_newthread"><code>lua_newthread</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>lua_State *lua_newthread (lua_State *L);</pre>
+
+<p>
+Creates a new thread, pushes it on the stack,
+and returns a pointer to a <a href="#lua_State"><code>lua_State</code></a> that represents this new thread.
+The new state returned by this function shares with the original state
+all global objects (such as tables),
+but has an independent execution stack.
+
+
+<p>
+There is no explicit function to close or to destroy a thread.
+Threads are subject to garbage collection,
+like any Lua object.
+
+
+
+
+
+<hr><h3><a name="lua_newuserdata"><code>lua_newuserdata</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void *lua_newuserdata (lua_State *L, size_t size);</pre>
+
+<p>
+This function allocates a new block of memory with the given size,
+pushes onto the stack a new full userdata with the block address,
+and returns this address.
+
+
+<p>
+Userdata represent C&nbsp;values in Lua.
+A <em>full userdata</em> represents a block of memory.
+It is an object (like a table):
+you must create it, it can have its own metatable,
+and you can detect when it is being collected.
+A full userdata is only equal to itself (under raw equality).
+
+
+<p>
+When Lua collects a full userdata with a <code>gc</code> metamethod,
+Lua calls the metamethod and marks the userdata as finalized.
+When this userdata is collected again then
+Lua frees its corresponding memory.
+
+
+
+
+
+<hr><h3><a name="lua_next"><code>lua_next</code></a></h3><p>
+<span class="apii">[-1, +(2|0), <em>e</em>]</span>
+<pre>int lua_next (lua_State *L, int index);</pre>
+
+<p>
+Pops a key from the stack,
+and pushes a key-value pair from the table at the given index
+(the "next" pair after the given key).
+If there are no more elements in the table,
+then <a href="#lua_next"><code>lua_next</code></a> returns 0 (and pushes nothing).
+
+
+<p>
+A typical traversal looks like this:
+
+<pre>
+ /* table is in the stack at index 't' */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, t) != 0) {
+ /* uses 'key' (at index -2) and 'value' (at index -1) */
+ printf("%s - %s\n",
+ lua_typename(L, lua_type(L, -2)),
+ lua_typename(L, lua_type(L, -1)));
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+</pre>
+
+<p>
+While traversing a table,
+do not call <a href="#lua_tolstring"><code>lua_tolstring</code></a> directly on a key,
+unless you know that the key is actually a string.
+Recall that <a href="#lua_tolstring"><code>lua_tolstring</code></a> <em>changes</em>
+the value at the given index;
+this confuses the next call to <a href="#lua_next"><code>lua_next</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_Number"><code>lua_Number</code></a></h3>
+<pre>typedef double lua_Number;</pre>
+
+<p>
+The type of numbers in Lua.
+By default, it is double, but that can be changed in <code>luaconf.h</code>.
+
+
+<p>
+Through the configuration file you can change
+Lua to operate with another type for numbers (e.g., float or long).
+
+
+
+
+
+<hr><h3><a name="lua_objlen"><code>lua_objlen</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>size_t lua_objlen (lua_State *L, int index);</pre>
+
+<p>
+Returns the "length" of the value at the given acceptable index:
+for strings, this is the string length;
+for tables, this is the result of the length operator ('<code>#</code>');
+for userdata, this is the size of the block of memory allocated
+for the userdata;
+for other values, it is&nbsp;0.
+
+
+
+
+
+<hr><h3><a name="lua_pcall"><code>lua_pcall</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +(nresults|1), <em>-</em>]</span>
+<pre>int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);</pre>
+
+<p>
+Calls a function in protected mode.
+
+
+<p>
+Both <code>nargs</code> and <code>nresults</code> have the same meaning as
+in <a href="#lua_call"><code>lua_call</code></a>.
+If there are no errors during the call,
+<a href="#lua_pcall"><code>lua_pcall</code></a> behaves exactly like <a href="#lua_call"><code>lua_call</code></a>.
+However, if there is any error,
+<a href="#lua_pcall"><code>lua_pcall</code></a> catches it,
+pushes a single value on the stack (the error message),
+and returns an error code.
+Like <a href="#lua_call"><code>lua_call</code></a>,
+<a href="#lua_pcall"><code>lua_pcall</code></a> always removes the function
+and its arguments from the stack.
+
+
+<p>
+If <code>errfunc</code> is 0,
+then the error message returned on the stack
+is exactly the original error message.
+Otherwise, <code>errfunc</code> is the stack index of an
+<em>error handler function</em>.
+(In the current implementation, this index cannot be a pseudo-index.)
+In case of runtime errors,
+this function will be called with the error message
+and its return value will be the message returned on the stack by <a href="#lua_pcall"><code>lua_pcall</code></a>.
+
+
+<p>
+Typically, the error handler function is used to add more debug
+information to the error message, such as a stack traceback.
+Such information cannot be gathered after the return of <a href="#lua_pcall"><code>lua_pcall</code></a>,
+since by then the stack has unwound.
+
+
+<p>
+The <a href="#lua_pcall"><code>lua_pcall</code></a> function returns 0 in case of success
+or one of the following error codes
+(defined in <code>lua.h</code>):
+
+<ul>
+
+<li><b><a name="pdf-LUA_ERRRUN"><code>LUA_ERRRUN</code></a>:</b>
+a runtime error.
+</li>
+
+<li><b><a name="pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>:</b>
+memory allocation error.
+For such errors, Lua does not call the error handler function.
+</li>
+
+<li><b><a name="pdf-LUA_ERRERR"><code>LUA_ERRERR</code></a>:</b>
+error while running the error handler function.
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_pop"><code>lua_pop</code></a></h3><p>
+<span class="apii">[-n, +0, <em>-</em>]</span>
+<pre>void lua_pop (lua_State *L, int n);</pre>
+
+<p>
+Pops <code>n</code> elements from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushboolean"><code>lua_pushboolean</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushboolean (lua_State *L, int b);</pre>
+
+<p>
+Pushes a boolean value with value <code>b</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushcclosure"><code>lua_pushcclosure</code></a></h3><p>
+<span class="apii">[-n, +1, <em>m</em>]</span>
+<pre>void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);</pre>
+
+<p>
+Pushes a new C&nbsp;closure onto the stack.
+
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+thus creating a C&nbsp;closure (see <a href="#3.4">&sect;3.4</a>);
+these values are then accessible to the function whenever it is called.
+To associate values with a C&nbsp;function,
+first these values should be pushed onto the stack
+(when there are multiple values, the first value is pushed first).
+Then <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>
+is called to create and push the C&nbsp;function onto the stack,
+with the argument <code>n</code> telling how many values should be
+associated with the function.
+<a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a> also pops these values from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushcfunction"><code>lua_pushcfunction</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_pushcfunction (lua_State *L, lua_CFunction f);</pre>
+
+<p>
+Pushes a C&nbsp;function onto the stack.
+This function receives a pointer to a C function
+and pushes onto the stack a Lua value of type <code>function</code> that,
+when called, invokes the corresponding C&nbsp;function.
+
+
+<p>
+Any function to be registered in Lua must
+follow the correct protocol to receive its parameters
+and return its results (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+<code>lua_pushcfunction</code> is defined as a macro:
+
+<pre>
+ #define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
+</pre>
+
+
+
+
+<hr><h3><a name="lua_pushfstring"><code>lua_pushfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushfstring (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Pushes onto the stack a formatted string
+and returns a pointer to this string.
+It is similar to the C&nbsp;function <code>sprintf</code>,
+but has some important differences:
+
+<ul>
+
+<li>
+You do not have to allocate space for the result:
+the result is a Lua string and Lua takes care of memory allocation
+(and deallocation, through garbage collection).
+</li>
+
+<li>
+The conversion specifiers are quite restricted.
+There are no flags, widths, or precisions.
+The conversion specifiers can only be
+'<code>%%</code>' (inserts a '<code>%</code>' in the string),
+'<code>%s</code>' (inserts a zero-terminated string, with no size restrictions),
+'<code>%f</code>' (inserts a <a href="#lua_Number"><code>lua_Number</code></a>),
+'<code>%p</code>' (inserts a pointer as a hexadecimal numeral),
+'<code>%d</code>' (inserts an <code>int</code>), and
+'<code>%c</code>' (inserts an <code>int</code> as a character).
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_pushinteger"><code>lua_pushinteger</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushinteger (lua_State *L, lua_Integer n);</pre>
+
+<p>
+Pushes a number with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushlightuserdata"><code>lua_pushlightuserdata</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushlightuserdata (lua_State *L, void *p);</pre>
+
+<p>
+Pushes a light userdata onto the stack.
+
+
+<p>
+Userdata represent C&nbsp;values in Lua.
+A <em>light userdata</em> represents a pointer.
+It is a value (like a number):
+you do not create it, it has no individual metatable,
+and it is not collected (as it was never created).
+A light userdata is equal to "any"
+light userdata with the same C&nbsp;address.
+
+
+
+
+
+<hr><h3><a name="lua_pushliteral"><code>lua_pushliteral</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_pushliteral (lua_State *L, const char *s);</pre>
+
+<p>
+This macro is equivalent to <a href="#lua_pushlstring"><code>lua_pushlstring</code></a>,
+but can be used only when <code>s</code> is a literal string.
+In these cases, it automatically provides the string length.
+
+
+
+
+
+<hr><h3><a name="lua_pushlstring"><code>lua_pushlstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_pushlstring (lua_State *L, const char *s, size_t len);</pre>
+
+<p>
+Pushes the string pointed to by <code>s</code> with size <code>len</code>
+onto the stack.
+Lua makes (or reuses) an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+The string can contain embedded zeros.
+
+
+
+
+
+<hr><h3><a name="lua_pushnil"><code>lua_pushnil</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushnil (lua_State *L);</pre>
+
+<p>
+Pushes a nil value onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushnumber"><code>lua_pushnumber</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushnumber (lua_State *L, lua_Number n);</pre>
+
+<p>
+Pushes a number with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushstring"><code>lua_pushstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_pushstring (lua_State *L, const char *s);</pre>
+
+<p>
+Pushes the zero-terminated string pointed to by <code>s</code>
+onto the stack.
+Lua makes (or reuses) an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+The string cannot contain embedded zeros;
+it is assumed to end at the first zero.
+
+
+
+
+
+<hr><h3><a name="lua_pushthread"><code>lua_pushthread</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>int lua_pushthread (lua_State *L);</pre>
+
+<p>
+Pushes the thread represented by <code>L</code> onto the stack.
+Returns 1 if this thread is the main thread of its state.
+
+
+
+
+
+<hr><h3><a name="lua_pushvalue"><code>lua_pushvalue</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_pushvalue (lua_State *L, int index);</pre>
+
+<p>
+Pushes a copy of the element at the given valid index
+onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushvfstring"><code>lua_pushvfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushvfstring (lua_State *L,
+ const char *fmt,
+ va_list argp);</pre>
+
+<p>
+Equivalent to <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, except that it receives a <code>va_list</code>
+instead of a variable number of arguments.
+
+
+
+
+
+<hr><h3><a name="lua_rawequal"><code>lua_rawequal</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_rawequal (lua_State *L, int index1, int index2);</pre>
+
+<p>
+Returns 1 if the two values in acceptable indices <code>index1</code> and
+<code>index2</code> are primitively equal
+(that is, without calling metamethods).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices are non valid.
+
+
+
+
+
+<hr><h3><a name="lua_rawget"><code>lua_rawget</code></a></h3><p>
+<span class="apii">[-1, +1, <em>-</em>]</span>
+<pre>void lua_rawget (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_gettable"><code>lua_gettable</code></a>, but does a raw access
+(i.e., without metamethods).
+
+
+
+
+
+<hr><h3><a name="lua_rawgeti"><code>lua_rawgeti</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void lua_rawgeti (lua_State *L, int index, int n);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[n]</code>,
+where <code>t</code> is the value at the given valid index.
+The access is raw;
+that is, it does not invoke metamethods.
+
+
+
+
+
+<hr><h3><a name="lua_rawset"><code>lua_rawset</code></a></h3><p>
+<span class="apii">[-2, +0, <em>m</em>]</span>
+<pre>void lua_rawset (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_settable"><code>lua_settable</code></a>, but does a raw assignment
+(i.e., without metamethods).
+
+
+
+
+
+<hr><h3><a name="lua_rawseti"><code>lua_rawseti</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void lua_rawseti (lua_State *L, int index, int n);</pre>
+
+<p>
+Does the equivalent of <code>t[n] = v</code>,
+where <code>t</code> is the value at the given valid index
+and <code>v</code> is the value at the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+The assignment is raw;
+that is, it does not invoke metamethods.
+
+
+
+
+
+<hr><h3><a name="lua_Reader"><code>lua_Reader</code></a></h3>
+<pre>typedef const char * (*lua_Reader) (lua_State *L,
+ void *data,
+ size_t *size);</pre>
+
+<p>
+The reader function used by <a href="#lua_load"><code>lua_load</code></a>.
+Every time it needs another piece of the chunk,
+<a href="#lua_load"><code>lua_load</code></a> calls the reader,
+passing along its <code>data</code> parameter.
+The reader must return a pointer to a block of memory
+with a new piece of the chunk
+and set <code>size</code> to the block size.
+The block must exist until the reader function is called again.
+To signal the end of the chunk, the reader must return <code>NULL</code>.
+The reader function may return pieces of any size greater than zero.
+
+
+
+
+
+<hr><h3><a name="lua_register"><code>lua_register</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void lua_register (lua_State *L,
+ const char *name,
+ lua_CFunction f);</pre>
+
+<p>
+Sets the C function <code>f</code> as the new value of global <code>name</code>.
+It is defined as a macro:
+
+<pre>
+ #define lua_register(L,n,f) \
+ (lua_pushcfunction(L, f), lua_setglobal(L, n))
+</pre>
+
+
+
+
+<hr><h3><a name="lua_remove"><code>lua_remove</code></a></h3><p>
+<span class="apii">[-1, +0, <em>-</em>]</span>
+<pre>void lua_remove (lua_State *L, int index);</pre>
+
+<p>
+Removes the element at the given valid index,
+shifting down the elements above this index to fill the gap.
+Cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_replace"><code>lua_replace</code></a></h3><p>
+<span class="apii">[-1, +0, <em>-</em>]</span>
+<pre>void lua_replace (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given position (and pops it),
+without shifting any element
+(therefore replacing the value at the given position).
+
+
+
+
+
+<hr><h3><a name="lua_resume"><code>lua_resume</code></a></h3><p>
+<span class="apii">[-?, +?, <em>-</em>]</span>
+<pre>int lua_resume (lua_State *L, int narg);</pre>
+
+<p>
+Starts and resumes a coroutine in a given thread.
+
+
+<p>
+To start a coroutine, you first create a new thread
+(see <a href="#lua_newthread"><code>lua_newthread</code></a>);
+then you push onto its stack the main function plus any arguments;
+then you call <a href="#lua_resume"><code>lua_resume</code></a>,
+with <code>narg</code> being the number of arguments.
+This call returns when the coroutine suspends or finishes its execution.
+When it returns, the stack contains all values passed to <a href="#lua_yield"><code>lua_yield</code></a>,
+or all values returned by the body function.
+<a href="#lua_resume"><code>lua_resume</code></a> returns
+<a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the coroutine yields,
+0 if the coroutine finishes its execution
+without errors,
+or an error code in case of errors (see <a href="#lua_pcall"><code>lua_pcall</code></a>).
+In case of errors,
+the stack is not unwound,
+so you can use the debug API over it.
+The error message is on the top of the stack.
+To restart a coroutine, you put on its stack only the values to
+be passed as results from <code>yield</code>,
+and then call <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_setallocf"><code>lua_setallocf</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);</pre>
+
+<p>
+Changes the allocator function of a given state to <code>f</code>
+with user data <code>ud</code>.
+
+
+
+
+
+<hr><h3><a name="lua_setfenv"><code>lua_setfenv</code></a></h3><p>
+<span class="apii">[-1, +0, <em>-</em>]</span>
+<pre>int lua_setfenv (lua_State *L, int index);</pre>
+
+<p>
+Pops a table from the stack and sets it as
+the new environment for the value at the given index.
+If the value at the given index is
+neither a function nor a thread nor a userdata,
+<a href="#lua_setfenv"><code>lua_setfenv</code></a> returns 0.
+Otherwise it returns 1.
+
+
+
+
+
+<hr><h3><a name="lua_setfield"><code>lua_setfield</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given valid index
+and <code>v</code> is the value at the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<hr><h3><a name="lua_setglobal"><code>lua_setglobal</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pops a value from the stack and
+sets it as the new value of global <code>name</code>.
+It is defined as a macro:
+
+<pre>
+ #define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
+</pre>
+
+
+
+
+<hr><h3><a name="lua_setmetatable"><code>lua_setmetatable</code></a></h3><p>
+<span class="apii">[-1, +0, <em>-</em>]</span>
+<pre>int lua_setmetatable (lua_State *L, int index);</pre>
+
+<p>
+Pops a table from the stack and
+sets it as the new metatable for the value at the given
+acceptable index.
+
+
+
+
+
+<hr><h3><a name="lua_settable"><code>lua_settable</code></a></h3><p>
+<span class="apii">[-2, +0, <em>e</em>]</span>
+<pre>void lua_settable (lua_State *L, int index);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given valid index,
+<code>v</code> is the value at the top of the stack,
+and <code>k</code> is the value just below the top.
+
+
+<p>
+This function pops both the key and the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.8">&sect;2.8</a>).
+
+
+
+
+
+<hr><h3><a name="lua_settop"><code>lua_settop</code></a></h3><p>
+<span class="apii">[-?, +?, <em>-</em>]</span>
+<pre>void lua_settop (lua_State *L, int index);</pre>
+
+<p>
+Accepts any acceptable index, or&nbsp;0,
+and sets the stack top to this index.
+If the new top is larger than the old one,
+then the new elements are filled with <b>nil</b>.
+If <code>index</code> is&nbsp;0, then all stack elements are removed.
+
+
+
+
+
+<hr><h3><a name="lua_State"><code>lua_State</code></a></h3>
+<pre>typedef struct lua_State lua_State;</pre>
+
+<p>
+Opaque structure that keeps the whole state of a Lua interpreter.
+The Lua library is fully reentrant:
+it has no global variables.
+All information about a state is kept in this structure.
+
+
+<p>
+A pointer to this state must be passed as the first argument to
+every function in the library, except to <a href="#lua_newstate"><code>lua_newstate</code></a>,
+which creates a Lua state from scratch.
+
+
+
+
+
+<hr><h3><a name="lua_status"><code>lua_status</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_status (lua_State *L);</pre>
+
+<p>
+Returns the status of the thread <code>L</code>.
+
+
+<p>
+The status can be 0 for a normal thread,
+an error code if the thread finished its execution with an error,
+or <a name="pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the thread is suspended.
+
+
+
+
+
+<hr><h3><a name="lua_toboolean"><code>lua_toboolean</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_toboolean (lua_State *L, int index);</pre>
+
+<p>
+Converts the Lua value at the given acceptable index to a C&nbsp;boolean
+value (0&nbsp;or&nbsp;1).
+Like all tests in Lua,
+<a href="#lua_toboolean"><code>lua_toboolean</code></a> returns 1 for any Lua value
+different from <b>false</b> and <b>nil</b>;
+otherwise it returns 0.
+It also returns 0 when called with a non-valid index.
+(If you want to accept only actual boolean values,
+use <a href="#lua_isboolean"><code>lua_isboolean</code></a> to test the value's type.)
+
+
+
+
+
+<hr><h3><a name="lua_tocfunction"><code>lua_tocfunction</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_CFunction lua_tocfunction (lua_State *L, int index);</pre>
+
+<p>
+Converts a value at the given acceptable index to a C&nbsp;function.
+That value must be a C&nbsp;function;
+otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tointeger"><code>lua_tointeger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_Integer lua_tointeger (lua_State *L, int index);</pre>
+
+<p>
+Converts the Lua value at the given acceptable index
+to the signed integral type <a href="#lua_Integer"><code>lua_Integer</code></a>.
+The Lua value must be a number or a string convertible to a number
+(see <a href="#2.2.1">&sect;2.2.1</a>);
+otherwise, <a href="#lua_tointeger"><code>lua_tointeger</code></a> returns&nbsp;0.
+
+
+<p>
+If the number is not an integer,
+it is truncated in some non-specified way.
+
+
+
+
+
+<hr><h3><a name="lua_tolstring"><code>lua_tolstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tolstring (lua_State *L, int index, size_t *len);</pre>
+
+<p>
+Converts the Lua value at the given acceptable index to a C&nbsp;string.
+If <code>len</code> is not <code>NULL</code>,
+it also sets <code>*len</code> with the string length.
+The Lua value must be a string or a number;
+otherwise, the function returns <code>NULL</code>.
+If the value is a number,
+then <a href="#lua_tolstring"><code>lua_tolstring</code></a> also
+<em>changes the actual value in the stack to a string</em>.
+(This change confuses <a href="#lua_next"><code>lua_next</code></a>
+when <a href="#lua_tolstring"><code>lua_tolstring</code></a> is applied to keys during a table traversal.)
+
+
+<p>
+<a href="#lua_tolstring"><code>lua_tolstring</code></a> returns a fully aligned pointer
+to a string inside the Lua state.
+This string always has a zero ('<code>\0</code>')
+after its last character (as in&nbsp;C),
+but may contain other zeros in its body.
+Because Lua has garbage collection,
+there is no guarantee that the pointer returned by <a href="#lua_tolstring"><code>lua_tolstring</code></a>
+will be valid after the corresponding value is removed from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_tonumber"><code>lua_tonumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_Number lua_tonumber (lua_State *L, int index);</pre>
+
+<p>
+Converts the Lua value at the given acceptable index
+to the C&nbsp;type <a href="#lua_Number"><code>lua_Number</code></a> (see <a href="#lua_Number"><code>lua_Number</code></a>).
+The Lua value must be a number or a string convertible to a number
+(see <a href="#2.2.1">&sect;2.2.1</a>);
+otherwise, <a href="#lua_tonumber"><code>lua_tonumber</code></a> returns&nbsp;0.
+
+
+
+
+
+<hr><h3><a name="lua_topointer"><code>lua_topointer</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>const void *lua_topointer (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given acceptable index to a generic
+C&nbsp;pointer (<code>void*</code>).
+The value may be a userdata, a table, a thread, or a function;
+otherwise, <a href="#lua_topointer"><code>lua_topointer</code></a> returns <code>NULL</code>.
+Different objects will give different pointers.
+There is no way to convert the pointer back to its original value.
+
+
+<p>
+Typically this function is used only for debug information.
+
+
+
+
+
+<hr><h3><a name="lua_tostring"><code>lua_tostring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tostring (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tolstring"><code>lua_tolstring</code></a> with <code>len</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tothread"><code>lua_tothread</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_State *lua_tothread (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given acceptable index to a Lua thread
+(represented as <code>lua_State*</code>).
+This value must be a thread;
+otherwise, the function returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_touserdata"><code>lua_touserdata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>void *lua_touserdata (lua_State *L, int index);</pre>
+
+<p>
+If the value at the given acceptable index is a full userdata,
+returns its block address.
+If the value is a light userdata,
+returns its pointer.
+Otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_type"><code>lua_type</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_type (lua_State *L, int index);</pre>
+
+<p>
+Returns the type of the value in the given acceptable index,
+or <code>LUA_TNONE</code> for a non-valid index
+(that is, an index to an "empty" stack position).
+The types returned by <a href="#lua_type"><code>lua_type</code></a> are coded by the following constants
+defined in <code>lua.h</code>:
+<code>LUA_TNIL</code>,
+<code>LUA_TNUMBER</code>,
+<code>LUA_TBOOLEAN</code>,
+<code>LUA_TSTRING</code>,
+<code>LUA_TTABLE</code>,
+<code>LUA_TFUNCTION</code>,
+<code>LUA_TUSERDATA</code>,
+<code>LUA_TTHREAD</code>,
+and
+<code>LUA_TLIGHTUSERDATA</code>.
+
+
+
+
+
+<hr><h3><a name="lua_typename"><code>lua_typename</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>const char *lua_typename (lua_State *L, int tp);</pre>
+
+<p>
+Returns the name of the type encoded by the value <code>tp</code>,
+which must be one the values returned by <a href="#lua_type"><code>lua_type</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_Writer"><code>lua_Writer</code></a></h3>
+<pre>typedef int (*lua_Writer) (lua_State *L,
+ const void* p,
+ size_t sz,
+ void* ud);</pre>
+
+<p>
+The type of the writer function used by <a href="#lua_dump"><code>lua_dump</code></a>.
+Every time it produces another piece of chunk,
+<a href="#lua_dump"><code>lua_dump</code></a> calls the writer,
+passing along the buffer to be written (<code>p</code>),
+its size (<code>sz</code>),
+and the <code>data</code> parameter supplied to <a href="#lua_dump"><code>lua_dump</code></a>.
+
+
+<p>
+The writer returns an error code:
+0&nbsp;means no errors;
+any other value means an error and stops <a href="#lua_dump"><code>lua_dump</code></a> from
+calling the writer again.
+
+
+
+
+
+<hr><h3><a name="lua_xmove"><code>lua_xmove</code></a></h3><p>
+<span class="apii">[-?, +?, <em>-</em>]</span>
+<pre>void lua_xmove (lua_State *from, lua_State *to, int n);</pre>
+
+<p>
+Exchange values between different threads of the <em>same</em> global state.
+
+
+<p>
+This function pops <code>n</code> values from the stack <code>from</code>,
+and pushes them onto the stack <code>to</code>.
+
+
+
+
+
+<hr><h3><a name="lua_yield"><code>lua_yield</code></a></h3><p>
+<span class="apii">[-?, +?, <em>-</em>]</span>
+<pre>int lua_yield (lua_State *L, int nresults);</pre>
+
+<p>
+Yields a coroutine.
+
+
+<p>
+This function should only be called as the
+return expression of a C&nbsp;function, as follows:
+
+<pre>
+ return lua_yield (L, nresults);
+</pre><p>
+When a C&nbsp;function calls <a href="#lua_yield"><code>lua_yield</code></a> in that way,
+the running coroutine suspends its execution,
+and the call to <a href="#lua_resume"><code>lua_resume</code></a> that started this coroutine returns.
+The parameter <code>nresults</code> is the number of values from the stack
+that are passed as results to <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+
+
+
+
+
+<h2>3.8 - <a name="3.8">The Debug Interface</a></h2>
+
+<p>
+Lua has no built-in debugging facilities.
+Instead, it offers a special interface
+by means of functions and <em>hooks</em>.
+This interface allows the construction of different
+kinds of debuggers, profilers, and other tools
+that need "inside information" from the interpreter.
+
+
+
+<hr><h3><a name="lua_Debug"><code>lua_Debug</code></a></h3>
+<pre>typedef struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) */
+ const char *what; /* (S) */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ <em>other fields</em>
+} lua_Debug;</pre>
+
+<p>
+A structure used to carry different pieces of
+information about an active function.
+<a href="#lua_getstack"><code>lua_getstack</code></a> fills only the private part
+of this structure, for later use.
+To fill the other fields of <a href="#lua_Debug"><code>lua_Debug</code></a> with useful information,
+call <a href="#lua_getinfo"><code>lua_getinfo</code></a>.
+
+
+<p>
+The fields of <a href="#lua_Debug"><code>lua_Debug</code></a> have the following meaning:
+
+<ul>
+
+<li><b><code>source</code>:</b>
+If the function was defined in a string,
+then <code>source</code> is that string.
+If the function was defined in a file,
+then <code>source</code> starts with a '<code>@</code>' followed by the file name.
+</li>
+
+<li><b><code>short_src</code>:</b>
+a "printable" version of <code>source</code>, to be used in error messages.
+</li>
+
+<li><b><code>linedefined</code>:</b>
+the line number where the definition of the function starts.
+</li>
+
+<li><b><code>lastlinedefined</code>:</b>
+the line number where the definition of the function ends.
+</li>
+
+<li><b><code>what</code>:</b>
+the string <code>"Lua"</code> if the function is a Lua function,
+<code>"C"</code> if it is a C&nbsp;function,
+<code>"main"</code> if it is the main part of a chunk,
+and <code>"tail"</code> if it was a function that did a tail call.
+In the latter case,
+Lua has no other information about the function.
+</li>
+
+<li><b><code>currentline</code>:</b>
+the current line where the given function is executing.
+When no line information is available,
+<code>currentline</code> is set to -1.
+</li>
+
+<li><b><code>name</code>:</b>
+a reasonable name for the given function.
+Because functions in Lua are first-class values,
+they do not have a fixed name:
+some functions may be the value of multiple global variables,
+while others may be stored only in a table field.
+The <code>lua_getinfo</code> function checks how the function was
+called to find a suitable name.
+If it cannot find a name,
+then <code>name</code> is set to <code>NULL</code>.
+</li>
+
+<li><b><code>namewhat</code>:</b>
+explains the <code>name</code> field.
+The value of <code>namewhat</code> can be
+<code>"global"</code>, <code>"local"</code>, <code>"method"</code>,
+<code>"field"</code>, <code>"upvalue"</code>, or <code>""</code> (the empty string),
+according to how the function was called.
+(Lua uses the empty string when no other option seems to apply.)
+</li>
+
+<li><b><code>nups</code>:</b>
+the number of upvalues of the function.
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_gethook"><code>lua_gethook</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_Hook lua_gethook (lua_State *L);</pre>
+
+<p>
+Returns the current hook function.
+
+
+
+
+
+<hr><h3><a name="lua_gethookcount"><code>lua_gethookcount</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_gethookcount (lua_State *L);</pre>
+
+<p>
+Returns the current hook count.
+
+
+
+
+
+<hr><h3><a name="lua_gethookmask"><code>lua_gethookmask</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_gethookmask (lua_State *L);</pre>
+
+<p>
+Returns the current hook mask.
+
+
+
+
+
+<hr><h3><a name="lua_getinfo"><code>lua_getinfo</code></a></h3><p>
+<span class="apii">[-(0|1), +(0|1|2), <em>m</em>]</span>
+<pre>int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);</pre>
+
+<p>
+Returns information about a specific function or function invocation.
+
+
+<p>
+To get information about a function invocation,
+the parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+
+
+<p>
+To get information about a function you push it onto the stack
+and start the <code>what</code> string with the character '<code>&gt;</code>'.
+(In that case,
+<code>lua_getinfo</code> pops the function in the top of the stack.)
+For instance, to know in which line a function <code>f</code> was defined,
+you can write the following code:
+
+<pre>
+ lua_Debug ar;
+ lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* get global 'f' */
+ lua_getinfo(L, "&gt;S", &amp;ar);
+ printf("%d\n", ar.linedefined);
+</pre>
+
+<p>
+Each character in the string <code>what</code>
+selects some fields of the structure <code>ar</code> to be filled or
+a value to be pushed on the stack:
+
+<ul>
+
+<li><b>'<code>n</code>':</b> fills in the field <code>name</code> and <code>namewhat</code>;
+</li>
+
+<li><b>'<code>S</code>':</b>
+fills in the fields <code>source</code>, <code>short_src</code>,
+<code>linedefined</code>, <code>lastlinedefined</code>, and <code>what</code>;
+</li>
+
+<li><b>'<code>l</code>':</b> fills in the field <code>currentline</code>;
+</li>
+
+<li><b>'<code>u</code>':</b> fills in the field <code>nups</code>;
+</li>
+
+<li><b>'<code>f</code>':</b>
+pushes onto the stack the function that is
+running at the given level;
+</li>
+
+<li><b>'<code>L</code>':</b>
+pushes onto the stack a table whose indices are the
+numbers of the lines that are valid on the function.
+(A <em>valid line</em> is a line with some associated code,
+that is, a line where you can put a break point.
+Non-valid lines include empty lines and comments.)
+</li>
+
+</ul>
+
+<p>
+This function returns 0 on error
+(for instance, an invalid option in <code>what</code>).
+
+
+
+
+
+<hr><h3><a name="lua_getlocal"><code>lua_getlocal</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>-</em>]</span>
+<pre>const char *lua_getlocal (lua_State *L, lua_Debug *ar, int n);</pre>
+
+<p>
+Gets information about a local variable of a given activation record.
+The parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+The index <code>n</code> selects which local variable to inspect
+(1 is the first parameter or active local variable, and so on,
+until the last active local variable).
+<a href="#lua_getlocal"><code>lua_getlocal</code></a> pushes the variable's value onto the stack
+and returns its name.
+
+
+<p>
+Variable names starting with '<code>(</code>' (open parentheses)
+represent internal variables
+(loop control variables, temporaries, and C&nbsp;function locals).
+
+
+<p>
+Returns <code>NULL</code> (and pushes nothing)
+when the index is greater than
+the number of active local variables.
+
+
+
+
+
+<hr><h3><a name="lua_getstack"><code>lua_getstack</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_getstack (lua_State *L, int level, lua_Debug *ar);</pre>
+
+<p>
+Get information about the interpreter runtime stack.
+
+
+<p>
+This function fills parts of a <a href="#lua_Debug"><code>lua_Debug</code></a> structure with
+an identification of the <em>activation record</em>
+of the function executing at a given level.
+Level&nbsp;0 is the current running function,
+whereas level <em>n+1</em> is the function that has called level <em>n</em>.
+When there are no errors, <a href="#lua_getstack"><code>lua_getstack</code></a> returns 1;
+when called with a level greater than the stack depth,
+it returns 0.
+
+
+
+
+
+<hr><h3><a name="lua_getupvalue"><code>lua_getupvalue</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>-</em>]</span>
+<pre>const char *lua_getupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Gets information about a closure's upvalue.
+(For Lua functions,
+upvalues are the external local variables that the function uses,
+and that are consequently included in its closure.)
+<a href="#lua_getupvalue"><code>lua_getupvalue</code></a> gets the index <code>n</code> of an upvalue,
+pushes the upvalue's value onto the stack,
+and returns its name.
+<code>funcindex</code> points to the closure in the stack.
+(Upvalues have no particular order,
+as they are active through the whole function.
+So, they are numbered in an arbitrary order.)
+
+
+<p>
+Returns <code>NULL</code> (and pushes nothing)
+when the index is greater than the number of upvalues.
+For C&nbsp;functions, this function uses the empty string <code>""</code>
+as a name for all upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_Hook"><code>lua_Hook</code></a></h3>
+<pre>typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);</pre>
+
+<p>
+Type for debugging hook functions.
+
+
+<p>
+Whenever a hook is called, its <code>ar</code> argument has its field
+<code>event</code> set to the specific event that triggered the hook.
+Lua identifies these events with the following constants:
+<a name="pdf-LUA_HOOKCALL"><code>LUA_HOOKCALL</code></a>, <a name="pdf-LUA_HOOKRET"><code>LUA_HOOKRET</code></a>,
+<a name="pdf-LUA_HOOKTAILRET"><code>LUA_HOOKTAILRET</code></a>, <a name="pdf-LUA_HOOKLINE"><code>LUA_HOOKLINE</code></a>,
+and <a name="pdf-LUA_HOOKCOUNT"><code>LUA_HOOKCOUNT</code></a>.
+Moreover, for line events, the field <code>currentline</code> is also set.
+To get the value of any other field in <code>ar</code>,
+the hook must call <a href="#lua_getinfo"><code>lua_getinfo</code></a>.
+For return events, <code>event</code> may be <code>LUA_HOOKRET</code>,
+the normal value, or <code>LUA_HOOKTAILRET</code>.
+In the latter case, Lua is simulating a return from
+a function that did a tail call;
+in this case, it is useless to call <a href="#lua_getinfo"><code>lua_getinfo</code></a>.
+
+
+<p>
+While Lua is running a hook, it disables other calls to hooks.
+Therefore, if a hook calls back Lua to execute a function or a chunk,
+this execution occurs without any calls to hooks.
+
+
+
+
+
+<hr><h3><a name="lua_sethook"><code>lua_sethook</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>int lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre>
+
+<p>
+Sets the debugging hook function.
+
+
+<p>
+Argument <code>f</code> is the hook function.
+<code>mask</code> specifies on which events the hook will be called:
+it is formed by a bitwise or of the constants
+<a name="pdf-LUA_MASKCALL"><code>LUA_MASKCALL</code></a>,
+<a name="pdf-LUA_MASKRET"><code>LUA_MASKRET</code></a>,
+<a name="pdf-LUA_MASKLINE"><code>LUA_MASKLINE</code></a>,
+and <a name="pdf-LUA_MASKCOUNT"><code>LUA_MASKCOUNT</code></a>.
+The <code>count</code> argument is only meaningful when the mask
+includes <code>LUA_MASKCOUNT</code>.
+For each event, the hook is called as explained below:
+
+<ul>
+
+<li><b>The call hook:</b> is called when the interpreter calls a function.
+The hook is called just after Lua enters the new function,
+before the function gets its arguments.
+</li>
+
+<li><b>The return hook:</b> is called when the interpreter returns from a function.
+The hook is called just before Lua leaves the function.
+You have no access to the values to be returned by the function.
+</li>
+
+<li><b>The line hook:</b> is called when the interpreter is about to
+start the execution of a new line of code,
+or when it jumps back in the code (even to the same line).
+(This event only happens while Lua is executing a Lua function.)
+</li>
+
+<li><b>The count hook:</b> is called after the interpreter executes every
+<code>count</code> instructions.
+(This event only happens while Lua is executing a Lua function.)
+</li>
+
+</ul>
+
+<p>
+A hook is disabled by setting <code>mask</code> to zero.
+
+
+
+
+
+<hr><h3><a name="lua_setlocal"><code>lua_setlocal</code></a></h3><p>
+<span class="apii">[-(0|1), +0, <em>-</em>]</span>
+<pre>const char *lua_setlocal (lua_State *L, lua_Debug *ar, int n);</pre>
+
+<p>
+Sets the value of a local variable of a given activation record.
+Parameters <code>ar</code> and <code>n</code> are as in <a href="#lua_getlocal"><code>lua_getlocal</code></a>
+(see <a href="#lua_getlocal"><code>lua_getlocal</code></a>).
+<a href="#lua_setlocal"><code>lua_setlocal</code></a> assigns the value at the top of the stack
+to the variable and returns its name.
+It also pops the value from the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index is greater than
+the number of active local variables.
+
+
+
+
+
+<hr><h3><a name="lua_setupvalue"><code>lua_setupvalue</code></a></h3><p>
+<span class="apii">[-(0|1), +0, <em>-</em>]</span>
+<pre>const char *lua_setupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Sets the value of a closure's upvalue.
+It assigns the value at the top of the stack
+to the upvalue and returns its name.
+It also pops the value from the stack.
+Parameters <code>funcindex</code> and <code>n</code> are as in the <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>
+(see <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>).
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index is greater than the number of upvalues.
+
+
+
+
+
+
+
+<h1>4 - <a name="4">The Auxiliary Library</a></h1>
+
+<p>
+
+The <em>auxiliary library</em> provides several convenient functions
+to interface C with Lua.
+While the basic API provides the primitive functions for all
+interactions between C and Lua,
+the auxiliary library provides higher-level functions for some
+common tasks.
+
+
+<p>
+All functions from the auxiliary library
+are defined in header file <code>lauxlib.h</code> and
+have a prefix <code>luaL_</code>.
+
+
+<p>
+All functions in the auxiliary library are built on
+top of the basic API,
+and so they provide nothing that cannot be done with this API.
+
+
+<p>
+Several functions in the auxiliary library are used to
+check C&nbsp;function arguments.
+Their names are always <code>luaL_check*</code> or <code>luaL_opt*</code>.
+All of these functions throw an error if the check is not satisfied.
+Because the error message is formatted for arguments
+(e.g., "<code>bad argument #1</code>"),
+you should not use these functions for other stack values.
+
+
+
+<h2>4.1 - <a name="4.1">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the auxiliary library
+in alphabetical order.
+
+
+
+<hr><h3><a name="luaL_addchar"><code>luaL_addchar</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void luaL_addchar (luaL_Buffer *B, char c);</pre>
+
+<p>
+Adds the character <code>c</code> to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addlstring"><code>luaL_addlstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);</pre>
+
+<p>
+Adds the string pointed to by <code>s</code> with length <code>l</code> to
+the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The string may contain embedded zeros.
+
+
+
+
+
+<hr><h3><a name="luaL_addsize"><code>luaL_addsize</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void luaL_addsize (luaL_Buffer *B, size_t n);</pre>
+
+<p>
+Adds to the buffer <code>B</code> (see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>)
+a string of length <code>n</code> previously copied to the
+buffer area (see <a href="#luaL_prepbuffer"><code>luaL_prepbuffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addstring"><code>luaL_addstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void luaL_addstring (luaL_Buffer *B, const char *s);</pre>
+
+<p>
+Adds the zero-terminated string pointed to by <code>s</code>
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The string may not contain embedded zeros.
+
+
+
+
+
+<hr><h3><a name="luaL_addvalue"><code>luaL_addvalue</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void luaL_addvalue (luaL_Buffer *B);</pre>
+
+<p>
+Adds the value at the top of the stack
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+Pops the value.
+
+
+<p>
+This is the only function on string buffers that can (and must)
+be called with an extra element on the stack,
+which is the value to be added to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_argcheck"><code>luaL_argcheck</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_argcheck (lua_State *L,
+ int cond,
+ int narg,
+ const char *extramsg);</pre>
+
+<p>
+Checks whether <code>cond</code> is true.
+If not, raises an error with the following message,
+where <code>func</code> is retrieved from the call stack:
+
+<pre>
+ bad argument #&lt;narg&gt; to &lt;func&gt; (&lt;extramsg&gt;)
+</pre>
+
+
+
+
+<hr><h3><a name="luaL_argerror"><code>luaL_argerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_argerror (lua_State *L, int narg, const char *extramsg);</pre>
+
+<p>
+Raises an error with the following message,
+where <code>func</code> is retrieved from the call stack:
+
+<pre>
+ bad argument #&lt;narg&gt; to &lt;func&gt; (&lt;extramsg&gt;)
+</pre>
+
+<p>
+This function never returns,
+but it is an idiom to use it in C&nbsp;functions
+as <code>return luaL_argerror(<em>args</em>)</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_Buffer"><code>luaL_Buffer</code></a></h3>
+<pre>typedef struct luaL_Buffer luaL_Buffer;</pre>
+
+<p>
+Type for a <em>string buffer</em>.
+
+
+<p>
+A string buffer allows C&nbsp;code to build Lua strings piecemeal.
+Its pattern of use is as follows:
+
+<ul>
+
+<li>First you declare a variable <code>b</code> of type <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>.</li>
+
+<li>Then you initialize it with a call <code>luaL_buffinit(L, &amp;b)</code>.</li>
+
+<li>
+Then you add string pieces to the buffer calling any of
+the <code>luaL_add*</code> functions.
+</li>
+
+<li>
+You finish by calling <code>luaL_pushresult(&amp;b)</code>.
+This call leaves the final string on the top of the stack.
+</li>
+
+</ul>
+
+<p>
+During its normal operation,
+a string buffer uses a variable number of stack slots.
+So, while using a buffer, you cannot assume that you know where
+the top of the stack is.
+You can use the stack between successive calls to buffer operations
+as long as that use is balanced;
+that is,
+when you call a buffer operation,
+the stack is at the same level
+it was immediately after the previous buffer operation.
+(The only exception to this rule is <a href="#luaL_addvalue"><code>luaL_addvalue</code></a>.)
+After calling <a href="#luaL_pushresult"><code>luaL_pushresult</code></a> the stack is back to its
+level when the buffer was initialized,
+plus the final string on its top.
+
+
+
+
+
+<hr><h3><a name="luaL_buffinit"><code>luaL_buffinit</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>void luaL_buffinit (lua_State *L, luaL_Buffer *B);</pre>
+
+<p>
+Initializes a buffer <code>B</code>.
+This function does not allocate any space;
+the buffer must be declared as a variable
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_callmeta"><code>luaL_callmeta</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>e</em>]</span>
+<pre>int luaL_callmeta (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Calls a metamethod.
+
+
+<p>
+If the object at index <code>obj</code> has a metatable and this
+metatable has a field <code>e</code>,
+this function calls this field and passes the object as its only argument.
+In this case this function returns 1 and pushes onto the
+stack the value returned by the call.
+If there is no metatable or no metamethod,
+this function returns 0 (without pushing any value on the stack).
+
+
+
+
+
+<hr><h3><a name="luaL_checkany"><code>luaL_checkany</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkany (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function has an argument
+of any type (including <b>nil</b>) at position <code>narg</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkint"><code>luaL_checkint</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_checkint (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a number
+and returns this number cast to an <code>int</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkinteger"><code>luaL_checkinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_checkinteger (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a number
+and returns this number cast to a <a href="#lua_Integer"><code>lua_Integer</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_checklong"><code>luaL_checklong</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>long luaL_checklong (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a number
+and returns this number cast to a <code>long</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checklstring"><code>luaL_checklstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checklstring (lua_State *L, int narg, size_t *l);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a string
+and returns this string;
+if <code>l</code> is not <code>NULL</code> fills <code>*l</code>
+with the string's length.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checknumber"><code>luaL_checknumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_checknumber (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a number
+and returns this number.
+
+
+
+
+
+<hr><h3><a name="luaL_checkoption"><code>luaL_checkoption</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_checkoption (lua_State *L,
+ int narg,
+ const char *def,
+ const char *const lst[]);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a string and
+searches for this string in the array <code>lst</code>
+(which must be NULL-terminated).
+Returns the index in the array where the string was found.
+Raises an error if the argument is not a string or
+if the string cannot be found.
+
+
+<p>
+If <code>def</code> is not <code>NULL</code>,
+the function uses <code>def</code> as a default value when
+there is no argument <code>narg</code> or if this argument is <b>nil</b>.
+
+
+<p>
+This is a useful function for mapping strings to C&nbsp;enums.
+(The usual convention in Lua libraries is
+to use strings instead of numbers to select options.)
+
+
+
+
+
+<hr><h3><a name="luaL_checkstack"><code>luaL_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkstack (lua_State *L, int sz, const char *msg);</pre>
+
+<p>
+Grows the stack size to <code>top + sz</code> elements,
+raising an error if the stack cannot grow to that size.
+<code>msg</code> is an additional text to go into the error message.
+
+
+
+
+
+<hr><h3><a name="luaL_checkstring"><code>luaL_checkstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checkstring (lua_State *L, int narg);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a string
+and returns this string.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checktype"><code>luaL_checktype</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checktype (lua_State *L, int narg, int t);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> has type <code>t</code>.
+See <a href="#lua_type"><code>lua_type</code></a> for the encoding of types for <code>t</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkudata"><code>luaL_checkudata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void *luaL_checkudata (lua_State *L, int narg, const char *tname);</pre>
+
+<p>
+Checks whether the function argument <code>narg</code> is a userdata
+of the type <code>tname</code> (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_dofile"><code>luaL_dofile</code></a></h3><p>
+<span class="apii">[-0, +?, <em>m</em>]</span>
+<pre>int luaL_dofile (lua_State *L, const char *filename);</pre>
+
+<p>
+Loads and runs the given file.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns 0 if there are no errors
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_dostring"><code>luaL_dostring</code></a></h3><p>
+<span class="apii">[-0, +?, <em>m</em>]</span>
+<pre>int luaL_dostring (lua_State *L, const char *str);</pre>
+
+<p>
+Loads and runs the given string.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns 0 if there are no errors
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_error"><code>luaL_error</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_error (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Raises an error.
+The error message format is given by <code>fmt</code>
+plus any extra arguments,
+following the same rules of <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>.
+It also adds at the beginning of the message the file name and
+the line number where the error occurred,
+if this information is available.
+
+
+<p>
+This function never returns,
+but it is an idiom to use it in C&nbsp;functions
+as <code>return luaL_error(<em>args</em>)</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_getmetafield"><code>luaL_getmetafield</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>m</em>]</span>
+<pre>int luaL_getmetafield (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Pushes onto the stack the field <code>e</code> from the metatable
+of the object at index <code>obj</code>.
+If the object does not have a metatable,
+or if the metatable does not have this field,
+returns 0 and pushes nothing.
+
+
+
+
+
+<hr><h3><a name="luaL_getmetatable"><code>luaL_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>-</em>]</span>
+<pre>void luaL_getmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+Pushes onto the stack the metatable associated with name <code>tname</code>
+in the registry (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_gsub"><code>luaL_gsub</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *luaL_gsub (lua_State *L,
+ const char *s,
+ const char *p,
+ const char *r);</pre>
+
+<p>
+Creates a copy of string <code>s</code> by replacing
+any occurrence of the string <code>p</code>
+with the string <code>r</code>.
+Pushes the resulting string on the stack and returns it.
+
+
+
+
+
+<hr><h3><a name="luaL_loadbuffer"><code>luaL_loadbuffer</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadbuffer (lua_State *L,
+ const char *buff,
+ size_t sz,
+ const char *name);</pre>
+
+<p>
+Loads a buffer as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the
+buffer pointed to by <code>buff</code> with size <code>sz</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+<code>name</code> is the chunk name,
+used for debug information and error messages.
+
+
+
+
+
+<hr><h3><a name="luaL_loadfile"><code>luaL_loadfile</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadfile (lua_State *L, const char *filename);</pre>
+
+<p>
+Loads a file as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the file
+named <code>filename</code>.
+If <code>filename</code> is <code>NULL</code>,
+then it loads from the standard input.
+The first line in the file is ignored if it starts with a <code>#</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>,
+but it has an extra error code <a name="pdf-LUA_ERRFILE"><code>LUA_ERRFILE</code></a>
+if it cannot open/read the file.
+
+
+<p>
+As <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_loadstring"><code>luaL_loadstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadstring (lua_State *L, const char *s);</pre>
+
+<p>
+Loads a string as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in
+the zero-terminated string <code>s</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+
+
+<p>
+Also as <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_newmetatable"><code>luaL_newmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_newmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+If the registry already has the key <code>tname</code>,
+returns 0.
+Otherwise,
+creates a new table to be used as a metatable for userdata,
+adds it to the registry with key <code>tname</code>,
+and returns 1.
+
+
+<p>
+In both cases pushes onto the stack the final value associated
+with <code>tname</code> in the registry.
+
+
+
+
+
+<hr><h3><a name="luaL_newstate"><code>luaL_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>lua_State *luaL_newstate (void);</pre>
+
+<p>
+Creates a new Lua state.
+It calls <a href="#lua_newstate"><code>lua_newstate</code></a> with an
+allocator based on the standard&nbsp;C <code>realloc</code> function
+and then sets a panic function (see <a href="#lua_atpanic"><code>lua_atpanic</code></a>) that prints
+an error message to the standard error output in case of fatal
+errors.
+
+
+<p>
+Returns the new state,
+or <code>NULL</code> if there is a memory allocation error.
+
+
+
+
+
+<hr><h3><a name="luaL_openlibs"><code>luaL_openlibs</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void luaL_openlibs (lua_State *L);</pre>
+
+<p>
+Opens all standard Lua libraries into the given state.
+
+
+
+
+
+<hr><h3><a name="luaL_optint"><code>luaL_optint</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_optint (lua_State *L, int narg, int d);</pre>
+
+<p>
+If the function argument <code>narg</code> is a number,
+returns this number cast to an <code>int</code>.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optinteger"><code>luaL_optinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_optinteger (lua_State *L,
+ int narg,
+ lua_Integer d);</pre>
+
+<p>
+If the function argument <code>narg</code> is a number,
+returns this number cast to a <a href="#lua_Integer"><code>lua_Integer</code></a>.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optlong"><code>luaL_optlong</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>long luaL_optlong (lua_State *L, int narg, long d);</pre>
+
+<p>
+If the function argument <code>narg</code> is a number,
+returns this number cast to a <code>long</code>.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optlstring"><code>luaL_optlstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optlstring (lua_State *L,
+ int narg,
+ const char *d,
+ size_t *l);</pre>
+
+<p>
+If the function argument <code>narg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+<p>
+If <code>l</code> is not <code>NULL</code>,
+fills the position <code>*l</code> with the results's length.
+
+
+
+
+
+<hr><h3><a name="luaL_optnumber"><code>luaL_optnumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number d);</pre>
+
+<p>
+If the function argument <code>narg</code> is a number,
+returns this number.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optstring"><code>luaL_optstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optstring (lua_State *L,
+ int narg,
+ const char *d);</pre>
+
+<p>
+If the function argument <code>narg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_prepbuffer"><code>luaL_prepbuffer</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>char *luaL_prepbuffer (luaL_Buffer *B);</pre>
+
+<p>
+Returns an address to a space of size <a name="pdf-LUAL_BUFFERSIZE"><code>LUAL_BUFFERSIZE</code></a>
+where you can copy a string to be added to buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+After copying the string into this space you must call
+<a href="#luaL_addsize"><code>luaL_addsize</code></a> with the size of the string to actually add
+it to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_pushresult"><code>luaL_pushresult</code></a></h3><p>
+<span class="apii">[-?, +1, <em>m</em>]</span>
+<pre>void luaL_pushresult (luaL_Buffer *B);</pre>
+
+<p>
+Finishes the use of buffer <code>B</code> leaving the final string on
+the top of the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_ref"><code>luaL_ref</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>int luaL_ref (lua_State *L, int t);</pre>
+
+<p>
+Creates and returns a <em>reference</em>,
+in the table at index <code>t</code>,
+for the object at the top of the stack (and pops the object).
+
+
+<p>
+A reference is a unique integer key.
+As long as you do not manually add integer keys into table <code>t</code>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> ensures the uniqueness of the key it returns.
+You can retrieve an object referred by reference <code>r</code>
+by calling <code>lua_rawgeti(L, t, r)</code>.
+Function <a href="#luaL_unref"><code>luaL_unref</code></a> frees a reference and its associated object.
+
+
+<p>
+If the object at the top of the stack is <b>nil</b>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> returns the constant <a name="pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>.
+The constant <a name="pdf-LUA_NOREF"><code>LUA_NOREF</code></a> is guaranteed to be different
+from any reference returned by <a href="#luaL_ref"><code>luaL_ref</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_Reg"><code>luaL_Reg</code></a></h3>
+<pre>typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;</pre>
+
+<p>
+Type for arrays of functions to be registered by
+<a href="#luaL_register"><code>luaL_register</code></a>.
+<code>name</code> is the function name and <code>func</code> is a pointer to
+the function.
+Any array of <a href="#luaL_Reg"><code>luaL_Reg</code></a> must end with an sentinel entry
+in which both <code>name</code> and <code>func</code> are <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_register"><code>luaL_register</code></a></h3><p>
+<span class="apii">[-(0|1), +1, <em>m</em>]</span>
+<pre>void luaL_register (lua_State *L,
+ const char *libname,
+ const luaL_Reg *l);</pre>
+
+<p>
+Opens a library.
+
+
+<p>
+When called with <code>libname</code> equal to <code>NULL</code>,
+it simply registers all functions in the list <code>l</code>
+(see <a href="#luaL_Reg"><code>luaL_Reg</code></a>) into the table on the top of the stack.
+
+
+<p>
+When called with a non-null <code>libname</code>,
+<code>luaL_register</code> creates a new table <code>t</code>,
+sets it as the value of the global variable <code>libname</code>,
+sets it as the value of <code>package.loaded[libname]</code>,
+and registers on it all functions in the list <code>l</code>.
+If there is a table in <code>package.loaded[libname]</code> or in
+variable <code>libname</code>,
+reuses this table instead of creating a new one.
+
+
+<p>
+In any case the function leaves the table
+on the top of the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_typename"><code>luaL_typename</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>const char *luaL_typename (lua_State *L, int index);</pre>
+
+<p>
+Returns the name of the type of the value at the given index.
+
+
+
+
+
+<hr><h3><a name="luaL_typerror"><code>luaL_typerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_typerror (lua_State *L, int narg, const char *tname);</pre>
+
+<p>
+Generates an error with a message like the following:
+
+<pre>
+ <em>location</em>: bad argument <em>narg</em> to '<em>func</em>' (<em>tname</em> expected, got <em>rt</em>)
+</pre><p>
+where <code><em>location</em></code> is produced by <a href="#luaL_where"><code>luaL_where</code></a>,
+<code><em>func</em></code> is the name of the current function,
+and <code><em>rt</em></code> is the type name of the actual argument.
+
+
+
+
+
+<hr><h3><a name="luaL_unref"><code>luaL_unref</code></a></h3><p>
+<span class="apii">[-0, +0, <em>-</em>]</span>
+<pre>void luaL_unref (lua_State *L, int t, int ref);</pre>
+
+<p>
+Releases reference <code>ref</code> from the table at index <code>t</code>
+(see <a href="#luaL_ref"><code>luaL_ref</code></a>).
+The entry is removed from the table,
+so that the referred object can be collected.
+The reference <code>ref</code> is also freed to be used again.
+
+
+<p>
+If <code>ref</code> is <a href="#pdf-LUA_NOREF"><code>LUA_NOREF</code></a> or <a href="#pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>,
+<a href="#luaL_unref"><code>luaL_unref</code></a> does nothing.
+
+
+
+
+
+<hr><h3><a name="luaL_where"><code>luaL_where</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_where (lua_State *L, int lvl);</pre>
+
+<p>
+Pushes onto the stack a string identifying the current position
+of the control at level <code>lvl</code> in the call stack.
+Typically this string has the following format:
+
+<pre>
+ <em>chunkname</em>:<em>currentline</em>:
+</pre><p>
+Level&nbsp;0 is the running function,
+level&nbsp;1 is the function that called the running function,
+etc.
+
+
+<p>
+This function is used to build a prefix for error messages.
+
+
+
+
+
+
+
+<h1>5 - <a name="5">Standard Libraries</a></h1>
+
+<p>
+The standard Lua libraries provide useful functions
+that are implemented directly through the C&nbsp;API.
+Some of these functions provide essential services to the language
+(e.g., <a href="#pdf-type"><code>type</code></a> and <a href="#pdf-getmetatable"><code>getmetatable</code></a>);
+others provide access to "outside" services (e.g., I/O);
+and others could be implemented in Lua itself,
+but are quite useful or have critical performance requirements that
+deserve an implementation in C (e.g., <a href="#pdf-table.sort"><code>table.sort</code></a>).
+
+
+<p>
+All libraries are implemented through the official C&nbsp;API
+and are provided as separate C&nbsp;modules.
+Currently, Lua has the following standard libraries:
+
+<ul>
+
+<li>basic library;</li>
+
+<li>package library;</li>
+
+<li>string manipulation;</li>
+
+<li>table manipulation;</li>
+
+<li>mathematical functions (sin, log, etc.);</li>
+
+<li>input and output;</li>
+
+<li>operating system facilities;</li>
+
+<li>debug facilities.</li>
+
+</ul><p>
+Except for the basic and package libraries,
+each library provides all its functions as fields of a global table
+or as methods of its objects.
+
+
+<p>
+To have access to these libraries,
+the C&nbsp;host program should call the <a href="#luaL_openlibs"><code>luaL_openlibs</code></a> function,
+which opens all standard libraries.
+Alternatively,
+it can open them individually by calling
+<a name="pdf-luaopen_base"><code>luaopen_base</code></a> (for the basic library),
+<a name="pdf-luaopen_package"><code>luaopen_package</code></a> (for the package library),
+<a name="pdf-luaopen_string"><code>luaopen_string</code></a> (for the string library),
+<a name="pdf-luaopen_table"><code>luaopen_table</code></a> (for the table library),
+<a name="pdf-luaopen_math"><code>luaopen_math</code></a> (for the mathematical library),
+<a name="pdf-luaopen_io"><code>luaopen_io</code></a> (for the I/O library),
+<a name="pdf-luaopen_os"><code>luaopen_os</code></a> (for the Operating System library),
+and <a name="pdf-luaopen_debug"><code>luaopen_debug</code></a> (for the debug library).
+These functions are declared in <a name="pdf-lualib.h"><code>lualib.h</code></a>
+and should not be called directly:
+you must call them like any other Lua C&nbsp;function,
+e.g., by using <a href="#lua_call"><code>lua_call</code></a>.
+
+
+
+<h2>5.1 - <a name="5.1">Basic Functions</a></h2>
+
+<p>
+The basic library provides some core functions to Lua.
+If you do not include this library in your application,
+you should check carefully whether you need to provide
+implementations for some of its facilities.
+
+
+<p>
+<hr><h3><a name="pdf-assert"><code>assert (v [, message])</code></a></h3>
+Issues an error when
+the value of its argument <code>v</code> is false (i.e., <b>nil</b> or <b>false</b>);
+otherwise, returns all its arguments.
+<code>message</code> is an error message;
+when absent, it defaults to "assertion failed!"
+
+
+
+
+<p>
+<hr><h3><a name="pdf-collectgarbage"><code>collectgarbage (opt [, arg])</code></a></h3>
+
+
+<p>
+This function is a generic interface to the garbage collector.
+It performs different functions according to its first argument, <code>opt</code>:
+
+<ul>
+
+<li><b>"stop":</b>
+stops the garbage collector.
+</li>
+
+<li><b>"restart":</b>
+restarts the garbage collector.
+</li>
+
+<li><b>"collect":</b>
+performs a full garbage-collection cycle.
+</li>
+
+<li><b>"count":</b>
+returns the total memory in use by Lua (in Kbytes).
+</li>
+
+<li><b>"step":</b>
+performs a garbage-collection step.
+The step "size" is controlled by <code>arg</code>
+(larger values mean more steps) in a non-specified way.
+If you want to control the step size
+you must experimentally tune the value of <code>arg</code>.
+Returns <b>true</b> if the step finished a collection cycle.
+</li>
+
+<li><b>"setpause":</b>
+sets <code>arg</code>/100 as the new value for the <em>pause</em> of
+the collector (see <a href="#2.10">&sect;2.10</a>).
+</li>
+
+<li><b>"setstepmul":</b>
+sets <code>arg</code>/100 as the new value for the <em>step multiplier</em> of
+the collector (see <a href="#2.10">&sect;2.10</a>).
+</li>
+
+</ul>
+
+
+
+<p>
+<hr><h3><a name="pdf-dofile"><code>dofile (filename)</code></a></h3>
+Opens the named file and executes its contents as a Lua chunk.
+When called without arguments,
+<code>dofile</code> executes the contents of the standard input (<code>stdin</code>).
+Returns all values returned by the chunk.
+In case of errors, <code>dofile</code> propagates the error
+to its caller (that is, <code>dofile</code> does not run in protected mode).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-error"><code>error (message [, level])</code></a></h3>
+Terminates the last protected function called
+and returns <code>message</code> as the error message.
+Function <code>error</code> never returns.
+
+
+<p>
+Usually, <code>error</code> adds some information about the error position
+at the beginning of the message.
+The <code>level</code> argument specifies how to get the error position.
+With level&nbsp;1 (the default), the error position is where the
+<code>error</code> function was called.
+Level&nbsp;2 points the error to where the function
+that called <code>error</code> was called; and so on.
+Passing a level&nbsp;0 avoids the addition of error position information
+to the message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_G"><code>_G</code></a></h3>
+A global variable (not a function) that
+holds the global environment (that is, <code>_G._G = _G</code>).
+Lua itself does not use this variable;
+changing its value does not affect any environment,
+nor vice-versa.
+(Use <a href="#pdf-setfenv"><code>setfenv</code></a> to change environments.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-getfenv"><code>getfenv ([f])</code></a></h3>
+Returns the current environment in use by the function.
+<code>f</code> can be a Lua function or a number
+that specifies the function at that stack level:
+Level&nbsp;1 is the function calling <code>getfenv</code>.
+If the given function is not a Lua function,
+or if <code>f</code> is 0,
+<code>getfenv</code> returns the global environment.
+The default for <code>f</code> is 1.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-getmetatable"><code>getmetatable (object)</code></a></h3>
+
+
+<p>
+If <code>object</code> does not have a metatable, returns <b>nil</b>.
+Otherwise,
+if the object's metatable has a <code>"__metatable"</code> field,
+returns the associated value.
+Otherwise, returns the metatable of the given object.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-ipairs"><code>ipairs (t)</code></a></h3>
+
+
+<p>
+Returns three values: an iterator function, the table <code>t</code>, and 0,
+so that the construction
+
+<pre>
+ for i,v in ipairs(t) do <em>body</em> end
+</pre><p>
+will iterate over the pairs (<code>1,t[1]</code>), (<code>2,t[2]</code>), &middot;&middot;&middot;,
+up to the first integer key absent from the table.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-load"><code>load (func [, chunkname])</code></a></h3>
+
+
+<p>
+Loads a chunk using function <code>func</code> to get its pieces.
+Each call to <code>func</code> must return a string that concatenates
+with previous results.
+A return of <b>nil</b> (or no value) signals the end of the chunk.
+
+
+<p>
+If there are no errors,
+returns the compiled chunk as a function;
+otherwise, returns <b>nil</b> plus the error message.
+The environment of the returned function is the global environment.
+
+
+<p>
+<code>chunkname</code> is used as the chunk name for error messages
+and debug information.
+When absent,
+it defaults to "<code>=(load)</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-loadfile"><code>loadfile ([filename])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-load"><code>load</code></a>,
+but gets the chunk from file <code>filename</code>
+or from the standard input,
+if no file name is given.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-loadstring"><code>loadstring (string [, chunkname])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-load"><code>load</code></a>,
+but gets the chunk from the given string.
+
+
+<p>
+To load and run a given string, use the idiom
+
+<pre>
+ assert(loadstring(s))()
+</pre>
+
+<p>
+When absent,
+<code>chunkname</code> defaults to the given string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-next"><code>next (table [, index])</code></a></h3>
+
+
+<p>
+Allows a program to traverse all fields of a table.
+Its first argument is a table and its second argument
+is an index in this table.
+<code>next</code> returns the next index of the table
+and its associated value.
+When called with <b>nil</b> as its second argument,
+<code>next</code> returns an initial index
+and its associated value.
+When called with the last index,
+or with <b>nil</b> in an empty table,
+<code>next</code> returns <b>nil</b>.
+If the second argument is absent, then it is interpreted as <b>nil</b>.
+In particular,
+you can use <code>next(t)</code> to check whether a table is empty.
+
+
+<p>
+The order in which the indices are enumerated is not specified,
+<em>even for numeric indices</em>.
+(To traverse a table in numeric order,
+use a numerical <b>for</b> or the <a href="#pdf-ipairs"><code>ipairs</code></a> function.)
+
+
+<p>
+The behavior of <code>next</code> is <em>undefined</em> if,
+during the traversal,
+you assign any value to a non-existent field in the table.
+You may however modify existing fields.
+In particular, you may clear existing fields.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pairs"><code>pairs (t)</code></a></h3>
+
+
+<p>
+Returns three values: the <a href="#pdf-next"><code>next</code></a> function, the table <code>t</code>, and <b>nil</b>,
+so that the construction
+
+<pre>
+ for k,v in pairs(t) do <em>body</em> end
+</pre><p>
+will iterate over all key&ndash;value pairs of table <code>t</code>.
+
+
+<p>
+See function <a href="#pdf-next"><code>next</code></a> for the caveats of modifying
+the table during its traversal.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pcall"><code>pcall (f, arg1, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Calls function <code>f</code> with
+the given arguments in <em>protected mode</em>.
+This means that any error inside&nbsp;<code>f</code> is not propagated;
+instead, <code>pcall</code> catches the error
+and returns a status code.
+Its first result is the status code (a boolean),
+which is true if the call succeeds without errors.
+In such case, <code>pcall</code> also returns all results from the call,
+after this first result.
+In case of any error, <code>pcall</code> returns <b>false</b> plus the error message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-print"><code>print (&middot;&middot;&middot;)</code></a></h3>
+Receives any number of arguments,
+and prints their values to <code>stdout</code>,
+using the <a href="#pdf-tostring"><code>tostring</code></a> function to convert them to strings.
+<code>print</code> is not intended for formatted output,
+but only as a quick way to show a value,
+typically for debugging.
+For formatted output, use <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawequal"><code>rawequal (v1, v2)</code></a></h3>
+Checks whether <code>v1</code> is equal to <code>v2</code>,
+without invoking any metamethod.
+Returns a boolean.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawget"><code>rawget (table, index)</code></a></h3>
+Gets the real value of <code>table[index]</code>,
+without invoking any metamethod.
+<code>table</code> must be a table;
+<code>index</code> may be any value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawset"><code>rawset (table, index, value)</code></a></h3>
+Sets the real value of <code>table[index]</code> to <code>value</code>,
+without invoking any metamethod.
+<code>table</code> must be a table,
+<code>index</code> any value different from <b>nil</b>,
+and <code>value</code> any Lua value.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-select"><code>select (index, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+If <code>index</code> is a number,
+returns all arguments after argument number <code>index</code>.
+Otherwise, <code>index</code> must be the string <code>"#"</code>,
+and <code>select</code> returns the total number of extra arguments it received.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-setfenv"><code>setfenv (f, table)</code></a></h3>
+
+
+<p>
+Sets the environment to be used by the given function.
+<code>f</code> can be a Lua function or a number
+that specifies the function at that stack level:
+Level&nbsp;1 is the function calling <code>setfenv</code>.
+<code>setfenv</code> returns the given function.
+
+
+<p>
+As a special case, when <code>f</code> is 0 <code>setfenv</code> changes
+the environment of the running thread.
+In this case, <code>setfenv</code> returns no values.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-setmetatable"><code>setmetatable (table, metatable)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given table.
+(You cannot change the metatable of other types from Lua, only from&nbsp;C.)
+If <code>metatable</code> is <b>nil</b>,
+removes the metatable of the given table.
+If the original metatable has a <code>"__metatable"</code> field,
+raises an error.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tonumber"><code>tonumber (e [, base])</code></a></h3>
+Tries to convert its argument to a number.
+If the argument is already a number or a string convertible
+to a number, then <code>tonumber</code> returns this number;
+otherwise, it returns <b>nil</b>.
+
+
+<p>
+An optional argument specifies the base to interpret the numeral.
+The base may be any integer between 2 and 36, inclusive.
+In bases above&nbsp;10, the letter '<code>A</code>' (in either upper or lower case)
+represents&nbsp;10, '<code>B</code>' represents&nbsp;11, and so forth,
+with '<code>Z</code>' representing 35.
+In base 10 (the default), the number may have a decimal part,
+as well as an optional exponent part (see <a href="#2.1">&sect;2.1</a>).
+In other bases, only unsigned integers are accepted.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tostring"><code>tostring (e)</code></a></h3>
+Receives an argument of any type and
+converts it to a string in a reasonable format.
+For complete control of how numbers are converted,
+use <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+<p>
+If the metatable of <code>e</code> has a <code>"__tostring"</code> field,
+then <code>tostring</code> calls the corresponding value
+with <code>e</code> as argument,
+and uses the result of the call as its result.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-type"><code>type (v)</code></a></h3>
+Returns the type of its only argument, coded as a string.
+The possible results of this function are
+"<code>nil</code>" (a string, not the value <b>nil</b>),
+"<code>number</code>",
+"<code>string</code>",
+"<code>boolean</code>",
+"<code>table</code>",
+"<code>function</code>",
+"<code>thread</code>",
+and "<code>userdata</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-unpack"><code>unpack (list [, i [, j]])</code></a></h3>
+Returns the elements from the given table.
+This function is equivalent to
+
+<pre>
+ return list[i], list[i+1], &middot;&middot;&middot;, list[j]
+</pre><p>
+except that the above code can be written only for a fixed number
+of elements.
+By default, <code>i</code> is&nbsp;1 and <code>j</code> is the length of the list,
+as defined by the length operator (see <a href="#2.5.5">&sect;2.5.5</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_VERSION"><code>_VERSION</code></a></h3>
+A global variable (not a function) that
+holds a string containing the current interpreter version.
+The current contents of this variable is "<code>Lua 5.1</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-xpcall"><code>xpcall (f, err)</code></a></h3>
+
+
+<p>
+This function is similar to <a href="#pdf-pcall"><code>pcall</code></a>,
+except that you can set a new error handler.
+
+
+<p>
+<code>xpcall</code> calls function <code>f</code> in protected mode,
+using <code>err</code> as the error handler.
+Any error inside <code>f</code> is not propagated;
+instead, <code>xpcall</code> catches the error,
+calls the <code>err</code> function with the original error object,
+and returns a status code.
+Its first result is the status code (a boolean),
+which is true if the call succeeds without errors.
+In this case, <code>xpcall</code> also returns all results from the call,
+after this first result.
+In case of any error,
+<code>xpcall</code> returns <b>false</b> plus the result from <code>err</code>.
+
+
+
+
+
+
+
+<h2>5.2 - <a name="5.2">Coroutine Manipulation</a></h2>
+
+<p>
+The operations related to coroutines comprise a sub-library of
+the basic library and come inside the table <a name="pdf-coroutine"><code>coroutine</code></a>.
+See <a href="#2.11">&sect;2.11</a> for a general description of coroutines.
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.create"><code>coroutine.create (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>.
+<code>f</code> must be a Lua function.
+Returns this new coroutine,
+an object with type <code>"thread"</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.resume"><code>coroutine.resume (co [, val1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Starts or continues the execution of coroutine <code>co</code>.
+The first time you resume a coroutine,
+it starts running its body.
+The values <code>val1</code>, &middot;&middot;&middot; are passed
+as the arguments to the body function.
+If the coroutine has yielded,
+<code>resume</code> restarts it;
+the values <code>val1</code>, &middot;&middot;&middot; are passed
+as the results from the yield.
+
+
+<p>
+If the coroutine runs without any errors,
+<code>resume</code> returns <b>true</b> plus any values passed to <code>yield</code>
+(if the coroutine yields) or any values returned by the body function
+(if the coroutine terminates).
+If there is any error,
+<code>resume</code> returns <b>false</b> plus the error message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.running"><code>coroutine.running ()</code></a></h3>
+
+
+<p>
+Returns the running coroutine,
+or <b>nil</b> when called by the main thread.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.status"><code>coroutine.status (co)</code></a></h3>
+
+
+<p>
+Returns the status of coroutine <code>co</code>, as a string:
+<code>"running"</code>,
+if the coroutine is running (that is, it called <code>status</code>);
+<code>"suspended"</code>, if the coroutine is suspended in a call to <code>yield</code>,
+or if it has not started running yet;
+<code>"normal"</code> if the coroutine is active but not running
+(that is, it has resumed another coroutine);
+and <code>"dead"</code> if the coroutine has finished its body function,
+or if it has stopped with an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.wrap"><code>coroutine.wrap (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>.
+<code>f</code> must be a Lua function.
+Returns a function that resumes the coroutine each time it is called.
+Any arguments passed to the function behave as the
+extra arguments to <code>resume</code>.
+Returns the same values returned by <code>resume</code>,
+except the first boolean.
+In case of error, propagates the error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.yield"><code>coroutine.yield (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Suspends the execution of the calling coroutine.
+The coroutine cannot be running a C&nbsp;function,
+a metamethod, or an iterator.
+Any arguments to <code>yield</code> are passed as extra results to <code>resume</code>.
+
+
+
+
+
+
+
+<h2>5.3 - <a name="5.3">Modules</a></h2>
+
+<p>
+The package library provides basic
+facilities for loading and building modules in Lua.
+It exports two of its functions directly in the global environment:
+<a href="#pdf-require"><code>require</code></a> and <a href="#pdf-module"><code>module</code></a>.
+Everything else is exported in a table <a name="pdf-package"><code>package</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-module"><code>module (name [, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Creates a module.
+If there is a table in <code>package.loaded[name]</code>,
+this table is the module.
+Otherwise, if there is a global table <code>t</code> with the given name,
+this table is the module.
+Otherwise creates a new table <code>t</code> and
+sets it as the value of the global <code>name</code> and
+the value of <code>package.loaded[name]</code>.
+This function also initializes <code>t._NAME</code> with the given name,
+<code>t._M</code> with the module (<code>t</code> itself),
+and <code>t._PACKAGE</code> with the package name
+(the full module name minus last component; see below).
+Finally, <code>module</code> sets <code>t</code> as the new environment
+of the current function and the new value of <code>package.loaded[name]</code>,
+so that <a href="#pdf-require"><code>require</code></a> returns <code>t</code>.
+
+
+<p>
+If <code>name</code> is a compound name
+(that is, one with components separated by dots),
+<code>module</code> creates (or reuses, if they already exist)
+tables for each component.
+For instance, if <code>name</code> is <code>a.b.c</code>,
+then <code>module</code> stores the module table in field <code>c</code> of
+field <code>b</code> of global <code>a</code>.
+
+
+<p>
+This function may receive optional <em>options</em> after
+the module name,
+where each option is a function to be applied over the module.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-require"><code>require (modname)</code></a></h3>
+
+
+<p>
+Loads the given module.
+The function starts by looking into the <a href="#pdf-package.loaded"><code>package.loaded</code></a> table
+to determine whether <code>modname</code> is already loaded.
+If it is, then <code>require</code> returns the value stored
+at <code>package.loaded[modname]</code>.
+Otherwise, it tries to find a <em>loader</em> for the module.
+
+
+<p>
+To find a loader,
+<code>require</code> is guided by the <a href="#pdf-package.loaders"><code>package.loaders</code></a> array.
+By changing this array,
+we can change how <code>require</code> looks for a module.
+The following explanation is based on the default configuration
+for <a href="#pdf-package.loaders"><code>package.loaders</code></a>.
+
+
+<p>
+First <code>require</code> queries <code>package.preload[modname]</code>.
+If it has a value,
+this value (which should be a function) is the loader.
+Otherwise <code>require</code> searches for a Lua loader using the
+path stored in <a href="#pdf-package.path"><code>package.path</code></a>.
+If that also fails, it searches for a C&nbsp;loader using the
+path stored in <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+If that also fails,
+it tries an <em>all-in-one</em> loader (see <a href="#pdf-package.loaders"><code>package.loaders</code></a>).
+
+
+<p>
+Once a loader is found,
+<code>require</code> calls the loader with a single argument, <code>modname</code>.
+If the loader returns any value,
+<code>require</code> assigns the returned value to <code>package.loaded[modname]</code>.
+If the loader returns no value and
+has not assigned any value to <code>package.loaded[modname]</code>,
+then <code>require</code> assigns <b>true</b> to this entry.
+In any case, <code>require</code> returns the
+final value of <code>package.loaded[modname]</code>.
+
+
+<p>
+If there is any error loading or running the module,
+or if it cannot find any loader for the module,
+then <code>require</code> signals an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.cpath"><code>package.cpath</code></a></h3>
+
+
+<p>
+The path used by <a href="#pdf-require"><code>require</code></a> to search for a C&nbsp;loader.
+
+
+<p>
+Lua initializes the C&nbsp;path <a href="#pdf-package.cpath"><code>package.cpath</code></a> in the same way
+it initializes the Lua path <a href="#pdf-package.path"><code>package.path</code></a>,
+using the environment variable <a name="pdf-LUA_CPATH"><code>LUA_CPATH</code></a>
+or a default path defined in <code>luaconf.h</code>.
+
+
+
+
+<p>
+
+<hr><h3><a name="pdf-package.loaded"><code>package.loaded</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control which
+modules are already loaded.
+When you require a module <code>modname</code> and
+<code>package.loaded[modname]</code> is not false,
+<a href="#pdf-require"><code>require</code></a> simply returns the value stored there.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loaders"><code>package.loaders</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control how to load modules.
+
+
+<p>
+Each entry in this table is a <em>searcher function</em>.
+When looking for a module,
+<a href="#pdf-require"><code>require</code></a> calls each of these searchers in ascending order,
+with the module name (the argument given to <a href="#pdf-require"><code>require</code></a>) as its
+sole parameter.
+The function may return another function (the module <em>loader</em>)
+or a string explaining why it did not find that module
+(or <b>nil</b> if it has nothing to say).
+Lua initializes this table with four functions.
+
+
+<p>
+The first searcher simply looks for a loader in the
+<a href="#pdf-package.preload"><code>package.preload</code></a> table.
+
+
+<p>
+The second searcher looks for a loader as a Lua library,
+using the path stored at <a href="#pdf-package.path"><code>package.path</code></a>.
+A path is a sequence of <em>templates</em> separated by semicolons.
+For each template,
+the searcher will change each interrogation
+mark in the template by <code>filename</code>,
+which is the module name with each dot replaced by a
+"directory separator" (such as "<code>/</code>" in Unix);
+then it will try to open the resulting file name.
+So, for instance, if the Lua path is the string
+
+<pre>
+ "./?.lua;./?.lc;/usr/local/?/init.lua"
+</pre><p>
+the search for a Lua file for module <code>foo</code>
+will try to open the files
+<code>./foo.lua</code>, <code>./foo.lc</code>, and
+<code>/usr/local/foo/init.lua</code>, in that order.
+
+
+<p>
+The third searcher looks for a loader as a C&nbsp;library,
+using the path given by the variable <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+For instance,
+if the C&nbsp;path is the string
+
+<pre>
+ "./?.so;./?.dll;/usr/local/?/init.so"
+</pre><p>
+the searcher for module <code>foo</code>
+will try to open the files <code>./foo.so</code>, <code>./foo.dll</code>,
+and <code>/usr/local/foo/init.so</code>, in that order.
+Once it finds a C&nbsp;library,
+this searcher first uses a dynamic link facility to link the
+application with the library.
+Then it tries to find a C&nbsp;function inside the library to
+be used as the loader.
+The name of this C&nbsp;function is the string "<code>luaopen_</code>"
+concatenated with a copy of the module name where each dot
+is replaced by an underscore.
+Moreover, if the module name has a hyphen,
+its prefix up to (and including) the first hyphen is removed.
+For instance, if the module name is <code>a.v1-b.c</code>,
+the function name will be <code>luaopen_b_c</code>.
+
+
+<p>
+The fourth searcher tries an <em>all-in-one loader</em>.
+It searches the C&nbsp;path for a library for
+the root name of the given module.
+For instance, when requiring <code>a.b.c</code>,
+it will search for a C&nbsp;library for <code>a</code>.
+If found, it looks into it for an open function for
+the submodule;
+in our example, that would be <code>luaopen_a_b_c</code>.
+With this facility, a package can pack several C&nbsp;submodules
+into one single library,
+with each submodule keeping its original open function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loadlib"><code>package.loadlib (libname, funcname)</code></a></h3>
+
+
+<p>
+Dynamically links the host program with the C&nbsp;library <code>libname</code>.
+Inside this library, looks for a function <code>funcname</code>
+and returns this function as a C&nbsp;function.
+(So, <code>funcname</code> must follow the protocol (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>)).
+
+
+<p>
+This is a low-level function.
+It completely bypasses the package and module system.
+Unlike <a href="#pdf-require"><code>require</code></a>,
+it does not perform any path searching and
+does not automatically adds extensions.
+<code>libname</code> must be the complete file name of the C&nbsp;library,
+including if necessary a path and extension.
+<code>funcname</code> must be the exact name exported by the C&nbsp;library
+(which may depend on the C&nbsp;compiler and linker used).
+
+
+<p>
+This function is not supported by ANSI C.
+As such, it is only available on some platforms
+(Windows, Linux, Mac OS X, Solaris, BSD,
+plus other Unix systems that support the <code>dlfcn</code> standard).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.path"><code>package.path</code></a></h3>
+
+
+<p>
+The path used by <a href="#pdf-require"><code>require</code></a> to search for a Lua loader.
+
+
+<p>
+At start-up, Lua initializes this variable with
+the value of the environment variable <a name="pdf-LUA_PATH"><code>LUA_PATH</code></a> or
+with a default path defined in <code>luaconf.h</code>,
+if the environment variable is not defined.
+Any "<code>;;</code>" in the value of the environment variable
+is replaced by the default path.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.preload"><code>package.preload</code></a></h3>
+
+
+<p>
+A table to store loaders for specific modules
+(see <a href="#pdf-require"><code>require</code></a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.seeall"><code>package.seeall (module)</code></a></h3>
+
+
+<p>
+Sets a metatable for <code>module</code> with
+its <code>__index</code> field referring to the global environment,
+so that this module inherits values
+from the global environment.
+To be used as an option to function <a href="#pdf-module"><code>module</code></a>.
+
+
+
+
+
+
+
+<h2>5.4 - <a name="5.4">String Manipulation</a></h2>
+
+<p>
+This library provides generic functions for string manipulation,
+such as finding and extracting substrings, and pattern matching.
+When indexing a string in Lua, the first character is at position&nbsp;1
+(not at&nbsp;0, as in C).
+Indices are allowed to be negative and are interpreted as indexing backwards,
+from the end of the string.
+Thus, the last character is at position -1, and so on.
+
+
+<p>
+The string library provides all its functions inside the table
+<a name="pdf-string"><code>string</code></a>.
+It also sets a metatable for strings
+where the <code>__index</code> field points to the <code>string</code> table.
+Therefore, you can use the string functions in object-oriented style.
+For instance, <code>string.byte(s, i)</code>
+can be written as <code>s:byte(i)</code>.
+
+
+<p>
+<hr><h3><a name="pdf-string.byte"><code>string.byte (s [, i [, j]])</code></a></h3>
+Returns the internal numerical codes of the characters <code>s[i]</code>,
+<code>s[i+1]</code>, &middot;&middot;&middot;, <code>s[j]</code>.
+The default value for <code>i</code> is&nbsp;1;
+the default value for <code>j</code> is&nbsp;<code>i</code>.
+
+
+<p>
+Note that numerical codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.char"><code>string.char (&middot;&middot;&middot;)</code></a></h3>
+Receives zero or more integers.
+Returns a string with length equal to the number of arguments,
+in which each character has the internal numerical code equal
+to its corresponding argument.
+
+
+<p>
+Note that numerical codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.dump"><code>string.dump (function)</code></a></h3>
+
+
+<p>
+Returns a string containing a binary representation of the given function,
+so that a later <a href="#pdf-loadstring"><code>loadstring</code></a> on this string returns
+a copy of the function.
+<code>function</code> must be a Lua function without upvalues.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.find"><code>string.find (s, pattern [, init [, plain]])</code></a></h3>
+Looks for the first match of
+<code>pattern</code> in the string <code>s</code>.
+If it finds a match, then <code>find</code> returns the indices of&nbsp;<code>s</code>
+where this occurrence starts and ends;
+otherwise, it returns <b>nil</b>.
+A third, optional numerical argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and may be negative.
+A value of <b>true</b> as a fourth, optional argument <code>plain</code>
+turns off the pattern matching facilities,
+so the function does a plain "find substring" operation,
+with no characters in <code>pattern</code> being considered "magic".
+Note that if <code>plain</code> is given, then <code>init</code> must be given as well.
+
+
+<p>
+If the pattern has captures,
+then in a successful match
+the captured values are also returned,
+after the two indices.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.format"><code>string.format (formatstring, &middot;&middot;&middot;)</code></a></h3>
+Returns a formatted version of its variable number of arguments
+following the description given in its first argument (which must be a string).
+The format string follows the same rules as the <code>printf</code> family of
+standard C&nbsp;functions.
+The only differences are that the options/modifiers
+<code>*</code>, <code>l</code>, <code>L</code>, <code>n</code>, <code>p</code>,
+and <code>h</code> are not supported
+and that there is an extra option, <code>q</code>.
+The <code>q</code> option formats a string in a form suitable to be safely read
+back by the Lua interpreter:
+the string is written between double quotes,
+and all double quotes, newlines, embedded zeros,
+and backslashes in the string
+are correctly escaped when written.
+For instance, the call
+
+<pre>
+ string.format('%q', 'a string with "quotes" and \n new line')
+</pre><p>
+will produce the string:
+
+<pre>
+ "a string with \"quotes\" and \
+ new line"
+</pre>
+
+<p>
+The options <code>c</code>, <code>d</code>, <code>E</code>, <code>e</code>, <code>f</code>,
+<code>g</code>, <code>G</code>, <code>i</code>, <code>o</code>, <code>u</code>, <code>X</code>, and <code>x</code> all
+expect a number as argument,
+whereas <code>q</code> and <code>s</code> expect a string.
+
+
+<p>
+This function does not accept string values
+containing embedded zeros,
+except as arguments to the <code>q</code> option.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gmatch"><code>string.gmatch (s, pattern)</code></a></h3>
+Returns an iterator function that,
+each time it is called,
+returns the next captures from <code>pattern</code> over string <code>s</code>.
+If <code>pattern</code> specifies no captures,
+then the whole match is produced in each call.
+
+
+<p>
+As an example, the following loop
+
+<pre>
+ s = "hello world from Lua"
+ for w in string.gmatch(s, "%a+") do
+ print(w)
+ end
+</pre><p>
+will iterate over all the words from string <code>s</code>,
+printing one per line.
+The next example collects all pairs <code>key=value</code> from the
+given string into a table:
+
+<pre>
+ t = {}
+ s = "from=world, to=Lua"
+ for k, v in string.gmatch(s, "(%w+)=(%w+)") do
+ t[k] = v
+ end
+</pre>
+
+<p>
+For this function, a '<code>^</code>' at the start of a pattern does not
+work as an anchor, as this would prevent the iteration.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gsub"><code>string.gsub (s, pattern, repl [, n])</code></a></h3>
+Returns a copy of <code>s</code>
+in which all (or the first <code>n</code>, if given)
+occurrences of the <code>pattern</code> have been
+replaced by a replacement string specified by <code>repl</code>,
+which may be a string, a table, or a function.
+<code>gsub</code> also returns, as its second value,
+the total number of matches that occurred.
+
+
+<p>
+If <code>repl</code> is a string, then its value is used for replacement.
+The character&nbsp;<code>%</code> works as an escape character:
+any sequence in <code>repl</code> of the form <code>%<em>n</em></code>,
+with <em>n</em> between 1 and 9,
+stands for the value of the <em>n</em>-th captured substring (see below).
+The sequence <code>%0</code> stands for the whole match.
+The sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.
+
+
+<p>
+If <code>repl</code> is a table, then the table is queried for every match,
+using the first capture as the key;
+if the pattern specifies no captures,
+then the whole match is used as the key.
+
+
+<p>
+If <code>repl</code> is a function, then this function is called every time a
+match occurs, with all captured substrings passed as arguments,
+in order;
+if the pattern specifies no captures,
+then the whole match is passed as a sole argument.
+
+
+<p>
+If the value returned by the table query or by the function call
+is a string or a number,
+then it is used as the replacement string;
+otherwise, if it is <b>false</b> or <b>nil</b>,
+then there is no replacement
+(that is, the original match is kept in the string).
+
+
+<p>
+Here are some examples:
+
+<pre>
+ x = string.gsub("hello world", "(%w+)", "%1 %1")
+ --&gt; x="hello hello world world"
+
+ x = string.gsub("hello world", "%w+", "%0 %0", 1)
+ --&gt; x="hello hello world"
+
+ x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
+ --&gt; x="world hello Lua from"
+
+ x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
+ --&gt; x="home = /home/roberto, user = roberto"
+
+ x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
+ return loadstring(s)()
+ end)
+ --&gt; x="4+5 = 9"
+
+ local t = {name="lua", version="5.1"}
+ x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
+ --&gt; x="lua-5.1.tar.gz"
+</pre>
+
+
+
+<p>
+<hr><h3><a name="pdf-string.len"><code>string.len (s)</code></a></h3>
+Receives a string and returns its length.
+The empty string <code>""</code> has length 0.
+Embedded zeros are counted,
+so <code>"a\000bc\000"</code> has length 5.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.lower"><code>string.lower (s)</code></a></h3>
+Receives a string and returns a copy of this string with all
+uppercase letters changed to lowercase.
+All other characters are left unchanged.
+The definition of what an uppercase letter is depends on the current locale.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.match"><code>string.match (s, pattern [, init])</code></a></h3>
+Looks for the first <em>match</em> of
+<code>pattern</code> in the string <code>s</code>.
+If it finds one, then <code>match</code> returns
+the captures from the pattern;
+otherwise it returns <b>nil</b>.
+If <code>pattern</code> specifies no captures,
+then the whole match is returned.
+A third, optional numerical argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and may be negative.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.rep"><code>string.rep (s, n)</code></a></h3>
+Returns a string that is the concatenation of <code>n</code> copies of
+the string <code>s</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.reverse"><code>string.reverse (s)</code></a></h3>
+Returns a string that is the string <code>s</code> reversed.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.sub"><code>string.sub (s, i [, j])</code></a></h3>
+Returns the substring of <code>s</code> that
+starts at <code>i</code> and continues until <code>j</code>;
+<code>i</code> and <code>j</code> may be negative.
+If <code>j</code> is absent, then it is assumed to be equal to -1
+(which is the same as the string length).
+In particular,
+the call <code>string.sub(s,1,j)</code> returns a prefix of <code>s</code>
+with length <code>j</code>,
+and <code>string.sub(s, -i)</code> returns a suffix of <code>s</code>
+with length <code>i</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.upper"><code>string.upper (s)</code></a></h3>
+Receives a string and returns a copy of this string with all
+lowercase letters changed to uppercase.
+All other characters are left unchanged.
+The definition of what a lowercase letter is depends on the current locale.
+
+
+
+<h3>5.4.1 - <a name="5.4.1">Patterns</a></h3>
+
+
+<h4>Character Class:</h4><p>
+A <em>character class</em> is used to represent a set of characters.
+The following combinations are allowed in describing a character class:
+
+<ul>
+
+<li><b><em>x</em>:</b>
+(where <em>x</em> is not one of the <em>magic characters</em>
+<code>^$()%.[]*+-?</code>)
+represents the character <em>x</em> itself.
+</li>
+
+<li><b><code>.</code>:</b> (a dot) represents all characters.</li>
+
+<li><b><code>%a</code>:</b> represents all letters.</li>
+
+<li><b><code>%c</code>:</b> represents all control characters.</li>
+
+<li><b><code>%d</code>:</b> represents all digits.</li>
+
+<li><b><code>%l</code>:</b> represents all lowercase letters.</li>
+
+<li><b><code>%p</code>:</b> represents all punctuation characters.</li>
+
+<li><b><code>%s</code>:</b> represents all space characters.</li>
+
+<li><b><code>%u</code>:</b> represents all uppercase letters.</li>
+
+<li><b><code>%w</code>:</b> represents all alphanumeric characters.</li>
+
+<li><b><code>%x</code>:</b> represents all hexadecimal digits.</li>
+
+<li><b><code>%z</code>:</b> represents the character with representation 0.</li>
+
+<li><b><code>%<em>x</em></code>:</b> (where <em>x</em> is any non-alphanumeric character)
+represents the character <em>x</em>.
+This is the standard way to escape the magic characters.
+Any punctuation character (even the non magic)
+can be preceded by a '<code>%</code>'
+when used to represent itself in a pattern.
+</li>
+
+<li><b><code>[<em>set</em>]</code>:</b>
+represents the class which is the union of all
+characters in <em>set</em>.
+A range of characters may be specified by
+separating the end characters of the range with a '<code>-</code>'.
+All classes <code>%</code><em>x</em> described above may also be used as
+components in <em>set</em>.
+All other characters in <em>set</em> represent themselves.
+For example, <code>[%w_]</code> (or <code>[_%w]</code>)
+represents all alphanumeric characters plus the underscore,
+<code>[0-7]</code> represents the octal digits,
+and <code>[0-7%l%-]</code> represents the octal digits plus
+the lowercase letters plus the '<code>-</code>' character.
+
+
+<p>
+The interaction between ranges and classes is not defined.
+Therefore, patterns like <code>[%a-z]</code> or <code>[a-%%]</code>
+have no meaning.
+</li>
+
+<li><b><code>[^<em>set</em>]</code>:</b>
+represents the complement of <em>set</em>,
+where <em>set</em> is interpreted as above.
+</li>
+
+</ul><p>
+For all classes represented by single letters (<code>%a</code>, <code>%c</code>, etc.),
+the corresponding uppercase letter represents the complement of the class.
+For instance, <code>%S</code> represents all non-space characters.
+
+
+<p>
+The definitions of letter, space, and other character groups
+depend on the current locale.
+In particular, the class <code>[a-z]</code> may not be equivalent to <code>%l</code>.
+
+
+
+
+
+<h4>Pattern Item:</h4><p>
+A <em>pattern item</em> may be
+
+<ul>
+
+<li>
+a single character class,
+which matches any single character in the class;
+</li>
+
+<li>
+a single character class followed by '<code>*</code>',
+which matches 0 or more repetitions of characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>+</code>',
+which matches 1 or more repetitions of characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>-</code>',
+which also matches 0 or more repetitions of characters in the class.
+Unlike '<code>*</code>',
+these repetition items will always match the <em>shortest</em> possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>?</code>',
+which matches 0 or 1 occurrence of a character in the class;
+</li>
+
+<li>
+<code>%<em>n</em></code>, for <em>n</em> between 1 and 9;
+such item matches a substring equal to the <em>n</em>-th captured string
+(see below);
+</li>
+
+<li>
+<code>%b<em>xy</em></code>, where <em>x</em> and <em>y</em> are two distinct characters;
+such item matches strings that start with&nbsp;<em>x</em>, end with&nbsp;<em>y</em>,
+and where the <em>x</em> and <em>y</em> are <em>balanced</em>.
+This means that, if one reads the string from left to right,
+counting <em>+1</em> for an <em>x</em> and <em>-1</em> for a <em>y</em>,
+the ending <em>y</em> is the first <em>y</em> where the count reaches 0.
+For instance, the item <code>%b()</code> matches expressions with
+balanced parentheses.
+</li>
+
+</ul>
+
+
+
+
+<h4>Pattern:</h4><p>
+A <em>pattern</em> is a sequence of pattern items.
+A '<code>^</code>' at the beginning of a pattern anchors the match at the
+beginning of the subject string.
+A '<code>$</code>' at the end of a pattern anchors the match at the
+end of the subject string.
+At other positions,
+'<code>^</code>' and '<code>$</code>' have no special meaning and represent themselves.
+
+
+
+
+
+<h4>Captures:</h4><p>
+A pattern may contain sub-patterns enclosed in parentheses;
+they describe <em>captures</em>.
+When a match succeeds, the substrings of the subject string
+that match captures are stored (<em>captured</em>) for future use.
+Captures are numbered according to their left parentheses.
+For instance, in the pattern <code>"(a*(.)%w(%s*))"</code>,
+the part of the string matching <code>"a*(.)%w(%s*)"</code> is
+stored as the first capture (and therefore has number&nbsp;1);
+the character matching "<code>.</code>" is captured with number&nbsp;2,
+and the part matching "<code>%s*</code>" has number&nbsp;3.
+
+
+<p>
+As a special case, the empty capture <code>()</code> captures
+the current string position (a number).
+For instance, if we apply the pattern <code>"()aa()"</code> on the
+string <code>"flaaap"</code>, there will be two captures: 3&nbsp;and&nbsp;5.
+
+
+<p>
+A pattern cannot contain embedded zeros. Use <code>%z</code> instead.
+
+
+
+
+
+
+
+
+
+
+
+<h2>5.5 - <a name="5.5">Table Manipulation</a></h2><p>
+This library provides generic functions for table manipulation.
+It provides all its functions inside the table <a name="pdf-table"><code>table</code></a>.
+
+
+<p>
+Most functions in the table library assume that the table
+represents an array or a list.
+For these functions, when we talk about the "length" of a table
+we mean the result of the length operator.
+
+
+<p>
+<hr><h3><a name="pdf-table.concat"><code>table.concat (table [, sep [, i [, j]]])</code></a></h3>
+Given an array where all elements are strings or numbers,
+returns <code>table[i]..sep..table[i+1] &middot;&middot;&middot; sep..table[j]</code>.
+The default value for <code>sep</code> is the empty string,
+the default for <code>i</code> is 1,
+and the default for <code>j</code> is the length of the table.
+If <code>i</code> is greater than <code>j</code>, returns the empty string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.insert"><code>table.insert (table, [pos,] value)</code></a></h3>
+
+
+<p>
+Inserts element <code>value</code> at position <code>pos</code> in <code>table</code>,
+shifting up other elements to open space, if necessary.
+The default value for <code>pos</code> is <code>n+1</code>,
+where <code>n</code> is the length of the table (see <a href="#2.5.5">&sect;2.5.5</a>),
+so that a call <code>table.insert(t,x)</code> inserts <code>x</code> at the end
+of table <code>t</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.maxn"><code>table.maxn (table)</code></a></h3>
+
+
+<p>
+Returns the largest positive numerical index of the given table,
+or zero if the table has no positive numerical indices.
+(To do its job this function does a linear traversal of
+the whole table.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.remove"><code>table.remove (table [, pos])</code></a></h3>
+
+
+<p>
+Removes from <code>table</code> the element at position <code>pos</code>,
+shifting down other elements to close the space, if necessary.
+Returns the value of the removed element.
+The default value for <code>pos</code> is <code>n</code>,
+where <code>n</code> is the length of the table,
+so that a call <code>table.remove(t)</code> removes the last element
+of table <code>t</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.sort"><code>table.sort (table [, comp])</code></a></h3>
+Sorts table elements in a given order, <em>in-place</em>,
+from <code>table[1]</code> to <code>table[n]</code>,
+where <code>n</code> is the length of the table.
+If <code>comp</code> is given,
+then it must be a function that receives two table elements,
+and returns true
+when the first is less than the second
+(so that <code>not comp(a[i+1],a[i])</code> will be true after the sort).
+If <code>comp</code> is not given,
+then the standard Lua operator <code>&lt;</code> is used instead.
+
+
+<p>
+The sort algorithm is not stable;
+that is, elements considered equal by the given order
+may have their relative positions changed by the sort.
+
+
+
+
+
+
+
+<h2>5.6 - <a name="5.6">Mathematical Functions</a></h2>
+
+<p>
+This library is an interface to the standard C&nbsp;math library.
+It provides all its functions inside the table <a name="pdf-math"><code>math</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-math.abs"><code>math.abs (x)</code></a></h3>
+
+
+<p>
+Returns the absolute value of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.acos"><code>math.acos (x)</code></a></h3>
+
+
+<p>
+Returns the arc cosine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.asin"><code>math.asin (x)</code></a></h3>
+
+
+<p>
+Returns the arc sine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.atan"><code>math.atan (x)</code></a></h3>
+
+
+<p>
+Returns the arc tangent of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.atan2"><code>math.atan2 (y, x)</code></a></h3>
+
+
+<p>
+Returns the arc tangent of <code>y/x</code> (in radians),
+but uses the signs of both parameters to find the
+quadrant of the result.
+(It also handles correctly the case of <code>x</code> being zero.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ceil"><code>math.ceil (x)</code></a></h3>
+
+
+<p>
+Returns the smallest integer larger than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.cos"><code>math.cos (x)</code></a></h3>
+
+
+<p>
+Returns the cosine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.cosh"><code>math.cosh (x)</code></a></h3>
+
+
+<p>
+Returns the hyperbolic cosine of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.deg"><code>math.deg (x)</code></a></h3>
+
+
+<p>
+Returns the angle <code>x</code> (given in radians) in degrees.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.exp"><code>math.exp (x)</code></a></h3>
+
+
+<p>
+Returns the value <em>e<sup>x</sup></em>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.floor"><code>math.floor (x)</code></a></h3>
+
+
+<p>
+Returns the largest integer smaller than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.fmod"><code>math.fmod (x, y)</code></a></h3>
+
+
+<p>
+Returns the remainder of the division of <code>x</code> by <code>y</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.frexp"><code>math.frexp (x)</code></a></h3>
+
+
+<p>
+Returns <code>m</code> and <code>e</code> such that <em>x = m2<sup>e</sup></em>,
+<code>e</code> is an integer and the absolute value of <code>m</code> is
+in the range <em>[0.5, 1)</em>
+(or zero when <code>x</code> is zero).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.huge"><code>math.huge</code></a></h3>
+
+
+<p>
+The value <code>HUGE_VAL</code>,
+a value larger than or equal to any other numerical value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ldexp"><code>math.ldexp (m, e)</code></a></h3>
+
+
+<p>
+Returns <em>m2<sup>e</sup></em> (<code>e</code> should be an integer).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.log"><code>math.log (x)</code></a></h3>
+
+
+<p>
+Returns the natural logarithm of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.log10"><code>math.log10 (x)</code></a></h3>
+
+
+<p>
+Returns the base-10 logarithm of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.max"><code>math.max (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the maximum value among its arguments.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.min"><code>math.min (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the minimum value among its arguments.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.modf"><code>math.modf (x)</code></a></h3>
+
+
+<p>
+Returns two numbers,
+the integral part of <code>x</code> and the fractional part of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.pi"><code>math.pi</code></a></h3>
+
+
+<p>
+The value of <em>pi</em>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.pow"><code>math.pow (x, y)</code></a></h3>
+
+
+<p>
+Returns <em>x<sup>y</sup></em>.
+(You can also use the expression <code>x^y</code> to compute this value.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.rad"><code>math.rad (x)</code></a></h3>
+
+
+<p>
+Returns the angle <code>x</code> (given in degrees) in radians.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.random"><code>math.random ([m [, n]])</code></a></h3>
+
+
+<p>
+This function is an interface to the simple
+pseudo-random generator function <code>rand</code> provided by ANSI&nbsp;C.
+(No guarantees can be given for its statistical properties.)
+
+
+<p>
+When called without arguments,
+returns a uniform pseudo-random real number
+in the range <em>[0,1)</em>.
+When called with an integer number <code>m</code>,
+<code>math.random</code> returns
+a uniform pseudo-random integer in the range <em>[1, m]</em>.
+When called with two integer numbers <code>m</code> and <code>n</code>,
+<code>math.random</code> returns a uniform pseudo-random
+integer in the range <em>[m, n]</em>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.randomseed"><code>math.randomseed (x)</code></a></h3>
+
+
+<p>
+Sets <code>x</code> as the "seed"
+for the pseudo-random generator:
+equal seeds produce equal sequences of numbers.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sin"><code>math.sin (x)</code></a></h3>
+
+
+<p>
+Returns the sine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sinh"><code>math.sinh (x)</code></a></h3>
+
+
+<p>
+Returns the hyperbolic sine of <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sqrt"><code>math.sqrt (x)</code></a></h3>
+
+
+<p>
+Returns the square root of <code>x</code>.
+(You can also use the expression <code>x^0.5</code> to compute this value.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tan"><code>math.tan (x)</code></a></h3>
+
+
+<p>
+Returns the tangent of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tanh"><code>math.tanh (x)</code></a></h3>
+
+
+<p>
+Returns the hyperbolic tangent of <code>x</code>.
+
+
+
+
+
+
+
+<h2>5.7 - <a name="5.7">Input and Output Facilities</a></h2>
+
+<p>
+The I/O library provides two different styles for file manipulation.
+The first one uses implicit file descriptors;
+that is, there are operations to set a default input file and a
+default output file,
+and all input/output operations are over these default files.
+The second style uses explicit file descriptors.
+
+
+<p>
+When using implicit file descriptors,
+all operations are supplied by table <a name="pdf-io"><code>io</code></a>.
+When using explicit file descriptors,
+the operation <a href="#pdf-io.open"><code>io.open</code></a> returns a file descriptor
+and then all operations are supplied as methods of the file descriptor.
+
+
+<p>
+The table <code>io</code> also provides
+three predefined file descriptors with their usual meanings from C:
+<a name="pdf-io.stdin"><code>io.stdin</code></a>, <a name="pdf-io.stdout"><code>io.stdout</code></a>, and <a name="pdf-io.stderr"><code>io.stderr</code></a>.
+The I/O library never closes these files.
+
+
+<p>
+Unless otherwise stated,
+all I/O functions return <b>nil</b> on failure
+(plus an error message as a second result and
+a system-dependent error code as a third result)
+and some value different from <b>nil</b> on success.
+
+
+<p>
+<hr><h3><a name="pdf-io.close"><code>io.close ([file])</code></a></h3>
+
+
+<p>
+Equivalent to <code>file:close()</code>.
+Without a <code>file</code>, closes the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.flush"><code>io.flush ()</code></a></h3>
+
+
+<p>
+Equivalent to <code>file:flush</code> over the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.input"><code>io.input ([file])</code></a></h3>
+
+
+<p>
+When called with a file name, it opens the named file (in text mode),
+and sets its handle as the default input file.
+When called with a file handle,
+it simply sets this file handle as the default input file.
+When called without parameters,
+it returns the current default input file.
+
+
+<p>
+In case of errors this function raises the error,
+instead of returning an error code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.lines"><code>io.lines ([filename])</code></a></h3>
+
+
+<p>
+Opens the given file name in read mode
+and returns an iterator function that,
+each time it is called,
+returns a new line from the file.
+Therefore, the construction
+
+<pre>
+ for line in io.lines(filename) do <em>body</em> end
+</pre><p>
+will iterate over all lines of the file.
+When the iterator function detects the end of file,
+it returns <b>nil</b> (to finish the loop) and automatically closes the file.
+
+
+<p>
+The call <code>io.lines()</code> (with no file name) is equivalent
+to <code>io.input():lines()</code>;
+that is, it iterates over the lines of the default input file.
+In this case it does not close the file when the loop ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.open"><code>io.open (filename [, mode])</code></a></h3>
+
+
+<p>
+This function opens a file,
+in the mode specified in the string <code>mode</code>.
+It returns a new file handle,
+or, in case of errors, <b>nil</b> plus an error message.
+
+
+<p>
+The <code>mode</code> string can be any of the following:
+
+<ul>
+<li><b>"r":</b> read mode (the default);</li>
+<li><b>"w":</b> write mode;</li>
+<li><b>"a":</b> append mode;</li>
+<li><b>"r+":</b> update mode, all previous data is preserved;</li>
+<li><b>"w+":</b> update mode, all previous data is erased;</li>
+<li><b>"a+":</b> append update mode, previous data is preserved,
+ writing is only allowed at the end of file.</li>
+</ul><p>
+The <code>mode</code> string may also have a '<code>b</code>' at the end,
+which is needed in some systems to open the file in binary mode.
+This string is exactly what is used in the
+standard&nbsp;C function <code>fopen</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.output"><code>io.output ([file])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-io.input"><code>io.input</code></a>, but operates over the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.popen"><code>io.popen (prog [, mode])</code></a></h3>
+
+
+<p>
+Starts program <code>prog</code> in a separated process and returns
+a file handle that you can use to read data from this program
+(if <code>mode</code> is <code>"r"</code>, the default)
+or to write data to this program
+(if <code>mode</code> is <code>"w"</code>).
+
+
+<p>
+This function is system dependent and is not available
+on all platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.read"><code>io.read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.input():read</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.tmpfile"><code>io.tmpfile ()</code></a></h3>
+
+
+<p>
+Returns a handle for a temporary file.
+This file is opened in update mode
+and it is automatically removed when the program ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.type"><code>io.type (obj)</code></a></h3>
+
+
+<p>
+Checks whether <code>obj</code> is a valid file handle.
+Returns the string <code>"file"</code> if <code>obj</code> is an open file handle,
+<code>"closed file"</code> if <code>obj</code> is a closed file handle,
+or <b>nil</b> if <code>obj</code> is not a file handle.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.write"><code>io.write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.output():write</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:close"><code>file:close ()</code></a></h3>
+
+
+<p>
+Closes <code>file</code>.
+Note that files are automatically closed when
+their handles are garbage collected,
+but that takes an unpredictable amount of time to happen.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:flush"><code>file:flush ()</code></a></h3>
+
+
+<p>
+Saves any written data to <code>file</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:lines"><code>file:lines ()</code></a></h3>
+
+
+<p>
+Returns an iterator function that,
+each time it is called,
+returns a new line from the file.
+Therefore, the construction
+
+<pre>
+ for line in file:lines() do <em>body</em> end
+</pre><p>
+will iterate over all lines of the file.
+(Unlike <a href="#pdf-io.lines"><code>io.lines</code></a>, this function does not close the file
+when the loop ends.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:read"><code>file:read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Reads the file <code>file</code>,
+according to the given formats, which specify what to read.
+For each format,
+the function returns a string (or a number) with the characters read,
+or <b>nil</b> if it cannot read data with the specified format.
+When called without formats,
+it uses a default format that reads the entire next line
+(see below).
+
+
+<p>
+The available formats are
+
+<ul>
+
+<li><b>"*n":</b>
+reads a number;
+this is the only format that returns a number instead of a string.
+</li>
+
+<li><b>"*a":</b>
+reads the whole file, starting at the current position.
+On end of file, it returns the empty string.
+</li>
+
+<li><b>"*l":</b>
+reads the next line (skipping the end of line),
+returning <b>nil</b> on end of file.
+This is the default format.
+</li>
+
+<li><b><em>number</em>:</b>
+reads a string with up to this number of characters,
+returning <b>nil</b> on end of file.
+If number is zero,
+it reads nothing and returns an empty string,
+or <b>nil</b> on end of file.
+</li>
+
+</ul>
+
+
+
+<p>
+<hr><h3><a name="pdf-file:seek"><code>file:seek ([whence] [, offset])</code></a></h3>
+
+
+<p>
+Sets and gets the file position,
+measured from the beginning of the file,
+to the position given by <code>offset</code> plus a base
+specified by the string <code>whence</code>, as follows:
+
+<ul>
+<li><b>"set":</b> base is position 0 (beginning of the file);</li>
+<li><b>"cur":</b> base is current position;</li>
+<li><b>"end":</b> base is end of file;</li>
+</ul><p>
+In case of success, function <code>seek</code> returns the final file position,
+measured in bytes from the beginning of the file.
+If this function fails, it returns <b>nil</b>,
+plus a string describing the error.
+
+
+<p>
+The default value for <code>whence</code> is <code>"cur"</code>,
+and for <code>offset</code> is 0.
+Therefore, the call <code>file:seek()</code> returns the current
+file position, without changing it;
+the call <code>file:seek("set")</code> sets the position to the
+beginning of the file (and returns 0);
+and the call <code>file:seek("end")</code> sets the position to the
+end of the file, and returns its size.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:setvbuf"><code>file:setvbuf (mode [, size])</code></a></h3>
+
+
+<p>
+Sets the buffering mode for an output file.
+There are three available modes:
+
+<ul>
+
+<li><b>"no":</b>
+no buffering; the result of any output operation appears immediately.
+</li>
+
+<li><b>"full":</b>
+full buffering; output operation is performed only
+when the buffer is full (or when you explicitly <code>flush</code> the file
+(see <a href="#pdf-io.flush"><code>io.flush</code></a>)).
+</li>
+
+<li><b>"line":</b>
+line buffering; output is buffered until a newline is output
+or there is any input from some special files
+(such as a terminal device).
+</li>
+
+</ul><p>
+For the last two cases, <code>size</code>
+specifies the size of the buffer, in bytes.
+The default is an appropriate size.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:write"><code>file:write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Writes the value of each of its arguments to
+the <code>file</code>.
+The arguments must be strings or numbers.
+To write other values,
+use <a href="#pdf-tostring"><code>tostring</code></a> or <a href="#pdf-string.format"><code>string.format</code></a> before <code>write</code>.
+
+
+
+
+
+
+
+<h2>5.8 - <a name="5.8">Operating System Facilities</a></h2>
+
+<p>
+This library is implemented through table <a name="pdf-os"><code>os</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-os.clock"><code>os.clock ()</code></a></h3>
+
+
+<p>
+Returns an approximation of the amount in seconds of CPU time
+used by the program.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.date"><code>os.date ([format [, time]])</code></a></h3>
+
+
+<p>
+Returns a string or a table containing date and time,
+formatted according to the given string <code>format</code>.
+
+
+<p>
+If the <code>time</code> argument is present,
+this is the time to be formatted
+(see the <a href="#pdf-os.time"><code>os.time</code></a> function for a description of this value).
+Otherwise, <code>date</code> formats the current time.
+
+
+<p>
+If <code>format</code> starts with '<code>!</code>',
+then the date is formatted in Coordinated Universal Time.
+After this optional character,
+if <code>format</code> is the string "<code>*t</code>",
+then <code>date</code> returns a table with the following fields:
+<code>year</code> (four digits), <code>month</code> (1--12), <code>day</code> (1--31),
+<code>hour</code> (0--23), <code>min</code> (0--59), <code>sec</code> (0--61),
+<code>wday</code> (weekday, Sunday is&nbsp;1),
+<code>yday</code> (day of the year),
+and <code>isdst</code> (daylight saving flag, a boolean).
+
+
+<p>
+If <code>format</code> is not "<code>*t</code>",
+then <code>date</code> returns the date as a string,
+formatted according to the same rules as the C&nbsp;function <code>strftime</code>.
+
+
+<p>
+When called without arguments,
+<code>date</code> returns a reasonable date and time representation that depends on
+the host system and on the current locale
+(that is, <code>os.date()</code> is equivalent to <code>os.date("%c")</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.difftime"><code>os.difftime (t2, t1)</code></a></h3>
+
+
+<p>
+Returns the number of seconds from time <code>t1</code> to time <code>t2</code>.
+In POSIX, Windows, and some other systems,
+this value is exactly <code>t2</code><em>-</em><code>t1</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.execute"><code>os.execute ([command])</code></a></h3>
+
+
+<p>
+This function is equivalent to the C&nbsp;function <code>system</code>.
+It passes <code>command</code> to be executed by an operating system shell.
+It returns a status code, which is system-dependent.
+If <code>command</code> is absent, then it returns nonzero if a shell is available
+and zero otherwise.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.exit"><code>os.exit ([code])</code></a></h3>
+
+
+<p>
+Calls the C&nbsp;function <code>exit</code>,
+with an optional <code>code</code>,
+to terminate the host program.
+The default value for <code>code</code> is the success code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.getenv"><code>os.getenv (varname)</code></a></h3>
+
+
+<p>
+Returns the value of the process environment variable <code>varname</code>,
+or <b>nil</b> if the variable is not defined.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.remove"><code>os.remove (filename)</code></a></h3>
+
+
+<p>
+Deletes the file or directory with the given name.
+Directories must be empty to be removed.
+If this function fails, it returns <b>nil</b>,
+plus a string describing the error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.rename"><code>os.rename (oldname, newname)</code></a></h3>
+
+
+<p>
+Renames file or directory named <code>oldname</code> to <code>newname</code>.
+If this function fails, it returns <b>nil</b>,
+plus a string describing the error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.setlocale"><code>os.setlocale (locale [, category])</code></a></h3>
+
+
+<p>
+Sets the current locale of the program.
+<code>locale</code> is a string specifying a locale;
+<code>category</code> is an optional string describing which category to change:
+<code>"all"</code>, <code>"collate"</code>, <code>"ctype"</code>,
+<code>"monetary"</code>, <code>"numeric"</code>, or <code>"time"</code>;
+the default category is <code>"all"</code>.
+The function returns the name of the new locale,
+or <b>nil</b> if the request cannot be honored.
+
+
+<p>
+If <code>locale</code> is the empty string,
+the current locale is set to an implementation-defined native locale.
+If <code>locale</code> is the string "<code>C</code>",
+the current locale is set to the standard C locale.
+
+
+<p>
+When called with <b>nil</b> as the first argument,
+this function only returns the name of the current locale
+for the given category.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.time"><code>os.time ([table])</code></a></h3>
+
+
+<p>
+Returns the current time when called without arguments,
+or a time representing the date and time specified by the given table.
+This table must have fields <code>year</code>, <code>month</code>, and <code>day</code>,
+and may have fields <code>hour</code>, <code>min</code>, <code>sec</code>, and <code>isdst</code>
+(for a description of these fields, see the <a href="#pdf-os.date"><code>os.date</code></a> function).
+
+
+<p>
+The returned value is a number, whose meaning depends on your system.
+In POSIX, Windows, and some other systems, this number counts the number
+of seconds since some given start time (the "epoch").
+In other systems, the meaning is not specified,
+and the number returned by <code>time</code> can be used only as an argument to
+<code>date</code> and <code>difftime</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.tmpname"><code>os.tmpname ()</code></a></h3>
+
+
+<p>
+Returns a string with a file name that can
+be used for a temporary file.
+The file must be explicitly opened before its use
+and explicitly removed when no longer needed.
+
+
+
+
+
+
+
+<h2>5.9 - <a name="5.9">The Debug Library</a></h2>
+
+<p>
+This library provides
+the functionality of the debug interface to Lua programs.
+You should exert care when using this library.
+The functions provided here should be used exclusively for debugging
+and similar tasks, such as profiling.
+Please resist the temptation to use them as a
+usual programming tool:
+they can be very slow.
+Moreover, several of these functions
+violate some assumptions about Lua code
+(e.g., that variables local to a function
+cannot be accessed from outside or
+that userdata metatables cannot be changed by Lua code)
+and therefore can compromise otherwise secure code.
+
+
+<p>
+All functions in this library are provided
+inside the <a name="pdf-debug"><code>debug</code></a> table.
+All functions that operate over a thread
+have an optional first argument which is the
+thread to operate over.
+The default is always the current thread.
+
+
+<p>
+<hr><h3><a name="pdf-debug.debug"><code>debug.debug ()</code></a></h3>
+
+
+<p>
+Enters an interactive mode with the user,
+running each string that the user enters.
+Using simple commands and other debug facilities,
+the user can inspect global and local variables,
+change their values, evaluate expressions, and so on.
+A line containing only the word <code>cont</code> finishes this function,
+so that the caller continues its execution.
+
+
+<p>
+Note that commands for <code>debug.debug</code> are not lexically nested
+within any function, and so have no direct access to local variables.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getfenv"><code>debug.getfenv (o)</code></a></h3>
+Returns the environment of object <code>o</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.gethook"><code>debug.gethook ([thread])</code></a></h3>
+
+
+<p>
+Returns the current hook settings of the thread, as three values:
+the current hook function, the current hook mask,
+and the current hook count
+(as set by the <a href="#pdf-debug.sethook"><code>debug.sethook</code></a> function).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getinfo"><code>debug.getinfo ([thread,] function [, what])</code></a></h3>
+
+
+<p>
+Returns a table with information about a function.
+You can give the function directly,
+or you can give a number as the value of <code>function</code>,
+which means the function running at level <code>function</code> of the call stack
+of the given thread:
+level&nbsp;0 is the current function (<code>getinfo</code> itself);
+level&nbsp;1 is the function that called <code>getinfo</code>;
+and so on.
+If <code>function</code> is a number larger than the number of active functions,
+then <code>getinfo</code> returns <b>nil</b>.
+
+
+<p>
+The returned table may contain all the fields returned by <a href="#lua_getinfo"><code>lua_getinfo</code></a>,
+with the string <code>what</code> describing which fields to fill in.
+The default for <code>what</code> is to get all information available,
+except the table of valid lines.
+If present,
+the option '<code>f</code>'
+adds a field named <code>func</code> with the function itself.
+If present,
+the option '<code>L</code>'
+adds a field named <code>activelines</code> with the table of
+valid lines.
+
+
+<p>
+For instance, the expression <code>debug.getinfo(1,"n").name</code> returns
+a table with a name for the current function,
+if a reasonable name can be found,
+and the expression <code>debug.getinfo(print)</code>
+returns a table with all available information
+about the <a href="#pdf-print"><code>print</code></a> function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getlocal"><code>debug.getlocal ([thread,] level, local)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the local variable
+with index <code>local</code> of the function at level <code>level</code> of the stack.
+(The first parameter or local variable has index&nbsp;1, and so on,
+until the last active local variable.)
+The function returns <b>nil</b> if there is no local
+variable with the given index,
+and raises an error when called with a <code>level</code> out of range.
+(You can call <a href="#pdf-debug.getinfo"><code>debug.getinfo</code></a> to check whether the level is valid.)
+
+
+<p>
+Variable names starting with '<code>(</code>' (open parentheses)
+represent internal variables
+(loop control variables, temporaries, and C&nbsp;function locals).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getmetatable"><code>debug.getmetatable (object)</code></a></h3>
+
+
+<p>
+Returns the metatable of the given <code>object</code>
+or <b>nil</b> if it does not have a metatable.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getregistry"><code>debug.getregistry ()</code></a></h3>
+
+
+<p>
+Returns the registry table (see <a href="#3.5">&sect;3.5</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getupvalue"><code>debug.getupvalue (func, up)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the upvalue
+with index <code>up</code> of the function <code>func</code>.
+The function returns <b>nil</b> if there is no upvalue with the given index.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setfenv"><code>debug.setfenv (object, table)</code></a></h3>
+
+
+<p>
+Sets the environment of the given <code>object</code> to the given <code>table</code>.
+Returns <code>object</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.sethook"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3>
+
+
+<p>
+Sets the given function as a hook.
+The string <code>mask</code> and the number <code>count</code> describe
+when the hook will be called.
+The string mask may have the following characters,
+with the given meaning:
+
+<ul>
+<li><b><code>"c"</code>:</b> the hook is called every time Lua calls a function;</li>
+<li><b><code>"r"</code>:</b> the hook is called every time Lua returns from a function;</li>
+<li><b><code>"l"</code>:</b> the hook is called every time Lua enters a new line of code.</li>
+</ul><p>
+With a <code>count</code> different from zero,
+the hook is called after every <code>count</code> instructions.
+
+
+<p>
+When called without arguments,
+<a href="#pdf-debug.sethook"><code>debug.sethook</code></a> turns off the hook.
+
+
+<p>
+When the hook is called, its first parameter is a string
+describing the event that has triggered its call:
+<code>"call"</code>, <code>"return"</code> (or <code>"tail return"</code>),
+<code>"line"</code>, and <code>"count"</code>.
+For line events,
+the hook also gets the new line number as its second parameter.
+Inside a hook,
+you can call <code>getinfo</code> with level&nbsp;2 to get more information about
+the running function
+(level&nbsp;0 is the <code>getinfo</code> function,
+and level&nbsp;1 is the hook function),
+unless the event is <code>"tail return"</code>.
+In this case, Lua is only simulating the return,
+and a call to <code>getinfo</code> will return invalid data.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setlocal"><code>debug.setlocal ([thread,] level, local, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the local variable
+with index <code>local</code> of the function at level <code>level</code> of the stack.
+The function returns <b>nil</b> if there is no local
+variable with the given index,
+and raises an error when called with a <code>level</code> out of range.
+(You can call <code>getinfo</code> to check whether the level is valid.)
+Otherwise, it returns the name of the local variable.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setmetatable"><code>debug.setmetatable (object, table)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given <code>object</code> to the given <code>table</code>
+(which can be <b>nil</b>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setupvalue"><code>debug.setupvalue (func, up, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the upvalue
+with index <code>up</code> of the function <code>func</code>.
+The function returns <b>nil</b> if there is no upvalue
+with the given index.
+Otherwise, it returns the name of the upvalue.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.traceback"><code>debug.traceback ([thread,] [message] [, level])</code></a></h3>
+
+
+<p>
+Returns a string with a traceback of the call stack.
+An optional <code>message</code> string is appended
+at the beginning of the traceback.
+An optional <code>level</code> number tells at which level
+to start the traceback
+(default is 1, the function calling <code>traceback</code>).
+
+
+
+
+
+
+
+<h1>6 - <a name="6">Lua Stand-alone</a></h1>
+
+<p>
+Although Lua has been designed as an extension language,
+to be embedded in a host C&nbsp;program,
+it is also frequently used as a stand-alone language.
+An interpreter for Lua as a stand-alone language,
+called simply <code>lua</code>,
+is provided with the standard distribution.
+The stand-alone interpreter includes
+all standard libraries, including the debug library.
+Its usage is:
+
+<pre>
+ lua [options] [script [args]]
+</pre><p>
+The options are:
+
+<ul>
+<li><b><code>-e <em>stat</em></code>:</b> executes string <em>stat</em>;</li>
+<li><b><code>-l <em>mod</em></code>:</b> "requires" <em>mod</em>;</li>
+<li><b><code>-i</code>:</b> enters interactive mode after running <em>script</em>;</li>
+<li><b><code>-v</code>:</b> prints version information;</li>
+<li><b><code>--</code>:</b> stops handling options;</li>
+<li><b><code>-</code>:</b> executes <code>stdin</code> as a file and stops handling options.</li>
+</ul><p>
+After handling its options, <code>lua</code> runs the given <em>script</em>,
+passing to it the given <em>args</em> as string arguments.
+When called without arguments,
+<code>lua</code> behaves as <code>lua -v -i</code>
+when the standard input (<code>stdin</code>) is a terminal,
+and as <code>lua -</code> otherwise.
+
+
+<p>
+Before running any argument,
+the interpreter checks for an environment variable <a name="pdf-LUA_INIT"><code>LUA_INIT</code></a>.
+If its format is <code>@<em>filename</em></code>,
+then <code>lua</code> executes the file.
+Otherwise, <code>lua</code> executes the string itself.
+
+
+<p>
+All options are handled in order, except <code>-i</code>.
+For instance, an invocation like
+
+<pre>
+ $ lua -e'a=1' -e 'print(a)' script.lua
+</pre><p>
+will first set <code>a</code> to 1, then print the value of <code>a</code> (which is '<code>1</code>'),
+and finally run the file <code>script.lua</code> with no arguments.
+(Here <code>$</code> is the shell prompt. Your prompt may be different.)
+
+
+<p>
+Before starting to run the script,
+<code>lua</code> collects all arguments in the command line
+in a global table called <code>arg</code>.
+The script name is stored at index 0,
+the first argument after the script name goes to index 1,
+and so on.
+Any arguments before the script name
+(that is, the interpreter name plus the options)
+go to negative indices.
+For instance, in the call
+
+<pre>
+ $ lua -la b.lua t1 t2
+</pre><p>
+the interpreter first runs the file <code>a.lua</code>,
+then creates a table
+
+<pre>
+ arg = { [-2] = "lua", [-1] = "-la",
+ [0] = "b.lua",
+ [1] = "t1", [2] = "t2" }
+</pre><p>
+and finally runs the file <code>b.lua</code>.
+The script is called with <code>arg[1]</code>, <code>arg[2]</code>, &middot;&middot;&middot;
+as arguments;
+it can also access these arguments with the vararg expression '<code>...</code>'.
+
+
+<p>
+In interactive mode,
+if you write an incomplete statement,
+the interpreter waits for its completion
+by issuing a different prompt.
+
+
+<p>
+If the global variable <a name="pdf-_PROMPT"><code>_PROMPT</code></a> contains a string,
+then its value is used as the prompt.
+Similarly, if the global variable <a name="pdf-_PROMPT2"><code>_PROMPT2</code></a> contains a string,
+its value is used as the secondary prompt
+(issued during incomplete statements).
+Therefore, both prompts can be changed directly on the command line
+or in any Lua programs by assigning to <code>_PROMPT</code>.
+See the next example:
+
+<pre>
+ $ lua -e"_PROMPT='myprompt&gt; '" -i
+</pre><p>
+(The outer pair of quotes is for the shell,
+the inner pair is for Lua.)
+Note the use of <code>-i</code> to enter interactive mode;
+otherwise,
+the program would just end silently
+right after the assignment to <code>_PROMPT</code>.
+
+
+<p>
+To allow the use of Lua as a
+script interpreter in Unix systems,
+the stand-alone interpreter skips
+the first line of a chunk if it starts with <code>#</code>.
+Therefore, Lua scripts can be made into executable programs
+by using <code>chmod +x</code> and the&nbsp;<code>#!</code> form,
+as in
+
+<pre>
+ #!/usr/local/bin/lua
+</pre><p>
+(Of course,
+the location of the Lua interpreter may be different in your machine.
+If <code>lua</code> is in your <code>PATH</code>,
+then
+
+<pre>
+ #!/usr/bin/env lua
+</pre><p>
+is a more portable solution.)
+
+
+
+<h1>7 - <a name="7">Incompatibilities with the Previous Version</a></h1>
+
+<p>
+Here we list the incompatibilities that you may find when moving a program
+from Lua&nbsp;5.0 to Lua&nbsp;5.1.
+You can avoid most of the incompatibilities compiling Lua with
+appropriate options (see file <code>luaconf.h</code>).
+However,
+all these compatibility options will be removed in the next version of Lua.
+
+
+
+<h2>7.1 - <a name="7.1">Changes in the Language</a></h2>
+<ul>
+
+<li>
+The vararg system changed from the pseudo-argument <code>arg</code> with a
+table with the extra arguments to the vararg expression.
+(See compile-time option <code>LUA_COMPAT_VARARG</code> in <code>luaconf.h</code>.)
+</li>
+
+<li>
+There was a subtle change in the scope of the implicit
+variables of the <b>for</b> statement and for the <b>repeat</b> statement.
+</li>
+
+<li>
+The long string/long comment syntax (<code>[[<em>string</em>]]</code>)
+does not allow nesting.
+You can use the new syntax (<code>[=[<em>string</em>]=]</code>) in these cases.
+(See compile-time option <code>LUA_COMPAT_LSTR</code> in <code>luaconf.h</code>.)
+</li>
+
+</ul>
+
+
+
+
+<h2>7.2 - <a name="7.2">Changes in the Libraries</a></h2>
+<ul>
+
+<li>
+Function <code>string.gfind</code> was renamed <a href="#pdf-string.gmatch"><code>string.gmatch</code></a>.
+(See compile-time option <code>LUA_COMPAT_GFIND</code> in <code>luaconf.h</code>.)
+</li>
+
+<li>
+When <a href="#pdf-string.gsub"><code>string.gsub</code></a> is called with a function as its
+third argument,
+whenever this function returns <b>nil</b> or <b>false</b> the
+replacement string is the whole match,
+instead of the empty string.
+</li>
+
+<li>
+Function <code>table.setn</code> was deprecated.
+Function <code>table.getn</code> corresponds
+to the new length operator (<code>#</code>);
+use the operator instead of the function.
+(See compile-time option <code>LUA_COMPAT_GETN</code> in <code>luaconf.h</code>.)
+</li>
+
+<li>
+Function <code>loadlib</code> was renamed <a href="#pdf-package.loadlib"><code>package.loadlib</code></a>.
+(See compile-time option <code>LUA_COMPAT_LOADLIB</code> in <code>luaconf.h</code>.)
+</li>
+
+<li>
+Function <code>math.mod</code> was renamed <a href="#pdf-math.fmod"><code>math.fmod</code></a>.
+(See compile-time option <code>LUA_COMPAT_MOD</code> in <code>luaconf.h</code>.)
+</li>
+
+<li>
+Functions <code>table.foreach</code> and <code>table.foreachi</code> are deprecated.
+You can use a for loop with <code>pairs</code> or <code>ipairs</code> instead.
+</li>
+
+<li>
+There were substantial changes in function <a href="#pdf-require"><code>require</code></a> due to
+the new module system.
+However, the new behavior is mostly compatible with the old,
+but <code>require</code> gets the path from <a href="#pdf-package.path"><code>package.path</code></a> instead
+of from <code>LUA_PATH</code>.
+</li>
+
+<li>
+Function <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> has different arguments.
+Function <code>gcinfo</code> is deprecated;
+use <code>collectgarbage("count")</code> instead.
+</li>
+
+</ul>
+
+
+
+
+<h2>7.3 - <a name="7.3">Changes in the API</a></h2>
+<ul>
+
+<li>
+The <code>luaopen_*</code> functions (to open libraries)
+cannot be called directly,
+like a regular C function.
+They must be called through Lua,
+like a Lua function.
+</li>
+
+<li>
+Function <code>lua_open</code> was replaced by <a href="#lua_newstate"><code>lua_newstate</code></a> to
+allow the user to set a memory-allocation function.
+You can use <a href="#luaL_newstate"><code>luaL_newstate</code></a> from the standard library to
+create a state with a standard allocation function
+(based on <code>realloc</code>).
+</li>
+
+<li>
+Functions <code>luaL_getn</code> and <code>luaL_setn</code>
+(from the auxiliary library) are deprecated.
+Use <a href="#lua_objlen"><code>lua_objlen</code></a> instead of <code>luaL_getn</code>
+and nothing instead of <code>luaL_setn</code>.
+</li>
+
+<li>
+Function <code>luaL_openlib</code> was replaced by <a href="#luaL_register"><code>luaL_register</code></a>.
+</li>
+
+<li>
+Function <code>luaL_checkudata</code> now throws an error when the given value
+is not a userdata of the expected type.
+(In Lua&nbsp;5.0 it returned <code>NULL</code>.)
+</li>
+
+</ul>
+
+
+
+
+<h1>8 - <a name="8">The Complete Syntax of Lua</a></h1>
+
+<p>
+Here is the complete syntax of Lua in extended BNF.
+(It does not describe operator precedences.)
+
+
+
+
+<pre>
+
+ chunk ::= {stat [`<b>;</b>&acute;]} [laststat [`<b>;</b>&acute;]]
+
+ block ::= chunk
+
+ stat ::= varlist `<b>=</b>&acute; explist |
+ functioncall |
+ <b>do</b> block <b>end</b> |
+ <b>while</b> exp <b>do</b> block <b>end</b> |
+ <b>repeat</b> block <b>until</b> exp |
+ <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b> |
+ <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b> |
+ <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b> |
+ <b>function</b> funcname funcbody |
+ <b>local</b> <b>function</b> Name funcbody |
+ <b>local</b> namelist [`<b>=</b>&acute; explist]
+
+ laststat ::= <b>return</b> [explist] | <b>break</b>
+
+ funcname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]
+
+ varlist ::= var {`<b>,</b>&acute; var}
+
+ var ::= Name | prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute; | prefixexp `<b>.</b>&acute; Name
+
+ namelist ::= Name {`<b>,</b>&acute; Name}
+
+ explist ::= {exp `<b>,</b>&acute;} exp
+
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b> | Number | String | `<b>...</b>&acute; | function |
+ prefixexp | tableconstructor | exp binop exp | unop exp
+
+ prefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;
+
+ functioncall ::= prefixexp args | prefixexp `<b>:</b>&acute; Name args
+
+ args ::= `<b>(</b>&acute; [explist] `<b>)</b>&acute; | tableconstructor | String
+
+ function ::= <b>function</b> funcbody
+
+ funcbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>
+
+ parlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;
+
+ tableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;
+
+ fieldlist ::= field {fieldsep field} [fieldsep]
+
+ field ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp
+
+ fieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;
+
+ binop ::= `<b>+</b>&acute; | `<b>-</b>&acute; | `<b>*</b>&acute; | `<b>/</b>&acute; | `<b>^</b>&acute; | `<b>%</b>&acute; | `<b>..</b>&acute; |
+ `<b>&lt;</b>&acute; | `<b>&lt;=</b>&acute; | `<b>&gt;</b>&acute; | `<b>&gt;=</b>&acute; | `<b>==</b>&acute; | `<b>~=</b>&acute; |
+ <b>and</b> | <b>or</b>
+
+ unop ::= `<b>-</b>&acute; | <b>not</b> | `<b>#</b>&acute;
+
+</pre>
+
+<p>
+
+
+
+
+
+
+
+<HR>
+<SMALL>
+Last update:
+Fri Jan 18 22:32:24 BRST 2008
+</SMALL>
+<!--
+Last change: revised for Lua 5.1.3
+-->
+
+</body></html>
+
diff --git a/engines/sword25/util/lua/doc/readme.html b/engines/sword25/util/lua/doc/readme.html
new file mode 100755
index 0000000000..972faddd97
--- /dev/null
+++ b/engines/sword25/util/lua/doc/readme.html
@@ -0,0 +1,40 @@
+<HTML>
+<HEAD>
+<TITLE>Lua documentation</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+</HEAD>
+
+<BODY>
+
+<HR>
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua" BORDER=0></A>
+Documentation
+</H1>
+
+This is the documentation included in the source distribution of Lua 5.1.3.
+
+<UL>
+<LI><A HREF="contents.html">Reference manual</A>
+<LI><A HREF="lua.html">lua man page</A>
+<LI><A HREF="luac.html">luac man page</A>
+<LI><A HREF="../README">lua/README</A>
+<LI><A HREF="../etc/README">lua/etc/README</A>
+<LI><A HREF="../test/README">lua/test/README</A>
+</UL>
+
+Lua's
+<A HREF="http://www.lua.org/">official web site</A>
+contains updated documentation,
+especially the
+<A HREF="http://www.lua.org/manual/5.1/">reference manual</A>.
+<P>
+
+<HR>
+<SMALL>
+Last update:
+Wed Dec 19 13:59:14 BRST 2007
+</SMALL>
+
+</BODY>
+</HTML>
diff --git a/engines/sword25/util/lua/etc/Makefile b/engines/sword25/util/lua/etc/Makefile
new file mode 100755
index 0000000000..6d00008d98
--- /dev/null
+++ b/engines/sword25/util/lua/etc/Makefile
@@ -0,0 +1,44 @@
+# makefile for Lua etc
+
+TOP= ..
+LIB= $(TOP)/src
+INC= $(TOP)/src
+BIN= $(TOP)/src
+SRC= $(TOP)/src
+TST= $(TOP)/test
+
+CC= gcc
+CFLAGS= -O2 -Wall -I$(INC) $(MYCFLAGS)
+MYCFLAGS=
+MYLDFLAGS= -Wl,-E
+MYLIBS= -lm
+#MYLIBS= -lm -Wl,-E -ldl -lreadline -lhistory -lncurses
+RM= rm -f
+
+default:
+ @echo 'Please choose a target: min noparser one strict clean'
+
+min: min.c
+ $(CC) $(CFLAGS) $@.c -L$(LIB) -llua $(MYLIBS)
+ echo 'print"Hello there!"' | ./a.out
+
+noparser: noparser.o
+ $(CC) noparser.o $(SRC)/lua.o -L$(LIB) -llua $(MYLIBS)
+ $(BIN)/luac $(TST)/hello.lua
+ -./a.out luac.out
+ -./a.out -e'a=1'
+
+one:
+ $(CC) $(CFLAGS) all.c $(MYLIBS)
+ ./a.out $(TST)/hello.lua
+
+strict:
+ -$(BIN)/lua -e 'print(a);b=2'
+ -$(BIN)/lua -lstrict -e 'print(a)'
+ -$(BIN)/lua -e 'function f() b=2 end f()'
+ -$(BIN)/lua -lstrict -e 'function f() b=2 end f()'
+
+clean:
+ $(RM) a.out core core.* *.o luac.out
+
+.PHONY: default min noparser one strict clean
diff --git a/engines/sword25/util/lua/etc/README b/engines/sword25/util/lua/etc/README
new file mode 100755
index 0000000000..5149fc91d4
--- /dev/null
+++ b/engines/sword25/util/lua/etc/README
@@ -0,0 +1,37 @@
+This directory contains some useful files and code.
+Unlike the code in ../src, everything here is in the public domain.
+
+If any of the makes fail, you're probably not using the same libraries
+used to build Lua. Set MYLIBS in Makefile accordingly.
+
+all.c
+ Full Lua interpreter in a single file.
+ Do "make one" for a demo.
+
+lua.hpp
+ Lua header files for C++ using 'extern "C"'.
+
+lua.ico
+ A Lua icon for Windows (and web sites: save as favicon.ico).
+ Drawn by hand by Markus Gritsch <gritsch@iue.tuwien.ac.at>.
+
+lua.pc
+ pkg-config data for Lua
+
+luavs.bat
+ Script to build Lua under "Visual Studio .NET Command Prompt".
+ Run it from the toplevel as etc\luavs.bat.
+
+min.c
+ A minimal Lua interpreter.
+ Good for learning and for starting your own.
+ Do "make min" for a demo.
+
+noparser.c
+ Linking with noparser.o avoids loading the parsing modules in lualib.a.
+ Do "make noparser" for a demo.
+
+strict.lua
+ Traps uses of undeclared global variables.
+ Do "make strict" for a demo.
+
diff --git a/engines/sword25/util/lua/etc/all.c b/engines/sword25/util/lua/etc/all.c
new file mode 100755
index 0000000000..dab68fac58
--- /dev/null
+++ b/engines/sword25/util/lua/etc/all.c
@@ -0,0 +1,38 @@
+/*
+* all.c -- Lua core, libraries and interpreter in a single file
+*/
+
+#define luaall_c
+
+#include "lapi.c"
+#include "lcode.c"
+#include "ldebug.c"
+#include "ldo.c"
+#include "ldump.c"
+#include "lfunc.c"
+#include "lgc.c"
+#include "llex.c"
+#include "lmem.c"
+#include "lobject.c"
+#include "lopcodes.c"
+#include "lparser.c"
+#include "lstate.c"
+#include "lstring.c"
+#include "ltable.c"
+#include "ltm.c"
+#include "lundump.c"
+#include "lvm.c"
+#include "lzio.c"
+
+#include "lauxlib.c"
+#include "lbaselib.c"
+#include "ldblib.c"
+#include "liolib.c"
+#include "linit.c"
+#include "lmathlib.c"
+#include "loadlib.c"
+#include "loslib.c"
+#include "lstrlib.c"
+#include "ltablib.c"
+
+#include "lua.c"
diff --git a/engines/sword25/util/lua/etc/lua.hpp b/engines/sword25/util/lua/etc/lua.hpp
new file mode 100755
index 0000000000..ec417f5946
--- /dev/null
+++ b/engines/sword25/util/lua/etc/lua.hpp
@@ -0,0 +1,9 @@
+// lua.hpp
+// Lua header files for C++
+// <<extern "C">> not supplied automatically because Lua also compiles as C++
+
+extern "C" {
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+}
diff --git a/engines/sword25/util/lua/etc/lua.ico b/engines/sword25/util/lua/etc/lua.ico
new file mode 100755
index 0000000000..ccbabc4e20
--- /dev/null
+++ b/engines/sword25/util/lua/etc/lua.ico
Binary files differ
diff --git a/engines/sword25/util/lua/etc/lua.pc b/engines/sword25/util/lua/etc/lua.pc
new file mode 100755
index 0000000000..19a5c91532
--- /dev/null
+++ b/engines/sword25/util/lua/etc/lua.pc
@@ -0,0 +1,31 @@
+# lua.pc -- pkg-config data for Lua
+
+# vars from install Makefile
+
+# grep '^V=' ../Makefile
+V= 5.1
+# grep '^R=' ../Makefile
+R= 5.1.3
+
+# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
+prefix= /usr/local
+INSTALL_BIN= ${prefix}/bin
+INSTALL_INC= ${prefix}/include
+INSTALL_LIB= ${prefix}/lib
+INSTALL_MAN= ${prefix}/man/man1
+INSTALL_LMOD= ${prefix}/share/lua/${V}
+INSTALL_CMOD= ${prefix}/lib/lua/${V}
+
+# canonical vars
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: Lua
+Description: An Extensible Extension Language
+Version: ${R}
+Requires:
+Libs: -L${libdir} -llua -lm
+Cflags: -I${includedir}
+
+# (end of lua.pc)
diff --git a/engines/sword25/util/lua/etc/luavs.bat b/engines/sword25/util/lua/etc/luavs.bat
new file mode 100755
index 0000000000..054b4625c5
--- /dev/null
+++ b/engines/sword25/util/lua/etc/luavs.bat
@@ -0,0 +1,28 @@
+@rem Script to build Lua under "Visual Studio .NET Command Prompt".
+@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
+@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
+@rem (contributed by David Manura and Mike Pall)
+
+@setlocal
+@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
+@set MYLINK=link /nologo
+@set MYMT=mt /nologo
+
+cd src
+%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
+del lua.obj luac.obj
+%MYLINK% /DLL /out:lua51.dll l*.obj
+if exist lua51.dll.manifest^
+ %MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
+%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
+%MYLINK% /out:lua.exe lua.obj lua51.lib
+if exist lua.exe.manifest^
+ %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
+%MYCOMPILE% l*.c print.c
+del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
+ loslib.obj ltablib.obj lstrlib.obj loadlib.obj
+%MYLINK% /out:luac.exe *.obj
+if exist luac.exe.manifest^
+ %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
+del *.obj *.manifest
+cd ..
diff --git a/engines/sword25/util/lua/etc/min.c b/engines/sword25/util/lua/etc/min.c
new file mode 100755
index 0000000000..6a85a4d10e
--- /dev/null
+++ b/engines/sword25/util/lua/etc/min.c
@@ -0,0 +1,39 @@
+/*
+* min.c -- a minimal Lua interpreter
+* loads stdin only with minimal error handling.
+* no interaction, and no standard library, only a "print" function.
+*/
+
+#include <stdio.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+static int print(lua_State *L)
+{
+ int n=lua_gettop(L);
+ int i;
+ for (i=1; i<=n; i++)
+ {
+ if (i>1) printf("\t");
+ if (lua_isstring(L,i))
+ printf("%s",lua_tostring(L,i));
+ else if (lua_isnil(L,i))
+ printf("%s","nil");
+ else if (lua_isboolean(L,i))
+ printf("%s",lua_toboolean(L,i) ? "true" : "false");
+ else
+ printf("%s:%p",luaL_typename(L,i),lua_topointer(L,i));
+ }
+ printf("\n");
+ return 0;
+}
+
+int main(void)
+{
+ lua_State *L=lua_open();
+ lua_register(L,"print",print);
+ if (luaL_dofile(L,NULL)!=0) fprintf(stderr,"%s\n",lua_tostring(L,-1));
+ lua_close(L);
+ return 0;
+}
diff --git a/engines/sword25/util/lua/etc/noparser.c b/engines/sword25/util/lua/etc/noparser.c
new file mode 100755
index 0000000000..13ba546239
--- /dev/null
+++ b/engines/sword25/util/lua/etc/noparser.c
@@ -0,0 +1,50 @@
+/*
+* The code below can be used to make a Lua core that does not contain the
+* parsing modules (lcode, llex, lparser), which represent 35% of the total core.
+* You'll only be able to load binary files and strings, precompiled with luac.
+* (Of course, you'll have to build luac with the original parsing modules!)
+*
+* To use this module, simply compile it ("make noparser" does that) and list
+* its object file before the Lua libraries. The linker should then not load
+* the parsing modules. To try it, do "make luab".
+*
+* If you also want to avoid the dump module (ldump.o), define NODUMP.
+* #define NODUMP
+*/
+
+#define LUA_CORE
+
+#include "llex.h"
+#include "lparser.h"
+#include "lzio.h"
+
+LUAI_FUNC void luaX_init (lua_State *L) {
+ UNUSED(L);
+}
+
+LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
+ UNUSED(z);
+ UNUSED(buff);
+ UNUSED(name);
+ lua_pushliteral(L,"parser not loaded");
+ lua_error(L);
+ return NULL;
+}
+
+#ifdef NODUMP
+#include "lundump.h"
+
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) {
+ UNUSED(f);
+ UNUSED(w);
+ UNUSED(data);
+ UNUSED(strip);
+#if 1
+ UNUSED(L);
+ return 0;
+#else
+ lua_pushliteral(L,"dumper not loaded");
+ lua_error(L);
+#endif
+}
+#endif
diff --git a/engines/sword25/util/lua/etc/strict.lua b/engines/sword25/util/lua/etc/strict.lua
new file mode 100755
index 0000000000..604619dd2e
--- /dev/null
+++ b/engines/sword25/util/lua/etc/strict.lua
@@ -0,0 +1,41 @@
+--
+-- strict.lua
+-- checks uses of undeclared global variables
+-- All global variables must be 'declared' through a regular assignment
+-- (even assigning nil will do) in a main chunk before being used
+-- anywhere or assigned to inside a function.
+--
+
+local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
+
+local mt = getmetatable(_G)
+if mt == nil then
+ mt = {}
+ setmetatable(_G, mt)
+end
+
+mt.__declared = {}
+
+local function what ()
+ local d = getinfo(3, "S")
+ return d and d.what or "C"
+end
+
+mt.__newindex = function (t, n, v)
+ if not mt.__declared[n] then
+ local w = what()
+ if w ~= "main" and w ~= "C" then
+ error("assign to undeclared variable '"..n.."'", 2)
+ end
+ mt.__declared[n] = true
+ end
+ rawset(t, n, v)
+end
+
+mt.__index = function (t, n)
+ if not mt.__declared[n] and what() ~= "C" then
+ error("variable '"..n.."' is not declared", 2)
+ end
+ return rawget(t, n)
+end
+
diff --git a/engines/sword25/util/lua/src/Makefile b/engines/sword25/util/lua/src/Makefile
new file mode 100755
index 0000000000..e4a3cd6108
--- /dev/null
+++ b/engines/sword25/util/lua/src/Makefile
@@ -0,0 +1,182 @@
+# makefile for building Lua
+# see ../INSTALL for installation instructions
+# see ../Makefile and luaconf.h for further customization
+
+# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
+
+# Your platform. See PLATS for possible values.
+PLAT= none
+
+CC= gcc
+CFLAGS= -O2 -Wall $(MYCFLAGS)
+AR= ar rcu
+RANLIB= ranlib
+RM= rm -f
+LIBS= -lm $(MYLIBS)
+
+MYCFLAGS=
+MYLDFLAGS=
+MYLIBS=
+
+# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
+
+PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
+
+LUA_A= liblua.a
+CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
+ lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \
+ lundump.o lvm.o lzio.o
+LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
+ lstrlib.o loadlib.o linit.o
+
+LUA_T= lua
+LUA_O= lua.o
+
+LUAC_T= luac
+LUAC_O= luac.o print.o
+
+ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O)
+ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
+ALL_A= $(LUA_A)
+
+default: $(PLAT)
+
+all: $(ALL_T)
+
+o: $(ALL_O)
+
+a: $(ALL_A)
+
+$(LUA_A): $(CORE_O) $(LIB_O)
+ $(AR) $@ $?
+ $(RANLIB) $@
+
+$(LUA_T): $(LUA_O) $(LUA_A)
+ $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
+
+$(LUAC_T): $(LUAC_O) $(LUA_A)
+ $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
+
+clean:
+ $(RM) $(ALL_T) $(ALL_O)
+
+depend:
+ @$(CC) $(CFLAGS) -MM l*.c print.c
+
+echo:
+ @echo "PLAT = $(PLAT)"
+ @echo "CC = $(CC)"
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "AR = $(AR)"
+ @echo "RANLIB = $(RANLIB)"
+ @echo "RM = $(RM)"
+ @echo "MYCFLAGS = $(MYCFLAGS)"
+ @echo "MYLDFLAGS = $(MYLDFLAGS)"
+ @echo "MYLIBS = $(MYLIBS)"
+
+# convenience targets for popular platforms
+
+none:
+ @echo "Please choose a platform:"
+ @echo " $(PLATS)"
+
+aix:
+ $(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall"
+
+ansi:
+ $(MAKE) all MYCFLAGS=-DLUA_ANSI
+
+bsd:
+ $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E"
+
+freebsd:
+ $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline"
+
+generic:
+ $(MAKE) all MYCFLAGS=
+
+linux:
+ $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"
+
+macosx:
+ $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline"
+# use this on Mac OS X 10.3-
+# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX
+
+mingw:
+ $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \
+ "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
+ "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe
+ $(MAKE) "LUAC_T=luac.exe" luac.exe
+
+posix:
+ $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX
+
+solaris:
+ $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl"
+
+# list targets that do not create files (but not all makes understand .PHONY)
+.PHONY: all $(PLATS) default o a clean depend echo none
+
+# DO NOT DELETE
+
+lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \
+ lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \
+ lundump.h lvm.h
+lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h
+lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h
+lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
+ lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \
+ ltable.h
+ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h
+ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \
+ llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
+ lfunc.h lstring.h lgc.h ltable.h lvm.h
+ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
+ lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \
+ ltable.h lundump.h lvm.h
+ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \
+ lzio.h lmem.h lundump.h
+lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \
+ lstate.h ltm.h lzio.h
+lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
+ lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
+linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h
+liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h
+llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \
+ lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h
+lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h
+lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
+ ltm.h lzio.h lmem.h ldo.h
+loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h
+lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \
+ ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h
+lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h
+loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h
+lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
+ lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
+ lfunc.h lstring.h lgc.h ltable.h
+lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
+ ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h
+lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \
+ ltm.h lzio.h lstring.h lgc.h
+lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h
+ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
+ ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h
+ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h
+ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \
+ lmem.h lstring.h lgc.h ltable.h
+lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h
+luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \
+ lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \
+ lundump.h
+lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \
+ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h
+lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
+ lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h
+lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
+ lzio.h
+print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \
+ ltm.h lzio.h lmem.h lopcodes.h lundump.h
+
+# (end of Makefile)
diff --git a/engines/sword25/util/lua/src/lapi.c b/engines/sword25/util/lua/src/lapi.c
new file mode 100755
index 0000000000..d7e8931e45
--- /dev/null
+++ b/engines/sword25/util/lua/src/lapi.c
@@ -0,0 +1,1085 @@
+/*
+** $Id: lapi.c,v 2.55.1.3 2008/01/03 15:20:39 roberto Exp $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define lapi_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+
+
+
+const char lua_ident[] =
+ "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n"
+ "$Authors: " LUA_AUTHORS " $\n"
+ "$URL: www.lua.org $\n";
+
+
+
+#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base))
+
+#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject)
+
+#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;}
+
+
+
+static TValue *index2adr (lua_State *L, int idx) {
+ if (idx > 0) {
+ TValue *o = L->base + (idx - 1);
+ api_check(L, idx <= L->ci->top - L->base);
+ if (o >= L->top) return cast(TValue *, luaO_nilobject);
+ else return o;
+ }
+ else if (idx > LUA_REGISTRYINDEX) {
+ api_check(L, idx != 0 && -idx <= L->top - L->base);
+ return L->top + idx;
+ }
+ else switch (idx) { /* pseudo-indices */
+ case LUA_REGISTRYINDEX: return registry(L);
+ case LUA_ENVIRONINDEX: {
+ Closure *func = curr_func(L);
+ sethvalue(L, &L->env, func->c.env);
+ return &L->env;
+ }
+ case LUA_GLOBALSINDEX: return gt(L);
+ default: {
+ Closure *func = curr_func(L);
+ idx = LUA_GLOBALSINDEX - idx;
+ return (idx <= func->c.nupvalues)
+ ? &func->c.upvalue[idx-1]
+ : cast(TValue *, luaO_nilobject);
+ }
+ }
+}
+
+
+static Table *getcurrenv (lua_State *L) {
+ if (L->ci == L->base_ci) /* no enclosing function? */
+ return hvalue(gt(L)); /* use global table as environment */
+ else {
+ Closure *func = curr_func(L);
+ return func->c.env;
+ }
+}
+
+
+void luaA_pushobject (lua_State *L, const TValue *o) {
+ setobj2s(L, L->top, o);
+ api_incr_top(L);
+}
+
+
+LUA_API int lua_checkstack (lua_State *L, int size) {
+ int res;
+ lua_lock(L);
+ if ((L->top - L->base + size) > LUAI_MAXCSTACK)
+ res = 0; /* stack overflow */
+ else {
+ luaD_checkstack(L, size);
+ if (L->ci->top < L->top + size)
+ L->ci->top = L->top + size;
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
+ int i;
+ if (from == to) return;
+ lua_lock(to);
+ api_checknelems(from, n);
+ api_check(from, G(from) == G(to));
+ api_check(from, to->ci->top - to->top >= n);
+ from->top -= n;
+ for (i = 0; i < n; i++) {
+ setobj2s(to, to->top++, from->top + i);
+ }
+ lua_unlock(to);
+}
+
+
+LUA_API void lua_setlevel (lua_State *from, lua_State *to) {
+ to->nCcalls = from->nCcalls;
+}
+
+
+LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
+ lua_CFunction old;
+ lua_lock(L);
+ old = G(L)->panic;
+ G(L)->panic = panicf;
+ lua_unlock(L);
+ return old;
+}
+
+
+LUA_API lua_State *lua_newthread (lua_State *L) {
+ lua_State *L1;
+ lua_lock(L);
+ luaC_checkGC(L);
+ L1 = luaE_newthread(L);
+ setthvalue(L, L->top, L1);
+ api_incr_top(L);
+ lua_unlock(L);
+ luai_userstatethread(L, L1);
+ return L1;
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return cast_int(L->top - L->base);
+}
+
+
+LUA_API void lua_settop (lua_State *L, int idx) {
+ lua_lock(L);
+ if (idx >= 0) {
+ api_check(L, idx <= L->stack_last - L->base);
+ while (L->top < L->base + idx)
+ setnilvalue(L->top++);
+ L->top = L->base + idx;
+ }
+ else {
+ api_check(L, -(idx+1) <= (L->top - L->base));
+ L->top += idx+1; /* `subtract' index (index is negative) */
+ }
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_remove (lua_State *L, int idx) {
+ StkId p;
+ lua_lock(L);
+ p = index2adr(L, idx);
+ api_checkvalidindex(L, p);
+ while (++p < L->top) setobjs2s(L, p-1, p);
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_insert (lua_State *L, int idx) {
+ StkId p;
+ StkId q;
+ lua_lock(L);
+ p = index2adr(L, idx);
+ api_checkvalidindex(L, p);
+ for (q = L->top; q>p; q--) setobjs2s(L, q, q-1);
+ setobjs2s(L, p, L->top);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_replace (lua_State *L, int idx) {
+ StkId o;
+ lua_lock(L);
+ /* explicit test for incompatible code */
+ if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)
+ luaG_runerror(L, "no calling environment");
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ if (idx == LUA_ENVIRONINDEX) {
+ Closure *func = curr_func(L);
+ api_check(L, ttistable(L->top - 1));
+ func->c.env = hvalue(L->top - 1);
+ luaC_barrier(L, func, L->top - 1);
+ }
+ else {
+ setobj(L, o, L->top - 1);
+ if (idx < LUA_GLOBALSINDEX) /* function upvalue? */
+ luaC_barrier(L, curr_func(L), L->top - 1);
+ }
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int idx) {
+ lua_lock(L);
+ setobj2s(L, L->top, index2adr(L, idx));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);
+}
+
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ return (t == LUA_TNONE) ? "no value" : luaT_typenames[t];
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return iscfunction(o);
+}
+
+
+LUA_API int lua_isnumber (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ return tonumber(o, &n);
+}
+
+
+LUA_API int lua_isstring (lua_State *L, int idx) {
+ int t = lua_type(L, idx);
+ return (t == LUA_TSTRING || t == LUA_TNUMBER);
+}
+
+
+LUA_API int lua_isuserdata (lua_State *L, int idx) {
+ const TValue *o = index2adr(L, idx);
+ return (ttisuserdata(o) || ttislightuserdata(o));
+}
+
+
+LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
+ StkId o1 = index2adr(L, index1);
+ StkId o2 = index2adr(L, index2);
+ return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
+ : luaO_rawequalObj(o1, o2);
+}
+
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2) {
+ StkId o1, o2;
+ int i;
+ lua_lock(L); /* may call tag method */
+ o1 = index2adr(L, index1);
+ o2 = index2adr(L, index2);
+ i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2);
+ lua_unlock(L);
+ return i;
+}
+
+
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2) {
+ StkId o1, o2;
+ int i;
+ lua_lock(L); /* may call tag method */
+ o1 = index2adr(L, index1);
+ o2 = index2adr(L, index2);
+ i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
+ : luaV_lessthan(L, o1, o2);
+ lua_unlock(L);
+ return i;
+}
+
+
+
+LUA_API lua_Number lua_tonumber (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ if (tonumber(o, &n))
+ return nvalue(o);
+ else
+ return 0;
+}
+
+
+LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ if (tonumber(o, &n)) {
+ lua_Integer res;
+ lua_Number num = nvalue(o);
+ lua_number2integer(res, num);
+ return res;
+ }
+ else
+ return 0;
+}
+
+
+LUA_API int lua_toboolean (lua_State *L, int idx) {
+ const TValue *o = index2adr(L, idx);
+ return !l_isfalse(o);
+}
+
+
+LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
+ StkId o = index2adr(L, idx);
+ if (!ttisstring(o)) {
+ lua_lock(L); /* `luaV_tostring' may create a new string */
+ if (!luaV_tostring(L, o)) { /* conversion failed? */
+ if (len != NULL) *len = 0;
+ lua_unlock(L);
+ return NULL;
+ }
+ luaC_checkGC(L);
+ o = index2adr(L, idx); /* previous call may reallocate the stack */
+ lua_unlock(L);
+ }
+ if (len != NULL) *len = tsvalue(o)->len;
+ return svalue(o);
+}
+
+
+LUA_API size_t lua_objlen (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TSTRING: return tsvalue(o)->len;
+ case LUA_TUSERDATA: return uvalue(o)->len;
+ case LUA_TTABLE: return luaH_getn(hvalue(o));
+ case LUA_TNUMBER: {
+ size_t l;
+ lua_lock(L); /* `luaV_tostring' may create a new string */
+ l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0);
+ lua_unlock(L);
+ return l;
+ }
+ default: return 0;
+ }
+}
+
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (!iscfunction(o)) ? NULL : clvalue(o)->c.f;
+}
+
+
+LUA_API void *lua_touserdata (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: return (rawuvalue(o) + 1);
+ case LUA_TLIGHTUSERDATA: return pvalue(o);
+ default: return NULL;
+ }
+}
+
+
+LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (!ttisthread(o)) ? NULL : thvalue(o);
+}
+
+
+LUA_API const void *lua_topointer (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TTABLE: return hvalue(o);
+ case LUA_TFUNCTION: return clvalue(o);
+ case LUA_TTHREAD: return thvalue(o);
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ return lua_touserdata(L, idx);
+ default: return NULL;
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ lua_lock(L);
+ setnilvalue(L->top);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
+ lua_lock(L);
+ setnvalue(L->top, n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
+ lua_lock(L);
+ setnvalue(L->top, cast_num(n));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ lua_lock(L);
+ luaC_checkGC(L);
+ setsvalue2s(L, L->top, luaS_newlstr(L, s, len));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushstring (lua_State *L, const char *s) {
+ if (s == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushlstring(L, s, strlen(s));
+}
+
+
+LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp) {
+ const char *ret;
+ lua_lock(L);
+ luaC_checkGC(L);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *ret;
+ va_list argp;
+ lua_lock(L);
+ luaC_checkGC(L);
+ va_start(argp, fmt);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ Closure *cl;
+ lua_lock(L);
+ luaC_checkGC(L);
+ api_checknelems(L, n);
+ cl = luaF_newCclosure(L, n, getcurrenv(L));
+ cl->c.f = fn;
+ L->top -= n;
+ while (n--)
+ setobj2n(L, &cl->c.upvalue[n], L->top+n);
+ setclvalue(L, L->top, cl);
+ lua_assert(iswhite(obj2gco(cl)));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushboolean (lua_State *L, int b) {
+ lua_lock(L);
+ setbvalue(L->top, (b != 0)); /* ensure that true is 1 */
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
+ lua_lock(L);
+ setpvalue(L->top, p);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_pushthread (lua_State *L) {
+ lua_lock(L);
+ setthvalue(L, L->top, L);
+ api_incr_top(L);
+ lua_unlock(L);
+ return (G(L)->mainthread == L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+LUA_API void lua_gettable (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ luaV_gettable(L, t, L->top - 1, L->top - 1);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
+ StkId t;
+ TValue key;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ setsvalue(L, &key, luaS_new(L, k));
+ luaV_gettable(L, t, &key, L->top);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawget (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawgeti (lua_State *L, int idx, int n) {
+ StkId o;
+ lua_lock(L);
+ o = index2adr(L, idx);
+ api_check(L, ttistable(o));
+ setobj2s(L, L->top, luaH_getnum(hvalue(o), n));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
+ lua_lock(L);
+ luaC_checkGC(L);
+ sethvalue(L, L->top, luaH_new(L, narray, nrec));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_getmetatable (lua_State *L, int objindex) {
+ const TValue *obj;
+ Table *mt = NULL;
+ int res;
+ lua_lock(L);
+ obj = index2adr(L, objindex);
+ switch (ttype(obj)) {
+ case LUA_TTABLE:
+ mt = hvalue(obj)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(obj)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(obj)];
+ break;
+ }
+ if (mt == NULL)
+ res = 0;
+ else {
+ sethvalue(L, L->top, mt);
+ api_incr_top(L);
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_getfenv (lua_State *L, int idx) {
+ StkId o;
+ lua_lock(L);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ switch (ttype(o)) {
+ case LUA_TFUNCTION:
+ sethvalue(L, L->top, clvalue(o)->c.env);
+ break;
+ case LUA_TUSERDATA:
+ sethvalue(L, L->top, uvalue(o)->env);
+ break;
+ case LUA_TTHREAD:
+ setobj2s(L, L->top, gt(thvalue(o)));
+ break;
+ default:
+ setnilvalue(L->top);
+ break;
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+
+LUA_API void lua_settable (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ luaV_settable(L, t, L->top - 2, L->top - 1);
+ L->top -= 2; /* pop index and value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
+ StkId t;
+ TValue key;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ setsvalue(L, &key, luaS_new(L, k));
+ luaV_settable(L, t, &key, L->top - 1);
+ L->top--; /* pop value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
+ luaC_barriert(L, hvalue(t), L->top-1);
+ L->top -= 2;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int idx, int n) {
+ StkId o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_check(L, ttistable(o));
+ setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);
+ luaC_barriert(L, hvalue(o), L->top-1);
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_setmetatable (lua_State *L, int objindex) {
+ TValue *obj;
+ Table *mt;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ obj = index2adr(L, objindex);
+ api_checkvalidindex(L, obj);
+ if (ttisnil(L->top - 1))
+ mt = NULL;
+ else {
+ api_check(L, ttistable(L->top - 1));
+ mt = hvalue(L->top - 1);
+ }
+ switch (ttype(obj)) {
+ case LUA_TTABLE: {
+ hvalue(obj)->metatable = mt;
+ if (mt)
+ luaC_objbarriert(L, hvalue(obj), mt);
+ break;
+ }
+ case LUA_TUSERDATA: {
+ uvalue(obj)->metatable = mt;
+ if (mt)
+ luaC_objbarrier(L, rawuvalue(obj), mt);
+ break;
+ }
+ default: {
+ G(L)->mt[ttype(obj)] = mt;
+ break;
+ }
+ }
+ L->top--;
+ lua_unlock(L);
+ return 1;
+}
+
+
+LUA_API int lua_setfenv (lua_State *L, int idx) {
+ StkId o;
+ int res = 1;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ api_check(L, ttistable(L->top - 1));
+ switch (ttype(o)) {
+ case LUA_TFUNCTION:
+ clvalue(o)->c.env = hvalue(L->top - 1);
+ break;
+ case LUA_TUSERDATA:
+ uvalue(o)->env = hvalue(L->top - 1);
+ break;
+ case LUA_TTHREAD:
+ sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));
+ break;
+ default:
+ res = 0;
+ break;
+ }
+ if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
+ L->top--;
+ lua_unlock(L);
+ return res;
+}
+
+
+/*
+** `load' and `call' functions (run Lua code)
+*/
+
+
+#define adjustresults(L,nres) \
+ { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; }
+
+
+#define checkresults(L,na,nr) \
+ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)))
+
+
+LUA_API void lua_call (lua_State *L, int nargs, int nresults) {
+ StkId func;
+ lua_lock(L);
+ api_checknelems(L, nargs+1);
+ checkresults(L, nargs, nresults);
+ func = L->top - (nargs+1);
+ luaD_call(L, func, nresults);
+ adjustresults(L, nresults);
+ lua_unlock(L);
+}
+
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to `f_call' */
+ StkId func;
+ int nresults;
+};
+
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = cast(struct CallS *, ud);
+ luaD_call(L, c->func, c->nresults);
+}
+
+
+
+LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
+ struct CallS c;
+ int status;
+ ptrdiff_t func;
+ lua_lock(L);
+ api_checknelems(L, nargs+1);
+ checkresults(L, nargs, nresults);
+ if (errfunc == 0)
+ func = 0;
+ else {
+ StkId o = index2adr(L, errfunc);
+ api_checkvalidindex(L, o);
+ func = savestack(L, o);
+ }
+ c.func = L->top - (nargs+1); /* function to be called */
+ c.nresults = nresults;
+ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
+ adjustresults(L, nresults);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** Execute a protected C call.
+*/
+struct CCallS { /* data to `f_Ccall' */
+ lua_CFunction func;
+ void *ud;
+};
+
+
+static void f_Ccall (lua_State *L, void *ud) {
+ struct CCallS *c = cast(struct CCallS *, ud);
+ Closure *cl;
+ cl = luaF_newCclosure(L, 0, getcurrenv(L));
+ cl->c.f = c->func;
+ setclvalue(L, L->top, cl); /* push function */
+ api_incr_top(L);
+ setpvalue(L->top, c->ud); /* push only argument */
+ api_incr_top(L);
+ luaD_call(L, L->top - 2, 0);
+}
+
+
+LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) {
+ struct CCallS c;
+ int status;
+ lua_lock(L);
+ c.func = func;
+ c.ud = ud;
+ status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
+ const char *chunkname) {
+ ZIO z;
+ int status;
+ lua_lock(L);
+ if (!chunkname) chunkname = "?";
+ luaZ_init(L, &z, reader, data);
+ status = luaD_protectedparser(L, &z, chunkname);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {
+ int status;
+ TValue *o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = L->top - 1;
+ if (isLfunction(o))
+ status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);
+ else
+ status = 1;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_status (lua_State *L) {
+ return L->status;
+}
+
+
+/*
+** Garbage-collection function
+*/
+
+LUA_API int lua_gc (lua_State *L, int what, int data) {
+ int res = 0;
+ global_State *g;
+ lua_lock(L);
+ g = G(L);
+ switch (what) {
+ case LUA_GCSTOP: {
+ g->GCthreshold = MAX_LUMEM;
+ break;
+ }
+ case LUA_GCRESTART: {
+ g->GCthreshold = g->totalbytes;
+ break;
+ }
+ case LUA_GCCOLLECT: {
+ luaC_fullgc(L);
+ break;
+ }
+ case LUA_GCCOUNT: {
+ /* GC values are expressed in Kbytes: #bytes/2^10 */
+ res = cast_int(g->totalbytes >> 10);
+ break;
+ }
+ case LUA_GCCOUNTB: {
+ res = cast_int(g->totalbytes & 0x3ff);
+ break;
+ }
+ case LUA_GCSTEP: {
+ lu_mem a = (cast(lu_mem, data) << 10);
+ if (a <= g->totalbytes)
+ g->GCthreshold = g->totalbytes - a;
+ else
+ g->GCthreshold = 0;
+ while (g->GCthreshold <= g->totalbytes)
+ luaC_step(L);
+ if (g->gcstate == GCSpause) /* end of cycle? */
+ res = 1; /* signal it */
+ break;
+ }
+ case LUA_GCSETPAUSE: {
+ res = g->gcpause;
+ g->gcpause = data;
+ break;
+ }
+ case LUA_GCSETSTEPMUL: {
+ res = g->gcstepmul;
+ g->gcstepmul = data;
+ break;
+ }
+ default: res = -1; /* invalid option */
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+
+/*
+** miscellaneous functions
+*/
+
+
+LUA_API int lua_error (lua_State *L) {
+ lua_lock(L);
+ api_checknelems(L, 1);
+ luaG_errormsg(L);
+ lua_unlock(L);
+ return 0; /* to avoid warnings */
+}
+
+
+LUA_API int lua_next (lua_State *L, int idx) {
+ StkId t;
+ int more;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ more = luaH_next(L, hvalue(t), L->top - 1);
+ if (more) {
+ api_incr_top(L);
+ }
+ else /* no more elements */
+ L->top -= 1; /* remove key */
+ lua_unlock(L);
+ return more;
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ lua_lock(L);
+ api_checknelems(L, n);
+ if (n >= 2) {
+ luaC_checkGC(L);
+ luaV_concat(L, n, cast_int(L->top - L->base) - 1);
+ L->top -= (n-1);
+ }
+ else if (n == 0) { /* push empty string */
+ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
+ api_incr_top(L);
+ }
+ /* else n == 1; nothing to do */
+ lua_unlock(L);
+}
+
+
+LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
+ lua_Alloc f;
+ lua_lock(L);
+ if (ud) *ud = G(L)->ud;
+ f = G(L)->frealloc;
+ lua_unlock(L);
+ return f;
+}
+
+
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
+ lua_lock(L);
+ G(L)->ud = ud;
+ G(L)->frealloc = f;
+ lua_unlock(L);
+}
+
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
+ Udata *u;
+ lua_lock(L);
+ luaC_checkGC(L);
+ u = luaS_newudata(L, size, getcurrenv(L));
+ setuvalue(L, L->top, u);
+ api_incr_top(L);
+ lua_unlock(L);
+ return u + 1;
+}
+
+
+
+
+static const char *aux_upvalue (StkId fi, int n, TValue **val) {
+ Closure *f;
+ if (!ttisfunction(fi)) return NULL;
+ f = clvalue(fi);
+ if (f->c.isC) {
+ if (!(1 <= n && n <= f->c.nupvalues)) return NULL;
+ *val = &f->c.upvalue[n-1];
+ return "";
+ }
+ else {
+ Proto *p = f->l.p;
+ if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
+ *val = f->l.upvals[n-1]->v;
+ return getstr(p->upvalues[n-1]);
+ }
+}
+
+
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val;
+ lua_lock(L);
+ name = aux_upvalue(index2adr(L, funcindex), n, &val);
+ if (name) {
+ setobj2s(L, L->top, val);
+ api_incr_top(L);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val;
+ StkId fi;
+ lua_lock(L);
+ fi = index2adr(L, funcindex);
+ api_checknelems(L, 1);
+ name = aux_upvalue(fi, n, &val);
+ if (name) {
+ L->top--;
+ setobj(L, val, L->top);
+ luaC_barrier(L, clvalue(fi), L->top);
+ }
+ lua_unlock(L);
+ return name;
+}
+
diff --git a/engines/sword25/util/lua/src/lapi.h b/engines/sword25/util/lua/src/lapi.h
new file mode 100755
index 0000000000..2c3fab244e
--- /dev/null
+++ b/engines/sword25/util/lua/src/lapi.h
@@ -0,0 +1,16 @@
+/*
+** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "lobject.h"
+
+
+LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);
+
+#endif
diff --git a/engines/sword25/util/lua/src/lauxlib.c b/engines/sword25/util/lua/src/lauxlib.c
new file mode 100755
index 0000000000..10f14e2c08
--- /dev/null
+++ b/engines/sword25/util/lua/src/lauxlib.c
@@ -0,0 +1,652 @@
+/*
+** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+*/
+
+#define lauxlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+
+
+#define FREELIST_REF 0 /* free list of references */
+
+
+/* convert a stack index to positive */
+#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
+ lua_gettop(L) + (i) + 1)
+
+
+/*
+** {======================================================
+** Error-report functions
+** =======================================================
+*/
+
+
+LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {
+ lua_Debug ar;
+ if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
+ return luaL_error(L, "bad argument #%d (%s)", narg, extramsg);
+ lua_getinfo(L, "n", &ar);
+ if (strcmp(ar.namewhat, "method") == 0) {
+ narg--; /* do not count `self' */
+ if (narg == 0) /* error is in the self argument itself? */
+ return luaL_error(L, "calling " LUA_QS " on bad self (%s)",
+ ar.name, extramsg);
+ }
+ if (ar.name == NULL)
+ ar.name = "?";
+ return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)",
+ narg, ar.name, extramsg);
+}
+
+
+LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {
+ const char *msg = lua_pushfstring(L, "%s expected, got %s",
+ tname, luaL_typename(L, narg));
+ return luaL_argerror(L, narg, msg);
+}
+
+
+static void tag_error (lua_State *L, int narg, int tag) {
+ luaL_typerror(L, narg, lua_typename(L, tag));
+}
+
+
+LUALIB_API void luaL_where (lua_State *L, int level) {
+ lua_Debug ar;
+ if (lua_getstack(L, level, &ar)) { /* check function at level */
+ lua_getinfo(L, "Sl", &ar); /* get info about it */
+ if (ar.currentline > 0) { /* is there info? */
+ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
+ return;
+ }
+ }
+ lua_pushliteral(L, ""); /* else, no information available... */
+}
+
+
+LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+ return lua_error(L);
+}
+
+/* }====================================================== */
+
+
+LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,
+ const char *const lst[]) {
+ const char *name = (def) ? luaL_optstring(L, narg, def) :
+ luaL_checkstring(L, narg);
+ int i;
+ for (i=0; lst[i]; i++)
+ if (strcmp(lst[i], name) == 0)
+ return i;
+ return luaL_argerror(L, narg,
+ lua_pushfstring(L, "invalid option " LUA_QS, name));
+}
+
+
+LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
+ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
+ if (!lua_isnil(L, -1)) /* name already in use? */
+ return 0; /* leave previous value on top, but return 0 */
+ lua_pop(L, 1);
+ lua_newtable(L); /* create metatable */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
+ return 1;
+}
+
+
+LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_pop(L, 2); /* remove both metatables */
+ return p;
+ }
+ }
+ }
+ luaL_typerror(L, ud, tname); /* else error */
+ return NULL; /* to avoid warnings */
+}
+
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
+ if (!lua_checkstack(L, space))
+ luaL_error(L, "stack overflow (%s)", mes);
+}
+
+
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {
+ if (lua_type(L, narg) != t)
+ tag_error(L, narg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int narg) {
+ if (lua_type(L, narg) == LUA_TNONE)
+ luaL_argerror(L, narg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {
+ const char *s = lua_tolstring(L, narg, len);
+ if (!s) tag_error(L, narg, LUA_TSTRING);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_optlstring (lua_State *L, int narg,
+ const char *def, size_t *len) {
+ if (lua_isnoneornil(L, narg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_checklstring(L, narg, len);
+}
+
+
+LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
+ lua_Number d = lua_tonumber(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ tag_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {
+ return luaL_opt(L, luaL_checknumber, narg, def);
+}
+
+
+LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {
+ lua_Integer d = lua_tointeger(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ tag_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,
+ lua_Integer def) {
+ return luaL_opt(L, luaL_checkinteger, narg, def);
+}
+
+
+LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
+ if (!lua_getmetatable(L, obj)) /* no metatable? */
+ return 0;
+ lua_pushstring(L, event);
+ lua_rawget(L, -2);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 2); /* remove metatable and metafield */
+ return 0;
+ }
+ else {
+ lua_remove(L, -2); /* remove only metatable */
+ return 1;
+ }
+}
+
+
+LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
+ obj = abs_index(L, obj);
+ if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
+ return 0;
+ lua_pushvalue(L, obj);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l) {
+ luaI_openlib(L, libname, l, 0);
+}
+
+
+static int libsize (const luaL_Reg *l) {
+ int size = 0;
+ for (; l->name; l++) size++;
+ return size;
+}
+
+
+LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup) {
+ if (libname) {
+ int size = libsize(l);
+ /* check whether lib already exists */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
+ lua_getfield(L, -1, libname); /* get _LOADED[libname] */
+ if (!lua_istable(L, -1)) { /* not found? */
+ lua_pop(L, 1); /* remove previous result */
+ /* try global variable (and create one if it does not exist) */
+ if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
+ luaL_error(L, "name conflict for module " LUA_QS, libname);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
+ }
+ lua_remove(L, -2); /* remove _LOADED table */
+ lua_insert(L, -(nup+1)); /* move library table to below upvalues */
+ }
+ for (; l->name; l++) {
+ int i;
+ for (i=0; i<nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -nup);
+ lua_pushcclosure(L, l->func, nup);
+ lua_setfield(L, -(nup+2), l->name);
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+
+
+/*
+** {======================================================
+** getn-setn: size for arrays
+** =======================================================
+*/
+
+#if defined(LUA_COMPAT_GETN)
+
+static int checkint (lua_State *L, int topop) {
+ int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;
+ lua_pop(L, topop);
+ return n;
+}
+
+
+static void getsizes (lua_State *L) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES");
+ if (lua_isnil(L, -1)) { /* no `size' table? */
+ lua_pop(L, 1); /* remove nil */
+ lua_newtable(L); /* create it */
+ lua_pushvalue(L, -1); /* `size' will be its own metatable */
+ lua_setmetatable(L, -2);
+ lua_pushliteral(L, "kv");
+ lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */
+ }
+}
+
+
+LUALIB_API void luaL_setn (lua_State *L, int t, int n) {
+ t = abs_index(L, t);
+ lua_pushliteral(L, "n");
+ lua_rawget(L, t);
+ if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */
+ lua_pushliteral(L, "n"); /* use it */
+ lua_pushinteger(L, n);
+ lua_rawset(L, t);
+ }
+ else { /* use `sizes' */
+ getsizes(L);
+ lua_pushvalue(L, t);
+ lua_pushinteger(L, n);
+ lua_rawset(L, -3); /* sizes[t] = n */
+ lua_pop(L, 1); /* remove `sizes' */
+ }
+}
+
+
+LUALIB_API int luaL_getn (lua_State *L, int t) {
+ int n;
+ t = abs_index(L, t);
+ lua_pushliteral(L, "n"); /* try t.n */
+ lua_rawget(L, t);
+ if ((n = checkint(L, 1)) >= 0) return n;
+ getsizes(L); /* else try sizes[t] */
+ lua_pushvalue(L, t);
+ lua_rawget(L, -2);
+ if ((n = checkint(L, 2)) >= 0) return n;
+ return (int)lua_objlen(L, t);
+}
+
+#endif
+
+/* }====================================================== */
+
+
+
+LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
+ const char *r) {
+ const char *wild;
+ size_t l = strlen(p);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while ((wild = strstr(s, p)) != NULL) {
+ luaL_addlstring(&b, s, wild - s); /* push prefix */
+ luaL_addstring(&b, r); /* push replacement in place of pattern */
+ s = wild + l; /* continue after `p' */
+ }
+ luaL_addstring(&b, s); /* push last suffix */
+ luaL_pushresult(&b);
+ return lua_tostring(L, -1);
+}
+
+
+LUALIB_API const char *luaL_findtable (lua_State *L, int idx,
+ const char *fname, int szhint) {
+ const char *e;
+ lua_pushvalue(L, idx);
+ do {
+ e = strchr(fname, '.');
+ if (e == NULL) e = fname + strlen(fname);
+ lua_pushlstring(L, fname, e - fname);
+ lua_rawget(L, -2);
+ if (lua_isnil(L, -1)) { /* no such field? */
+ lua_pop(L, 1); /* remove this nil */
+ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
+ lua_pushlstring(L, fname, e - fname);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4); /* set new table into field */
+ }
+ else if (!lua_istable(L, -1)) { /* field has a non-table value? */
+ lua_pop(L, 2); /* remove table and value */
+ return fname; /* return problematic part of the name */
+ }
+ lua_remove(L, -2); /* remove previous table */
+ fname = e + 1;
+ } while (*e == '.');
+ return NULL;
+}
+
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#define bufflen(B) ((B)->p - (B)->buffer)
+#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
+
+#define LIMIT (LUA_MINSTACK/2)
+
+
+static int emptybuffer (luaL_Buffer *B) {
+ size_t l = bufflen(B);
+ if (l == 0) return 0; /* put nothing on stack */
+ else {
+ lua_pushlstring(B->L, B->buffer, l);
+ B->p = B->buffer;
+ B->lvl++;
+ return 1;
+ }
+}
+
+
+static void adjuststack (luaL_Buffer *B) {
+ if (B->lvl > 1) {
+ lua_State *L = B->L;
+ int toget = 1; /* number of levels to concat */
+ size_t toplen = lua_strlen(L, -1);
+ do {
+ size_t l = lua_strlen(L, -(toget+1));
+ if (B->lvl - toget + 1 >= LIMIT || toplen > l) {
+ toplen += l;
+ toget++;
+ }
+ else break;
+ } while (toget < B->lvl);
+ lua_concat(L, toget);
+ B->lvl = B->lvl - toget + 1;
+ }
+}
+
+
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
+ if (emptybuffer(B))
+ adjuststack(B);
+ return B->buffer;
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ while (l--)
+ luaL_addchar(B, *s++);
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ emptybuffer(B);
+ lua_concat(B->L, B->lvl);
+ B->lvl = 1;
+}
+
+
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t vl;
+ const char *s = lua_tolstring(L, -1, &vl);
+ if (vl <= bufffree(B)) { /* fit into buffer? */
+ memcpy(B->p, s, vl); /* put it there */
+ B->p += vl;
+ lua_pop(L, 1); /* remove from stack */
+ }
+ else {
+ if (emptybuffer(B))
+ lua_insert(L, -2); /* put buffer before new value */
+ B->lvl++; /* add new value into B stack */
+ adjuststack(B);
+ }
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->p = B->buffer;
+ B->lvl = 0;
+}
+
+/* }====================================================== */
+
+
+LUALIB_API int luaL_ref (lua_State *L, int t) {
+ int ref;
+ t = abs_index(L, t);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* remove from stack */
+ return LUA_REFNIL; /* `nil' has a unique fixed reference */
+ }
+ lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
+ ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
+ lua_pop(L, 1); /* remove it from stack */
+ if (ref != 0) { /* any free element? */
+ lua_rawgeti(L, t, ref); /* remove it from list */
+ lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
+ }
+ else { /* no free elements */
+ ref = (int)lua_objlen(L, t);
+ ref++; /* create new reference */
+ }
+ lua_rawseti(L, t, ref);
+ return ref;
+}
+
+
+LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
+ if (ref >= 0) {
+ t = abs_index(L, t);
+ lua_rawgeti(L, t, FREELIST_REF);
+ lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
+ lua_pushinteger(L, ref);
+ lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
+ }
+}
+
+
+
+/*
+** {======================================================
+** Load functions
+** =======================================================
+*/
+
+typedef struct LoadF {
+ int extraline;
+ FILE *f;
+ char buff[LUAL_BUFFERSIZE];
+} LoadF;
+
+
+static const char *getF (lua_State *L, void *ud, size_t *size) {
+ LoadF *lf = (LoadF *)ud;
+ (void)L;
+ if (lf->extraline) {
+ lf->extraline = 0;
+ *size = 1;
+ return "\n";
+ }
+ if (feof(lf->f)) return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
+ return (*size > 0) ? lf->buff : NULL;
+}
+
+
+static int errfile (lua_State *L, const char *what, int fnameindex) {
+ const char *serr = strerror(errno);
+ const char *filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+
+LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
+ LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ lf.extraline = 0;
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+ lf.f = fopen(filename, "r");
+ if (lf.f == NULL) return errfile(L, "open", fnameindex);
+ }
+ c = getc(lf.f);
+ if (c == '#') { /* Unix exec. file? */
+ lf.extraline = 1;
+ while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */
+ if (c == '\n') c = getc(lf.f);
+ }
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
+ /* skip eventual `#!...' */
+ while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
+ lf.extraline = 0;
+ }
+ ungetc(c, lf.f);
+ status = lua_load(L, getF, &lf, lua_tostring(L, -1));
+ readstatus = ferror(lf.f);
+ if (filename) fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from `lua_load' */
+ return errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+
+typedef struct LoadS {
+ const char *s;
+ size_t size;
+} LoadS;
+
+
+static const char *getS (lua_State *L, void *ud, size_t *size) {
+ LoadS *ls = (LoadS *)ud;
+ (void)L;
+ if (ls->size == 0) return NULL;
+ *size = ls->size;
+ ls->size = 0;
+ return ls->s;
+}
+
+
+LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,
+ const char *name) {
+ LoadS ls;
+ ls.s = buff;
+ ls.size = size;
+ return lua_load(L, getS, &ls, name);
+}
+
+
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {
+ return luaL_loadbuffer(L, s, strlen(s), s);
+}
+
+
+
+/* }====================================================== */
+
+
+static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+ (void)ud;
+ (void)osize;
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+}
+
+
+static int panic (lua_State *L) {
+ (void)L; /* to avoid warnings */
+ fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
+ lua_tostring(L, -1));
+ return 0;
+}
+
+
+LUALIB_API lua_State *luaL_newstate (void) {
+ lua_State *L = lua_newstate(l_alloc, NULL);
+ if (L) lua_atpanic(L, &panic);
+ return L;
+}
+
diff --git a/engines/sword25/util/lua/src/lauxlib.h b/engines/sword25/util/lua/src/lauxlib.h
new file mode 100755
index 0000000000..34258235db
--- /dev/null
+++ b/engines/sword25/util/lua/src/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#if defined(LUA_COMPAT_GETN)
+LUALIB_API int (luaL_getn) (lua_State *L, int t);
+LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
+#else
+#define luaL_getn(L,i) ((int)lua_objlen(L, i))
+#define luaL_setn(L,i,j) ((void)0) /* no op! */
+#endif
+
+#if defined(LUA_COMPAT_OPENLIB)
+#define luaI_openlib luaL_openlib
+#endif
+
+
+/* extra error code for `luaL_load' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+
+LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup);
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l);
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
+LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
+LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
+ const char *name);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
+ const char *r);
+
+LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
+ const char *fname, int szhint);
+
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_argcheck(L, cond,numarg,extramsg) \
+ ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int lvl; /* number of strings in the stack (level) */
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+/* compatibility only */
+#define luaL_putchar(B,c) luaL_addchar(B,c)
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+/* compatibility with ref system */
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
+ (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
+
+#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
+
+#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
+
+
+#define luaL_reg luaL_Reg
+
+#endif
+
+
diff --git a/engines/sword25/util/lua/src/lbaselib.c b/engines/sword25/util/lua/src/lbaselib.c
new file mode 100755
index 0000000000..20d7f62845
--- /dev/null
+++ b/engines/sword25/util/lua/src/lbaselib.c
@@ -0,0 +1,663 @@
+/*
+** $Id: lbaselib.c,v 1.191.1.4 2008/01/20 13:53:22 roberto Exp $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lbaselib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+/*
+** If your system does not support `stdout', you can just remove this function.
+** If you need, you can define your own `print' function, following this
+** model but changing `fputs' to put the strings at a proper place
+** (a console window or a log file, for instance).
+*/
+// -----------------------------------------------------------------------------
+// BS25
+// Aufruf der BS25 Log-Funktion
+// -----------------------------------------------------------------------------
+void BS_Log_C(const char* Message);
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ lua_getglobal(L, "tostring");
+ BS_Log_C("LUA: ");
+ for (i=1; i<=n; i++) {
+ const char *s;
+ lua_pushvalue(L, -1); /* function to be called */
+ lua_pushvalue(L, i); /* value to print */
+ lua_call(L, 1, 1);
+ s = lua_tostring(L, -1); /* get result */
+ if (s == NULL)
+ return luaL_error(L, LUA_QL("tostring") " must return a string to "
+ LUA_QL("print"));
+ if (i>1) BS_Log_C("\t");
+ BS_Log_C(s);
+ lua_pop(L, 1); /* pop result */
+ }
+ BS_Log_C("\n");
+ return 0;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ int base = luaL_optint(L, 2, 10);
+ if (base == 10) { /* standard conversion */
+ luaL_checkany(L, 1);
+ if (lua_isnumber(L, 1)) {
+ lua_pushnumber(L, lua_tonumber(L, 1));
+ return 1;
+ }
+ }
+ else {
+ const char *s1 = luaL_checkstring(L, 1);
+ char *s2;
+ unsigned long n;
+ luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
+ n = strtoul(s1, &s2, base);
+ if (s1 != s2) { /* at least one valid digit? */
+ while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */
+ if (*s2 == '\0') { /* no invalid trailing characters? */
+ lua_pushnumber(L, (lua_Number)n);
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L); /* else not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ int level = luaL_optint(L, 2, 1);
+ lua_settop(L, 1);
+ if (lua_isstring(L, 1) && level > 0) { /* add extra information? */
+ luaL_where(L, level);
+ lua_pushvalue(L, 1);
+ lua_concat(L, 2);
+ }
+ return lua_error(L);
+}
+
+
+static int luaB_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L);
+ return 1; /* no metatable */
+ }
+ luaL_getmetafield(L, 1, "__metatable");
+ return 1; /* returns either __metatable field (if present) or metatable */
+}
+
+
+static int luaB_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
+ "nil or table expected");
+ if (luaL_getmetafield(L, 1, "__metatable"))
+ luaL_error(L, "cannot change a protected metatable");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1;
+}
+
+
+static void getfunc (lua_State *L, int opt) {
+ if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
+ else {
+ lua_Debug ar;
+ int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
+ luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
+ if (lua_getstack(L, level, &ar) == 0)
+ luaL_argerror(L, 1, "invalid level");
+ lua_getinfo(L, "f", &ar);
+ if (lua_isnil(L, -1))
+ luaL_error(L, "no function environment for tail call at level %d",
+ level);
+ }
+}
+
+
+static int luaB_getfenv (lua_State *L) {
+ getfunc(L, 1);
+ if (lua_iscfunction(L, -1)) /* is a C function? */
+ lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
+ else
+ lua_getfenv(L, -1);
+ return 1;
+}
+
+
+static int luaB_setfenv (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ getfunc(L, 0);
+ lua_pushvalue(L, 2);
+ if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {
+ /* change environment of current thread */
+ lua_pushthread(L);
+ lua_insert(L, -2);
+ lua_setfenv(L, -2);
+ return 0;
+ }
+ else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
+ luaL_error(L,
+ LUA_QL("setfenv") " cannot change environment of given object");
+ return 1;
+}
+
+
+static int luaB_rawequal (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_checkany(L, 2);
+ lua_pushboolean(L, lua_rawequal(L, 1, 2));
+ return 1;
+}
+
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_rawget(L, 1);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_settop(L, 3);
+ lua_rawset(L, 1);
+ return 1;
+}
+
+
+static int luaB_gcinfo (lua_State *L) {
+ lua_pushinteger(L, lua_getgccount(L));
+ return 1;
+}
+
+
+static int luaB_collectgarbage (lua_State *L) {
+ static const char *const opts[] = {"stop", "restart", "collect",
+ "count", "step", "setpause", "setstepmul", NULL};
+ static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
+ LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};
+ int o = luaL_checkoption(L, 1, "collect", opts);
+ int ex = luaL_optint(L, 2, 0);
+ int res = lua_gc(L, optsnum[o], ex);
+ switch (optsnum[o]) {
+ case LUA_GCCOUNT: {
+ int b = lua_gc(L, LUA_GCCOUNTB, 0);
+ lua_pushnumber(L, res + ((lua_Number)b/1024));
+ return 1;
+ }
+ case LUA_GCSTEP: {
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ default: {
+ lua_pushnumber(L, res);
+ return 1;
+ }
+ }
+}
+
+
+static int luaB_type (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushstring(L, luaL_typename(L, 1));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int luaB_pairs (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushnil(L); /* and initial value */
+ return 3;
+}
+
+
+static int ipairsaux (lua_State *L) {
+ int i = luaL_checkint(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i++; /* next value */
+ lua_pushinteger(L, i);
+ lua_rawgeti(L, 1, i);
+ return (lua_isnil(L, -1)) ? 0 : 2;
+}
+
+
+static int luaB_ipairs (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushinteger(L, 0); /* and initial value */
+ return 3;
+}
+
+
+static int load_aux (lua_State *L, int status) {
+ if (status == 0) /* OK? */
+ return 1;
+ else {
+ lua_pushnil(L);
+ lua_insert(L, -2); /* put before error message */
+ return 2; /* return nil plus error message */
+ }
+}
+
+
+static int luaB_loadstring (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ const char *chunkname = luaL_optstring(L, 2, s);
+ return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
+}
+
+
+static int luaB_loadfile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ return load_aux(L, luaL_loadfile(L, fname));
+}
+
+
+/*
+** Reader for generic `load' function: `lua_load' uses the
+** stack for internal stuff, so the reader cannot change the
+** stack top. Instead, it keeps its resulting string in a
+** reserved slot inside the stack.
+*/
+static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
+ (void)ud; /* to avoid warnings */
+ luaL_checkstack(L, 2, "too many nested functions");
+ lua_pushvalue(L, 1); /* get function */
+ lua_call(L, 0, 1); /* call it */
+ if (lua_isnil(L, -1)) {
+ *size = 0;
+ return NULL;
+ }
+ else if (lua_isstring(L, -1)) {
+ lua_replace(L, 3); /* save string in a reserved stack slot */
+ return lua_tolstring(L, 3, size);
+ }
+ else luaL_error(L, "reader function must return a string");
+ return NULL; /* to avoid warnings */
+}
+
+
+static int luaB_load (lua_State *L) {
+ int status;
+ const char *cname = luaL_optstring(L, 2, "=(load)");
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 3); /* function, eventual name, plus one reserved slot */
+ status = lua_load(L, generic_reader, NULL, cname);
+ return load_aux(L, status);
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ int n = lua_gettop(L);
+ if (luaL_loadfile(L, fname) != 0) lua_error(L);
+ lua_call(L, 0, LUA_MULTRET);
+ return lua_gettop(L) - n;
+}
+
+
+static int luaB_assert (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_toboolean(L, 1))
+ return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
+ return lua_gettop(L);
+}
+
+
+static int luaB_unpack (lua_State *L) {
+ int i, e, n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i = luaL_optint(L, 2, 1);
+ e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
+ n = e - i + 1; /* number of elements */
+ if (n <= 0) return 0; /* empty range */
+ luaL_checkstack(L, n, "table too big to unpack");
+ for (; i<=e; i++) /* push arg[i...e] */
+ lua_rawgeti(L, 1, i);
+ return n;
+}
+
+
+static int luaB_select (lua_State *L) {
+ int n = lua_gettop(L);
+ if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
+ lua_pushinteger(L, n-1);
+ return 1;
+ }
+ else {
+ int i = luaL_checkint(L, 1);
+ if (i < 0) i = n + i;
+ else if (i > n) i = n;
+ luaL_argcheck(L, 1 <= i, 1, "index out of range");
+ return n - i;
+ }
+}
+
+
+static int luaB_pcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 1);
+ status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
+ lua_pushboolean(L, (status == 0));
+ lua_insert(L, 1);
+ return lua_gettop(L); /* return status + all results */
+}
+
+
+static int luaB_xpcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_insert(L, 1); /* put error function under function to be called */
+ status = lua_pcall(L, 0, LUA_MULTRET, 1);
+ lua_pushboolean(L, (status == 0));
+ lua_replace(L, 1);
+ return lua_gettop(L); /* return status + all results */
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */
+ return 1; /* use its value */
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, 1));
+ break;
+ case LUA_TSTRING:
+ lua_pushvalue(L, 1);
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
+ break;
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ default:
+ lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
+ break;
+ }
+ return 1;
+}
+
+
+static int luaB_newproxy (lua_State *L) {
+ lua_settop(L, 1);
+ lua_newuserdata(L, 0); /* create proxy */
+ if (lua_toboolean(L, 1) == 0)
+ return 1; /* no metatable */
+ else if (lua_isboolean(L, 1)) {
+ lua_newtable(L); /* create a new metatable `m' ... */
+ lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */
+ lua_pushboolean(L, 1);
+ lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */
+ }
+ else {
+ int validproxy = 0; /* to check if weaktable[metatable(u)] == true */
+ if (lua_getmetatable(L, 1)) {
+ lua_rawget(L, lua_upvalueindex(1));
+ validproxy = lua_toboolean(L, -1);
+ lua_pop(L, 1); /* remove value */
+ }
+ luaL_argcheck(L, validproxy, 1, "boolean or proxy expected");
+ lua_getmetatable(L, 1); /* metatable is valid; get it */
+ }
+ lua_setmetatable(L, 2);
+ return 1;
+}
+
+
+static const luaL_Reg base_funcs[] = {
+ {"assert", luaB_assert},
+ {"collectgarbage", luaB_collectgarbage},
+ {"dofile", luaB_dofile},
+ {"error", luaB_error},
+ {"gcinfo", luaB_gcinfo},
+ {"getfenv", luaB_getfenv},
+ {"getmetatable", luaB_getmetatable},
+ {"loadfile", luaB_loadfile},
+ {"load", luaB_load},
+ {"loadstring", luaB_loadstring},
+ {"next", luaB_next},
+ {"pcall", luaB_pcall},
+ {"print", luaB_print},
+ {"rawequal", luaB_rawequal},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"select", luaB_select},
+ {"setfenv", luaB_setfenv},
+ {"setmetatable", luaB_setmetatable},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"unpack", luaB_unpack},
+ {"xpcall", luaB_xpcall},
+ {NULL, NULL}
+};
+
+
+/*
+** {======================================================
+** Coroutine library
+** =======================================================
+*/
+
+#define CO_RUN 0 /* running */
+#define CO_SUS 1 /* suspended */
+#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */
+#define CO_DEAD 3
+
+static const char *const statnames[] =
+ {"running", "suspended", "normal", "dead"};
+
+static int costatus (lua_State *L, lua_State *co) {
+ if (L == co) return CO_RUN;
+ switch (lua_status(co)) {
+ case LUA_YIELD:
+ return CO_SUS;
+ case 0: {
+ lua_Debug ar;
+ if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
+ return CO_NOR; /* it is running */
+ else if (lua_gettop(co) == 0)
+ return CO_DEAD;
+ else
+ return CO_SUS; /* initial state */
+ }
+ default: /* some error occured */
+ return CO_DEAD;
+ }
+}
+
+
+static int luaB_costatus (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ luaL_argcheck(L, co, 1, "coroutine expected");
+ lua_pushstring(L, statnames[costatus(L, co)]);
+ return 1;
+}
+
+
+static int auxresume (lua_State *L, lua_State *co, int narg) {
+ int status = costatus(L, co);
+ if (!lua_checkstack(co, narg))
+ luaL_error(L, "too many arguments to resume");
+ if (status != CO_SUS) {
+ lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
+ return -1; /* error flag */
+ }
+ lua_xmove(L, co, narg);
+ lua_setlevel(L, co);
+ status = lua_resume(co, narg);
+ if (status == 0 || status == LUA_YIELD) {
+ int nres = lua_gettop(co);
+ if (!lua_checkstack(L, nres))
+ luaL_error(L, "too many results to resume");
+ lua_xmove(co, L, nres); /* move yielded values */
+ return nres;
+ }
+ else {
+ lua_xmove(co, L, 1); /* move error message */
+ return -1; /* error flag */
+ }
+}
+
+
+static int luaB_coresume (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ int r;
+ luaL_argcheck(L, co, 1, "coroutine expected");
+ r = auxresume(L, co, lua_gettop(L) - 1);
+ if (r < 0) {
+ lua_pushboolean(L, 0);
+ lua_insert(L, -2);
+ return 2; /* return false + error message */
+ }
+ else {
+ lua_pushboolean(L, 1);
+ lua_insert(L, -(r + 1));
+ return r + 1; /* return true + `resume' returns */
+ }
+}
+
+
+static int luaB_auxwrap (lua_State *L) {
+ lua_State *co = lua_tothread(L, lua_upvalueindex(1));
+ int r = auxresume(L, co, lua_gettop(L));
+ if (r < 0) {
+ if (lua_isstring(L, -1)) { /* error object is a string? */
+ luaL_where(L, 1); /* add extra info */
+ lua_insert(L, -2);
+ lua_concat(L, 2);
+ }
+ lua_error(L); /* propagate error */
+ }
+ return r;
+}
+
+
+static int luaB_cocreate (lua_State *L) {
+ lua_State *NL = lua_newthread(L);
+ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
+ "Lua function expected");
+ lua_pushvalue(L, 1); /* move function to top */
+ lua_xmove(L, NL, 1); /* move function from L to NL */
+ return 1;
+}
+
+
+static int luaB_cowrap (lua_State *L) {
+ luaB_cocreate(L);
+ lua_pushcclosure(L, luaB_auxwrap, 1);
+ return 1;
+}
+
+
+static int luaB_yield (lua_State *L) {
+ return lua_yield(L, lua_gettop(L));
+}
+
+
+static int luaB_corunning (lua_State *L) {
+ if (lua_pushthread(L))
+ lua_pushnil(L); /* main thread is not a coroutine */
+ return 1;
+}
+
+
+static const luaL_Reg co_funcs[] = {
+ {"create", luaB_cocreate},
+ {"resume", luaB_coresume},
+ {"running", luaB_corunning},
+ {"status", luaB_costatus},
+ {"wrap", luaB_cowrap},
+ {"yield", luaB_yield},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+static void auxopen (lua_State *L, const char *name,
+ lua_CFunction f, lua_CFunction u) {
+ lua_pushcfunction(L, u);
+ /* BS25 ==== */
+ lua_pushstring(L, name);
+ lua_pushstring(L, "_next");
+ lua_concat(L, 2);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ /* ==== BS25 */
+ lua_pushcclosure(L, f, 1);
+ lua_setfield(L, -2, name);
+}
+
+
+static void base_open (lua_State *L) {
+ /* set global _G */
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_setglobal(L, "_G");
+ /* open lib into global table */
+ luaL_register(L, "_G", base_funcs);
+ lua_pushliteral(L, LUA_VERSION);
+ lua_setglobal(L, "_VERSION"); /* set global _VERSION */
+ /* `ipairs' and `pairs' need auxliliary functions as upvalues */
+ auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
+ auxopen(L, "pairs", luaB_pairs, luaB_next);
+ /* `newproxy' needs a weaktable as upvalue */
+ lua_createtable(L, 0, 1); /* new table `w' */
+ lua_pushvalue(L, -1); /* `w' will be its own metatable */
+ lua_setmetatable(L, -2);
+ lua_pushliteral(L, "kv");
+ lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
+ lua_pushcclosure(L, luaB_newproxy, 1);
+ lua_setglobal(L, "newproxy"); /* set global `newproxy' */
+}
+
+
+LUALIB_API int luaopen_base (lua_State *L) {
+ base_open(L);
+ luaL_register(L, LUA_COLIBNAME, co_funcs);
+ return 2;
+}
+
diff --git a/engines/sword25/util/lua/src/lcode.c b/engines/sword25/util/lua/src/lcode.c
new file mode 100755
index 0000000000..cff626b7fa
--- /dev/null
+++ b/engines/sword25/util/lua/src/lcode.c
@@ -0,0 +1,839 @@
+/*
+** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#define lcode_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "ltable.h"
+
+
+#define hasjumps(e) ((e)->t != (e)->f)
+
+
+static int isnumeral(expdesc *e) {
+ return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
+}
+
+
+void luaK_nil (FuncState *fs, int from, int n) {
+ Instruction *previous;
+ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
+ if (fs->pc == 0) { /* function start? */
+ if (from >= fs->nactvar)
+ return; /* positions are already clean */
+ }
+ else {
+ previous = &fs->f->code[fs->pc-1];
+ if (GET_OPCODE(*previous) == OP_LOADNIL) {
+ int pfrom = GETARG_A(*previous);
+ int pto = GETARG_B(*previous);
+ if (pfrom <= from && from <= pto+1) { /* can connect both? */
+ if (from+n-1 > pto)
+ SETARG_B(*previous, from+n-1);
+ return;
+ }
+ }
+ }
+ }
+ luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
+}
+
+
+int luaK_jump (FuncState *fs) {
+ int jpc = fs->jpc; /* save list of jumps to here */
+ int j;
+ fs->jpc = NO_JUMP;
+ j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
+ luaK_concat(fs, &j, jpc); /* keep them on hold */
+ return j;
+}
+
+
+void luaK_ret (FuncState *fs, int first, int nret) {
+ luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
+}
+
+
+static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
+ luaK_codeABC(fs, op, A, B, C);
+ return luaK_jump(fs);
+}
+
+
+static void fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest-(pc+1);
+ lua_assert(dest != NO_JUMP);
+ if (abs(offset) > MAXARG_sBx)
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ SETARG_sBx(*jmp, offset);
+}
+
+
+/*
+** returns current `pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+*/
+int luaK_getlabel (FuncState *fs) {
+ fs->lasttarget = fs->pc;
+ return fs->pc;
+}
+
+
+static int getjump (FuncState *fs, int pc) {
+ int offset = GETARG_sBx(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+static Instruction *getjumpcontrol (FuncState *fs, int pc) {
+ Instruction *pi = &fs->f->code[pc];
+ if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
+ return pi-1;
+ else
+ return pi;
+}
+
+
+/*
+** check whether list has any jump that do not produce a value
+** (or produce an inverted value)
+*/
+static int need_value (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list)) {
+ Instruction i = *getjumpcontrol(fs, list);
+ if (GET_OPCODE(i) != OP_TESTSET) return 1;
+ }
+ return 0; /* not found */
+}
+
+
+static int patchtestreg (FuncState *fs, int node, int reg) {
+ Instruction *i = getjumpcontrol(fs, node);
+ if (GET_OPCODE(*i) != OP_TESTSET)
+ return 0; /* cannot patch other instructions */
+ if (reg != NO_REG && reg != GETARG_B(*i))
+ SETARG_A(*i, reg);
+ else /* no register to put value or register already has the value */
+ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
+
+ return 1;
+}
+
+
+static void removevalues (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list))
+ patchtestreg(fs, list, NO_REG);
+}
+
+
+static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
+ int dtarget) {
+ while (list != NO_JUMP) {
+ int next = getjump(fs, list);
+ if (patchtestreg(fs, list, reg))
+ fixjump(fs, list, vtarget);
+ else
+ fixjump(fs, list, dtarget); /* jump to default target */
+ list = next;
+ }
+}
+
+
+static void dischargejpc (FuncState *fs) {
+ patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
+ fs->jpc = NO_JUMP;
+}
+
+
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ if (target == fs->pc)
+ luaK_patchtohere(fs, list);
+ else {
+ lua_assert(target < fs->pc);
+ patchlistaux(fs, list, target, NO_REG, target);
+ }
+}
+
+
+void luaK_patchtohere (FuncState *fs, int list) {
+ luaK_getlabel(fs);
+ luaK_concat(fs, &fs->jpc, list);
+}
+
+
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (l2 == NO_JUMP) return;
+ else if (*l1 == NO_JUMP)
+ *l1 = l2;
+ else {
+ int list = *l1;
+ int next;
+ while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
+ list = next;
+ fixjump(fs, list, l2);
+ }
+}
+
+
+void luaK_checkstack (FuncState *fs, int n) {
+ int newstack = fs->freereg + n;
+ if (newstack > fs->f->maxstacksize) {
+ if (newstack >= MAXSTACK)
+ luaX_syntaxerror(fs->ls, "function or expression too complex");
+ fs->f->maxstacksize = cast_byte(newstack);
+ }
+}
+
+
+void luaK_reserveregs (FuncState *fs, int n) {
+ luaK_checkstack(fs, n);
+ fs->freereg += n;
+}
+
+
+static void freereg (FuncState *fs, int reg) {
+ if (!ISK(reg) && reg >= fs->nactvar) {
+ fs->freereg--;
+ lua_assert(reg == fs->freereg);
+ }
+}
+
+
+static void freeexp (FuncState *fs, expdesc *e) {
+ if (e->k == VNONRELOC)
+ freereg(fs, e->u.s.info);
+}
+
+
+static int addk (FuncState *fs, TValue *k, TValue *v) {
+ lua_State *L = fs->L;
+ TValue *idx = luaH_set(L, fs->h, k);
+ Proto *f = fs->f;
+ int oldsize = f->sizek;
+ if (ttisnumber(idx)) {
+ lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
+ return cast_int(nvalue(idx));
+ }
+ else { /* constant not found; create a new entry */
+ setnvalue(idx, cast_num(fs->nk));
+ luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
+ MAXARG_Bx, "constant table overflow");
+ while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
+ setobj(L, &f->k[fs->nk], v);
+ luaC_barrier(L, f, v);
+ return fs->nk++;
+ }
+}
+
+
+int luaK_stringK (FuncState *fs, TString *s) {
+ TValue o;
+ setsvalue(fs->L, &o, s);
+ return addk(fs, &o, &o);
+}
+
+
+int luaK_numberK (FuncState *fs, lua_Number r) {
+ TValue o;
+ setnvalue(&o, r);
+ return addk(fs, &o, &o);
+}
+
+
+static int boolK (FuncState *fs, int b) {
+ TValue o;
+ setbvalue(&o, b);
+ return addk(fs, &o, &o);
+}
+
+
+static int nilK (FuncState *fs) {
+ TValue k, v;
+ setnilvalue(&v);
+ /* cannot use nil as key; instead use table itself to represent nil */
+ sethvalue(fs->L, &k, fs->h);
+ return addk(fs, &k, &v);
+}
+
+
+void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ SETARG_C(getcode(fs, e), nresults+1);
+ }
+ else if (e->k == VVARARG) {
+ SETARG_B(getcode(fs, e), nresults+1);
+ SETARG_A(getcode(fs, e), fs->freereg);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+void luaK_setoneret (FuncState *fs, expdesc *e) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ e->k = VNONRELOC;
+ e->u.s.info = GETARG_A(getcode(fs, e));
+ }
+ else if (e->k == VVARARG) {
+ SETARG_B(getcode(fs, e), 2);
+ e->k = VRELOCABLE; /* can relocate its simple result */
+ }
+}
+
+
+void luaK_dischargevars (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VLOCAL: {
+ e->k = VNONRELOC;
+ break;
+ }
+ case VUPVAL: {
+ e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VGLOBAL: {
+ e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VINDEXED: {
+ freereg(fs, e->u.s.aux);
+ freereg(fs, e->u.s.info);
+ e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VVARARG:
+ case VCALL: {
+ luaK_setoneret(fs, e);
+ break;
+ }
+ default: break; /* there is one value available (somewhere) */
+ }
+}
+
+
+static int code_label (FuncState *fs, int A, int b, int jump) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
+}
+
+
+static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: {
+ luaK_nil(fs, reg, 1);
+ break;
+ }
+ case VFALSE: case VTRUE: {
+ luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
+ break;
+ }
+ case VK: {
+ luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
+ break;
+ }
+ case VKNUM: {
+ luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
+ break;
+ }
+ case VRELOCABLE: {
+ Instruction *pc = &getcode(fs, e);
+ SETARG_A(*pc, reg);
+ break;
+ }
+ case VNONRELOC: {
+ if (reg != e->u.s.info)
+ luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
+ break;
+ }
+ default: {
+ lua_assert(e->k == VVOID || e->k == VJMP);
+ return; /* nothing to do... */
+ }
+ }
+ e->u.s.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+static void discharge2anyreg (FuncState *fs, expdesc *e) {
+ if (e->k != VNONRELOC) {
+ luaK_reserveregs(fs, 1);
+ discharge2reg(fs, e, fs->freereg-1);
+ }
+}
+
+
+static void exp2reg (FuncState *fs, expdesc *e, int reg) {
+ discharge2reg(fs, e, reg);
+ if (e->k == VJMP)
+ luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
+ if (hasjumps(e)) {
+ int final; /* position after whole expression */
+ int p_f = NO_JUMP; /* position of an eventual LOAD false */
+ int p_t = NO_JUMP; /* position of an eventual LOAD true */
+ if (need_value(fs, e->t) || need_value(fs, e->f)) {
+ int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
+ p_f = code_label(fs, reg, 0, 1);
+ p_t = code_label(fs, reg, 1, 0);
+ luaK_patchtohere(fs, fj);
+ }
+ final = luaK_getlabel(fs);
+ patchlistaux(fs, e->f, final, reg, p_f);
+ patchlistaux(fs, e->t, final, reg, p_t);
+ }
+ e->f = e->t = NO_JUMP;
+ e->u.s.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ freeexp(fs, e);
+ luaK_reserveregs(fs, 1);
+ exp2reg(fs, e, fs->freereg - 1);
+}
+
+
+int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ if (e->k == VNONRELOC) {
+ if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
+ if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
+ exp2reg(fs, e, e->u.s.info); /* put value on it */
+ return e->u.s.info;
+ }
+ }
+ luaK_exp2nextreg(fs, e); /* default */
+ return e->u.s.info;
+}
+
+
+void luaK_exp2val (FuncState *fs, expdesc *e) {
+ if (hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+ else
+ luaK_dischargevars(fs, e);
+}
+
+
+int luaK_exp2RK (FuncState *fs, expdesc *e) {
+ luaK_exp2val(fs, e);
+ switch (e->k) {
+ case VKNUM:
+ case VTRUE:
+ case VFALSE:
+ case VNIL: {
+ if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
+ e->u.s.info = (e->k == VNIL) ? nilK(fs) :
+ (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
+ boolK(fs, (e->k == VTRUE));
+ e->k = VK;
+ return RKASK(e->u.s.info);
+ }
+ else break;
+ }
+ case VK: {
+ if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
+ return RKASK(e->u.s.info);
+ else break;
+ }
+ default: break;
+ }
+ /* not a constant in the right range: put it in a register */
+ return luaK_exp2anyreg(fs, e);
+}
+
+
+void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
+ switch (var->k) {
+ case VLOCAL: {
+ freeexp(fs, ex);
+ exp2reg(fs, ex, var->u.s.info);
+ return;
+ }
+ case VUPVAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
+ break;
+ }
+ case VGLOBAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
+ break;
+ }
+ case VINDEXED: {
+ int e = luaK_exp2RK(fs, ex);
+ luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
+ break;
+ }
+ default: {
+ lua_assert(0); /* invalid var kind to store */
+ break;
+ }
+ }
+ freeexp(fs, ex);
+}
+
+
+void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
+ int func;
+ luaK_exp2anyreg(fs, e);
+ freeexp(fs, e);
+ func = fs->freereg;
+ luaK_reserveregs(fs, 2);
+ luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
+ freeexp(fs, key);
+ e->u.s.info = func;
+ e->k = VNONRELOC;
+}
+
+
+static void invertjump (FuncState *fs, expdesc *e) {
+ Instruction *pc = getjumpcontrol(fs, e->u.s.info);
+ lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+ GET_OPCODE(*pc) != OP_TEST);
+ SETARG_A(*pc, !(GETARG_A(*pc)));
+}
+
+
+static int jumponcond (FuncState *fs, expdesc *e, int cond) {
+ if (e->k == VRELOCABLE) {
+ Instruction ie = getcode(fs, e);
+ if (GET_OPCODE(ie) == OP_NOT) {
+ fs->pc--; /* remove previous OP_NOT */
+ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
+}
+
+
+void luaK_goiftrue (FuncState *fs, expdesc *e) {
+ int pc; /* pc of last jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VK: case VKNUM: case VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ case VFALSE: {
+ pc = luaK_jump(fs); /* always jump */
+ break;
+ }
+ case VJMP: {
+ invertjump(fs, e);
+ pc = e->u.s.info;
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 0);
+ break;
+ }
+ }
+ luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
+ luaK_patchtohere(fs, e->t);
+ e->t = NO_JUMP;
+}
+
+
+static void luaK_goiffalse (FuncState *fs, expdesc *e) {
+ int pc; /* pc of last jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ case VTRUE: {
+ pc = luaK_jump(fs); /* always jump */
+ break;
+ }
+ case VJMP: {
+ pc = e->u.s.info;
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 1);
+ break;
+ }
+ }
+ luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
+ luaK_patchtohere(fs, e->f);
+ e->f = NO_JUMP;
+}
+
+
+static void codenot (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ e->k = VTRUE;
+ break;
+ }
+ case VK: case VKNUM: case VTRUE: {
+ e->k = VFALSE;
+ break;
+ }
+ case VJMP: {
+ invertjump(fs, e);
+ break;
+ }
+ case VRELOCABLE:
+ case VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
+ e->k = VRELOCABLE;
+ break;
+ }
+ default: {
+ lua_assert(0); /* cannot happen */
+ break;
+ }
+ }
+ /* interchange true and false lists */
+ { int temp = e->f; e->f = e->t; e->t = temp; }
+ removevalues(fs, e->f);
+ removevalues(fs, e->t);
+}
+
+
+void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
+ t->u.s.aux = luaK_exp2RK(fs, k);
+ t->k = VINDEXED;
+}
+
+
+static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
+ lua_Number v1, v2, r;
+ if (!isnumeral(e1) || !isnumeral(e2)) return 0;
+ v1 = e1->u.nval;
+ v2 = e2->u.nval;
+ switch (op) {
+ case OP_ADD: r = luai_numadd(v1, v2); break;
+ case OP_SUB: r = luai_numsub(v1, v2); break;
+ case OP_MUL: r = luai_nummul(v1, v2); break;
+ case OP_DIV:
+ if (v2 == 0) return 0; /* do not attempt to divide by 0 */
+ r = luai_numdiv(v1, v2); break;
+ case OP_MOD:
+ if (v2 == 0) return 0; /* do not attempt to divide by 0 */
+ r = luai_nummod(v1, v2); break;
+ case OP_POW: r = luai_numpow(v1, v2); break;
+ case OP_UNM: r = luai_numunm(v1); break;
+ case OP_LEN: return 0; /* no constant folding for 'len' */
+ default: lua_assert(0); r = 0; break;
+ }
+ if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
+ e1->u.nval = r;
+ return 1;
+}
+
+
+static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
+ if (constfolding(op, e1, e2))
+ return;
+ else {
+ int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
+ int o1 = luaK_exp2RK(fs, e1);
+ if (o1 > o2) {
+ freeexp(fs, e1);
+ freeexp(fs, e2);
+ }
+ else {
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ }
+ e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
+ e1->k = VRELOCABLE;
+ }
+}
+
+
+static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
+ expdesc *e2) {
+ int o1 = luaK_exp2RK(fs, e1);
+ int o2 = luaK_exp2RK(fs, e2);
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ if (cond == 0 && op != OP_EQ) {
+ int temp; /* exchange args to replace by `<' or `<=' */
+ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
+ cond = 1;
+ }
+ e1->u.s.info = condjump(fs, op, cond, o1, o2);
+ e1->k = VJMP;
+}
+
+
+void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
+ expdesc e2;
+ e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
+ switch (op) {
+ case OPR_MINUS: {
+ if (!isnumeral(e))
+ luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
+ codearith(fs, OP_UNM, e, &e2);
+ break;
+ }
+ case OPR_NOT: codenot(fs, e); break;
+ case OPR_LEN: {
+ luaK_exp2anyreg(fs, e); /* cannot operate on constants */
+ codearith(fs, OP_LEN, e, &e2);
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
+ switch (op) {
+ case OPR_AND: {
+ luaK_goiftrue(fs, v);
+ break;
+ }
+ case OPR_OR: {
+ luaK_goiffalse(fs, v);
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
+ break;
+ }
+ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+ case OPR_MOD: case OPR_POW: {
+ if (!isnumeral(v)) luaK_exp2RK(fs, v);
+ break;
+ }
+ default: {
+ luaK_exp2RK(fs, v);
+ break;
+ }
+ }
+}
+
+
+void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
+ switch (op) {
+ case OPR_AND: {
+ lua_assert(e1->t == NO_JUMP); /* list must be closed */
+ luaK_dischargevars(fs, e2);
+ luaK_concat(fs, &e2->f, e1->f);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_OR: {
+ lua_assert(e1->f == NO_JUMP); /* list must be closed */
+ luaK_dischargevars(fs, e2);
+ luaK_concat(fs, &e2->t, e1->t);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2val(fs, e2);
+ if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
+ lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
+ freeexp(fs, e1);
+ SETARG_B(getcode(fs, e2), e1->u.s.info);
+ e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
+ }
+ else {
+ luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
+ codearith(fs, OP_CONCAT, e1, e2);
+ }
+ break;
+ }
+ case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
+ case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
+ case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
+ case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
+ case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
+ case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
+ case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
+ case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
+ case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
+ case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
+ case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
+ case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
+ default: lua_assert(0);
+ }
+}
+
+
+void luaK_fixline (FuncState *fs, int line) {
+ fs->f->lineinfo[fs->pc - 1] = line;
+}
+
+
+static int luaK_code (FuncState *fs, Instruction i, int line) {
+ Proto *f = fs->f;
+ dischargejpc(fs); /* `pc' will change */
+ /* put new instruction in code array */
+ luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
+ MAX_INT, "code size overflow");
+ f->code[fs->pc] = i;
+ /* save corresponding line information */
+ luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
+ MAX_INT, "code size overflow");
+ f->lineinfo[fs->pc] = line;
+ return fs->pc++;
+}
+
+
+int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
+ lua_assert(getOpMode(o) == iABC);
+ lua_assert(getBMode(o) != OpArgN || b == 0);
+ lua_assert(getCMode(o) != OpArgN || c == 0);
+ return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
+}
+
+
+int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
+ lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
+ lua_assert(getCMode(o) == OpArgN);
+ return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
+}
+
+
+void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
+ int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
+ int b = (tostore == LUA_MULTRET) ? 0 : tostore;
+ lua_assert(tostore != 0);
+ if (c <= MAXARG_C)
+ luaK_codeABC(fs, OP_SETLIST, base, b, c);
+ else {
+ luaK_codeABC(fs, OP_SETLIST, base, b, 0);
+ luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
+ }
+ fs->freereg = base + 1; /* free registers with list values */
+}
+
diff --git a/engines/sword25/util/lua/src/lcode.h b/engines/sword25/util/lua/src/lcode.h
new file mode 100755
index 0000000000..b941c60721
--- /dev/null
+++ b/engines/sword25/util/lua/src/lcode.h
@@ -0,0 +1,76 @@
+/*
+** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums
+*/
+typedef enum BinOpr {
+ OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
+ OPR_CONCAT,
+ OPR_NE, OPR_EQ,
+ OPR_LT, OPR_LE, OPR_GT, OPR_GE,
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+
+typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
+
+
+#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info])
+
+#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
+
+#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
+
+LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
+LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
+LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
+LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
+LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
+LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
+LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
+LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);
+LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
+LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
+LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
+LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
+LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_jump (FuncState *fs);
+LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
+LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
+LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
+LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
+LUAI_FUNC int luaK_getlabel (FuncState *fs);
+LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);
+LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
+LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);
+LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/ldblib.c b/engines/sword25/util/lua/src/ldblib.c
new file mode 100755
index 0000000000..67de1222a9
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldblib.c
@@ -0,0 +1,397 @@
+/*
+** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static int db_getregistry (lua_State *L) {
+ lua_pushvalue(L, LUA_REGISTRYINDEX);
+ return 1;
+}
+
+
+static int db_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+}
+
+
+static int db_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
+ "nil or table expected");
+ lua_settop(L, 2);
+ lua_pushboolean(L, lua_setmetatable(L, 1));
+ return 1;
+}
+
+
+static int db_getfenv (lua_State *L) {
+ lua_getfenv(L, 1);
+ return 1;
+}
+
+
+static int db_setfenv (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ lua_settop(L, 2);
+ if (lua_setfenv(L, 1) == 0)
+ luaL_error(L, LUA_QL("setfenv")
+ " cannot change environment of given object");
+ return 1;
+}
+
+
+static void settabss (lua_State *L, const char *i, const char *v) {
+ lua_pushstring(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static void settabsi (lua_State *L, const char *i, int v) {
+ lua_pushinteger(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ }
+ else {
+ *arg = 0;
+ return L;
+ }
+}
+
+
+static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
+ if (L == L1) {
+ lua_pushvalue(L, -2);
+ lua_remove(L, -3);
+ }
+ else
+ lua_xmove(L1, L, 1);
+ lua_setfield(L, -2, fname);
+}
+
+
+static int db_getinfo (lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *options = luaL_optstring(L, arg+2, "flnSu");
+ if (lua_isnumber(L, arg+1)) {
+ if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
+ lua_pushnil(L); /* level out of range */
+ return 1;
+ }
+ }
+ else if (lua_isfunction(L, arg+1)) {
+ lua_pushfstring(L, ">%s", options);
+ options = lua_tostring(L, -1);
+ lua_pushvalue(L, arg+1);
+ lua_xmove(L, L1, 1);
+ }
+ else
+ return luaL_argerror(L, arg+1, "function or level expected");
+ if (!lua_getinfo(L1, options, &ar))
+ return luaL_argerror(L, arg+2, "invalid option");
+ lua_createtable(L, 0, 2);
+ if (strchr(options, 'S')) {
+ settabss(L, "source", ar.source);
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabsi(L, "lastlinedefined", ar.lastlinedefined);
+ settabss(L, "what", ar.what);
+ }
+ if (strchr(options, 'l'))
+ settabsi(L, "currentline", ar.currentline);
+ if (strchr(options, 'u'))
+ settabsi(L, "nups", ar.nups);
+ if (strchr(options, 'n')) {
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ }
+ if (strchr(options, 'L'))
+ treatstackoption(L, L1, "activelines");
+ if (strchr(options, 'f'))
+ treatstackoption(L, L1, "func");
+ return 1; /* return table */
+}
+
+
+static int db_getlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ const char *name;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
+ if (name) {
+ lua_xmove(L1, L, 1);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int db_setlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ luaL_checkany(L, arg+3);
+ lua_settop(L, arg+3);
+ lua_xmove(L, L1, 1);
+ lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
+ return 1;
+}
+
+
+static int auxupvalue (lua_State *L, int get) {
+ const char *name;
+ int n = luaL_checkint(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
+ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+ if (name == NULL) return 0;
+ lua_pushstring(L, name);
+ lua_insert(L, -(get+1));
+ return get + 1;
+}
+
+
+static int db_getupvalue (lua_State *L) {
+ return auxupvalue(L, 1);
+}
+
+
+static int db_setupvalue (lua_State *L) {
+ luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+}
+
+
+
+static const char KEY_HOOK = 'h';
+
+
+static void hookf (lua_State *L, lua_Debug *ar) {
+ static const char *const hooknames[] =
+ {"call", "return", "line", "count", "tail return"};
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushlightuserdata(L, L);
+ lua_rawget(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushstring(L, hooknames[(int)ar->event]);
+ if (ar->currentline >= 0)
+ lua_pushinteger(L, ar->currentline);
+ else lua_pushnil(L);
+ lua_assert(lua_getinfo(L, "lS", ar));
+ lua_call(L, 2, 0);
+ }
+}
+
+
+static int makemask (const char *smask, int count) {
+ int mask = 0;
+ if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
+ if (strchr(smask, 'r')) mask |= LUA_MASKRET;
+ if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
+ if (count > 0) mask |= LUA_MASKCOUNT;
+ return mask;
+}
+
+
+static char *unmakemask (int mask, char *smask) {
+ int i = 0;
+ if (mask & LUA_MASKCALL) smask[i++] = 'c';
+ if (mask & LUA_MASKRET) smask[i++] = 'r';
+ if (mask & LUA_MASKLINE) smask[i++] = 'l';
+ smask[i] = '\0';
+ return smask;
+}
+
+
+static void gethooktable (lua_State *L) {
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 1);
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+}
+
+
+static int db_sethook (lua_State *L) {
+ int arg, mask, count;
+ lua_Hook func;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_isnoneornil(L, arg+1)) {
+ lua_settop(L, arg+1);
+ func = NULL; mask = 0; count = 0; /* turn off hooks */
+ }
+ else {
+ const char *smask = luaL_checkstring(L, arg+2);
+ luaL_checktype(L, arg+1, LUA_TFUNCTION);
+ count = luaL_optint(L, arg+3, 0);
+ func = hookf; mask = makemask(smask, count);
+ }
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_pushvalue(L, arg+1);
+ lua_rawset(L, -3); /* set new hook */
+ lua_pop(L, 1); /* remove hook table */
+ lua_sethook(L1, func, mask, count); /* set hooks */
+ return 0;
+}
+
+
+static int db_gethook (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ char buff[5];
+ int mask = lua_gethookmask(L1);
+ lua_Hook hook = lua_gethook(L1);
+ if (hook != NULL && hook != hookf) /* external hook? */
+ lua_pushliteral(L, "external hook");
+ else {
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_rawget(L, -2); /* get hook */
+ lua_remove(L, -2); /* remove hook table */
+ }
+ lua_pushstring(L, unmakemask(mask, buff));
+ lua_pushinteger(L, lua_gethookcount(L1));
+ return 3;
+}
+
+
+static int db_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ fputs("lua_debug> ", stderr);
+ if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
+ lua_pcall(L, 0, 0, 0)) {
+ fputs(lua_tostring(L, -1), stderr);
+ fputs("\n", stderr);
+ }
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+#define LEVELS1 12 /* size of the first part of the stack */
+#define LEVELS2 10 /* size of the second part of the stack */
+
+static int db_errorfb (lua_State *L) {
+ int level;
+ int firstpart = 1; /* still before eventual `...' */
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (lua_isnumber(L, arg+2)) {
+ level = (int)lua_tointeger(L, arg+2);
+ lua_pop(L, 1);
+ }
+ else
+ level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
+ if (lua_gettop(L) == arg)
+ lua_pushliteral(L, "");
+ else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
+ else lua_pushliteral(L, "\n");
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level > LEVELS1 && firstpart) {
+ /* no more than `LEVELS2' more levels? */
+ if (!lua_getstack(L1, level+LEVELS2, &ar))
+ level--; /* keep going */
+ else {
+ lua_pushliteral(L, "\n\t..."); /* too many levels */
+ while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
+ level++;
+ }
+ firstpart = 0;
+ continue;
+ }
+ lua_pushliteral(L, "\n\t");
+ lua_getinfo(L1, "Snl", &ar);
+ lua_pushfstring(L, "%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ if (*ar.namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, " in function " LUA_QS, ar.name);
+ else {
+ if (*ar.what == 'm') /* main? */
+ lua_pushfstring(L, " in main chunk");
+ else if (*ar.what == 'C' || *ar.what == 't')
+ lua_pushliteral(L, " ?"); /* C function or tail call */
+ else
+ lua_pushfstring(L, " in function <%s:%d>",
+ ar.short_src, ar.linedefined);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ return 1;
+}
+
+
+static const luaL_Reg dblib[] = {
+ {"debug", db_debug},
+ {"getfenv", db_getfenv},
+ {"gethook", db_gethook},
+ {"getinfo", db_getinfo},
+ {"getlocal", db_getlocal},
+ {"getregistry", db_getregistry},
+ {"getmetatable", db_getmetatable},
+ {"getupvalue", db_getupvalue},
+ {"setfenv", db_setfenv},
+ {"sethook", db_sethook},
+ {"setlocal", db_setlocal},
+ {"setmetatable", db_setmetatable},
+ {"setupvalue", db_setupvalue},
+ {"traceback", db_errorfb},
+ {NULL, NULL}
+};
+
+
+LUALIB_API int luaopen_debug (lua_State *L) {
+ luaL_register(L, LUA_DBLIBNAME, dblib);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/ldebug.c b/engines/sword25/util/lua/src/ldebug.c
new file mode 100755
index 0000000000..9eac4a9b41
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldebug.c
@@ -0,0 +1,622 @@
+/*
+** $Id: ldebug.c,v 2.29.1.3 2007/12/28 15:32:23 roberto Exp $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+
+#define ldebug_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
+
+
+static int currentpc (lua_State *L, CallInfo *ci) {
+ if (!isLua(ci)) return -1; /* function is not a Lua function? */
+ if (ci == L->ci)
+ ci->savedpc = L->savedpc;
+ return pcRel(ci->savedpc, ci_func(ci)->l.p);
+}
+
+
+static int currentline (lua_State *L, CallInfo *ci) {
+ int pc = currentpc(L, ci);
+ if (pc < 0)
+ return -1; /* only active lua functions have current-line information */
+ else
+ return getline(ci_func(ci)->l.p, pc);
+}
+
+
+/*
+** this function can be called asynchronous (e.g. during a signal)
+*/
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
+ if (func == NULL || mask == 0) { /* turn off hooks? */
+ mask = 0;
+ func = NULL;
+ }
+ L->hook = func;
+ L->basehookcount = count;
+ resethookcount(L);
+ L->hookmask = cast_byte(mask);
+ return 1;
+}
+
+
+LUA_API lua_Hook lua_gethook (lua_State *L) {
+ return L->hook;
+}
+
+
+LUA_API int lua_gethookmask (lua_State *L) {
+ return L->hookmask;
+}
+
+
+LUA_API int lua_gethookcount (lua_State *L) {
+ return L->basehookcount;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ int status;
+ CallInfo *ci;
+ lua_lock(L);
+ for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {
+ level--;
+ if (f_isLua(ci)) /* Lua function? */
+ level -= ci->tailcalls; /* skip lost tail calls */
+ }
+ if (level == 0 && ci > L->base_ci) { /* level found? */
+ status = 1;
+ ar->i_ci = cast_int(ci - L->base_ci);
+ }
+ else if (level < 0) { /* level is of a lost tail call? */
+ status = 1;
+ ar->i_ci = 0;
+ }
+ else status = 0; /* no such level */
+ lua_unlock(L);
+ return status;
+}
+
+
+static Proto *getluaproto (CallInfo *ci) {
+ return (isLua(ci) ? ci_func(ci)->l.p : NULL);
+}
+
+
+static const char *findlocal (lua_State *L, CallInfo *ci, int n) {
+ const char *name;
+ Proto *fp = getluaproto(ci);
+ if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)
+ return name; /* is a local variable in a Lua function */
+ else {
+ StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;
+ if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */
+ return "(*temporary)";
+ else
+ return NULL;
+ }
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ CallInfo *ci = L->base_ci + ar->i_ci;
+ const char *name = findlocal(L, ci, n);
+ lua_lock(L);
+ if (name)
+ luaA_pushobject(L, ci->base + (n - 1));
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ CallInfo *ci = L->base_ci + ar->i_ci;
+ const char *name = findlocal(L, ci, n);
+ lua_lock(L);
+ if (name)
+ setobjs2s(L, ci->base + (n - 1), L->top - 1);
+ L->top--; /* pop value */
+ lua_unlock(L);
+ return name;
+}
+
+
+static void funcinfo (lua_Debug *ar, Closure *cl) {
+ if (cl->c.isC) {
+ ar->source = "=[C]";
+ ar->linedefined = -1;
+ ar->lastlinedefined = -1;
+ ar->what = "C";
+ }
+ else {
+ ar->source = getstr(cl->l.p->source);
+ ar->linedefined = cl->l.p->linedefined;
+ ar->lastlinedefined = cl->l.p->lastlinedefined;
+ ar->what = (ar->linedefined == 0) ? "main" : "Lua";
+ }
+ luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
+}
+
+
+static void info_tailcall (lua_Debug *ar) {
+ ar->name = ar->namewhat = "";
+ ar->what = "tail";
+ ar->lastlinedefined = ar->linedefined = ar->currentline = -1;
+ ar->source = "=(tail call)";
+ luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
+ ar->nups = 0;
+}
+
+
+static void collectvalidlines (lua_State *L, Closure *f) {
+ if (f == NULL || f->c.isC) {
+ setnilvalue(L->top);
+ }
+ else {
+ Table *t = luaH_new(L, 0, 0);
+ int *lineinfo = f->l.p->lineinfo;
+ int i;
+ for (i=0; i<f->l.p->sizelineinfo; i++)
+ setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);
+ sethvalue(L, L->top, t);
+ }
+ incr_top(L);
+}
+
+
+static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
+ Closure *f, CallInfo *ci) {
+ int status = 1;
+ if (f == NULL) {
+ info_tailcall(ar);
+ return status;
+ }
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(ar, f);
+ break;
+ }
+ case 'l': {
+ ar->currentline = (ci) ? currentline(L, ci) : -1;
+ break;
+ }
+ case 'u': {
+ ar->nups = f->c.nupvalues;
+ break;
+ }
+ case 'n': {
+ ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;
+ if (ar->namewhat == NULL) {
+ ar->namewhat = ""; /* not found */
+ ar->name = NULL;
+ }
+ break;
+ }
+ case 'L':
+ case 'f': /* handled by lua_getinfo */
+ break;
+ default: status = 0; /* invalid option */
+ }
+ }
+ return status;
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ int status;
+ Closure *f = NULL;
+ CallInfo *ci = NULL;
+ lua_lock(L);
+ if (*what == '>') {
+ StkId func = L->top - 1;
+ luai_apicheck(L, ttisfunction(func));
+ what++; /* skip the '>' */
+ f = clvalue(func);
+ L->top--; /* pop function */
+ }
+ else if (ar->i_ci != 0) { /* no tail call? */
+ ci = L->base_ci + ar->i_ci;
+ lua_assert(ttisfunction(ci->func));
+ f = clvalue(ci->func);
+ }
+ status = auxgetinfo(L, what, ar, f, ci);
+ if (strchr(what, 'f')) {
+ if (f == NULL) setnilvalue(L->top);
+ else setclvalue(L, L->top, f);
+ incr_top(L);
+ }
+ if (strchr(what, 'L'))
+ collectvalidlines(L, f);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution and code checker
+** =======================================================
+*/
+
+#define check(x) if (!(x)) return 0;
+
+#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode)
+
+#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize)
+
+
+
+static int precheck (const Proto *pt) {
+ check(pt->maxstacksize <= MAXSTACK);
+ lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
+ lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) ||
+ (pt->is_vararg & VARARG_HASARG));
+ check(pt->sizeupvalues <= pt->nups);
+ check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
+ check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
+ return 1;
+}
+
+
+#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1])
+
+int luaG_checkopenop (Instruction i) {
+ switch (GET_OPCODE(i)) {
+ case OP_CALL:
+ case OP_TAILCALL:
+ case OP_RETURN:
+ case OP_SETLIST: {
+ check(GETARG_B(i) == 0);
+ return 1;
+ }
+ default: return 0; /* invalid instruction after an open call */
+ }
+}
+
+
+static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {
+ switch (mode) {
+ case OpArgN: check(r == 0); break;
+ case OpArgU: break;
+ case OpArgR: checkreg(pt, r); break;
+ case OpArgK:
+ check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);
+ break;
+ }
+ return 1;
+}
+
+
+static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
+ int pc;
+ int last; /* stores position of last instruction that changed `reg' */
+ last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
+ check(precheck(pt));
+ for (pc = 0; pc < lastpc; pc++) {
+ Instruction i = pt->code[pc];
+ OpCode op = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int b = 0;
+ int c = 0;
+ check(op < NUM_OPCODES);
+ checkreg(pt, a);
+ switch (getOpMode(op)) {
+ case iABC: {
+ b = GETARG_B(i);
+ c = GETARG_C(i);
+ check(checkArgMode(pt, b, getBMode(op)));
+ check(checkArgMode(pt, c, getCMode(op)));
+ break;
+ }
+ case iABx: {
+ b = GETARG_Bx(i);
+ if (getBMode(op) == OpArgK) check(b < pt->sizek);
+ break;
+ }
+ case iAsBx: {
+ b = GETARG_sBx(i);
+ if (getBMode(op) == OpArgR) {
+ int dest = pc+1+b;
+ check(0 <= dest && dest < pt->sizecode);
+ if (dest > 0) {
+ /* cannot jump to a setlist count */
+ Instruction d = pt->code[dest-1];
+ check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0));
+ }
+ }
+ break;
+ }
+ }
+ if (testAMode(op)) {
+ if (a == reg) last = pc; /* change register `a' */
+ }
+ if (testTMode(op)) {
+ check(pc+2 < pt->sizecode); /* check skip */
+ check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);
+ }
+ switch (op) {
+ case OP_LOADBOOL: {
+ check(c == 0 || pc+2 < pt->sizecode); /* check its jump */
+ break;
+ }
+ case OP_LOADNIL: {
+ if (a <= reg && reg <= b)
+ last = pc; /* set registers from `a' to `b' */
+ break;
+ }
+ case OP_GETUPVAL:
+ case OP_SETUPVAL: {
+ check(b < pt->nups);
+ break;
+ }
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL: {
+ check(ttisstring(&pt->k[b]));
+ break;
+ }
+ case OP_SELF: {
+ checkreg(pt, a+1);
+ if (reg == a+1) last = pc;
+ break;
+ }
+ case OP_CONCAT: {
+ check(b < c); /* at least two operands */
+ break;
+ }
+ case OP_TFORLOOP: {
+ check(c >= 1); /* at least one result (control variable) */
+ checkreg(pt, a+2+c); /* space for results */
+ if (reg >= a+2) last = pc; /* affect all regs above its base */
+ break;
+ }
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ checkreg(pt, a+3);
+ /* go through */
+ case OP_JMP: {
+ int dest = pc+1+b;
+ /* not full check and jump is forward and do not skip `lastpc'? */
+ if (reg != NO_REG && pc < dest && dest <= lastpc)
+ pc += b; /* do the jump */
+ break;
+ }
+ case OP_CALL:
+ case OP_TAILCALL: {
+ if (b != 0) {
+ checkreg(pt, a+b-1);
+ }
+ c--; /* c = num. returns */
+ if (c == LUA_MULTRET) {
+ check(checkopenop(pt, pc));
+ }
+ else if (c != 0)
+ checkreg(pt, a+c-1);
+ if (reg >= a) last = pc; /* affect all registers above base */
+ break;
+ }
+ case OP_RETURN: {
+ b--; /* b = num. returns */
+ if (b > 0) checkreg(pt, a+b-1);
+ break;
+ }
+ case OP_SETLIST: {
+ if (b > 0) checkreg(pt, a + b);
+ if (c == 0) pc++;
+ break;
+ }
+ case OP_CLOSURE: {
+ int nup, j;
+ check(b < pt->sizep);
+ nup = pt->p[b]->nups;
+ check(pc + nup < pt->sizecode);
+ for (j = 1; j <= nup; j++) {
+ OpCode op1 = GET_OPCODE(pt->code[pc + j]);
+ check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
+ }
+ if (reg != NO_REG) /* tracing? */
+ pc += nup; /* do not 'execute' these pseudo-instructions */
+ break;
+ }
+ case OP_VARARG: {
+ check((pt->is_vararg & VARARG_ISVARARG) &&
+ !(pt->is_vararg & VARARG_NEEDSARG));
+ b--;
+ if (b == LUA_MULTRET) check(checkopenop(pt, pc));
+ checkreg(pt, a+b-1);
+ break;
+ }
+ default: break;
+ }
+ }
+ return pt->code[last];
+}
+
+#undef check
+#undef checkjump
+#undef checkreg
+
+/* }====================================================== */
+
+
+int luaG_checkcode (const Proto *pt) {
+ return (symbexec(pt, pt->sizecode, NO_REG) != 0);
+}
+
+
+static const char *kname (Proto *p, int c) {
+ if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))
+ return svalue(&p->k[INDEXK(c)]);
+ else
+ return "?";
+}
+
+
+static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,
+ const char **name) {
+ if (isLua(ci)) { /* a Lua function? */
+ Proto *p = ci_func(ci)->l.p;
+ int pc = currentpc(L, ci);
+ Instruction i;
+ *name = luaF_getlocalname(p, stackpos+1, pc);
+ if (*name) /* is a local? */
+ return "local";
+ i = symbexec(p, pc, stackpos); /* try symbolic execution */
+ lua_assert(pc != -1);
+ switch (GET_OPCODE(i)) {
+ case OP_GETGLOBAL: {
+ int g = GETARG_Bx(i); /* global index */
+ lua_assert(ttisstring(&p->k[g]));
+ *name = svalue(&p->k[g]);
+ return "global";
+ }
+ case OP_MOVE: {
+ int a = GETARG_A(i);
+ int b = GETARG_B(i); /* move from `b' to `a' */
+ if (b < a)
+ return getobjname(L, ci, b, name); /* get name for `b' */
+ break;
+ }
+ case OP_GETTABLE: {
+ int k = GETARG_C(i); /* key index */
+ *name = kname(p, k);
+ return "field";
+ }
+ case OP_GETUPVAL: {
+ int u = GETARG_B(i); /* upvalue index */
+ *name = p->upvalues ? getstr(p->upvalues[u]) : "?";
+ return "upvalue";
+ }
+ case OP_SELF: {
+ int k = GETARG_C(i); /* key index */
+ *name = kname(p, k);
+ return "method";
+ }
+ default: break;
+ }
+ }
+ return NULL; /* no useful name found */
+}
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
+ Instruction i;
+ if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))
+ return NULL; /* calling function is not Lua (or is unknown) */
+ ci--; /* calling function */
+ i = ci_func(ci)->l.p->code[currentpc(L, ci)];
+ if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||
+ GET_OPCODE(i) == OP_TFORLOOP)
+ return getobjname(L, ci, GETARG_A(i), name);
+ else
+ return NULL; /* no useful name can be found */
+}
+
+
+/* only ANSI way to check whether a pointer points to an array */
+static int isinstack (CallInfo *ci, const TValue *o) {
+ StkId p;
+ for (p = ci->base; p < ci->top; p++)
+ if (o == p) return 1;
+ return 0;
+}
+
+
+void luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
+ const char *name = NULL;
+ const char *t = luaT_typenames[ttype(o)];
+ const char *kind = (isinstack(L->ci, o)) ?
+ getobjname(L, L->ci, cast_int(o - L->base), &name) :
+ NULL;
+ if (kind)
+ luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)",
+ op, kind, name, t);
+ else
+ luaG_runerror(L, "attempt to %s a %s value", op, t);
+}
+
+
+void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
+ if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
+ lua_assert(!ttisstring(p1) && !ttisnumber(p1));
+ luaG_typeerror(L, p1, "concatenate");
+}
+
+
+void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
+ TValue temp;
+ if (luaV_tonumber(p1, &temp) == NULL)
+ p2 = p1; /* first operand is wrong */
+ luaG_typeerror(L, p2, "perform arithmetic on");
+}
+
+
+int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
+ const char *t1 = luaT_typenames[ttype(p1)];
+ const char *t2 = luaT_typenames[ttype(p2)];
+ if (t1[2] == t2[2])
+ luaG_runerror(L, "attempt to compare two %s values", t1);
+ else
+ luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
+ return 0;
+}
+
+
+static void addinfo (lua_State *L, const char *msg) {
+ CallInfo *ci = L->ci;
+ if (isLua(ci)) { /* is Lua code? */
+ char buff[LUA_IDSIZE]; /* add file:line information */
+ int line = currentline(L, ci);
+ luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);
+ luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
+ }
+}
+
+
+void luaG_errormsg (lua_State *L) {
+ if (L->errfunc != 0) { /* is there an error handling function? */
+ StkId errfunc = restorestack(L, L->errfunc);
+ if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
+ setobjs2s(L, L->top, L->top - 1); /* move argument */
+ setobjs2s(L, L->top - 1, errfunc); /* push function */
+ incr_top(L);
+ luaD_call(L, L->top - 2, 1); /* call it */
+ }
+ luaD_throw(L, LUA_ERRRUN);
+}
+
+
+void luaG_runerror (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ addinfo(L, luaO_pushvfstring(L, fmt, argp));
+ va_end(argp);
+ luaG_errormsg(L);
+}
+
diff --git a/engines/sword25/util/lua/src/ldebug.h b/engines/sword25/util/lua/src/ldebug.h
new file mode 100755
index 0000000000..ba28a97248
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldebug.h
@@ -0,0 +1,33 @@
+/*
+** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+
+
+#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
+
+#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
+
+#define resethookcount(L) (L->hookcount = L->basehookcount)
+
+
+LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,
+ const char *opname);
+LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);
+LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaG_errormsg (lua_State *L);
+LUAI_FUNC int luaG_checkcode (const Proto *pt);
+LUAI_FUNC int luaG_checkopenop (Instruction i);
+
+#endif
diff --git a/engines/sword25/util/lua/src/ldo.c b/engines/sword25/util/lua/src/ldo.c
new file mode 100755
index 0000000000..8de05f728e
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldo.c
@@ -0,0 +1,518 @@
+/*
+** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ldo_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+
+
+/*
+** {======================================================
+** Error-recovery functions
+** =======================================================
+*/
+
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ struct lua_longjmp *previous;
+ luai_jmpbuf b;
+ volatile int status; /* error code */
+};
+
+
+void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
+ switch (errcode) {
+ case LUA_ERRMEM: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
+ break;
+ }
+ case LUA_ERRERR: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
+ break;
+ }
+ case LUA_ERRSYNTAX:
+ case LUA_ERRRUN: {
+ setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
+ break;
+ }
+ }
+ L->top = oldtop + 1;
+}
+
+
+static void restore_stack_limit (lua_State *L) {
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
+ int inuse = cast_int(L->ci - L->base_ci);
+ if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
+ luaD_reallocCI(L, LUAI_MAXCALLS);
+ }
+}
+
+
+static void resetstack (lua_State *L, int status) {
+ L->ci = L->base_ci;
+ L->base = L->ci->base;
+ luaF_close(L, L->base); /* close eventual pending closures */
+ luaD_seterrorobj(L, status, L->base);
+ L->nCcalls = L->baseCcalls;
+ L->allowhook = 1;
+ restore_stack_limit(L);
+ L->errfunc = 0;
+ L->errorJmp = NULL;
+}
+
+
+void luaD_throw (lua_State *L, int errcode) {
+ if (L->errorJmp) {
+ L->errorJmp->status = errcode;
+ LUAI_THROW(L, L->errorJmp);
+ }
+ else {
+ L->status = cast_byte(errcode);
+ if (G(L)->panic) {
+ resetstack(L, errcode);
+ lua_unlock(L);
+ G(L)->panic(L);
+ }
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
+ struct lua_longjmp lj;
+ lj.status = 0;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ LUAI_TRY(L, &lj,
+ (*f)(L, ud);
+ );
+ L->errorJmp = lj.previous; /* restore old error handler */
+ return lj.status;
+}
+
+/* }====================================================== */
+
+
+static void correctstack (lua_State *L, TValue *oldstack) {
+ CallInfo *ci;
+ GCObject *up;
+ L->top = (L->top - oldstack) + L->stack;
+ for (up = L->openupval; up != NULL; up = up->gch.next)
+ gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
+ for (ci = L->base_ci; ci <= L->ci; ci++) {
+ ci->top = (ci->top - oldstack) + L->stack;
+ ci->base = (ci->base - oldstack) + L->stack;
+ ci->func = (ci->func - oldstack) + L->stack;
+ }
+ L->base = (L->base - oldstack) + L->stack;
+}
+
+
+void luaD_reallocstack (lua_State *L, int newsize) {
+ TValue *oldstack = L->stack;
+ int realsize = newsize + 1 + EXTRA_STACK;
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
+ L->stacksize = realsize;
+ L->stack_last = L->stack+newsize;
+ correctstack(L, oldstack);
+}
+
+
+void luaD_reallocCI (lua_State *L, int newsize) {
+ CallInfo *oldci = L->base_ci;
+ luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
+ L->size_ci = newsize;
+ L->ci = (L->ci - oldci) + L->base_ci;
+ L->end_ci = L->base_ci + L->size_ci - 1;
+}
+
+
+void luaD_growstack (lua_State *L, int n) {
+ if (n <= L->stacksize) /* double size is enough? */
+ luaD_reallocstack(L, 2*L->stacksize);
+ else
+ luaD_reallocstack(L, L->stacksize + n);
+}
+
+
+static CallInfo *growCI (lua_State *L) {
+ if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */
+ luaD_throw(L, LUA_ERRERR);
+ else {
+ luaD_reallocCI(L, 2*L->size_ci);
+ if (L->size_ci > LUAI_MAXCALLS)
+ luaG_runerror(L, "stack overflow");
+ }
+ return ++L->ci;
+}
+
+
+void luaD_callhook (lua_State *L, int event, int line) {
+ lua_Hook hook = L->hook;
+ if (hook && L->allowhook) {
+ ptrdiff_t top = savestack(L, L->top);
+ ptrdiff_t ci_top = savestack(L, L->ci->top);
+ lua_Debug ar;
+ ar.event = event;
+ ar.currentline = line;
+ if (event == LUA_HOOKTAILRET)
+ ar.i_ci = 0; /* tail call; no debug information about it */
+ else
+ ar.i_ci = cast_int(L->ci - L->base_ci);
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ L->ci->top = L->top + LUA_MINSTACK;
+ lua_assert(L->ci->top <= L->stack_last);
+ L->allowhook = 0; /* cannot call hooks inside a hook */
+ lua_unlock(L);
+ (*hook)(L, &ar);
+ lua_lock(L);
+ lua_assert(!L->allowhook);
+ L->allowhook = 1;
+ L->ci->top = restorestack(L, ci_top);
+ L->top = restorestack(L, top);
+ }
+}
+
+
+static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
+ int i;
+ int nfixargs = p->numparams;
+ Table *htab = NULL;
+ StkId base, fixed;
+ for (; actual < nfixargs; ++actual)
+ setnilvalue(L->top++);
+#if defined(LUA_COMPAT_VARARG)
+ if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
+ int nvar = actual - nfixargs; /* number of extra arguments */
+ lua_assert(p->is_vararg & VARARG_HASARG);
+ luaC_checkGC(L);
+ htab = luaH_new(L, nvar, 1); /* create `arg' table */
+ for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
+ setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
+ /* store counter in field `n' */
+ setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
+ }
+#endif
+ /* move fixed parameters to final position */
+ fixed = L->top - actual; /* first fixed argument */
+ base = L->top; /* final position of first argument */
+ for (i=0; i<nfixargs; i++) {
+ setobjs2s(L, L->top++, fixed+i);
+ setnilvalue(fixed+i);
+ }
+ /* add `arg' parameter */
+ if (htab) {
+ sethvalue(L, L->top++, htab);
+ lua_assert(iswhite(obj2gco(htab)));
+ }
+ return base;
+}
+
+
+static StkId tryfuncTM (lua_State *L, StkId func) {
+ const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
+ StkId p;
+ ptrdiff_t funcr = savestack(L, func);
+ if (!ttisfunction(tm))
+ luaG_typeerror(L, func, "call");
+ /* Open a hole inside the stack at `func' */
+ for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
+ incr_top(L);
+ func = restorestack(L, funcr); /* previous call may change stack */
+ setobj2s(L, func, tm); /* tag method is the new function to be called */
+ return func;
+}
+
+
+
+#define inc_ci(L) \
+ ((L->ci == L->end_ci) ? growCI(L) : \
+ (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))
+
+
+int luaD_precall (lua_State *L, StkId func, int nresults) {
+ LClosure *cl;
+ ptrdiff_t funcr;
+ if (!ttisfunction(func)) /* `func' is not a function? */
+ func = tryfuncTM(L, func); /* check the `function' tag method */
+ funcr = savestack(L, func);
+ cl = &clvalue(func)->l;
+ L->ci->savedpc = L->savedpc;
+ if (!cl->isC) { /* Lua function? prepare its call */
+ CallInfo *ci;
+ StkId st, base;
+ Proto *p = cl->p;
+ luaD_checkstack(L, p->maxstacksize);
+ func = restorestack(L, funcr);
+ if (!p->is_vararg) { /* no varargs? */
+ base = func + 1;
+ if (L->top > base + p->numparams)
+ L->top = base + p->numparams;
+ }
+ else { /* vararg function */
+ int nargs = cast_int(L->top - func) - 1;
+ base = adjust_varargs(L, p, nargs);
+ func = restorestack(L, funcr); /* previous call may change the stack */
+ }
+ ci = inc_ci(L); /* now `enter' new function */
+ ci->func = func;
+ L->base = ci->base = base;
+ ci->top = L->base + p->maxstacksize;
+ lua_assert(ci->top <= L->stack_last);
+ L->savedpc = p->code; /* starting point */
+ ci->tailcalls = 0;
+ ci->nresults = nresults;
+ for (st = L->top; st < ci->top; st++)
+ setnilvalue(st);
+ L->top = ci->top;
+ if (L->hookmask & LUA_MASKCALL) {
+ L->savedpc++; /* hooks assume 'pc' is already incremented */
+ luaD_callhook(L, LUA_HOOKCALL, -1);
+ L->savedpc--; /* correct 'pc' */
+ }
+ return PCRLUA;
+ }
+ else { /* if is a C function, call it */
+ CallInfo *ci;
+ int n;
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ ci = inc_ci(L); /* now `enter' new function */
+ ci->func = restorestack(L, funcr);
+ L->base = ci->base = ci->func + 1;
+ ci->top = L->top + LUA_MINSTACK;
+ lua_assert(ci->top <= L->stack_last);
+ ci->nresults = nresults;
+ if (L->hookmask & LUA_MASKCALL)
+ luaD_callhook(L, LUA_HOOKCALL, -1);
+ lua_unlock(L);
+ n = (*curr_func(L)->c.f)(L); /* do the actual call */
+ lua_lock(L);
+ if (n < 0) /* yielding? */
+ return PCRYIELD;
+ else {
+ luaD_poscall(L, L->top - n);
+ return PCRC;
+ }
+ }
+}
+
+
+static StkId callrethooks (lua_State *L, StkId firstResult) {
+ ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */
+ luaD_callhook(L, LUA_HOOKRET, -1);
+ if (f_isLua(L->ci)) { /* Lua function? */
+ while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */
+ luaD_callhook(L, LUA_HOOKTAILRET, -1);
+ }
+ return restorestack(L, fr);
+}
+
+
+int luaD_poscall (lua_State *L, StkId firstResult) {
+ StkId res;
+ int wanted, i;
+ CallInfo *ci;
+ if (L->hookmask & LUA_MASKRET)
+ firstResult = callrethooks(L, firstResult);
+ ci = L->ci--;
+ res = ci->func; /* res == final position of 1st result */
+ wanted = ci->nresults;
+ L->base = (ci - 1)->base; /* restore base */
+ L->savedpc = (ci - 1)->savedpc; /* restore savedpc */
+ /* move results to correct place */
+ for (i = wanted; i != 0 && firstResult < L->top; i--)
+ setobjs2s(L, res++, firstResult++);
+ while (i-- > 0)
+ setnilvalue(res++);
+ L->top = res;
+ return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
+}
+
+
+/*
+** Call a function (C or Lua). The function to be called is at *func.
+** The arguments are on the stack, right after the function.
+** When returns, all the results are on the stack, starting at the original
+** function position.
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ if (++L->nCcalls >= LUAI_MAXCCALLS) {
+ if (L->nCcalls == LUAI_MAXCCALLS)
+ luaG_runerror(L, "C stack overflow");
+ else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
+ luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
+ }
+ if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
+ luaV_execute(L, 1); /* call it */
+ L->nCcalls--;
+ luaC_checkGC(L);
+}
+
+
+static void resume (lua_State *L, void *ud) {
+ StkId firstArg = cast(StkId, ud);
+ CallInfo *ci = L->ci;
+ if (L->status == 0) { /* start coroutine? */
+ lua_assert(ci == L->base_ci && firstArg > L->base);
+ if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)
+ return;
+ }
+ else { /* resuming from previous yield */
+ lua_assert(L->status == LUA_YIELD);
+ L->status = 0;
+ if (!f_isLua(ci)) { /* `common' yield? */
+ /* finish interrupted execution of `OP_CALL' */
+ lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
+ GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);
+ if (luaD_poscall(L, firstArg)) /* complete it... */
+ L->top = L->ci->top; /* and correct top if not multiple results */
+ }
+ else /* yielded inside a hook: just continue its execution */
+ L->base = L->ci->base;
+ }
+ luaV_execute(L, cast_int(L->ci - L->base_ci));
+}
+
+
+static int resume_error (lua_State *L, const char *msg) {
+ L->top = L->ci->base;
+ setsvalue2s(L, L->top, luaS_new(L, msg));
+ incr_top(L);
+ lua_unlock(L);
+ return LUA_ERRRUN;
+}
+
+
+LUA_API int lua_resume (lua_State *L, int nargs) {
+ int status;
+ lua_lock(L);
+ if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))
+ return resume_error(L, "cannot resume non-suspended coroutine");
+ if (L->nCcalls >= LUAI_MAXCCALLS)
+ return resume_error(L, "C stack overflow");
+ luai_userstateresume(L, nargs);
+ lua_assert(L->errfunc == 0);
+ L->baseCcalls = ++L->nCcalls;
+ status = luaD_rawrunprotected(L, resume, L->top - nargs);
+ if (status != 0) { /* error? */
+ L->status = cast_byte(status); /* mark thread as `dead' */
+ luaD_seterrorobj(L, status, L->top);
+ L->ci->top = L->top;
+ }
+ else {
+ lua_assert(L->nCcalls == L->baseCcalls);
+ status = L->status;
+ }
+ --L->nCcalls;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_yield (lua_State *L, int nresults) {
+ luai_userstateyield(L, nresults);
+ lua_lock(L);
+ if (L->nCcalls > L->baseCcalls)
+ luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
+ L->base = L->top - nresults; /* protect stack slots below */
+ L->status = LUA_YIELD;
+ lua_unlock(L);
+ return -1;
+}
+
+
+int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t old_top, ptrdiff_t ef) {
+ int status;
+ unsigned short oldnCcalls = L->nCcalls;
+ ptrdiff_t old_ci = saveci(L, L->ci);
+ lu_byte old_allowhooks = L->allowhook;
+ ptrdiff_t old_errfunc = L->errfunc;
+ L->errfunc = ef;
+ status = luaD_rawrunprotected(L, func, u);
+ if (status != 0) { /* an error occurred? */
+ StkId oldtop = restorestack(L, old_top);
+ luaF_close(L, oldtop); /* close eventual pending closures */
+ luaD_seterrorobj(L, status, oldtop);
+ L->nCcalls = oldnCcalls;
+ L->ci = restoreci(L, old_ci);
+ L->base = L->ci->base;
+ L->savedpc = L->ci->savedpc;
+ L->allowhook = old_allowhooks;
+ restore_stack_limit(L);
+ }
+ L->errfunc = old_errfunc;
+ return status;
+}
+
+
+
+/*
+** Execute a protected parser.
+*/
+struct SParser { /* data to `f_parser' */
+ ZIO *z;
+ Mbuffer buff; /* buffer to be used by the scanner */
+ const char *name;
+};
+
+static void f_parser (lua_State *L, void *ud) {
+ int i;
+ Proto *tf;
+ Closure *cl;
+ struct SParser *p = cast(struct SParser *, ud);
+ int c = luaZ_lookahead(p->z);
+ luaC_checkGC(L);
+ tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
+ &p->buff, p->name);
+ cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
+ cl->l.p = tf;
+ for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
+ cl->l.upvals[i] = luaF_newupval(L);
+ setclvalue(L, L->top, cl);
+ incr_top(L);
+}
+
+
+int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
+ struct SParser p;
+ int status;
+ p.z = z; p.name = name;
+ luaZ_initbuffer(L, &p.buff);
+ status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
+ luaZ_freebuffer(L, &p.buff);
+ return status;
+}
+
+
diff --git a/engines/sword25/util/lua/src/ldo.h b/engines/sword25/util/lua/src/ldo.h
new file mode 100755
index 0000000000..98fddac59f
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+#define luaD_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ luaD_growstack(L, n); \
+ else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
+
+
+#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
+
+#define savestack(L,p) ((char *)(p) - (char *)L->stack)
+#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
+
+#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
+#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
+
+
+/* results from luaD_precall */
+#define PCRLUA 0 /* initiated a call to a Lua function */
+#define PCRC 1 /* did a call to a C function */
+#define PCRYIELD 2 /* C funtion yielded */
+
+
+/* type of protected functions, to be ran by `runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
+LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
+LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
+LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
+LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
+LUAI_FUNC void luaD_growstack (lua_State *L, int n);
+
+LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+
+#endif
+
diff --git a/engines/sword25/util/lua/src/ldump.c b/engines/sword25/util/lua/src/ldump.c
new file mode 100755
index 0000000000..c9d3d4870f
--- /dev/null
+++ b/engines/sword25/util/lua/src/ldump.c
@@ -0,0 +1,164 @@
+/*
+** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** save precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#include <stddef.h>
+
+#define ldump_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lundump.h"
+
+typedef struct {
+ lua_State* L;
+ lua_Writer writer;
+ void* data;
+ int strip;
+ int status;
+} DumpState;
+
+#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D)
+#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D)
+
+static void DumpBlock(const void* b, size_t size, DumpState* D)
+{
+ if (D->status==0)
+ {
+ lua_unlock(D->L);
+ D->status=(*D->writer)(D->L,b,size,D->data);
+ lua_lock(D->L);
+ }
+}
+
+static void DumpChar(int y, DumpState* D)
+{
+ char x=(char)y;
+ DumpVar(x,D);
+}
+
+static void DumpInt(int x, DumpState* D)
+{
+ DumpVar(x,D);
+}
+
+static void DumpNumber(lua_Number x, DumpState* D)
+{
+ DumpVar(x,D);
+}
+
+static void DumpVector(const void* b, int n, size_t size, DumpState* D)
+{
+ DumpInt(n,D);
+ DumpMem(b,n,size,D);
+}
+
+static void DumpString(const TString* s, DumpState* D)
+{
+ if (s==NULL || getstr(s)==NULL)
+ {
+ size_t size=0;
+ DumpVar(size,D);
+ }
+ else
+ {
+ size_t size=s->tsv.len+1; /* include trailing '\0' */
+ DumpVar(size,D);
+ DumpBlock(getstr(s),size,D);
+ }
+}
+
+#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D)
+
+static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
+
+static void DumpConstants(const Proto* f, DumpState* D)
+{
+ int i,n=f->sizek;
+ DumpInt(n,D);
+ for (i=0; i<n; i++)
+ {
+ const TValue* o=&f->k[i];
+ DumpChar(ttype(o),D);
+ switch (ttype(o))
+ {
+ case LUA_TNIL:
+ break;
+ case LUA_TBOOLEAN:
+ DumpChar(bvalue(o),D);
+ break;
+ case LUA_TNUMBER:
+ DumpNumber(nvalue(o),D);
+ break;
+ case LUA_TSTRING:
+ DumpString(rawtsvalue(o),D);
+ break;
+ default:
+ lua_assert(0); /* cannot happen */
+ break;
+ }
+ }
+ n=f->sizep;
+ DumpInt(n,D);
+ for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
+}
+
+static void DumpDebug(const Proto* f, DumpState* D)
+{
+ int i,n;
+ n= (D->strip) ? 0 : f->sizelineinfo;
+ DumpVector(f->lineinfo,n,sizeof(int),D);
+ n= (D->strip) ? 0 : f->sizelocvars;
+ DumpInt(n,D);
+ for (i=0; i<n; i++)
+ {
+ DumpString(f->locvars[i].varname,D);
+ DumpInt(f->locvars[i].startpc,D);
+ DumpInt(f->locvars[i].endpc,D);
+ }
+ n= (D->strip) ? 0 : f->sizeupvalues;
+ DumpInt(n,D);
+ for (i=0; i<n; i++) DumpString(f->upvalues[i],D);
+}
+
+static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
+{
+ DumpString((f->source==p || D->strip) ? NULL : f->source,D);
+ DumpInt(f->linedefined,D);
+ DumpInt(f->lastlinedefined,D);
+ DumpChar(f->nups,D);
+ DumpChar(f->numparams,D);
+ DumpChar(f->is_vararg,D);
+ DumpChar(f->maxstacksize,D);
+ DumpCode(f,D);
+ DumpConstants(f,D);
+ DumpDebug(f,D);
+}
+
+static void DumpHeader(DumpState* D)
+{
+ char h[LUAC_HEADERSIZE];
+ luaU_header(h);
+ DumpBlock(h,LUAC_HEADERSIZE,D);
+}
+
+/*
+** dump Lua function as precompiled chunk
+*/
+int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
+{
+ DumpState D;
+ D.L=L;
+ D.writer=w;
+ D.data=data;
+ D.strip=strip;
+ D.status=0;
+ DumpHeader(&D);
+ DumpFunction(f,NULL,&D);
+ return D.status;
+}
diff --git a/engines/sword25/util/lua/src/lfunc.c b/engines/sword25/util/lua/src/lfunc.c
new file mode 100755
index 0000000000..813e88f583
--- /dev/null
+++ b/engines/sword25/util/lua/src/lfunc.c
@@ -0,0 +1,174 @@
+/*
+** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lfunc_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));
+ luaC_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->c.isC = 1;
+ c->c.env = e;
+ c->c.nupvalues = cast_byte(nelems);
+ return c;
+}
+
+
+Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));
+ luaC_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->l.isC = 0;
+ c->l.env = e;
+ c->l.nupvalues = cast_byte(nelems);
+ while (nelems--) c->l.upvals[nelems] = NULL;
+ return c;
+}
+
+
+UpVal *luaF_newupval (lua_State *L) {
+ UpVal *uv = luaM_new(L, UpVal);
+ luaC_link(L, obj2gco(uv), LUA_TUPVAL);
+ uv->v = &uv->u.value;
+ setnilvalue(uv->v);
+ return uv;
+}
+
+
+UpVal *luaF_findupval (lua_State *L, StkId level) {
+ global_State *g = G(L);
+ GCObject **pp = &L->openupval;
+ UpVal *p;
+ UpVal *uv;
+ while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {
+ lua_assert(p->v != &p->u.value);
+ if (p->v == level) { /* found a corresponding upvalue? */
+ if (isdead(g, obj2gco(p))) /* is it dead? */
+ changewhite(obj2gco(p)); /* ressurect it */
+ return p;
+ }
+ pp = &p->next;
+ }
+ uv = luaM_new(L, UpVal); /* not found: create a new one */
+ uv->tt = LUA_TUPVAL;
+ uv->marked = luaC_white(g);
+ uv->v = level; /* current value lives in the stack */
+ uv->next = *pp; /* chain it in the proper position */
+ *pp = obj2gco(uv);
+ uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
+ uv->u.l.next = g->uvhead.u.l.next;
+ uv->u.l.next->u.l.prev = uv;
+ g->uvhead.u.l.next = uv;
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ return uv;
+}
+
+
+static void unlinkupval (UpVal *uv) {
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */
+ uv->u.l.prev->u.l.next = uv->u.l.next;
+}
+
+
+void luaF_freeupval (lua_State *L, UpVal *uv) {
+ if (uv->v != &uv->u.value) /* is it open? */
+ unlinkupval(uv); /* remove from open list */
+ luaM_free(L, uv); /* free upvalue */
+}
+
+
+void luaF_close (lua_State *L, StkId level) {
+ UpVal *uv;
+ global_State *g = G(L);
+ while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {
+ GCObject *o = obj2gco(uv);
+ lua_assert(!isblack(o) && uv->v != &uv->u.value);
+ L->openupval = uv->next; /* remove from `open' list */
+ if (isdead(g, o))
+ luaF_freeupval(L, uv); /* free upvalue */
+ else {
+ unlinkupval(uv);
+ setobj(L, &uv->u.value, uv->v);
+ uv->v = &uv->u.value; /* now current value lives here */
+ luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
+ }
+ }
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ Proto *f = luaM_new(L, Proto);
+ luaC_link(L, obj2gco(f), LUA_TPROTO);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->sizelineinfo = 0;
+ f->sizeupvalues = 0;
+ f->nups = 0;
+ f->upvalues = NULL;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->lineinfo = NULL;
+ f->sizelocvars = 0;
+ f->locvars = NULL;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ luaM_freearray(L, f->code, f->sizecode, Instruction);
+ luaM_freearray(L, f->p, f->sizep, Proto *);
+ luaM_freearray(L, f->k, f->sizek, TValue);
+ luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
+ luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
+ luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
+ luaM_free(L, f);
+}
+
+
+void luaF_freeclosure (lua_State *L, Closure *c) {
+ int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
+ sizeLclosure(c->l.nupvalues);
+ luaM_freemem(L, c, size);
+}
+
+
+/*
+** Look for n-th local variable at line `line' in function `func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return getstr(f->locvars[i].varname);
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/engines/sword25/util/lua/src/lfunc.h b/engines/sword25/util/lua/src/lfunc.h
new file mode 100755
index 0000000000..a68cf5151c
--- /dev/null
+++ b/engines/sword25/util/lua/src/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
+ cast(int, sizeof(TValue)*((n)-1)))
+
+#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
+ cast(int, sizeof(TValue *)*((n)-1)))
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_close (lua_State *L, StkId level);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
+LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/lgc.c b/engines/sword25/util/lua/src/lgc.c
new file mode 100755
index 0000000000..d9e0b78294
--- /dev/null
+++ b/engines/sword25/util/lua/src/lgc.c
@@ -0,0 +1,711 @@
+/*
+** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#include <string.h>
+
+#define lgc_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#define GCSTEPSIZE 1024u
+#define GCSWEEPMAX 40
+#define GCSWEEPCOST 10
+#define GCFINALIZECOST 100
+
+
+#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
+
+#define makewhite(g,x) \
+ ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))
+
+#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT)
+
+#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)
+
+
+#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT)
+#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT)
+
+
+#define KEYWEAK bitmask(KEYWEAKBIT)
+#define VALUEWEAK bitmask(VALUEWEAKBIT)
+
+
+
+#define markvalue(g,o) { checkconsistency(o); \
+ if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }
+
+#define markobject(g,t) { if (iswhite(obj2gco(t))) \
+ reallymarkobject(g, obj2gco(t)); }
+
+
+#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause)
+
+
+static void removeentry (Node *n) {
+ lua_assert(ttisnil(gval(n)));
+ if (iscollectable(gkey(n)))
+ setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */
+}
+
+
+static void reallymarkobject (global_State *g, GCObject *o) {
+ lua_assert(iswhite(o) && !isdead(g, o));
+ white2gray(o);
+ switch (o->gch.tt) {
+ case LUA_TSTRING: {
+ return;
+ }
+ case LUA_TUSERDATA: {
+ Table *mt = gco2u(o)->metatable;
+ gray2black(o); /* udata are never gray */
+ if (mt) markobject(g, mt);
+ markobject(g, gco2u(o)->env);
+ return;
+ }
+ case LUA_TUPVAL: {
+ UpVal *uv = gco2uv(o);
+ markvalue(g, uv->v);
+ if (uv->v == &uv->u.value) /* closed? */
+ gray2black(o); /* open upvalues are never black */
+ return;
+ }
+ case LUA_TFUNCTION: {
+ gco2cl(o)->c.gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TTABLE: {
+ gco2h(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TTHREAD: {
+ gco2th(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TPROTO: {
+ gco2p(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+static void marktmu (global_State *g) {
+ GCObject *u = g->tmudata;
+ if (u) {
+ do {
+ u = u->gch.next;
+ makewhite(g, u); /* may be marked, if left from previous GC */
+ reallymarkobject(g, u);
+ } while (u != g->tmudata);
+ }
+}
+
+
+/* move `dead' udata that need finalization to list `tmudata' */
+size_t luaC_separateudata (lua_State *L, int all) {
+ global_State *g = G(L);
+ size_t deadmem = 0;
+ GCObject **p = &g->mainthread->next;
+ GCObject *curr;
+ while ((curr = *p) != NULL) {
+ if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))
+ p = &curr->gch.next; /* don't bother with them */
+ else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {
+ markfinalized(gco2u(curr)); /* don't need finalization */
+ p = &curr->gch.next;
+ }
+ else { /* must call its gc method */
+ deadmem += sizeudata(gco2u(curr));
+ markfinalized(gco2u(curr));
+ *p = curr->gch.next;
+ /* link `curr' at the end of `tmudata' list */
+ if (g->tmudata == NULL) /* list is empty? */
+ g->tmudata = curr->gch.next = curr; /* creates a circular list */
+ else {
+ curr->gch.next = g->tmudata->gch.next;
+ g->tmudata->gch.next = curr;
+ g->tmudata = curr;
+ }
+ }
+ }
+ return deadmem;
+}
+
+
+static int traversetable (global_State *g, Table *h) {
+ int i;
+ int weakkey = 0;
+ int weakvalue = 0;
+ const TValue *mode;
+ if (h->metatable)
+ markobject(g, h->metatable);
+ mode = gfasttm(g, h->metatable, TM_MODE);
+ if (mode && ttisstring(mode)) { /* is there a weak mode? */
+ weakkey = (strchr(svalue(mode), 'k') != NULL);
+ weakvalue = (strchr(svalue(mode), 'v') != NULL);
+ if (weakkey || weakvalue) { /* is really weak? */
+ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
+ h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
+ (weakvalue << VALUEWEAKBIT));
+ h->gclist = g->weak; /* must be cleared after GC, ... */
+ g->weak = obj2gco(h); /* ... so put in the appropriate list */
+ }
+ }
+ if (weakkey && weakvalue) return 1;
+ if (!weakvalue) {
+ i = h->sizearray;
+ while (i--)
+ markvalue(g, &h->array[i]);
+ }
+ i = sizenode(h);
+ while (i--) {
+ Node *n = gnode(h, i);
+ lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
+ if (ttisnil(gval(n)))
+ removeentry(n); /* remove empty entries */
+ else {
+ lua_assert(!ttisnil(gkey(n)));
+ if (!weakkey) markvalue(g, gkey(n));
+ if (!weakvalue) markvalue(g, gval(n));
+ }
+ }
+ return weakkey || weakvalue;
+}
+
+
+/*
+** All marks are conditional because a GC may happen while the
+** prototype is still being created
+*/
+static void traverseproto (global_State *g, Proto *f) {
+ int i;
+ if (f->source) stringmark(f->source);
+ for (i=0; i<f->sizek; i++) /* mark literals */
+ markvalue(g, &f->k[i]);
+ for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */
+ if (f->upvalues[i])
+ stringmark(f->upvalues[i]);
+ }
+ for (i=0; i<f->sizep; i++) { /* mark nested protos */
+ if (f->p[i])
+ markobject(g, f->p[i]);
+ }
+ for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */
+ if (f->locvars[i].varname)
+ stringmark(f->locvars[i].varname);
+ }
+}
+
+
+
+static void traverseclosure (global_State *g, Closure *cl) {
+ markobject(g, cl->c.env);
+ if (cl->c.isC) {
+ int i;
+ for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */
+ markvalue(g, &cl->c.upvalue[i]);
+ }
+ else {
+ int i;
+ lua_assert(cl->l.nupvalues == cl->l.p->nups);
+ markobject(g, cl->l.p);
+ for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */
+ markobject(g, cl->l.upvals[i]);
+ }
+}
+
+
+static void checkstacksizes (lua_State *L, StkId max) {
+ int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */
+ int s_used = cast_int(max - L->stack); /* part of stack in use */
+ if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */
+ return; /* do not touch the stacks */
+ if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
+ luaD_reallocCI(L, L->size_ci/2); /* still big enough... */
+ condhardstacktests(luaD_reallocCI(L, ci_used + 1));
+ if (4*s_used < L->stacksize &&
+ 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
+ luaD_reallocstack(L, L->stacksize/2); /* still big enough... */
+ condhardstacktests(luaD_reallocstack(L, s_used));
+}
+
+
+static void traversestack (global_State *g, lua_State *l) {
+ StkId o, lim;
+ CallInfo *ci;
+ markvalue(g, gt(l));
+ lim = l->top;
+ for (ci = l->base_ci; ci <= l->ci; ci++) {
+ lua_assert(ci->top <= l->stack_last);
+ if (lim < ci->top) lim = ci->top;
+ }
+ for (o = l->stack; o < l->top; o++)
+ markvalue(g, o);
+ for (; o <= lim; o++)
+ setnilvalue(o);
+ checkstacksizes(l, lim);
+}
+
+
+/*
+** traverse one gray object, turning it to black.
+** Returns `quantity' traversed.
+*/
+static l_mem propagatemark (global_State *g) {
+ GCObject *o = g->gray;
+ lua_assert(isgray(o));
+ gray2black(o);
+ switch (o->gch.tt) {
+ case LUA_TTABLE: {
+ Table *h = gco2h(o);
+ g->gray = h->gclist;
+ if (traversetable(g, h)) /* table is weak? */
+ black2gray(o); /* keep it gray */
+ return sizeof(Table) + sizeof(TValue) * h->sizearray +
+ sizeof(Node) * sizenode(h);
+ }
+ case LUA_TFUNCTION: {
+ Closure *cl = gco2cl(o);
+ g->gray = cl->c.gclist;
+ traverseclosure(g, cl);
+ return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
+ sizeLclosure(cl->l.nupvalues);
+ }
+ case LUA_TTHREAD: {
+ lua_State *th = gco2th(o);
+ g->gray = th->gclist;
+ th->gclist = g->grayagain;
+ g->grayagain = o;
+ black2gray(o);
+ traversestack(g, th);
+ return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
+ sizeof(CallInfo) * th->size_ci;
+ }
+ case LUA_TPROTO: {
+ Proto *p = gco2p(o);
+ g->gray = p->gclist;
+ traverseproto(g, p);
+ return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
+ sizeof(Proto *) * p->sizep +
+ sizeof(TValue) * p->sizek +
+ sizeof(int) * p->sizelineinfo +
+ sizeof(LocVar) * p->sizelocvars +
+ sizeof(TString *) * p->sizeupvalues;
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static size_t propagateall (global_State *g) {
+ size_t m = 0;
+ while (g->gray) m += propagatemark(g);
+ return m;
+}
+
+
+/*
+** The next function tells whether a key or value can be cleared from
+** a weak table. Non-collectable objects are never removed from weak
+** tables. Strings behave as `values', so are never removed too. for
+** other objects: if really collected, cannot keep them; for userdata
+** being finalized, keep them in keys, but not in values
+*/
+static int iscleared (const TValue *o, int iskey) {
+ if (!iscollectable(o)) return 0;
+ if (ttisstring(o)) {
+ stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
+ return 0;
+ }
+ return iswhite(gcvalue(o)) ||
+ (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
+}
+
+
+/*
+** clear collected entries from weaktables
+*/
+static void cleartable (GCObject *l) {
+ while (l) {
+ Table *h = gco2h(l);
+ int i = h->sizearray;
+ lua_assert(testbit(h->marked, VALUEWEAKBIT) ||
+ testbit(h->marked, KEYWEAKBIT));
+ if (testbit(h->marked, VALUEWEAKBIT)) {
+ while (i--) {
+ TValue *o = &h->array[i];
+ if (iscleared(o, 0)) /* value was collected? */
+ setnilvalue(o); /* remove value */
+ }
+ }
+ i = sizenode(h);
+ while (i--) {
+ Node *n = gnode(h, i);
+ if (!ttisnil(gval(n)) && /* non-empty entry? */
+ (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {
+ setnilvalue(gval(n)); /* remove value ... */
+ removeentry(n); /* remove entry from table */
+ }
+ }
+ l = h->gclist;
+ }
+}
+
+
+static void freeobj (lua_State *L, GCObject *o) {
+ switch (o->gch.tt) {
+ case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
+ case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
+ case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
+ case LUA_TTABLE: luaH_free(L, gco2h(o)); break;
+ case LUA_TTHREAD: {
+ lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);
+ luaE_freethread(L, gco2th(o));
+ break;
+ }
+ case LUA_TSTRING: {
+ G(L)->strt.nuse--;
+ luaM_freemem(L, o, sizestring(gco2ts(o)));
+ break;
+ }
+ case LUA_TUSERDATA: {
+ luaM_freemem(L, o, sizeudata(gco2u(o)));
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+
+#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM)
+
+
+static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
+ GCObject *curr;
+ global_State *g = G(L);
+ int deadmask = otherwhite(g);
+ while ((curr = *p) != NULL && count-- > 0) {
+ if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */
+ sweepwholelist(L, &gco2th(curr)->openupval);
+ if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */
+ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
+ makewhite(g, curr); /* make it white (for next cycle) */
+ p = &curr->gch.next;
+ }
+ else { /* must erase `curr' */
+ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
+ *p = curr->gch.next;
+ if (curr == g->rootgc) /* is the first element of the list? */
+ g->rootgc = curr->gch.next; /* adjust first */
+ freeobj(L, curr);
+ }
+ }
+ return p;
+}
+
+
+static void checkSizes (lua_State *L) {
+ global_State *g = G(L);
+ /* check size of string hash */
+ if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
+ g->strt.size > MINSTRTABSIZE*2)
+ luaS_resize(L, g->strt.size/2); /* table is too big */
+ /* check size of buffer */
+ if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */
+ size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
+ luaZ_resizebuffer(L, &g->buff, newsize);
+ }
+}
+
+
+static void GCTM (lua_State *L) {
+ global_State *g = G(L);
+ GCObject *o = g->tmudata->gch.next; /* get first element */
+ Udata *udata = rawgco2u(o);
+ const TValue *tm;
+ /* remove udata from `tmudata' */
+ if (o == g->tmudata) /* last element? */
+ g->tmudata = NULL;
+ else
+ g->tmudata->gch.next = udata->uv.next;
+ udata->uv.next = g->mainthread->next; /* return it to `root' list */
+ g->mainthread->next = o;
+ makewhite(g, o);
+ tm = fasttm(L, udata->uv.metatable, TM_GC);
+ if (tm != NULL) {
+ lu_byte oldah = L->allowhook;
+ lu_mem oldt = g->GCthreshold;
+ L->allowhook = 0; /* stop debug hooks during GC tag method */
+ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */
+ setobj2s(L, L->top, tm);
+ setuvalue(L, L->top+1, udata);
+ L->top += 2;
+ luaD_call(L, L->top - 2, 0);
+ L->allowhook = oldah; /* restore hooks */
+ g->GCthreshold = oldt; /* restore threshold */
+ }
+}
+
+
+/*
+** Call all GC tag methods
+*/
+void luaC_callGCTM (lua_State *L) {
+ while (G(L)->tmudata)
+ GCTM(L);
+}
+
+
+void luaC_freeall (lua_State *L) {
+ global_State *g = G(L);
+ int i;
+ g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */
+ sweepwholelist(L, &g->rootgc);
+ for (i = 0; i < g->strt.size; i++) /* free all string lists */
+ sweepwholelist(L, &g->strt.hash[i]);
+}
+
+
+static void markmt (global_State *g) {
+ int i;
+ for (i=0; i<NUM_TAGS; i++)
+ if (g->mt[i]) markobject(g, g->mt[i]);
+}
+
+
+/* mark root set */
+static void markroot (lua_State *L) {
+ global_State *g = G(L);
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ markobject(g, g->mainthread);
+ /* make global table be traversed before main stack */
+ markvalue(g, gt(g->mainthread));
+ markvalue(g, registry(L));
+ markmt(g);
+ g->gcstate = GCSpropagate;
+}
+
+
+static void remarkupvals (global_State *g) {
+ UpVal *uv;
+ for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ if (isgray(obj2gco(uv)))
+ markvalue(g, uv->v);
+ }
+}
+
+
+static void atomic (lua_State *L) {
+ global_State *g = G(L);
+ size_t udsize; /* total size of userdata to be finalized */
+ /* remark occasional upvalues of (maybe) dead threads */
+ remarkupvals(g);
+ /* traverse objects cautch by write barrier and by 'remarkupvals' */
+ propagateall(g);
+ /* remark weak tables */
+ g->gray = g->weak;
+ g->weak = NULL;
+ lua_assert(!iswhite(obj2gco(g->mainthread)));
+ markobject(g, L); /* mark running thread */
+ markmt(g); /* mark basic metatables (again) */
+ propagateall(g);
+ /* remark gray again */
+ g->gray = g->grayagain;
+ g->grayagain = NULL;
+ propagateall(g);
+ udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */
+ marktmu(g); /* mark `preserved' userdata */
+ udsize += propagateall(g); /* remark, to propagate `preserveness' */
+ cleartable(g->weak); /* remove collected objects from weak tables */
+ /* flip current white */
+ g->currentwhite = cast_byte(otherwhite(g));
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ g->gcstate = GCSsweepstring;
+ g->estimate = g->totalbytes - udsize; /* first estimate */
+}
+
+
+static l_mem singlestep (lua_State *L) {
+ global_State *g = G(L);
+ /*lua_checkmemory(L);*/
+ switch (g->gcstate) {
+ case GCSpause: {
+ markroot(L); /* start a new collection */
+ return 0;
+ }
+ case GCSpropagate: {
+ if (g->gray)
+ return propagatemark(g);
+ else { /* no more `gray' objects */
+ atomic(L); /* finish mark phase */
+ return 0;
+ }
+ }
+ case GCSsweepstring: {
+ lu_mem old = g->totalbytes;
+ sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
+ if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */
+ g->gcstate = GCSsweep; /* end sweep-string phase */
+ lua_assert(old >= g->totalbytes);
+ g->estimate -= old - g->totalbytes;
+ return GCSWEEPCOST;
+ }
+ case GCSsweep: {
+ lu_mem old = g->totalbytes;
+ g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
+ if (*g->sweepgc == NULL) { /* nothing more to sweep? */
+ checkSizes(L);
+ g->gcstate = GCSfinalize; /* end sweep phase */
+ }
+ lua_assert(old >= g->totalbytes);
+ g->estimate -= old - g->totalbytes;
+ return GCSWEEPMAX*GCSWEEPCOST;
+ }
+ case GCSfinalize: {
+ if (g->tmudata) {
+ GCTM(L);
+ if (g->estimate > GCFINALIZECOST)
+ g->estimate -= GCFINALIZECOST;
+ return GCFINALIZECOST;
+ }
+ else {
+ g->gcstate = GCSpause; /* end collection */
+ g->gcdept = 0;
+ return 0;
+ }
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+void luaC_step (lua_State *L) {
+ global_State *g = G(L);
+ l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
+ if (lim == 0)
+ lim = (MAX_LUMEM-1)/2; /* no limit */
+ g->gcdept += g->totalbytes - g->GCthreshold;
+ do {
+ lim -= singlestep(L);
+ if (g->gcstate == GCSpause)
+ break;
+ } while (lim > 0);
+ if (g->gcstate != GCSpause) {
+ if (g->gcdept < GCSTEPSIZE)
+ g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/
+ else {
+ g->gcdept -= GCSTEPSIZE;
+ g->GCthreshold = g->totalbytes;
+ }
+ }
+ else {
+ lua_assert(g->totalbytes >= g->estimate);
+ setthreshold(g);
+ }
+}
+
+
+void luaC_fullgc (lua_State *L) {
+ global_State *g = G(L);
+ if (g->gcstate <= GCSpropagate) {
+ /* reset sweep marks to sweep all elements (returning them to white) */
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ /* reset other collector lists */
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ g->gcstate = GCSsweepstring;
+ }
+ lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);
+ /* finish any pending sweep phase */
+ while (g->gcstate != GCSfinalize) {
+ lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
+ singlestep(L);
+ }
+ markroot(L);
+ while (g->gcstate != GCSpause) {
+ singlestep(L);
+ }
+ setthreshold(g);
+}
+
+
+void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ lua_assert(ttype(&o->gch) != LUA_TTABLE);
+ /* must keep invariant? */
+ if (g->gcstate == GCSpropagate)
+ reallymarkobject(g, v); /* restore invariant */
+ else /* don't mind */
+ makewhite(g, o); /* mark as white just to avoid other barriers */
+}
+
+
+void luaC_barrierback (lua_State *L, Table *t) {
+ global_State *g = G(L);
+ GCObject *o = obj2gco(t);
+ lua_assert(isblack(o) && !isdead(g, o));
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ black2gray(o); /* make table gray (again) */
+ t->gclist = g->grayagain;
+ g->grayagain = o;
+}
+
+
+void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
+ global_State *g = G(L);
+ o->gch.next = g->rootgc;
+ g->rootgc = o;
+ o->gch.marked = luaC_white(g);
+ o->gch.tt = tt;
+}
+
+
+void luaC_linkupval (lua_State *L, UpVal *uv) {
+ global_State *g = G(L);
+ GCObject *o = obj2gco(uv);
+ o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */
+ g->rootgc = o;
+ if (isgray(o)) {
+ if (g->gcstate == GCSpropagate) {
+ gray2black(o); /* closed upvalues need barrier */
+ luaC_barrier(L, uv, uv->v);
+ }
+ else { /* sweep phase: sweep it (turning it into white) */
+ makewhite(g, o);
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ }
+ }
+}
+
diff --git a/engines/sword25/util/lua/src/lgc.h b/engines/sword25/util/lua/src/lgc.h
new file mode 100755
index 0000000000..5a8dc605b3
--- /dev/null
+++ b/engines/sword25/util/lua/src/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpause 0
+#define GCSpropagate 1
+#define GCSsweepstring 2
+#define GCSsweep 3
+#define GCSfinalize 4
+
+
+/*
+** some userful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
+#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
+#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
+
+
+
+/*
+** Layout for bit use in `marked' field:
+** bit 0 - object is white (type 0)
+** bit 1 - object is white (type 1)
+** bit 2 - object is black
+** bit 3 - for userdata: has been finalized
+** bit 3 - for tables: has weak keys
+** bit 4 - for tables: has weak values
+** bit 5 - object is fixed (should not be collected)
+** bit 6 - object is "super" fixed (only the main thread)
+*/
+
+
+#define WHITE0BIT 0
+#define WHITE1BIT 1
+#define BLACKBIT 2
+#define FINALIZEDBIT 3
+#define KEYWEAKBIT 3
+#define VALUEWEAKBIT 4
+#define FIXEDBIT 5
+#define SFIXEDBIT 6
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
+#define isgray(x) (!isblack(x) && !iswhite(x))
+
+#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
+#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
+
+#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
+#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
+
+
+#define luaC_checkGC(L) { \
+ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
+ if (G(L)->totalbytes >= G(L)->GCthreshold) \
+ luaC_step(L); }
+
+
+#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
+
+#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
+ luaC_barrierback(L,t); }
+
+#define luaC_objbarrier(L,p,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
+
+#define luaC_objbarriert(L,t,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
+
+LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
+LUAI_FUNC void luaC_callGCTM (lua_State *L);
+LUAI_FUNC void luaC_freeall (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_fullgc (lua_State *L);
+LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
+LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
+LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/linit.c b/engines/sword25/util/lua/src/linit.c
new file mode 100755
index 0000000000..c1f90dfab7
--- /dev/null
+++ b/engines/sword25/util/lua/src/linit.c
@@ -0,0 +1,38 @@
+/*
+** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
+** Initialization of libraries for lua.c
+** See Copyright Notice in lua.h
+*/
+
+
+#define linit_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+
+
+static const luaL_Reg lualibs[] = {
+ {"", luaopen_base},
+ {LUA_LOADLIBNAME, luaopen_package},
+ {LUA_TABLIBNAME, luaopen_table},
+ {LUA_IOLIBNAME, luaopen_io},
+ {LUA_OSLIBNAME, luaopen_os},
+ {LUA_STRLIBNAME, luaopen_string},
+ {LUA_MATHLIBNAME, luaopen_math},
+ {LUA_DBLIBNAME, luaopen_debug},
+ {NULL, NULL}
+};
+
+
+LUALIB_API void luaL_openlibs (lua_State *L) {
+ const luaL_Reg *lib = lualibs;
+ for (; lib->func; lib++) {
+ lua_pushcfunction(L, lib->func);
+ lua_pushstring(L, lib->name);
+ lua_call(L, 1, 0);
+ }
+}
+
diff --git a/engines/sword25/util/lua/src/liolib.c b/engines/sword25/util/lua/src/liolib.c
new file mode 100755
index 0000000000..e79ed1cb2e
--- /dev/null
+++ b/engines/sword25/util/lua/src/liolib.c
@@ -0,0 +1,553 @@
+/*
+** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define liolib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+#define IO_INPUT 1
+#define IO_OUTPUT 2
+
+
+static const char *const fnames[] = {"input", "output"};
+
+
+static int pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ if (filename)
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ else
+ lua_pushfstring(L, "%s", strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+static void fileerror (lua_State *L, int arg, const char *filename) {
+ lua_pushfstring(L, "%s: %s", filename, strerror(errno));
+ luaL_argerror(L, arg, lua_tostring(L, -1));
+}
+
+
+#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+
+
+static int io_type (lua_State *L) {
+ void *ud;
+ luaL_checkany(L, 1);
+ ud = lua_touserdata(L, 1);
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
+ if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
+ lua_pushnil(L); /* not a file */
+ else if (*((FILE **)ud) == NULL)
+ lua_pushliteral(L, "closed file");
+ else
+ lua_pushliteral(L, "file");
+ return 1;
+}
+
+
+static FILE *tofile (lua_State *L) {
+ FILE **f = tofilep(L);
+ if (*f == NULL)
+ luaL_error(L, "attempt to use a closed file");
+ return *f;
+}
+
+
+
+/*
+** When creating file handles, always creates a `closed' file handle
+** before opening the actual file; so, if there is a memory error, the
+** file is not left opened.
+*/
+static FILE **newfile (lua_State *L) {
+ FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
+ *pf = NULL; /* file handle is currently `closed' */
+ luaL_getmetatable(L, LUA_FILEHANDLE);
+ lua_setmetatable(L, -2);
+ return pf;
+}
+
+
+/*
+** function to (not) close the standard files stdin, stdout, and stderr
+*/
+static int io_noclose (lua_State *L) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "cannot close standard file");
+ return 2;
+}
+
+
+/*
+** function to close 'popen' files
+*/
+static int io_pclose (lua_State *L) {
+ FILE **p = tofilep(L);
+ int ok = lua_pclose(L, *p);
+ *p = NULL;
+ return pushresult(L, ok, NULL);
+}
+
+
+/*
+** function to close regular files
+*/
+static int io_fclose (lua_State *L) {
+ FILE **p = tofilep(L);
+ int ok = (fclose(*p) == 0);
+ *p = NULL;
+ return pushresult(L, ok, NULL);
+}
+
+
+static int aux_close (lua_State *L) {
+ lua_getfenv(L, 1);
+ lua_getfield(L, -1, "__close");
+ return (lua_tocfunction(L, -1))(L);
+}
+
+
+static int io_close (lua_State *L) {
+ if (lua_isnone(L, 1))
+ lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
+ tofile(L); /* make sure argument is a file */
+ return aux_close(L);
+}
+
+
+static int io_gc (lua_State *L) {
+ FILE *f = *tofilep(L);
+ /* ignore closed files */
+ if (f != NULL)
+ aux_close(L);
+ return 0;
+}
+
+
+static int io_tostring (lua_State *L) {
+ FILE *f = *tofilep(L);
+ if (f == NULL)
+ lua_pushliteral(L, "file (closed)");
+ else
+ lua_pushfstring(L, "file (%p)", f);
+ return 1;
+}
+
+
+static int io_open (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, mode);
+ return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
+}
+
+
+/*
+** this function has a separated environment, which defines the
+** correct __close for 'popen' files
+*/
+static int io_popen (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ FILE **pf = newfile(L);
+ *pf = lua_popen(L, filename, mode);
+ return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
+}
+
+
+static int io_tmpfile (lua_State *L) {
+ FILE **pf = newfile(L);
+ *pf = tmpfile();
+ return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
+}
+
+
+static FILE *getiofile (lua_State *L, int findex) {
+ FILE *f;
+ lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
+ f = *(FILE **)lua_touserdata(L, -1);
+ if (f == NULL)
+ luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
+ return f;
+}
+
+
+static int g_iofile (lua_State *L, int f, const char *mode) {
+ if (!lua_isnoneornil(L, 1)) {
+ const char *filename = lua_tostring(L, 1);
+ if (filename) {
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, mode);
+ if (*pf == NULL)
+ fileerror(L, 1, filename);
+ }
+ else {
+ tofile(L); /* check that it's a valid file handle */
+ lua_pushvalue(L, 1);
+ }
+ lua_rawseti(L, LUA_ENVIRONINDEX, f);
+ }
+ /* return current value */
+ lua_rawgeti(L, LUA_ENVIRONINDEX, f);
+ return 1;
+}
+
+
+static int io_input (lua_State *L) {
+ return g_iofile(L, IO_INPUT, "r");
+}
+
+
+static int io_output (lua_State *L) {
+ return g_iofile(L, IO_OUTPUT, "w");
+}
+
+
+static int io_readline (lua_State *L);
+
+
+static void aux_lines (lua_State *L, int idx, int toclose) {
+ lua_pushvalue(L, idx);
+ lua_pushboolean(L, toclose); /* close/not close file when finished */
+ lua_pushcclosure(L, io_readline, 2);
+}
+
+
+static int f_lines (lua_State *L) {
+ tofile(L); /* check that it's a valid file handle */
+ aux_lines(L, 1, 0);
+ return 1;
+}
+
+
+static int io_lines (lua_State *L) {
+ if (lua_isnoneornil(L, 1)) { /* no arguments? */
+ /* will iterate over default input */
+ lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
+ return f_lines(L);
+ }
+ else {
+ const char *filename = luaL_checkstring(L, 1);
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, "r");
+ if (*pf == NULL)
+ fileerror(L, 1, filename);
+ aux_lines(L, lua_gettop(L), 1);
+ return 1;
+ }
+}
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+static int read_number (lua_State *L, FILE *f) {
+ lua_Number d;
+ if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ else return 0; /* read fails */
+}
+
+
+static int test_eof (lua_State *L, FILE *f) {
+ int c = getc(f);
+ ungetc(c, f);
+ lua_pushlstring(L, NULL, 0);
+ return (c != EOF);
+}
+
+
+static int read_line (lua_State *L, FILE *f) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (;;) {
+ size_t l;
+ char *p = luaL_prepbuffer(&b);
+ if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
+ luaL_pushresult(&b); /* close buffer */
+ return (lua_objlen(L, -1) > 0); /* check whether read something */
+ }
+ l = strlen(p);
+ if (l == 0 || p[l-1] != '\n')
+ luaL_addsize(&b, l);
+ else {
+ luaL_addsize(&b, l - 1); /* do not include `eol' */
+ luaL_pushresult(&b); /* close buffer */
+ return 1; /* read at least an `eol' */
+ }
+ }
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ size_t rlen; /* how much to read */
+ size_t nr; /* number of chars actually read */
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
+ do {
+ char *p = luaL_prepbuffer(&b);
+ if (rlen > n) rlen = n; /* cannot read more than asked */
+ nr = fread(p, sizeof(char), rlen, f);
+ luaL_addsize(&b, nr);
+ n -= nr; /* still have to read `n' chars */
+ } while (n > 0 && nr == rlen); /* until end of count or eof */
+ luaL_pushresult(&b); /* close buffer */
+ return (n == 0 || lua_objlen(L, -1) > 0);
+}
+
+
+static int g_read (lua_State *L, FILE *f, int first) {
+ int nargs = lua_gettop(L) - 1;
+ int success;
+ int n;
+ clearerr(f);
+ if (nargs == 0) { /* no arguments? */
+ success = read_line(L, f);
+ n = first+1; /* to return 1 result */
+ }
+ else { /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = first; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)lua_tointeger(L, n);
+ success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
+ }
+ else {
+ const char *p = lua_tostring(L, n);
+ luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
+ switch (p[1]) {
+ case 'n': /* number */
+ success = read_number(L, f);
+ break;
+ case 'l': /* line */
+ success = read_line(L, f);
+ break;
+ case 'a': /* file */
+ read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
+ success = 1; /* always success */
+ break;
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+ }
+ if (ferror(f))
+ return pushresult(L, 0, NULL);
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ lua_pushnil(L); /* push nil instead */
+ }
+ return n - first;
+}
+
+
+static int io_read (lua_State *L) {
+ return g_read(L, getiofile(L, IO_INPUT), 1);
+}
+
+
+static int f_read (lua_State *L) {
+ return g_read(L, tofile(L), 2);
+}
+
+
+static int io_readline (lua_State *L) {
+ FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
+ int sucess;
+ if (f == NULL) /* file is already closed? */
+ luaL_error(L, "file is already closed");
+ sucess = read_line(L, f);
+ if (ferror(f))
+ return luaL_error(L, "%s", strerror(errno));
+ if (sucess) return 1;
+ else { /* EOF */
+ if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
+ lua_settop(L, 0);
+ lua_pushvalue(L, lua_upvalueindex(1));
+ aux_close(L); /* close it */
+ }
+ return 0;
+ }
+}
+
+/* }====================================================== */
+
+
+static int g_write (lua_State *L, FILE *f, int arg) {
+ int nargs = lua_gettop(L) - 1;
+ int status = 1;
+ for (; nargs--; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) {
+ /* optimization: could be done exactly as for strings */
+ status = status &&
+ fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
+ }
+ else {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ return pushresult(L, status, NULL);
+}
+
+
+static int io_write (lua_State *L) {
+ return g_write(L, getiofile(L, IO_OUTPUT), 1);
+}
+
+
+static int f_write (lua_State *L) {
+ return g_write(L, tofile(L), 2);
+}
+
+
+static int f_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ long offset = luaL_optlong(L, 3, 0);
+ op = fseek(f, offset, mode[op]);
+ if (op)
+ return pushresult(L, 0, NULL); /* error */
+ else {
+ lua_pushinteger(L, ftell(f));
+ return 1;
+ }
+}
+
+
+static int f_setvbuf (lua_State *L) {
+ static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
+ static const char *const modenames[] = {"no", "full", "line", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, NULL, modenames);
+ lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
+ int res = setvbuf(f, NULL, mode[op], sz);
+ return pushresult(L, res == 0, NULL);
+}
+
+
+
+static int io_flush (lua_State *L) {
+ return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
+}
+
+
+static int f_flush (lua_State *L) {
+ return pushresult(L, fflush(tofile(L)) == 0, NULL);
+}
+
+
+static const luaL_Reg iolib[] = {
+ {"close", io_close},
+ {"flush", io_flush},
+ {"input", io_input},
+ {"lines", io_lines},
+ {"open", io_open},
+ {"output", io_output},
+ {"popen", io_popen},
+ {"read", io_read},
+ {"tmpfile", io_tmpfile},
+ {"type", io_type},
+ {"write", io_write},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg flib[] = {
+ {"close", io_close},
+ {"flush", f_flush},
+ {"lines", f_lines},
+ {"read", f_read},
+ {"seek", f_seek},
+ {"setvbuf", f_setvbuf},
+ {"write", f_write},
+ {"__gc", io_gc},
+ {"__tostring", io_tostring},
+ {NULL, NULL}
+};
+
+
+static void createmeta (lua_State *L) {
+ luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
+ lua_pushvalue(L, -1); /* push metatable */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
+ luaL_register(L, NULL, flib); /* file methods */
+}
+
+
+static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
+ *newfile(L) = f;
+ if (k > 0) {
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, LUA_ENVIRONINDEX, k);
+ }
+ lua_pushvalue(L, -2); /* copy environment */
+ lua_setfenv(L, -2); /* set it */
+ lua_setfield(L, -3, fname);
+}
+
+
+static void newfenv (lua_State *L, lua_CFunction cls) {
+ lua_createtable(L, 0, 1);
+ lua_pushcfunction(L, cls);
+ lua_setfield(L, -2, "__close");
+}
+
+
+LUALIB_API int luaopen_io (lua_State *L) {
+ createmeta(L);
+ /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
+ newfenv(L, io_fclose);
+ lua_replace(L, LUA_ENVIRONINDEX);
+ /* open library */
+ luaL_register(L, LUA_IOLIBNAME, iolib);
+ /* create (and set) default files */
+ newfenv(L, io_noclose); /* close function for default files */
+ createstdfile(L, stdin, IO_INPUT, "stdin");
+ createstdfile(L, stdout, IO_OUTPUT, "stdout");
+ createstdfile(L, stderr, 0, "stderr");
+ lua_pop(L, 1); /* pop environment for default files */
+ lua_getfield(L, -1, "popen");
+ newfenv(L, io_pclose); /* create environment for 'popen' */
+ lua_setfenv(L, -2); /* set fenv for 'popen' */
+ lua_pop(L, 1); /* pop 'popen' */
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/llex.c b/engines/sword25/util/lua/src/llex.c
new file mode 100755
index 0000000000..6dc319358c
--- /dev/null
+++ b/engines/sword25/util/lua/src/llex.c
@@ -0,0 +1,461 @@
+/*
+** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <locale.h>
+#include <string.h>
+
+#define llex_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "llex.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lzio.h"
+
+
+
+#define next(ls) (ls->current = zgetc(ls->z))
+
+
+
+
+#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
+
+
+/* ORDER RESERVED */
+const char *const luaX_tokens [] = {
+ "and", "break", "do", "else", "elseif",
+ "end", "false", "for", "function", "if",
+ "in", "local", "nil", "not", "or", "repeat",
+ "return", "then", "true", "until", "while",
+ "..", "...", "==", ">=", "<=", "~=",
+ "<number>", "<name>", "<string>", "<eof>",
+ NULL
+};
+
+
+#define save_and_next(ls) (save(ls, ls->current), next(ls))
+
+
+static void save (LexState *ls, int c) {
+ Mbuffer *b = ls->buff;
+ if (b->n + 1 > b->buffsize) {
+ size_t newsize;
+ if (b->buffsize >= MAX_SIZET/2)
+ luaX_lexerror(ls, "lexical element too long", 0);
+ newsize = b->buffsize * 2;
+ luaZ_resizebuffer(ls->L, b, newsize);
+ }
+ b->buffer[b->n++] = cast(char, c);
+}
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, luaX_tokens[i]);
+ luaS_fix(ts); /* reserved words are never collected */
+ lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
+ ts->tsv.reserved = cast_byte(i+1); /* reserved word */
+ }
+}
+
+
+#define MAXSRC 80
+
+
+const char *luaX_token2str (LexState *ls, int token) {
+ if (token < FIRST_RESERVED) {
+ lua_assert(token == cast(unsigned char, token));
+ return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
+ luaO_pushfstring(ls->L, "%c", token);
+ }
+ else
+ return luaX_tokens[token-FIRST_RESERVED];
+}
+
+
+static const char *txtToken (LexState *ls, int token) {
+ switch (token) {
+ case TK_NAME:
+ case TK_STRING:
+ case TK_NUMBER:
+ save(ls, '\0');
+ return luaZ_buffer(ls->buff);
+ default:
+ return luaX_token2str(ls, token);
+ }
+}
+
+
+void luaX_lexerror (LexState *ls, const char *msg, int token) {
+ char buff[MAXSRC];
+ luaO_chunkid(buff, getstr(ls->source), MAXSRC);
+ msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
+ if (token)
+ luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
+ luaD_throw(ls->L, LUA_ERRSYNTAX);
+}
+
+
+void luaX_syntaxerror (LexState *ls, const char *msg) {
+ luaX_lexerror(ls, msg, ls->t.token);
+}
+
+
+TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
+ lua_State *L = ls->L;
+ TString *ts = luaS_newlstr(L, str, l);
+ TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
+ if (ttisnil(o))
+ setbvalue(o, 1); /* make sure `str' will not be collected */
+ return ts;
+}
+
+
+static void inclinenumber (LexState *ls) {
+ int old = ls->current;
+ lua_assert(currIsNewline(ls));
+ next(ls); /* skip `\n' or `\r' */
+ if (currIsNewline(ls) && ls->current != old)
+ next(ls); /* skip `\n\r' or `\r\n' */
+ if (++ls->linenumber >= MAX_INT)
+ luaX_syntaxerror(ls, "chunk has too many lines");
+}
+
+
+void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
+ ls->decpoint = '.';
+ ls->L = L;
+ ls->lookahead.token = TK_EOS; /* no look-ahead token */
+ ls->z = z;
+ ls->fs = NULL;
+ ls->linenumber = 1;
+ ls->lastline = 1;
+ ls->source = source;
+ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
+ next(ls); /* read first char */
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+
+static int check_next (LexState *ls, const char *set) {
+ if (!strchr(set, ls->current))
+ return 0;
+ save_and_next(ls);
+ return 1;
+}
+
+
+static void buffreplace (LexState *ls, char from, char to) {
+ size_t n = luaZ_bufflen(ls->buff);
+ char *p = luaZ_buffer(ls->buff);
+ while (n--)
+ if (p[n] == from) p[n] = to;
+}
+
+
+static void trydecpoint (LexState *ls, SemInfo *seminfo) {
+ /* format error: try to update decimal point separator */
+ struct lconv *cv = localeconv();
+ char old = ls->decpoint;
+ ls->decpoint = (cv ? cv->decimal_point[0] : '.');
+ buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
+ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
+ /* format error with correct decimal point: no more options */
+ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
+ luaX_lexerror(ls, "malformed number", TK_NUMBER);
+ }
+}
+
+
+/* LUA_NUMBER */
+static void read_numeral (LexState *ls, SemInfo *seminfo) {
+ lua_assert(isdigit(ls->current));
+ do {
+ save_and_next(ls);
+ } while (isdigit(ls->current) || ls->current == '.');
+ if (check_next(ls, "Ee")) /* `E'? */
+ check_next(ls, "+-"); /* optional exponent sign */
+ while (isalnum(ls->current) || ls->current == '_')
+ save_and_next(ls);
+ save(ls, '\0');
+ buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
+ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
+ trydecpoint(ls, seminfo); /* try to update decimal point separator */
+}
+
+
+static int skip_sep (LexState *ls) {
+ int count = 0;
+ int s = ls->current;
+ lua_assert(s == '[' || s == ']');
+ save_and_next(ls);
+ while (ls->current == '=') {
+ save_and_next(ls);
+ count++;
+ }
+ return (ls->current == s) ? count : (-count) - 1;
+}
+
+
+static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
+ int cont = 0;
+ (void)(cont); /* avoid warnings when `cont' is not used */
+ save_and_next(ls); /* skip 2nd `[' */
+ if (currIsNewline(ls)) /* string starts with a newline? */
+ inclinenumber(ls); /* skip it */
+ for (;;) {
+ switch (ls->current) {
+ case EOZ:
+ luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
+ "unfinished long comment", TK_EOS);
+ break; /* to avoid warnings */
+#if defined(LUA_COMPAT_LSTR)
+ case '[': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd `[' */
+ cont++;
+#if LUA_COMPAT_LSTR == 1
+ if (sep == 0)
+ luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
+#endif
+ }
+ break;
+ }
+#endif
+ case ']': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd `]' */
+#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
+ cont--;
+ if (sep == 0 && cont >= 0) break;
+#endif
+ goto endloop;
+ }
+ break;
+ }
+ case '\n':
+ case '\r': {
+ save(ls, '\n');
+ inclinenumber(ls);
+ if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
+ break;
+ }
+ default: {
+ if (seminfo) save_and_next(ls);
+ else next(ls);
+ }
+ }
+ } endloop:
+ if (seminfo)
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
+ luaZ_bufflen(ls->buff) - 2*(2 + sep));
+}
+
+
+static void read_string (LexState *ls, int del, SemInfo *seminfo) {
+ save_and_next(ls);
+ while (ls->current != del) {
+ switch (ls->current) {
+ case EOZ:
+ luaX_lexerror(ls, "unfinished string", TK_EOS);
+ continue; /* to avoid warnings */
+ case '\n':
+ case '\r':
+ luaX_lexerror(ls, "unfinished string", TK_STRING);
+ continue; /* to avoid warnings */
+ case '\\': {
+ int c;
+ next(ls); /* do not save the `\' */
+ switch (ls->current) {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case '\n': /* go through */
+ case '\r': save(ls, '\n'); inclinenumber(ls); continue;
+ case EOZ: continue; /* will raise an error next loop */
+ default: {
+ if (!isdigit(ls->current))
+ save_and_next(ls); /* handles \\, \", \', and \? */
+ else { /* \xxx */
+ int i = 0;
+ c = 0;
+ do {
+ c = 10*c + (ls->current-'0');
+ next(ls);
+ } while (++i<3 && isdigit(ls->current));
+ if (c > UCHAR_MAX)
+ luaX_lexerror(ls, "escape sequence too large", TK_STRING);
+ save(ls, c);
+ }
+ continue;
+ }
+ }
+ save(ls, c);
+ next(ls);
+ continue;
+ }
+ default:
+ save_and_next(ls);
+ }
+ }
+ save_and_next(ls); /* skip delimiter */
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
+ luaZ_bufflen(ls->buff) - 2);
+}
+
+
+static int llex (LexState *ls, SemInfo *seminfo) {
+ luaZ_resetbuffer(ls->buff);
+ for (;;) {
+ switch (ls->current) {
+ case '\n':
+ case '\r': {
+ inclinenumber(ls);
+ continue;
+ }
+ case '-': {
+ next(ls);
+ if (ls->current != '-') return '-';
+ /* else is a comment */
+ next(ls);
+ if (ls->current == '[') {
+ int sep = skip_sep(ls);
+ luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
+ if (sep >= 0) {
+ read_long_string(ls, NULL, sep); /* long comment */
+ luaZ_resetbuffer(ls->buff);
+ continue;
+ }
+ }
+ /* else short comment */
+ while (!currIsNewline(ls) && ls->current != EOZ)
+ next(ls);
+ continue;
+ }
+ case '[': {
+ int sep = skip_sep(ls);
+ if (sep >= 0) {
+ read_long_string(ls, seminfo, sep);
+ return TK_STRING;
+ }
+ else if (sep == -1) return '[';
+ else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
+ }
+ case '=': {
+ next(ls);
+ if (ls->current != '=') return '=';
+ else { next(ls); return TK_EQ; }
+ }
+ case '<': {
+ next(ls);
+ if (ls->current != '=') return '<';
+ else { next(ls); return TK_LE; }
+ }
+ case '>': {
+ next(ls);
+ if (ls->current != '=') return '>';
+ else { next(ls); return TK_GE; }
+ }
+ case '~': {
+ next(ls);
+ if (ls->current != '=') return '~';
+ else { next(ls); return TK_NE; }
+ }
+ case '"':
+ case '\'': {
+ read_string(ls, ls->current, seminfo);
+ return TK_STRING;
+ }
+ case '.': {
+ save_and_next(ls);
+ if (check_next(ls, ".")) {
+ if (check_next(ls, "."))
+ return TK_DOTS; /* ... */
+ else return TK_CONCAT; /* .. */
+ }
+ else if (!isdigit(ls->current)) return '.';
+ else {
+ read_numeral(ls, seminfo);
+ return TK_NUMBER;
+ }
+ }
+ case EOZ: {
+ return TK_EOS;
+ }
+ default: {
+ if (isspace(ls->current)) {
+ lua_assert(!currIsNewline(ls));
+ next(ls);
+ continue;
+ }
+ else if (isdigit(ls->current)) {
+ read_numeral(ls, seminfo);
+ return TK_NUMBER;
+ }
+ else if (isalpha(ls->current) || ls->current == '_') {
+ /* identifier or reserved word */
+ TString *ts;
+ do {
+ save_and_next(ls);
+ } while (isalnum(ls->current) || ls->current == '_');
+ ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
+ luaZ_bufflen(ls->buff));
+ if (ts->tsv.reserved > 0) /* reserved word? */
+ return ts->tsv.reserved - 1 + FIRST_RESERVED;
+ else {
+ seminfo->ts = ts;
+ return TK_NAME;
+ }
+ }
+ else {
+ int c = ls->current;
+ next(ls);
+ return c; /* single-char tokens (+ - / ...) */
+ }
+ }
+ }
+ }
+}
+
+
+void luaX_next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+void luaX_lookahead (LexState *ls) {
+ lua_assert(ls->lookahead.token == TK_EOS);
+ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+}
+
diff --git a/engines/sword25/util/lua/src/llex.h b/engines/sword25/util/lua/src/llex.h
new file mode 100755
index 0000000000..a9201cee48
--- /dev/null
+++ b/engines/sword25/util/lua/src/llex.h
@@ -0,0 +1,81 @@
+/*
+** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+#define FIRST_RESERVED 257
+
+/* maximum length of a reserved word */
+#define TOKEN_LEN (sizeof("function")/sizeof(char))
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+ TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+ TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
+ TK_NAME, TK_STRING, TK_EOS
+};
+
+/* number of reserved words */
+#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
+
+
+/* array with token `names' */
+LUAI_DATA const char *const luaX_tokens [];
+
+
+typedef union {
+ lua_Number r;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+typedef struct LexState {
+ int current; /* current character (charint) */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token `consumed' */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* `FuncState' is private to the parser */
+ struct lua_State *L;
+ ZIO *z; /* input stream */
+ Mbuffer *buff; /* buffer for tokens */
+ TString *source; /* current source name */
+ char decpoint; /* locale decimal point */
+} LexState;
+
+
+LUAI_FUNC void luaX_init (lua_State *L);
+LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
+ TString *source);
+LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
+LUAI_FUNC void luaX_next (LexState *ls);
+LUAI_FUNC void luaX_lookahead (LexState *ls);
+LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
+LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
+LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/llimits.h b/engines/sword25/util/lua/src/llimits.h
new file mode 100755
index 0000000000..ca8dcb7224
--- /dev/null
+++ b/engines/sword25/util/lua/src/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** Limits, basic types, and some other `installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+typedef LUAI_UINT32 lu_int32;
+
+typedef LUAI_UMEM lu_mem;
+
+typedef LUAI_MEM l_mem;
+
+
+
+/* chars used as small naturals (so that `char' is reserved for characters) */
+typedef unsigned char lu_byte;
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to integer
+** this is for hashing only; there is no problem if the integer
+** cannot hold the whole pointer value
+*/
+#define IntPoint(p) ((unsigned int)(lu_mem)(p))
+
+
+
+/* type to ensure maximum alignment */
+typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
+
+
+/* result of a `usual argument conversion' over lua_Number */
+typedef LUAI_UACNUMBER l_uacNumber;
+
+
+/* internal assertions for in-house debugging */
+#ifdef lua_assert
+
+#define check_exp(c,e) (lua_assert(c), (e))
+#define api_check(l,e) lua_assert(e)
+
+#else
+
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define api_check luai_apicheck
+
+#endif
+
+
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+#ifndef cast
+#define cast(t, exp) ((t)(exp))
+#endif
+
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+typedef lu_int32 Instruction;
+
+
+
+/* maximum stack for a Lua function */
+#define MAXSTACK 250
+
+
+
+/* minimum size for the string table (must be power of 2) */
+#ifndef MINSTRTABSIZE
+#define MINSTRTABSIZE 32
+#endif
+
+
+/* minimum size for string buffer */
+#ifndef LUA_MINBUFFER
+#define LUA_MINBUFFER 32
+#endif
+
+
+#ifndef lua_lock
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+#ifndef luai_threadyield
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#ifndef HARDSTACKTESTS
+#define condhardstacktests(x) ((void)0)
+#else
+#define condhardstacktests(x) x
+#endif
+
+#endif
diff --git a/engines/sword25/util/lua/src/lmathlib.c b/engines/sword25/util/lua/src/lmathlib.c
new file mode 100755
index 0000000000..441fbf736c
--- /dev/null
+++ b/engines/sword25/util/lua/src/lmathlib.c
@@ -0,0 +1,263 @@
+/*
+** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $
+** Standard mathematical library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+#include <math.h>
+
+#define lmathlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#undef PI
+#define PI (3.14159265358979323846)
+#define RADIANS_PER_DEGREE (PI/180.0)
+
+
+
+static int math_abs (lua_State *L) {
+ lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sin (lua_State *L) {
+ lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sinh (lua_State *L) {
+ lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cos (lua_State *L) {
+ lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cosh (lua_State *L) {
+ lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tan (lua_State *L) {
+ lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tanh (lua_State *L) {
+ lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_asin (lua_State *L) {
+ lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_acos (lua_State *L) {
+ lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan (lua_State *L) {
+ lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan2 (lua_State *L) {
+ lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_ceil (lua_State *L) {
+ lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_floor (lua_State *L) {
+ lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_fmod (lua_State *L) {
+ lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_modf (lua_State *L) {
+ double ip;
+ double fp = modf(luaL_checknumber(L, 1), &ip);
+ lua_pushnumber(L, ip);
+ lua_pushnumber(L, fp);
+ return 2;
+}
+
+static int math_sqrt (lua_State *L) {
+ lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_pow (lua_State *L) {
+ lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_log (lua_State *L) {
+ lua_pushnumber(L, log(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_log10 (lua_State *L) {
+ lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_exp (lua_State *L) {
+ lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_deg (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);
+ return 1;
+}
+
+static int math_rad (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);
+ return 1;
+}
+
+static int math_frexp (lua_State *L) {
+ int e;
+ lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
+ lua_pushinteger(L, e);
+ return 2;
+}
+
+static int math_ldexp (lua_State *L) {
+ lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
+ return 1;
+}
+
+
+
+static int math_min (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number dmin = luaL_checknumber(L, 1);
+ int i;
+ for (i=2; i<=n; i++) {
+ lua_Number d = luaL_checknumber(L, i);
+ if (d < dmin)
+ dmin = d;
+ }
+ lua_pushnumber(L, dmin);
+ return 1;
+}
+
+
+static int math_max (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number dmax = luaL_checknumber(L, 1);
+ int i;
+ for (i=2; i<=n; i++) {
+ lua_Number d = luaL_checknumber(L, i);
+ if (d > dmax)
+ dmax = d;
+ }
+ lua_pushnumber(L, dmax);
+ return 1;
+}
+
+
+static int math_random (lua_State *L) {
+ /* the `%' avoids the (rare) case of r==1, and is needed also because on
+ some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
+ lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
+ switch (lua_gettop(L)) { /* check number of arguments */
+ case 0: { /* no arguments */
+ lua_pushnumber(L, r); /* Number between 0 and 1 */
+ break;
+ }
+ case 1: { /* only upper limit */
+ int u = luaL_checkint(L, 1);
+ luaL_argcheck(L, 1<=u, 1, "interval is empty");
+ lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
+ break;
+ }
+ case 2: { /* lower and upper limits */
+ int l = luaL_checkint(L, 1);
+ int u = luaL_checkint(L, 2);
+ luaL_argcheck(L, l<=u, 2, "interval is empty");
+ lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
+ break;
+ }
+ default: return luaL_error(L, "wrong number of arguments");
+ }
+ return 1;
+}
+
+
+static int math_randomseed (lua_State *L) {
+ srand(luaL_checkint(L, 1));
+ return 0;
+}
+
+
+static const luaL_Reg mathlib[] = {
+ {"abs", math_abs},
+ {"acos", math_acos},
+ {"asin", math_asin},
+ {"atan2", math_atan2},
+ {"atan", math_atan},
+ {"ceil", math_ceil},
+ {"cosh", math_cosh},
+ {"cos", math_cos},
+ {"deg", math_deg},
+ {"exp", math_exp},
+ {"floor", math_floor},
+ {"fmod", math_fmod},
+ {"frexp", math_frexp},
+ {"ldexp", math_ldexp},
+ {"log10", math_log10},
+ {"log", math_log},
+ {"max", math_max},
+ {"min", math_min},
+ {"modf", math_modf},
+ {"pow", math_pow},
+ {"rad", math_rad},
+ {"random", math_random},
+ {"randomseed", math_randomseed},
+ {"sinh", math_sinh},
+ {"sin", math_sin},
+ {"sqrt", math_sqrt},
+ {"tanh", math_tanh},
+ {"tan", math_tan},
+ {NULL, NULL}
+};
+
+
+/*
+** Open math library
+*/
+LUALIB_API int luaopen_math (lua_State *L) {
+ luaL_register(L, LUA_MATHLIBNAME, mathlib);
+ lua_pushnumber(L, PI);
+ lua_setfield(L, -2, "pi");
+ lua_pushnumber(L, HUGE_VAL);
+ lua_setfield(L, -2, "huge");
+#if defined(LUA_COMPAT_MOD)
+ lua_getfield(L, -1, "fmod");
+ lua_setfield(L, -2, "mod");
+#endif
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/lmem.c b/engines/sword25/util/lua/src/lmem.c
new file mode 100755
index 0000000000..ae7d8c965f
--- /dev/null
+++ b/engines/sword25/util/lua/src/lmem.c
@@ -0,0 +1,86 @@
+/*
+** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lmem_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+/*
+** About the realloc function:
+** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
+** (`osize' is the old size, `nsize' is the new size)
+**
+** Lua ensures that (ptr == NULL) iff (osize == 0).
+**
+** * frealloc(ud, NULL, 0, x) creates a new block of size `x'
+**
+** * frealloc(ud, p, x, 0) frees the block `p'
+** (in this specific case, frealloc must return NULL).
+** particularly, frealloc(ud, NULL, 0, 0) does nothing
+** (which is equivalent to free(NULL) in ANSI C)
+**
+** frealloc returns NULL if it cannot create or reallocate the area
+** (any reallocation to an equal or smaller size cannot fail!)
+*/
+
+
+
+#define MINSIZEARRAY 4
+
+
+void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
+ int limit, const char *errormsg) {
+ void *newblock;
+ int newsize;
+ if (*size >= limit/2) { /* cannot double it? */
+ if (*size >= limit) /* cannot grow even a little? */
+ luaG_runerror(L, errormsg);
+ newsize = limit; /* still have at least one free place */
+ }
+ else {
+ newsize = (*size)*2;
+ if (newsize < MINSIZEARRAY)
+ newsize = MINSIZEARRAY; /* minimum size */
+ }
+ newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
+ *size = newsize; /* update only when everything else is OK */
+ return newblock;
+}
+
+
+void *luaM_toobig (lua_State *L) {
+ luaG_runerror(L, "memory allocation error: block too big");
+ return NULL; /* to avoid warnings */
+}
+
+
+
+/*
+** generic allocation routine.
+*/
+void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ block = (*g->frealloc)(g->ud, block, osize, nsize);
+ if (block == NULL && nsize > 0)
+ luaD_throw(L, LUA_ERRMEM);
+ lua_assert((nsize == 0) == (block == NULL));
+ g->totalbytes = (g->totalbytes - osize) + nsize;
+ return block;
+}
+
diff --git a/engines/sword25/util/lua/src/lmem.h b/engines/sword25/util/lua/src/lmem.h
new file mode 100755
index 0000000000..7c2dcb3220
--- /dev/null
+++ b/engines/sword25/util/lua/src/lmem.h
@@ -0,0 +1,49 @@
+/*
+** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+#define MEMERRMSG "not enough memory"
+
+
+#define luaM_reallocv(L,b,on,n,e) \
+ ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \
+ luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \
+ luaM_toobig(L))
+
+#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
+#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
+#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t))
+
+#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t))
+#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
+#define luaM_newvector(L,n,t) \
+ cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
+
+#define luaM_growvector(L,v,nelems,size,t,limit,e) \
+ if ((nelems)+1 > (size)) \
+ ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
+
+#define luaM_reallocvector(L, v,oldn,n,t) \
+ ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
+
+
+LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void *luaM_toobig (lua_State *L);
+LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
+ size_t size_elem, int limit,
+ const char *errormsg);
+
+#endif
+
diff --git a/engines/sword25/util/lua/src/loadlib.c b/engines/sword25/util/lua/src/loadlib.c
new file mode 100755
index 0000000000..d955f3ef41
--- /dev/null
+++ b/engines/sword25/util/lua/src/loadlib.c
@@ -0,0 +1,664 @@
+/*
+** $Id: loadlib.c,v 1.52.1.2 2007/12/28 14:58:43 roberto Exp $
+** Dynamic library loader for Lua
+** See Copyright Notice in lua.h
+**
+** This module contains an implementation of loadlib for Unix systems
+** that have dlfcn, an implementation for Darwin (Mac OS X), an
+** implementation for Windows, and a stub for other systems.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#define loadlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/* prefix for open functions in C libraries */
+#define LUA_POF "luaopen_"
+
+/* separator for open functions in C libraries */
+#define LUA_OFSEP "_"
+
+
+#define LIBPREFIX "LOADLIB: "
+
+#define POF LUA_POF
+#define LIB_FAIL "open"
+
+
+/* error codes for ll_loadfunc */
+#define ERRLIB 1
+#define ERRFUNC 2
+
+#define setprogdir(L) ((void)0)
+
+
+static void ll_unloadlib (void *lib);
+static void *ll_load (lua_State *L, const char *path);
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
+
+
+
+#if defined(LUA_DL_DLOPEN)
+/*
+** {========================================================================
+** This is an implementation of loadlib based on the dlfcn interface.
+** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
+** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
+** as an emulation layer on top of native functions.
+** =========================================================================
+*/
+
+#include <dlfcn.h>
+
+static void ll_unloadlib (void *lib) {
+ dlclose(lib);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ void *lib = dlopen(path, RTLD_NOW);
+ if (lib == NULL) lua_pushstring(L, dlerror());
+ return lib;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
+ if (f == NULL) lua_pushstring(L, dlerror());
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DLL)
+/*
+** {======================================================================
+** This is an implementation of loadlib for Windows using native functions.
+** =======================================================================
+*/
+
+#include <windows.h>
+
+
+#undef setprogdir
+
+static void setprogdir (lua_State *L) {
+ char buff[MAX_PATH + 1];
+ char *lb;
+ DWORD nsize = sizeof(buff)/sizeof(char);
+ DWORD n = GetModuleFileNameA(NULL, buff, nsize);
+ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
+ luaL_error(L, "unable to get ModuleFileName");
+ else {
+ *lb = '\0';
+ luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
+ lua_remove(L, -2); /* remove original string */
+ }
+}
+
+
+static void pusherror (lua_State *L) {
+ int error = GetLastError();
+ char buffer[128];
+ if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error, 0, buffer, sizeof(buffer), NULL))
+ lua_pushstring(L, buffer);
+ else
+ lua_pushfstring(L, "system error %d\n", error);
+}
+
+static void ll_unloadlib (void *lib) {
+ FreeLibrary((HINSTANCE)lib);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ HINSTANCE lib = LoadLibraryA(path);
+ if (lib == NULL) pusherror(L);
+ return lib;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
+ if (f == NULL) pusherror(L);
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DYLD)
+/*
+** {======================================================================
+** Native Mac OS X / Darwin Implementation
+** =======================================================================
+*/
+
+#include <mach-o/dyld.h>
+
+
+/* Mac appends a `_' before C function names */
+#undef POF
+#define POF "_" LUA_POF
+
+
+static void pusherror (lua_State *L) {
+ const char *err_str;
+ const char *err_file;
+ NSLinkEditErrors err;
+ int err_num;
+ NSLinkEditError(&err, &err_num, &err_file, &err_str);
+ lua_pushstring(L, err_str);
+}
+
+
+static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
+ switch (ret) {
+ case NSObjectFileImageInappropriateFile:
+ return "file is not a bundle";
+ case NSObjectFileImageArch:
+ return "library is for wrong CPU type";
+ case NSObjectFileImageFormat:
+ return "bad format";
+ case NSObjectFileImageAccess:
+ return "cannot access file";
+ case NSObjectFileImageFailure:
+ default:
+ return "unable to load library";
+ }
+}
+
+
+static void ll_unloadlib (void *lib) {
+ NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ NSObjectFileImage img;
+ NSObjectFileImageReturnCode ret;
+ /* this would be a rare case, but prevents crashing if it happens */
+ if(!_dyld_present()) {
+ lua_pushliteral(L, "dyld not present");
+ return NULL;
+ }
+ ret = NSCreateObjectFileImageFromFile(path, &img);
+ if (ret == NSObjectFileImageSuccess) {
+ NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
+ NSLINKMODULE_OPTION_RETURN_ON_ERROR);
+ NSDestroyObjectFileImage(img);
+ if (mod == NULL) pusherror(L);
+ return mod;
+ }
+ lua_pushstring(L, errorfromcode(ret));
+ return NULL;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
+ if (nss == NULL) {
+ lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
+ return NULL;
+ }
+ return (lua_CFunction)NSAddressOfSymbol(nss);
+}
+
+/* }====================================================== */
+
+
+
+#else
+/*
+** {======================================================
+** Fallback for other systems
+** =======================================================
+*/
+
+#undef LIB_FAIL
+#define LIB_FAIL "absent"
+
+
+#define DLMSG "dynamic libraries not enabled; check your Lua installation"
+
+
+static void ll_unloadlib (void *lib) {
+ (void)lib; /* to avoid warnings */
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ (void)path; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ (void)lib; (void)sym; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+/* }====================================================== */
+#endif
+
+
+
+static void **ll_register (lua_State *L, const char *path) {
+ void **plib;
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
+ if (!lua_isnil(L, -1)) /* is there an entry? */
+ plib = (void **)lua_touserdata(L, -1);
+ else { /* no entry yet; create one */
+ lua_pop(L, 1);
+ plib = (void **)lua_newuserdata(L, sizeof(const void *));
+ *plib = NULL;
+ luaL_getmetatable(L, "_LOADLIB");
+ lua_setmetatable(L, -2);
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+ return plib;
+}
+
+
+/*
+** __gc tag method: calls library's `ll_unloadlib' function with the lib
+** handle
+*/
+static int gctm (lua_State *L) {
+ void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
+ if (*lib) ll_unloadlib(*lib);
+ *lib = NULL; /* mark library as closed */
+ return 0;
+}
+
+
+static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
+ void **reg = ll_register(L, path);
+ if (*reg == NULL) *reg = ll_load(L, path);
+ if (*reg == NULL)
+ return ERRLIB; /* unable to load library */
+ else {
+ lua_CFunction f = ll_sym(L, *reg, sym);
+ if (f == NULL)
+ return ERRFUNC; /* unable to find function */
+ lua_pushcfunction(L, f);
+ return 0; /* return function */
+ }
+}
+
+
+static int ll_loadlib (lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ const char *init = luaL_checkstring(L, 2);
+ int stat = ll_loadfunc(L, path, init);
+ if (stat == 0) /* no errors? */
+ return 1; /* return the loaded function */
+ else { /* error; error message is on stack top */
+ lua_pushnil(L);
+ lua_insert(L, -2);
+ lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
+ return 3; /* return nil, error message, and where */
+ }
+}
+
+
+
+/*
+** {======================================================
+** 'require' function
+** =======================================================
+*/
+
+
+static int readable (const char *filename) {
+ FILE *f = fopen(filename, "r"); /* try to open file */
+ if (f == NULL) return 0; /* open failed */
+ fclose(f);
+ return 1;
+}
+
+
+static const char *pushnexttemplate (lua_State *L, const char *path) {
+ const char *l;
+ while (*path == *LUA_PATHSEP) path++; /* skip separators */
+ if (*path == '\0') return NULL; /* no more templates */
+ l = strchr(path, *LUA_PATHSEP); /* find next separator */
+ if (l == NULL) l = path + strlen(path);
+ lua_pushlstring(L, path, l - path); /* template */
+ return l;
+}
+
+
+static const char *findfile (lua_State *L, const char *name,
+ const char *pname) {
+ const char *path;
+ name = luaL_gsub(L, name, ".", LUA_DIRSEP);
+ lua_getfield(L, LUA_ENVIRONINDEX, pname);
+ path = lua_tostring(L, -1);
+ if (path == NULL)
+ luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
+ lua_pushliteral(L, ""); /* error accumulator */
+ while ((path = pushnexttemplate(L, path)) != NULL) {
+ const char *filename;
+ filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
+ lua_remove(L, -2); /* remove path template */
+ if (readable(filename)) /* does file exist and is readable? */
+ return filename; /* return that file name */
+ lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
+ lua_remove(L, -2); /* remove file name */
+ lua_concat(L, 2); /* add entry to possible error message */
+ }
+ return NULL; /* not found */
+}
+
+
+static void loaderror (lua_State *L, const char *filename) {
+ luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
+ lua_tostring(L, 1), filename, lua_tostring(L, -1));
+}
+
+
+static int loader_Lua (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ filename = findfile(L, name, "path");
+ if (filename == NULL) return 1; /* library not found in this path */
+ if (luaL_loadfile(L, filename) != 0)
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static const char *mkfuncname (lua_State *L, const char *modname) {
+ const char *funcname;
+ const char *mark = strchr(modname, *LUA_IGMARK);
+ if (mark) modname = mark + 1;
+ funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
+ funcname = lua_pushfstring(L, POF"%s", funcname);
+ lua_remove(L, -2); /* remove 'gsub' result */
+ return funcname;
+}
+
+
+static int loader_C (lua_State *L) {
+ const char *funcname;
+ const char *name = luaL_checkstring(L, 1);
+ const char *filename = findfile(L, name, "cpath");
+ if (filename == NULL) return 1; /* library not found in this path */
+ funcname = mkfuncname(L, name);
+ if (ll_loadfunc(L, filename, funcname) != 0)
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static int loader_Croot (lua_State *L) {
+ const char *funcname;
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ const char *p = strchr(name, '.');
+ int stat;
+ if (p == NULL) return 0; /* is root */
+ lua_pushlstring(L, name, p - name);
+ filename = findfile(L, lua_tostring(L, -1), "cpath");
+ if (filename == NULL) return 1; /* root not found */
+ funcname = mkfuncname(L, name);
+ if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
+ if (stat != ERRFUNC) loaderror(L, filename); /* real error */
+ lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
+ name, filename);
+ return 1; /* function not found */
+ }
+ return 1;
+}
+
+
+static int loader_preload (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_getfield(L, LUA_ENVIRONINDEX, "preload");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.preload") " must be a table");
+ lua_getfield(L, -1, name);
+ if (lua_isnil(L, -1)) /* not found? */
+ lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
+ return 1;
+}
+
+
+static const int sentinel_ = 0;
+#define sentinel ((void *)&sentinel_)
+
+
+static int ll_require (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ int i;
+ lua_settop(L, 1); /* _LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, 2, name);
+ if (lua_toboolean(L, -1)) { /* is it there? */
+ if (lua_touserdata(L, -1) == sentinel) /* check loops */
+ luaL_error(L, "loop or previous error loading module " LUA_QS, name);
+ return 1; /* package is already loaded */
+ }
+ /* else must load it; iterate over available loaders */
+ lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.loaders") " must be a table");
+ lua_pushliteral(L, ""); /* error message accumulator */
+ for (i=1; ; i++) {
+ lua_rawgeti(L, -2, i); /* get a loader */
+ if (lua_isnil(L, -1))
+ luaL_error(L, "module " LUA_QS " not found:%s",
+ name, lua_tostring(L, -2));
+ lua_pushstring(L, name);
+ lua_call(L, 1, 1); /* call it */
+ if (lua_isfunction(L, -1)) /* did it find module? */
+ break; /* module loaded successfully */
+ else if (lua_isstring(L, -1)) /* loader returned error message? */
+ lua_concat(L, 2); /* accumulate it */
+ else
+ lua_pop(L, 1);
+ }
+ lua_pushlightuserdata(L, sentinel);
+ lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
+ lua_pushstring(L, name); /* pass name as argument to module */
+ lua_call(L, 1, 1); /* run loaded module */
+ if (!lua_isnil(L, -1)) /* non-nil return? */
+ lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
+ lua_getfield(L, 2, name);
+ if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
+ lua_pushboolean(L, 1); /* use true as result */
+ lua_pushvalue(L, -1); /* extra copy to be returned */
+ lua_setfield(L, 2, name); /* _LOADED[name] = true */
+ }
+ return 1;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** 'module' function
+** =======================================================
+*/
+
+
+static void setfenv (lua_State *L) {
+ lua_Debug ar;
+ lua_getstack(L, 1, &ar);
+ lua_getinfo(L, "f", &ar);
+ lua_pushvalue(L, -2);
+ lua_setfenv(L, -2);
+ lua_pop(L, 1);
+}
+
+
+static void dooptions (lua_State *L, int n) {
+ int i;
+ for (i = 2; i <= n; i++) {
+ lua_pushvalue(L, i); /* get option (a function) */
+ lua_pushvalue(L, -2); /* module */
+ lua_call(L, 1, 0);
+ }
+}
+
+
+static void modinit (lua_State *L, const char *modname) {
+ const char *dot;
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "_M"); /* module._M = module */
+ lua_pushstring(L, modname);
+ lua_setfield(L, -2, "_NAME");
+ dot = strrchr(modname, '.'); /* look for last dot in module name */
+ if (dot == NULL) dot = modname;
+ else dot++;
+ /* set _PACKAGE as package name (full module name minus last part) */
+ lua_pushlstring(L, modname, dot - modname);
+ lua_setfield(L, -2, "_PACKAGE");
+}
+
+
+static int ll_module (lua_State *L) {
+ const char *modname = luaL_checkstring(L, 1);
+ int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
+ if (!lua_istable(L, -1)) { /* not found? */
+ lua_pop(L, 1); /* remove previous result */
+ /* try global variable (and create one if it does not exist) */
+ if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
+ return luaL_error(L, "name conflict for module " LUA_QS, modname);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
+ }
+ /* check whether table already has a _NAME field */
+ lua_getfield(L, -1, "_NAME");
+ if (!lua_isnil(L, -1)) /* is table an initialized module? */
+ lua_pop(L, 1);
+ else { /* no; initialize it */
+ lua_pop(L, 1);
+ modinit(L, modname);
+ }
+ lua_pushvalue(L, -1);
+ setfenv(L);
+ dooptions(L, loaded - 1);
+ return 0;
+}
+
+
+static int ll_seeall (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ if (!lua_getmetatable(L, 1)) {
+ lua_createtable(L, 0, 1); /* create new metatable */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, 1);
+ }
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_setfield(L, -2, "__index"); /* mt.__index = _G */
+ return 0;
+}
+
+
+/* }====================================================== */
+
+
+
+/* auxiliary mark (for internal use) */
+#define AUXMARK "\1"
+
+static void setpath (lua_State *L, const char *fieldname, const char *envname,
+ const char *def) {
+ const char *path = getenv(envname);
+ if (path == NULL) /* no environment variable? */
+ lua_pushstring(L, def); /* use default */
+ else {
+ /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
+ path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
+ LUA_PATHSEP AUXMARK LUA_PATHSEP);
+ luaL_gsub(L, path, AUXMARK, def);
+ lua_remove(L, -2);
+ }
+ setprogdir(L);
+ lua_setfield(L, -2, fieldname);
+}
+
+
+static const luaL_Reg pk_funcs[] = {
+ {"loadlib", ll_loadlib},
+ {"seeall", ll_seeall},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+ {"module", ll_module},
+ {"require", ll_require},
+ {NULL, NULL}
+};
+
+
+static const lua_CFunction loaders[] =
+ {loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
+
+
+LUALIB_API int luaopen_package (lua_State *L) {
+ int i;
+ /* create new type _LOADLIB */
+ luaL_newmetatable(L, "_LOADLIB");
+ lua_pushcfunction(L, gctm);
+ lua_setfield(L, -2, "__gc");
+ /* create `package' table */
+ luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
+#if defined(LUA_COMPAT_LOADLIB)
+ lua_getfield(L, -1, "loadlib");
+ lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
+#endif
+ lua_pushvalue(L, -1);
+ lua_replace(L, LUA_ENVIRONINDEX);
+ /* create `loaders' table */
+ lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
+ /* fill it with pre-defined loaders */
+ for (i=0; loaders[i] != NULL; i++) {
+ lua_pushcfunction(L, loaders[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
+ setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
+ setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
+ /* store config information */
+ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
+ LUA_EXECDIR "\n" LUA_IGMARK);
+ lua_setfield(L, -2, "config");
+ /* set field `loaded' */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
+ lua_setfield(L, -2, "loaded");
+ /* set field `preload' */
+ lua_newtable(L);
+ lua_setfield(L, -2, "preload");
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ luaL_register(L, NULL, ll_funcs); /* open lib into global table */
+ lua_pop(L, 1);
+ return 1; /* return 'package' table */
+}
+
diff --git a/engines/sword25/util/lua/src/lobject.c b/engines/sword25/util/lua/src/lobject.c
new file mode 100755
index 0000000000..4ff50732a4
--- /dev/null
+++ b/engines/sword25/util/lua/src/lobject.c
@@ -0,0 +1,214 @@
+/*
+** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lobject_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lvm.h"
+
+
+
+const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
+
+
+/*
+** converts an integer to a "floating point byte", represented as
+** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
+** eeeee != 0 and (xxx) otherwise.
+*/
+int luaO_int2fb (unsigned int x) {
+ int e = 0; /* expoent */
+ while (x >= 16) {
+ x = (x+1) >> 1;
+ e++;
+ }
+ if (x < 8) return x;
+ else return ((e+1) << 3) | (cast_int(x) - 8);
+}
+
+
+/* converts back */
+int luaO_fb2int (int x) {
+ int e = (x >> 3) & 31;
+ if (e == 0) return x;
+ else return ((x & 7)+8) << (e - 1);
+}
+
+
+int luaO_log2 (unsigned int x) {
+ static const lu_byte log_2[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = -1;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+
+}
+
+
+int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
+ if (ttype(t1) != ttype(t2)) return 0;
+ else switch (ttype(t1)) {
+ case LUA_TNIL:
+ return 1;
+ case LUA_TNUMBER:
+ return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN:
+ return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
+ case LUA_TLIGHTUSERDATA:
+ return pvalue(t1) == pvalue(t2);
+ default:
+ lua_assert(iscollectable(t1));
+ return gcvalue(t1) == gcvalue(t2);
+ }
+}
+
+
+int luaO_str2d (const char *s, lua_Number *result) {
+ char *endptr;
+ *result = lua_str2number(s, &endptr);
+ if (endptr == s) return 0; /* conversion failed */
+ if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
+ *result = cast_num(strtoul(s, &endptr, 16));
+ if (*endptr == '\0') return 1; /* most common case */
+ while (isspace(cast(unsigned char, *endptr))) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
+ return 1;
+}
+
+
+
+static void pushstr (lua_State *L, const char *str) {
+ setsvalue2s(L, L->top, luaS_new(L, str));
+ incr_top(L);
+}
+
+
+/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
+const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
+ int n = 1;
+ pushstr(L, "");
+ for (;;) {
+ const char *e = strchr(fmt, '%');
+ if (e == NULL) break;
+ setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
+ incr_top(L);
+ switch (*(e+1)) {
+ case 's': {
+ const char *s = va_arg(argp, char *);
+ if (s == NULL) s = "(null)";
+ pushstr(L, s);
+ break;
+ }
+ case 'c': {
+ char buff[2];
+ buff[0] = cast(char, va_arg(argp, int));
+ buff[1] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ case 'd': {
+ setnvalue(L->top, cast_num(va_arg(argp, int)));
+ incr_top(L);
+ break;
+ }
+ case 'f': {
+ setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));
+ incr_top(L);
+ break;
+ }
+ case 'p': {
+ char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
+ sprintf(buff, "%p", va_arg(argp, void *));
+ pushstr(L, buff);
+ break;
+ }
+ case '%': {
+ pushstr(L, "%");
+ break;
+ }
+ default: {
+ char buff[3];
+ buff[0] = '%';
+ buff[1] = *(e+1);
+ buff[2] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ }
+ n += 2;
+ fmt = e+2;
+ }
+ pushstr(L, fmt);
+ luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);
+ L->top -= n;
+ return svalue(L->top - 1);
+}
+
+
+const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *msg;
+ va_list argp;
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ return msg;
+}
+
+
+void luaO_chunkid (char *out, const char *source, size_t bufflen) {
+ if (*source == '=') {
+ strncpy(out, source+1, bufflen); /* remove first char */
+ out[bufflen-1] = '\0'; /* ensures null termination */
+ }
+ else { /* out = "source", or "...source" */
+ if (*source == '@') {
+ size_t l;
+ source++; /* skip the `@' */
+ bufflen -= sizeof(" '...' ");
+ l = strlen(source);
+ strcpy(out, "");
+ if (l > bufflen) {
+ source += (l-bufflen); /* get last part of file name */
+ strcat(out, "...");
+ }
+ strcat(out, source);
+ }
+ else { /* out = [string "string"] */
+ size_t len = strcspn(source, "\n\r"); /* stop at first newline */
+ bufflen -= sizeof(" [string \"...\"] ");
+ if (len > bufflen) len = bufflen;
+ strcpy(out, "[string \"");
+ if (source[len] != '\0') { /* must truncate? */
+ strncat(out, source, len);
+ strcat(out, "...");
+ }
+ else
+ strcat(out, source);
+ strcat(out, "\"]");
+ }
+ }
+}
diff --git a/engines/sword25/util/lua/src/lobject.h b/engines/sword25/util/lua/src/lobject.h
new file mode 100755
index 0000000000..e7199dfc68
--- /dev/null
+++ b/engines/sword25/util/lua/src/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/* tags for values visible from Lua */
+#define LAST_TAG LUA_TTHREAD
+
+#define NUM_TAGS (LAST_TAG+1)
+
+
+/*
+** Extra tags for non-values
+*/
+#define LUA_TPROTO (LAST_TAG+1)
+#define LUA_TUPVAL (LAST_TAG+2)
+#define LUA_TDEADKEY (LAST_TAG+3)
+
+
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
+
+
+/*
+** Common header in struct form
+*/
+typedef struct GCheader {
+ CommonHeader;
+} GCheader;
+
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union {
+ GCObject *gc;
+ void *p;
+ lua_Number n;
+ int b;
+} Value;
+
+
+/*
+** Tagged Values
+*/
+
+#define TValuefields Value value; int tt
+
+typedef struct lua_TValue {
+ TValuefields;
+} TValue;
+
+
+/* Macros to test type */
+#define ttisnil(o) (ttype(o) == LUA_TNIL)
+#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
+#define ttisstring(o) (ttype(o) == LUA_TSTRING)
+#define ttistable(o) (ttype(o) == LUA_TTABLE)
+#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
+#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
+#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
+#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
+
+/* Macros to access values */
+#define ttype(o) ((o)->tt)
+#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
+#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
+#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
+#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
+#define tsvalue(o) (&rawtsvalue(o)->tsv)
+#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
+#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
+#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
+
+#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
+
+/*
+** for internal debug only
+*/
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
+
+
+/* Macros to set values */
+#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
+
+#define setnvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
+
+#define setpvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
+
+#define setbvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
+
+#define setsvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; o1->tt=o2->tt; \
+ checkliveness(G(L),o1); }
+
+
+/*
+** different types of sets, according to destination
+*/
+
+/* from stack to (same) stack */
+#define setobjs2s setobj
+/* to stack (not from same stack) */
+#define setobj2s setobj
+#define setsvalue2s setsvalue
+#define sethvalue2s sethvalue
+#define setptvalue2s setptvalue
+/* from table to same table */
+#define setobjt2t setobj
+/* to table */
+#define setobj2t setobj
+/* to new object */
+#define setobj2n setobj
+#define setsvalue2n setsvalue
+
+#define setttype(obj, tt) (ttype(obj) = (tt))
+
+
+#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
+
+
+
+typedef TValue *StkId; /* index to stack elements */
+
+
+/*
+** String headers for string table
+*/
+typedef union TString {
+ L_Umaxalign dummy; /* ensures maximum alignment for strings */
+ struct {
+ CommonHeader;
+ lu_byte reserved;
+ unsigned int hash;
+ size_t len;
+ } tsv;
+} TString;
+
+
+#define getstr(ts) cast(const char *, (ts) + 1)
+#define svalue(o) getstr(tsvalue(o))
+
+
+
+typedef union Udata {
+ L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+ struct {
+ CommonHeader;
+ struct Table *metatable;
+ struct Table *env;
+ size_t len;
+ } uv;
+} Udata;
+
+
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ TValue *k; /* constants used by the function */
+ Instruction *code;
+ struct Proto **p; /* functions defined inside the function */
+ int *lineinfo; /* map from opcodes to source lines */
+ struct LocVar *locvars; /* information about local variables */
+ TString **upvalues; /* upvalue names */
+ TString *source;
+ int sizeupvalues;
+ int sizek; /* size of `k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of `p' */
+ int sizelocvars;
+ int linedefined;
+ int lastlinedefined;
+ GCObject *gclist;
+ lu_byte nups; /* number of upvalues */
+ lu_byte numparams;
+ lu_byte is_vararg;
+ lu_byte maxstacksize;
+} Proto;
+
+
+/* masks for new-style vararg */
+#define VARARG_HASARG 1
+#define VARARG_ISVARARG 2
+#define VARARG_NEEDSARG 4
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+
+/*
+** Upvalues
+*/
+
+typedef struct UpVal {
+ CommonHeader;
+ TValue *v; /* points to stack or to its own value */
+ union {
+ TValue value; /* the value (when closed) */
+ struct { /* double linked list (when open) */
+ struct UpVal *prev;
+ struct UpVal *next;
+ } l;
+ } u;
+} UpVal;
+
+
+/*
+** Closures
+*/
+
+#define ClosureHeader \
+ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
+ struct Table *env
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1];
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1];
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
+#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
+
+
+/*
+** Tables
+*/
+
+typedef union TKey {
+ struct {
+ TValuefields;
+ struct Node *next; /* for chaining */
+ } nk;
+ TValue tvk;
+} TKey;
+
+
+typedef struct Node {
+ TValue i_val;
+ TKey i_key;
+} Node;
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of `node' array */
+ struct Table *metatable;
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ GCObject *gclist;
+ int sizearray; /* size of `array' array */
+} Table;
+
+
+
+/*
+** `module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+#define luaO_nilobject (&luaO_nilobject_)
+
+LUAI_DATA const TValue luaO_nilobject_;
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+LUAI_FUNC int luaO_log2 (unsigned int x);
+LUAI_FUNC int luaO_int2fb (unsigned int x);
+LUAI_FUNC int luaO_fb2int (int x);
+LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
+
+
+#endif
+
diff --git a/engines/sword25/util/lua/src/lopcodes.c b/engines/sword25/util/lua/src/lopcodes.c
new file mode 100755
index 0000000000..4cc745230b
--- /dev/null
+++ b/engines/sword25/util/lua/src/lopcodes.c
@@ -0,0 +1,102 @@
+/*
+** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** See Copyright Notice in lua.h
+*/
+
+
+#define lopcodes_c
+#define LUA_CORE
+
+
+#include "lopcodes.h"
+
+
+/* ORDER OP */
+
+const char *const luaP_opnames[NUM_OPCODES+1] = {
+ "MOVE",
+ "LOADK",
+ "LOADBOOL",
+ "LOADNIL",
+ "GETUPVAL",
+ "GETGLOBAL",
+ "GETTABLE",
+ "SETGLOBAL",
+ "SETUPVAL",
+ "SETTABLE",
+ "NEWTABLE",
+ "SELF",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "MOD",
+ "POW",
+ "UNM",
+ "NOT",
+ "LEN",
+ "CONCAT",
+ "JMP",
+ "EQ",
+ "LT",
+ "LE",
+ "TEST",
+ "TESTSET",
+ "CALL",
+ "TAILCALL",
+ "RETURN",
+ "FORLOOP",
+ "FORPREP",
+ "TFORLOOP",
+ "SETLIST",
+ "CLOSE",
+ "CLOSURE",
+ "VARARG",
+ NULL
+};
+
+
+#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
+
+const lu_byte luaP_opmodes[NUM_OPCODES] = {
+/* T A B C mode opcode */
+ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
+ ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
+ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
+ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
+ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
+};
+
diff --git a/engines/sword25/util/lua/src/lopcodes.h b/engines/sword25/util/lua/src/lopcodes.h
new file mode 100755
index 0000000000..41224d6ee1
--- /dev/null
+++ b/engines/sword25/util/lua/src/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits.
+ Instructions can have the following fields:
+ `A' : 8 bits
+ `B' : 9 bits
+ `C' : 9 bits
+ `Bx' : 18 bits (`B' and `C' together)
+ `sBx' : signed Bx
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 9
+#define SIZE_B 9
+#define SIZE_Bx (SIZE_C + SIZE_B)
+#define SIZE_A 8
+
+#define SIZE_OP 6
+
+#define POS_OP 0
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_C (POS_A + SIZE_A)
+#define POS_B (POS_C + SIZE_C)
+#define POS_Bx POS_C
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+*/
+#if SIZE_Bx < LUAI_BITSINT-1
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
+#else
+#define MAXARG_Bx MAX_INT
+#define MAXARG_sBx MAX_INT
+#endif
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
+#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
+
+#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
+
+#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
+#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
+ ((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
+
+#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
+#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
+ ((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
+
+#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
+
+
+#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+
+/*
+** Macros to operate RK indices
+*/
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x) ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r) ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x) ((x) | BITRK)
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R(x) - register
+** Kst(x) - constant (in constant table)
+** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+*/
+
+
+/*
+** grep "ORDER OP" if you change these enums
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R(A) := R(B) */
+OP_LOADK,/* A Bx R(A) := Kst(Bx) */
+OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
+OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
+OP_GETUPVAL,/* A B R(A) := UpValue[B] */
+
+OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
+OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
+
+OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
+OP_SETUPVAL,/* A B UpValue[B] := R(A) */
+OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
+
+OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
+
+OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
+
+OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
+OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
+OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
+OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
+OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
+OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
+OP_UNM,/* A B R(A) := -R(B) */
+OP_NOT,/* A B R(A) := not R(B) */
+OP_LEN,/* A B R(A) := length of R(B) */
+
+OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
+
+OP_JMP,/* sBx pc+=sBx */
+
+OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
+OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
+OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
+
+OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
+OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
+
+OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
+
+OP_FORLOOP,/* A sBx R(A)+=R(A+2);
+ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
+
+OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+ if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
+OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
+
+OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
+OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
+
+OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
+} OpCode;
+
+
+#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+ (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
+ and can be 0: OP_CALL then sets `top' to last_result+1, so
+ next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
+
+ (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to `top'
+
+ (*) In OP_SETLIST, if (B == 0) then B = `top';
+ if (C == 0) then next `instruction' is real C
+
+ (*) For comparisons, A specifies what condition the test should accept
+ (true or false).
+
+ (*) All `skips' (pc++) assume that next instruction is a jump
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-1: op mode
+** bits 2-3: C arg mode
+** bits 4-5: B arg mode
+** bit 6: instruction set register A
+** bit 7: operator is a test
+*/
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
+#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
+#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
+
+
+LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/loslib.c b/engines/sword25/util/lua/src/loslib.c
new file mode 100755
index 0000000000..da06a572ac
--- /dev/null
+++ b/engines/sword25/util/lua/src/loslib.c
@@ -0,0 +1,243 @@
+/*
+** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static int os_pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+static int os_execute (lua_State *L) {
+ lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
+ return 1;
+}
+
+
+static int os_remove (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ return os_pushresult(L, remove(filename) == 0, filename);
+}
+
+
+static int os_rename (lua_State *L) {
+ const char *fromname = luaL_checkstring(L, 1);
+ const char *toname = luaL_checkstring(L, 2);
+ return os_pushresult(L, rename(fromname, toname) == 0, fromname);
+}
+
+
+static int os_tmpname (lua_State *L) {
+ char buff[LUA_TMPNAMBUFSIZE];
+ int err;
+ lua_tmpnam(buff, err);
+ if (err)
+ return luaL_error(L, "unable to generate a unique filename");
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int os_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int os_clock (lua_State *L) {
+ lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+** wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+static void setfield (lua_State *L, const char *key, int value) {
+ lua_pushinteger(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+ if (value < 0) /* undefined? */
+ return; /* does not set field */
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static int getboolfield (lua_State *L, const char *key) {
+ int res;
+ lua_getfield(L, -1, key);
+ res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d) {
+ int res;
+ lua_getfield(L, -1, key);
+ if (lua_isnumber(L, -1))
+ res = (int)lua_tointeger(L, -1);
+ else {
+ if (d < 0)
+ return luaL_error(L, "field " LUA_QS " missing in date table", key);
+ res = d;
+ }
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int os_date (lua_State *L) {
+ const char *s = luaL_optstring(L, 1, "%c");
+ time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
+ struct tm *stm;
+ if (*s == '!') { /* UTC? */
+ stm = gmtime(&t);
+ s++; /* skip `!' */
+ }
+ else
+ stm = localtime(&t);
+ if (stm == NULL) /* invalid date? */
+ lua_pushnil(L);
+ else if (strcmp(s, "*t") == 0) {
+ lua_createtable(L, 0, 9); /* 9 = number of fields */
+ setfield(L, "sec", stm->tm_sec);
+ setfield(L, "min", stm->tm_min);
+ setfield(L, "hour", stm->tm_hour);
+ setfield(L, "day", stm->tm_mday);
+ setfield(L, "month", stm->tm_mon+1);
+ setfield(L, "year", stm->tm_year+1900);
+ setfield(L, "wday", stm->tm_wday+1);
+ setfield(L, "yday", stm->tm_yday+1);
+ setboolfield(L, "isdst", stm->tm_isdst);
+ }
+ else {
+ char cc[3];
+ luaL_Buffer b;
+ cc[0] = '%'; cc[2] = '\0';
+ luaL_buffinit(L, &b);
+ for (; *s; s++) {
+ if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
+ luaL_addchar(&b, *s);
+ else {
+ size_t reslen;
+ char buff[200]; /* should be big enough for any conversion result */
+ cc[1] = *(++s);
+ reslen = strftime(buff, sizeof(buff), cc, stm);
+ luaL_addlstring(&b, buff, reslen);
+ }
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+static int os_time (lua_State *L) {
+ time_t t;
+ if (lua_isnoneornil(L, 1)) /* called without args? */
+ t = time(NULL); /* get current time */
+ else {
+ struct tm ts;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1); /* make sure table is at the top */
+ ts.tm_sec = getfield(L, "sec", 0);
+ ts.tm_min = getfield(L, "min", 0);
+ ts.tm_hour = getfield(L, "hour", 12);
+ ts.tm_mday = getfield(L, "day", -1);
+ ts.tm_mon = getfield(L, "month", -1) - 1;
+ ts.tm_year = getfield(L, "year", -1) - 1900;
+ ts.tm_isdst = getboolfield(L, "isdst");
+ t = mktime(&ts);
+ }
+ if (t == (time_t)(-1))
+ lua_pushnil(L);
+ else
+ lua_pushnumber(L, (lua_Number)t);
+ return 1;
+}
+
+
+static int os_difftime (lua_State *L) {
+ lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
+ (time_t)(luaL_optnumber(L, 2, 0))));
+ return 1;
+}
+
+/* }====================================================== */
+
+
+static int os_setlocale (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ const char *l = luaL_optstring(L, 1, NULL);
+ int op = luaL_checkoption(L, 2, "all", catnames);
+ lua_pushstring(L, setlocale(cat[op], l));
+ return 1;
+}
+
+
+static int os_exit (lua_State *L) {
+ exit(luaL_optint(L, 1, EXIT_SUCCESS));
+}
+
+static const luaL_Reg syslib[] = {
+ {"clock", os_clock},
+ {"date", os_date},
+ {"difftime", os_difftime},
+ {"execute", os_execute},
+ {"exit", os_exit},
+ {"getenv", os_getenv},
+ {"remove", os_remove},
+ {"rename", os_rename},
+ {"setlocale", os_setlocale},
+ {"time", os_time},
+ {"tmpname", os_tmpname},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaopen_os (lua_State *L) {
+ luaL_register(L, LUA_OSLIBNAME, syslib);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/lparser.c b/engines/sword25/util/lua/src/lparser.c
new file mode 100755
index 0000000000..1e2a9a88b7
--- /dev/null
+++ b/engines/sword25/util/lua/src/lparser.c
@@ -0,0 +1,1339 @@
+/*
+** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lparser_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+
+#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
+
+#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]])
+
+#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m)
+
+
+/*
+** nodes for block list (list of active blocks)
+*/
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
+ int breaklist; /* list of jumps out of this loop */
+ lu_byte nactvar; /* # active locals outside the breakable structure */
+ lu_byte upval; /* true if some variable in the block is an upvalue */
+ lu_byte isbreakable; /* true if `block' is a loop */
+} BlockCnt;
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void chunk (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+
+
+static void anchor_token (LexState *ls) {
+ if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {
+ TString *ts = ls->t.seminfo.ts;
+ luaX_newstring(ls, getstr(ts), ts->tsv.len);
+ }
+}
+
+
+static void error_expected (LexState *ls, int token) {
+ luaX_syntaxerror(ls,
+ luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token)));
+}
+
+
+static void errorlimit (FuncState *fs, int limit, const char *what) {
+ const char *msg = (fs->f->linedefined == 0) ?
+ luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) :
+ luaO_pushfstring(fs->L, "function at line %d has more than %d %s",
+ fs->f->linedefined, limit, what);
+ luaX_lexerror(fs->ls, msg, 0);
+}
+
+
+static int testnext (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ luaX_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+}
+
+static void checknext (LexState *ls, int c) {
+ check(ls, c);
+ luaX_next(ls);
+}
+
+
+#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
+
+
+
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (!testnext(ls, what)) {
+ if (where == ls->linenumber)
+ error_expected(ls, what);
+ else {
+ luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+ LUA_QS " expected (to close " LUA_QS " at line %d)",
+ luaX_token2str(ls, what), luaX_token2str(ls, who), where));
+ }
+ }
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check(ls, TK_NAME);
+ ts = ls->t.seminfo.ts;
+ luaX_next(ls);
+ return ts;
+}
+
+
+static void init_exp (expdesc *e, expkind k, int i) {
+ e->f = e->t = NO_JUMP;
+ e->k = k;
+ e->u.s.info = i;
+}
+
+
+static void codestring (LexState *ls, expdesc *e, TString *s) {
+ init_exp(e, VK, luaK_stringK(ls->fs, s));
+}
+
+
+static void checkname(LexState *ls, expdesc *e) {
+ codestring(ls, e, str_checkname(ls));
+}
+
+
+static int registerlocalvar (LexState *ls, TString *varname) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizelocvars;
+ luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
+ LocVar, SHRT_MAX, "too many local variables");
+ while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;
+ f->locvars[fs->nlocvars].varname = varname;
+ luaC_objbarrier(ls->L, f, varname);
+ return fs->nlocvars++;
+}
+
+
+#define new_localvarliteral(ls,v,n) \
+ new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n)
+
+
+static void new_localvar (LexState *ls, TString *name, int n) {
+ FuncState *fs = ls->fs;
+ luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables");
+ fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));
+}
+
+
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ fs->nactvar = cast_byte(fs->nactvar + nvars);
+ for (; nvars; nvars--) {
+ getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;
+ }
+}
+
+
+static void removevars (LexState *ls, int tolevel) {
+ FuncState *fs = ls->fs;
+ while (fs->nactvar > tolevel)
+ getlocvar(fs, --fs->nactvar).endpc = fs->pc;
+}
+
+
+static int indexupvalue (FuncState *fs, TString *name, expdesc *v) {
+ int i;
+ Proto *f = fs->f;
+ int oldsize = f->sizeupvalues;
+ for (i=0; i<f->nups; i++) {
+ if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {
+ lua_assert(f->upvalues[i] == name);
+ return i;
+ }
+ }
+ /* new one */
+ luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues");
+ luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,
+ TString *, MAX_INT, "");
+ while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;
+ f->upvalues[f->nups] = name;
+ luaC_objbarrier(fs->L, f, name);
+ lua_assert(v->k == VLOCAL || v->k == VUPVAL);
+ fs->upvalues[f->nups].k = cast_byte(v->k);
+ fs->upvalues[f->nups].info = cast_byte(v->u.s.info);
+ return f->nups++;
+}
+
+
+static int searchvar (FuncState *fs, TString *n) {
+ int i;
+ for (i=fs->nactvar-1; i >= 0; i--) {
+ if (n == getlocvar(fs, i).varname)
+ return i;
+ }
+ return -1; /* not found */
+}
+
+
+static void markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl && bl->nactvar > level) bl = bl->previous;
+ if (bl) bl->upval = 1;
+}
+
+
+static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+ if (fs == NULL) { /* no more levels? */
+ init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
+ return VGLOBAL;
+ }
+ else {
+ int v = searchvar(fs, n); /* look up at current level */
+ if (v >= 0) {
+ init_exp(var, VLOCAL, v);
+ if (!base)
+ markupval(fs, v); /* local will be used as an upval */
+ return VLOCAL;
+ }
+ else { /* not found at current level; try upper one */
+ if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)
+ return VGLOBAL;
+ var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
+ var->k = VUPVAL; /* upvalue in this level */
+ return VUPVAL;
+ }
+ }
+}
+
+
+static void singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ FuncState *fs = ls->fs;
+ if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
+ var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */
+}
+
+
+static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
+ FuncState *fs = ls->fs;
+ int extra = nvars - nexps;
+ if (hasmultret(e->k)) {
+ extra++; /* includes call itself */
+ if (extra < 0) extra = 0;
+ luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ if (extra > 1) luaK_reserveregs(fs, extra-1);
+ }
+ else {
+ if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */
+ if (extra > 0) {
+ int reg = fs->freereg;
+ luaK_reserveregs(fs, extra);
+ luaK_nil(fs, reg, extra);
+ }
+ }
+}
+
+
+static void enterlevel (LexState *ls) {
+ if (++ls->L->nCcalls > LUAI_MAXCCALLS)
+ luaX_lexerror(ls, "chunk has too many syntax levels", 0);
+}
+
+
+#define leavelevel(ls) ((ls)->L->nCcalls--)
+
+
+static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {
+ bl->breaklist = NO_JUMP;
+ bl->isbreakable = isbreakable;
+ bl->nactvar = fs->nactvar;
+ bl->upval = 0;
+ bl->previous = fs->bl;
+ fs->bl = bl;
+ lua_assert(fs->freereg == fs->nactvar);
+}
+
+
+static void leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ fs->bl = bl->previous;
+ removevars(fs->ls, bl->nactvar);
+ if (bl->upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ /* a block either controls scope or breaks (never both) */
+ lua_assert(!bl->isbreakable || !bl->upval);
+ lua_assert(bl->nactvar == fs->nactvar);
+ fs->freereg = fs->nactvar; /* free registers */
+ luaK_patchtohere(fs, bl->breaklist);
+}
+
+
+static void pushclosure (LexState *ls, FuncState *func, expdesc *v) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizep;
+ int i;
+ luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
+ MAXARG_Bx, "constant table overflow");
+ while (oldsize < f->sizep) f->p[oldsize++] = NULL;
+ f->p[fs->np++] = func->f;
+ luaC_objbarrier(ls->L, f, func->f);
+ init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
+ for (i=0; i<func->f->nups; i++) {
+ OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
+ luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);
+ }
+}
+
+
+static void open_func (LexState *ls, FuncState *fs) {
+ lua_State *L = ls->L;
+ Proto *f = luaF_newproto(L);
+ fs->f = f;
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ fs->L = L;
+ ls->fs = fs;
+ fs->pc = 0;
+ fs->lasttarget = -1;
+ fs->jpc = NO_JUMP;
+ fs->freereg = 0;
+ fs->nk = 0;
+ fs->np = 0;
+ fs->nlocvars = 0;
+ fs->nactvar = 0;
+ fs->bl = NULL;
+ f->source = ls->source;
+ f->maxstacksize = 2; /* registers 0/1 are always valid */
+ fs->h = luaH_new(L, 0, 0);
+ /* anchor table of constants and prototype (to avoid being collected) */
+ sethvalue2s(L, L->top, fs->h);
+ incr_top(L);
+ setptvalue2s(L, L->top, f);
+ incr_top(L);
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ removevars(ls, 0);
+ luaK_ret(fs, 0, 0); /* final return */
+ luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ f->sizecode = fs->pc;
+ luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);
+ f->sizelineinfo = fs->pc;
+ luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);
+ f->sizek = fs->nk;
+ luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);
+ f->sizep = fs->np;
+ luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
+ f->sizelocvars = fs->nlocvars;
+ luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);
+ f->sizeupvalues = f->nups;
+ lua_assert(luaG_checkcode(f));
+ lua_assert(fs->bl == NULL);
+ ls->fs = fs->prev;
+ L->top -= 2; /* remove table and prototype from the stack */
+ /* last token read was anchored in defunct function; must reanchor it */
+ if (fs) anchor_token(ls);
+}
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
+ struct LexState lexstate;
+ struct FuncState funcstate;
+ lexstate.buff = buff;
+ luaX_setinput(L, &lexstate, z, luaS_new(L, name));
+ open_func(&lexstate, &funcstate);
+ funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
+ luaX_next(&lexstate); /* read first token */
+ chunk(&lexstate);
+ check(&lexstate, TK_EOS);
+ close_func(&lexstate);
+ lua_assert(funcstate.prev == NULL);
+ lua_assert(funcstate.f->nups == 0);
+ lua_assert(lexstate.fs == NULL);
+ return funcstate.f;
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+static void field (LexState *ls, expdesc *v) {
+ /* field -> ['.' | ':'] NAME */
+ FuncState *fs = ls->fs;
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ luaX_next(ls); /* skip the dot or colon */
+ checkname(ls, &key);
+ luaK_indexed(fs, v, &key);
+}
+
+
+static void yindex (LexState *ls, expdesc *v) {
+ /* index -> '[' expr ']' */
+ luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ luaK_exp2val(ls->fs, v);
+ checknext(ls, ']');
+}
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+struct ConsControl {
+ expdesc v; /* last list item read */
+ expdesc *t; /* table descriptor */
+ int nh; /* total number of `record' elements */
+ int na; /* total number of array elements */
+ int tostore; /* number of array elements pending to be stored */
+};
+
+
+static void recfield (LexState *ls, struct ConsControl *cc) {
+ /* recfield -> (NAME | `['exp1`]') = exp1 */
+ FuncState *fs = ls->fs;
+ int reg = ls->fs->freereg;
+ expdesc key, val;
+ int rkkey;
+ if (ls->t.token == TK_NAME) {
+ luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ checkname(ls, &key);
+ }
+ else /* ls->t.token == '[' */
+ yindex(ls, &key);
+ cc->nh++;
+ checknext(ls, '=');
+ rkkey = luaK_exp2RK(fs, &key);
+ expr(ls, &val);
+ luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));
+ fs->freereg = reg; /* free registers */
+}
+
+
+static void closelistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->v.k == VVOID) return; /* there is no list item */
+ luaK_exp2nextreg(fs, &cc->v);
+ cc->v.k = VVOID;
+ if (cc->tostore == LFIELDS_PER_FLUSH) {
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */
+ cc->tostore = 0; /* no more items pending */
+ }
+}
+
+
+static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->tostore == 0) return;
+ if (hasmultret(cc->v.k)) {
+ luaK_setmultret(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);
+ cc->na--; /* do not count last expression (unknown number of elements) */
+ }
+ else {
+ if (cc->v.k != VVOID)
+ luaK_exp2nextreg(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);
+ }
+}
+
+
+static void listfield (LexState *ls, struct ConsControl *cc) {
+ expr(ls, &cc->v);
+ luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
+ cc->na++;
+ cc->tostore++;
+}
+
+
+static void constructor (LexState *ls, expdesc *t) {
+ /* constructor -> ?? */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+ struct ConsControl cc;
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, VRELOCABLE, pc);
+ init_exp(&cc.v, VVOID, 0); /* no value (yet) */
+ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */
+ checknext(ls, '{');
+ do {
+ lua_assert(cc.v.k == VVOID || cc.tostore > 0);
+ if (ls->t.token == '}') break;
+ closelistfield(fs, &cc);
+ switch(ls->t.token) {
+ case TK_NAME: { /* may be listfields or recfields */
+ luaX_lookahead(ls);
+ if (ls->lookahead.token != '=') /* expression? */
+ listfield(ls, &cc);
+ else
+ recfield(ls, &cc);
+ break;
+ }
+ case '[': { /* constructor_item -> recfield */
+ recfield(ls, &cc);
+ break;
+ }
+ default: { /* constructor_part -> listfield */
+ listfield(ls, &cc);
+ break;
+ }
+ }
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, &cc);
+ SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
+ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
+}
+
+/* }====================================================================== */
+
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ param { `,' param } ] */
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int nparams = 0;
+ f->is_vararg = 0;
+ if (ls->t.token != ')') { /* is `parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_NAME: { /* param -> NAME */
+ new_localvar(ls, str_checkname(ls), nparams++);
+ break;
+ }
+ case TK_DOTS: { /* param -> `...' */
+ luaX_next(ls);
+#if defined(LUA_COMPAT_VARARG)
+ /* use `arg' as default name */
+ new_localvarliteral(ls, "arg", nparams++);
+ f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;
+#endif
+ f->is_vararg |= VARARG_ISVARARG;
+ break;
+ }
+ default: luaX_syntaxerror(ls, "<name> or " LUA_QL("...") " expected");
+ }
+ } while (!f->is_vararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));
+ luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */
+}
+
+
+static void body (LexState *ls, expdesc *e, int needself, int line) {
+ /* body -> `(' parlist `)' chunk END */
+ FuncState new_fs;
+ open_func(ls, &new_fs);
+ new_fs.f->linedefined = line;
+ checknext(ls, '(');
+ if (needself) {
+ new_localvarliteral(ls, "self", 0);
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ chunk(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ close_func(ls);
+ pushclosure(ls, &new_fs, e);
+}
+
+
+static int explist1 (LexState *ls, expdesc *v) {
+ /* explist1 -> expr { `,' expr } */
+ int n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ luaK_exp2nextreg(ls->fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+}
+
+
+static void funcargs (LexState *ls, expdesc *f) {
+ FuncState *fs = ls->fs;
+ expdesc args;
+ int base, nparams;
+ int line = ls->linenumber;
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> `(' [ explist1 ] `)' */
+ if (line != ls->lastline)
+ luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
+ luaX_next(ls);
+ if (ls->t.token == ')') /* arg list is empty? */
+ args.k = VVOID;
+ else {
+ explist1(ls, &args);
+ luaK_setmultret(fs, &args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, &args);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ codestring(ls, &args, ls->t.seminfo.ts);
+ luaX_next(ls); /* must use `seminfo' before `next' */
+ break;
+ }
+ default: {
+ luaX_syntaxerror(ls, "function arguments expected");
+ return;
+ }
+ }
+ lua_assert(f->k == VNONRELOC);
+ base = f->u.s.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = LUA_MULTRET; /* open call */
+ else {
+ if (args.k != VVOID)
+ luaK_exp2nextreg(fs, &args); /* close last argument */
+ nparams = fs->freereg - (base+1);
+ }
+ init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+ luaK_fixline(fs, line);
+ fs->freereg = base+1; /* call remove function and arguments and leaves
+ (unless changed) one result */
+}
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void prefixexp (LexState *ls, expdesc *v) {
+ /* prefixexp -> NAME | '(' expr ')' */
+ switch (ls->t.token) {
+ case '(': {
+ int line = ls->linenumber;
+ luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ luaK_dischargevars(ls->fs, v);
+ return;
+ }
+ case TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ luaX_syntaxerror(ls, "unexpected symbol");
+ return;
+ }
+ }
+}
+
+
+static void primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp ->
+ prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */
+ FuncState *fs = ls->fs;
+ prefixexp(ls, v);
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* field */
+ field(ls, v);
+ break;
+ }
+ case '[': { /* `[' exp1 `]' */
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ yindex(ls, &key);
+ luaK_indexed(fs, v, &key);
+ break;
+ }
+ case ':': { /* `:' NAME funcargs */
+ expdesc key;
+ luaX_next(ls);
+ checkname(ls, &key);
+ luaK_self(fs, v, &key);
+ funcargs(ls, v);
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* funcargs */
+ luaK_exp2nextreg(fs, v);
+ funcargs(ls, v);
+ break;
+ }
+ default: return;
+ }
+ }
+}
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
+ constructor | FUNCTION body | primaryexp */
+ switch (ls->t.token) {
+ case TK_NUMBER: {
+ init_exp(v, VKNUM, 0);
+ v->u.nval = ls->t.seminfo.r;
+ break;
+ }
+ case TK_STRING: {
+ codestring(ls, v, ls->t.seminfo.ts);
+ break;
+ }
+ case TK_NIL: {
+ init_exp(v, VNIL, 0);
+ break;
+ }
+ case TK_TRUE: {
+ init_exp(v, VTRUE, 0);
+ break;
+ }
+ case TK_FALSE: {
+ init_exp(v, VFALSE, 0);
+ break;
+ }
+ case TK_DOTS: { /* vararg */
+ FuncState *fs = ls->fs;
+ check_condition(ls, fs->f->is_vararg,
+ "cannot use " LUA_QL("...") " outside a vararg function");
+ fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case TK_FUNCTION: {
+ luaX_next(ls);
+ body(ls, v, 0, ls->linenumber);
+ return;
+ }
+ default: {
+ primaryexp(ls, v);
+ return;
+ }
+ }
+ luaX_next(ls);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ case '#': return OPR_LEN;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MUL;
+ case '/': return OPR_DIV;
+ case '%': return OPR_MOD;
+ case '^': return OPR_POW;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+static const struct {
+ lu_byte left; /* left priority for each binary operator */
+ lu_byte right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */
+ {10, 9}, {5, 4}, /* power and concat (right associative) */
+ {3, 3}, {3, 3}, /* equality and inequality */
+ {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */
+ {2, 2}, {1, 1} /* logical (and/or) */
+};
+
+#define UNARY_PRIORITY 8 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where `binop' is any binary operator with a priority higher than `limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) {
+ BinOpr op;
+ UnOpr uop;
+ enterlevel(ls);
+ uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) {
+ luaX_next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls->fs, uop, v);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than `limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ luaX_next(ls);
+ luaK_infix(ls->fs, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls->fs, op, v, &v2);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static int block_follow (int token) {
+ switch (token) {
+ case TK_ELSE: case TK_ELSEIF: case TK_END:
+ case TK_UNTIL: case TK_EOS:
+ return 1;
+ default: return 0;
+ }
+}
+
+
+static void block (LexState *ls) {
+ /* block -> chunk */
+ FuncState *fs = ls->fs;
+ BlockCnt bl;
+ enterblock(fs, &bl, 0);
+ chunk(ls);
+ lua_assert(bl.breaklist == NO_JUMP);
+ leaveblock(fs);
+}
+
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+struct LHS_assign {
+ struct LHS_assign *prev;
+ expdesc v; /* variable (global, local, upvalue, or indexed) */
+};
+
+
+/*
+** check whether, in an assignment to a local variable, the local variable
+** is needed in a previous assignment (to a table). If so, save original
+** local value in a safe place and use this safe copy in the previous
+** assignment.
+*/
+static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int extra = fs->freereg; /* eventual position to save local variable */
+ int conflict = 0;
+ for (; lh; lh = lh->prev) {
+ if (lh->v.k == VINDEXED) {
+ if (lh->v.u.s.info == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.info = extra; /* previous assignment will use safe copy */
+ }
+ if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.aux = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ if (conflict) {
+ luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
+ expdesc e;
+ check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
+ "syntax error");
+ if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
+ struct LHS_assign nv;
+ nv.prev = lh;
+ primaryexp(ls, &nv.v);
+ if (nv.v.k == VLOCAL)
+ check_conflict(ls, lh, &nv.v);
+ luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
+ "variables in assignment");
+ assignment(ls, &nv, nvars+1);
+ }
+ else { /* assignment -> `=' explist1 */
+ int nexps;
+ checknext(ls, '=');
+ nexps = explist1(ls, &e);
+ if (nexps != nvars) {
+ adjust_assign(ls, nvars, nexps, &e);
+ if (nexps > nvars)
+ ls->fs->freereg -= nexps - nvars; /* remove extra values */
+ }
+ else {
+ luaK_setoneret(ls->fs, &e); /* close last expression */
+ luaK_storevar(ls->fs, &lh->v, &e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
+ luaK_storevar(ls->fs, &lh->v, &e);
+}
+
+
+static int cond (LexState *ls) {
+ /* cond -> exp */
+ expdesc v;
+ expr(ls, &v); /* read condition */
+ if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */
+ luaK_goiftrue(ls->fs, &v);
+ return v.f;
+}
+
+
+static void breakstat (LexState *ls) {
+ FuncState *fs = ls->fs;
+ BlockCnt *bl = fs->bl;
+ int upval = 0;
+ while (bl && !bl->isbreakable) {
+ upval |= bl->upval;
+ bl = bl->previous;
+ }
+ if (!bl)
+ luaX_syntaxerror(ls, "no loop to break");
+ if (upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int whileinit;
+ int condexit;
+ BlockCnt bl;
+ luaX_next(ls); /* skip WHILE */
+ whileinit = luaK_getlabel(fs);
+ condexit = cond(ls);
+ enterblock(fs, &bl, 1);
+ checknext(ls, TK_DO);
+ block(ls);
+ luaK_patchlist(fs, luaK_jump(fs), whileinit);
+ check_match(ls, TK_END, TK_WHILE, line);
+ leaveblock(fs);
+ luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ int condexit;
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ BlockCnt bl1, bl2;
+ enterblock(fs, &bl1, 1); /* loop block */
+ enterblock(fs, &bl2, 0); /* scope block */
+ luaX_next(ls); /* skip REPEAT */
+ chunk(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ condexit = cond(ls); /* read condition (inside scope block) */
+ if (!bl2.upval) { /* no upvalues? */
+ leaveblock(fs); /* finish scope */
+ luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */
+ }
+ else { /* complete semantics when there are upvalues */
+ breakstat(ls); /* if condition then break */
+ luaK_patchtohere(ls->fs, condexit); /* else... */
+ leaveblock(fs); /* finish scope... */
+ luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */
+ }
+ leaveblock(fs); /* finish loop */
+}
+
+
+static int exp1 (LexState *ls) {
+ expdesc e;
+ int k;
+ expr(ls, &e);
+ k = e.k;
+ luaK_exp2nextreg(ls->fs, &e);
+ return k;
+}
+
+
+static void forbody (LexState *ls, int base, int line, int nvars, int isnum) {
+ /* forbody -> DO block */
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ int prep, endfor;
+ adjustlocalvars(ls, 3); /* control variables */
+ checknext(ls, TK_DO);
+ prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);
+ enterblock(fs, &bl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ luaK_patchtohere(fs, prep);
+ endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :
+ luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);
+ luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */
+ luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);
+}
+
+
+static void fornum (LexState *ls, TString *varname, int line) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ FuncState *fs = ls->fs;
+ int base = fs->freereg;
+ new_localvarliteral(ls, "(for index)", 0);
+ new_localvarliteral(ls, "(for limit)", 1);
+ new_localvarliteral(ls, "(for step)", 2);
+ new_localvar(ls, varname, 3);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));
+ luaK_reserveregs(fs, 1);
+ }
+ forbody(ls, base, line, 1, 1);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME {,NAME} IN explist1 forbody */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nvars = 0;
+ int line;
+ int base = fs->freereg;
+ /* create control variables */
+ new_localvarliteral(ls, "(for generator)", nvars++);
+ new_localvarliteral(ls, "(for state)", nvars++);
+ new_localvarliteral(ls, "(for control)", nvars++);
+ /* create declared variables */
+ new_localvar(ls, indexname, nvars++);
+ while (testnext(ls, ','))
+ new_localvar(ls, str_checkname(ls), nvars++);
+ checknext(ls, TK_IN);
+ line = ls->linenumber;
+ adjust_assign(ls, 3, explist1(ls, &e), &e);
+ luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 3, 0);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ BlockCnt bl;
+ enterblock(fs, &bl, 1); /* scope for loop and control variables */
+ luaX_next(ls); /* skip `for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case TK_IN: forlist(ls, varname); break;
+ default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leaveblock(fs); /* loop scope (`break' jumps to this point) */
+}
+
+
+static int test_then_block (LexState *ls) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ int condexit;
+ luaX_next(ls); /* skip IF or ELSEIF */
+ condexit = cond(ls);
+ checknext(ls, TK_THEN);
+ block(ls); /* `then' part */
+ return condexit;
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ int flist;
+ int escapelist = NO_JUMP;
+ flist = test_then_block(ls); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ flist = test_then_block(ls); /* ELSEIF cond THEN block */
+ }
+ if (ls->t.token == TK_ELSE) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ luaX_next(ls); /* skip ELSE (after patch, for correct line info) */
+ block(ls); /* `else' part */
+ }
+ else
+ luaK_concat(fs, &escapelist, flist);
+ luaK_patchtohere(fs, escapelist);
+ check_match(ls, TK_END, TK_IF, line);
+}
+
+
+static void localfunc (LexState *ls) {
+ expdesc v, b;
+ FuncState *fs = ls->fs;
+ new_localvar(ls, str_checkname(ls), 0);
+ init_exp(&v, VLOCAL, fs->freereg);
+ luaK_reserveregs(fs, 1);
+ adjustlocalvars(ls, 1);
+ body(ls, &b, 0, ls->linenumber);
+ luaK_storevar(fs, &v, &b);
+ /* debug information will only see the variable after this point! */
+ getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
+ int nvars = 0;
+ int nexps;
+ expdesc e;
+ do {
+ new_localvar(ls, str_checkname(ls), nvars++);
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist1(ls, &e);
+ else {
+ e.k = VVOID;
+ nexps = 0;
+ }
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME {field} [`:' NAME] */
+ int needself = 0;
+ singlevar(ls, v);
+ while (ls->t.token == '.')
+ field(ls, v);
+ if (ls->t.token == ':') {
+ needself = 1;
+ field(ls, v);
+ }
+ return needself;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int needself;
+ expdesc v, b;
+ luaX_next(ls); /* skip FUNCTION */
+ needself = funcname(ls, &v);
+ body(ls, &b, needself, line);
+ luaK_storevar(ls->fs, &v, &b);
+ luaK_fixline(ls->fs, line); /* definition `happens' in the first line */
+}
+
+
+static void exprstat (LexState *ls) {
+ /* stat -> func | assignment */
+ FuncState *fs = ls->fs;
+ struct LHS_assign v;
+ primaryexp(ls, &v.v);
+ if (v.v.k == VCALL) /* stat -> func */
+ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
+ else { /* stat -> assignment */
+ v.prev = NULL;
+ assignment(ls, &v, 1);
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN explist */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int first, nret; /* registers with returned values */
+ luaX_next(ls); /* skip RETURN */
+ if (block_follow(ls->t.token) || ls->t.token == ';')
+ first = nret = 0; /* return no values */
+ else {
+ nret = explist1(ls, &e); /* optional return values */
+ if (hasmultret(e.k)) {
+ luaK_setmultret(fs, &e);
+ if (e.k == VCALL && nret == 1) { /* tail call? */
+ SET_OPCODE(getcode(fs,&e), OP_TAILCALL);
+ lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);
+ }
+ first = fs->nactvar;
+ nret = LUA_MULTRET; /* return all values */
+ }
+ else {
+ if (nret == 1) /* only one single value? */
+ first = luaK_exp2anyreg(fs, &e);
+ else {
+ luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */
+ first = fs->nactvar; /* return all `active' values */
+ lua_assert(nret == fs->freereg - first);
+ }
+ }
+ }
+ luaK_ret(fs, first, nret);
+}
+
+
+static int statement (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ switch (ls->t.token) {
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ return 0;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ return 0;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ return 0;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ return 0;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ return 0;
+ }
+ case TK_FUNCTION: {
+ funcstat(ls, line); /* stat -> funcstat */
+ return 0;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ return 0;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ retstat(ls);
+ return 1; /* must be last statement */
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ luaX_next(ls); /* skip BREAK */
+ breakstat(ls);
+ return 1; /* must be last statement */
+ }
+ default: {
+ exprstat(ls);
+ return 0; /* to avoid warnings */
+ }
+ }
+}
+
+
+static void chunk (LexState *ls) {
+ /* chunk -> { stat [`;'] } */
+ int islast = 0;
+ enterlevel(ls);
+ while (!islast && !block_follow(ls->t.token)) {
+ islast = statement(ls);
+ testnext(ls, ';');
+ lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+ ls->fs->freereg >= ls->fs->nactvar);
+ ls->fs->freereg = ls->fs->nactvar; /* free registers */
+ }
+ leavelevel(ls);
+}
+
+/* }====================================================================== */
diff --git a/engines/sword25/util/lua/src/lparser.h b/engines/sword25/util/lua/src/lparser.h
new file mode 100755
index 0000000000..18836afd1c
--- /dev/null
+++ b/engines/sword25/util/lua/src/lparser.h
@@ -0,0 +1,82 @@
+/*
+** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression descriptor
+*/
+
+typedef enum {
+ VVOID, /* no value */
+ VNIL,
+ VTRUE,
+ VFALSE,
+ VK, /* info = index of constant in `k' */
+ VKNUM, /* nval = numerical value */
+ VLOCAL, /* info = local register */
+ VUPVAL, /* info = index of upvalue in `upvalues' */
+ VGLOBAL, /* info = index of table; aux = index of global name in `k' */
+ VINDEXED, /* info = table register; aux = index register (or `k') */
+ VJMP, /* info = instruction pc */
+ VRELOCABLE, /* info = instruction pc */
+ VNONRELOC, /* info = result register */
+ VCALL, /* info = instruction pc */
+ VVARARG /* info = instruction pc */
+} expkind;
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ struct { int info, aux; } s;
+ lua_Number nval;
+ } u;
+ int t; /* patch list of `exit when true' */
+ int f; /* patch list of `exit when false' */
+} expdesc;
+
+
+typedef struct upvaldesc {
+ lu_byte k;
+ lu_byte info;
+} upvaldesc;
+
+
+struct BlockCnt; /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ Table *h; /* table to find (and reuse) elements in `k' */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct lua_State *L; /* copy of the Lua state */
+ struct BlockCnt *bl; /* chain of current blocks */
+ int pc; /* next position to code (equivalent to `ncode') */
+ int lasttarget; /* `pc' of last `jump target' */
+ int jpc; /* list of pending jumps to `pc' */
+ int freereg; /* first free register */
+ int nk; /* number of elements in `k' */
+ int np; /* number of elements in `p' */
+ short nlocvars; /* number of elements in `locvars' */
+ lu_byte nactvar; /* number of active local variables */
+ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
+ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
+} FuncState;
+
+
+LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ const char *name);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/lstate.c b/engines/sword25/util/lua/src/lstate.c
new file mode 100755
index 0000000000..4313b83a0c
--- /dev/null
+++ b/engines/sword25/util/lua/src/lstate.c
@@ -0,0 +1,214 @@
+/*
+** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lstate_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)
+#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
+#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
+
+
+/*
+** Main thread combines a thread state and the global state
+*/
+typedef struct LG {
+ lua_State l;
+ global_State g;
+} LG;
+
+
+
+static void stack_init (lua_State *L1, lua_State *L) {
+ /* initialize CallInfo array */
+ L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
+ L1->ci = L1->base_ci;
+ L1->size_ci = BASIC_CI_SIZE;
+ L1->end_ci = L1->base_ci + L1->size_ci - 1;
+ /* initialize stack array */
+ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
+ L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
+ L1->top = L1->stack;
+ L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
+ /* initialize first ci */
+ L1->ci->func = L1->top;
+ setnilvalue(L1->top++); /* `function' entry for this `ci' */
+ L1->base = L1->ci->base = L1->top;
+ L1->ci->top = L1->top + LUA_MINSTACK;
+}
+
+
+static void freestack (lua_State *L, lua_State *L1) {
+ luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
+ luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+}
+
+
+/*
+** open parts that may cause memory-allocation errors
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ global_State *g = G(L);
+ UNUSED(ud);
+ stack_init(L, L); /* init stack */
+ sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */
+ sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
+ luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */
+ luaT_init(L);
+ luaX_init(L);
+ luaS_fix(luaS_newliteral(L, MEMERRMSG));
+ g->GCthreshold = 4*g->totalbytes;
+}
+
+
+static void preinit_state (lua_State *L, global_State *g) {
+ G(L) = g;
+ L->stack = NULL;
+ L->stacksize = 0;
+ L->errorJmp = NULL;
+ L->hook = NULL;
+ L->hookmask = 0;
+ L->basehookcount = 0;
+ L->allowhook = 1;
+ resethookcount(L);
+ L->openupval = NULL;
+ L->size_ci = 0;
+ L->nCcalls = L->baseCcalls = 0;
+ L->status = 0;
+ L->base_ci = L->ci = NULL;
+ L->savedpc = NULL;
+ L->errfunc = 0;
+ setnilvalue(gt(L));
+}
+
+
+static void close_state (lua_State *L) {
+ global_State *g = G(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_freeall(L); /* collect all objects */
+ lua_assert(g->rootgc == obj2gco(L));
+ lua_assert(g->strt.nuse == 0);
+ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
+ luaZ_freebuffer(L, &g->buff);
+ freestack(L, L);
+ lua_assert(g->totalbytes == sizeof(LG));
+ (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);
+}
+
+
+lua_State *luaE_newthread (lua_State *L) {
+ lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
+ luaC_link(L, obj2gco(L1), LUA_TTHREAD);
+ preinit_state(L1, G(L));
+ stack_init(L1, L); /* init stack */
+ setobj2n(L, gt(L1), gt(L)); /* share table of globals */
+ L1->hookmask = L->hookmask;
+ L1->basehookcount = L->basehookcount;
+ L1->hook = L->hook;
+ resethookcount(L1);
+ lua_assert(iswhite(obj2gco(L1)));
+ return L1;
+}
+
+
+void luaE_freethread (lua_State *L, lua_State *L1) {
+ luaF_close(L1, L1->stack); /* close all upvalues for this thread */
+ lua_assert(L1->openupval == NULL);
+ luai_userstatefree(L1);
+ freestack(L, L1);
+ luaM_freemem(L, fromstate(L1), state_size(lua_State));
+}
+
+
+LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
+ int i;
+ lua_State *L;
+ global_State *g;
+ void *l = (*f)(ud, NULL, 0, state_size(LG));
+ if (l == NULL) return NULL;
+ L = tostate(l);
+ g = &((LG *)L)->g;
+ L->next = NULL;
+ L->tt = LUA_TTHREAD;
+ g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
+ L->marked = luaC_white(g);
+ set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
+ preinit_state(L, g);
+ g->frealloc = f;
+ g->ud = ud;
+ g->mainthread = L;
+ g->uvhead.u.l.prev = &g->uvhead;
+ g->uvhead.u.l.next = &g->uvhead;
+ g->GCthreshold = 0; /* mark it as unfinished state */
+ g->strt.size = 0;
+ g->strt.nuse = 0;
+ g->strt.hash = NULL;
+ setnilvalue(registry(L));
+ luaZ_initbuffer(L, &g->buff);
+ g->panic = NULL;
+ g->gcstate = GCSpause;
+ g->rootgc = obj2gco(L);
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ g->tmudata = NULL;
+ g->totalbytes = sizeof(LG);
+ g->gcpause = LUAI_GCPAUSE;
+ g->gcstepmul = LUAI_GCMUL;
+ g->gcdept = 0;
+ for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
+ if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
+ /* memory allocation error: free partial state */
+ close_state(L);
+ L = NULL;
+ }
+ else
+ luai_userstateopen(L);
+ return L;
+}
+
+
+static void callallgcTM (lua_State *L, void *ud) {
+ UNUSED(ud);
+ luaC_callGCTM(L); /* call GC metamethods for all udata */
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ L = G(L)->mainthread; /* only the main thread can be closed */
+ lua_lock(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_separateudata(L, 1); /* separate udata that have GC metamethods */
+ L->errfunc = 0; /* no error function during GC metamethods */
+ do { /* repeat until no more errors */
+ L->ci = L->base_ci;
+ L->base = L->top = L->ci->base;
+ L->nCcalls = L->baseCcalls = 0;
+ } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);
+ lua_assert(G(L)->tmudata == NULL);
+ luai_userstateclose(L);
+ close_state(L);
+}
+
diff --git a/engines/sword25/util/lua/src/lstate.h b/engines/sword25/util/lua/src/lstate.h
new file mode 100755
index 0000000000..3bc575b6bc
--- /dev/null
+++ b/engines/sword25/util/lua/src/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/* table of globals */
+#define gt(L) (&L->l_gt)
+
+/* registry */
+#define registry(L) (&G(L)->l_registry)
+
+
+/* extra stack space to handle TM calls and some other extras */
+#define EXTRA_STACK 5
+
+
+#define BASIC_CI_SIZE 8
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+
+
+typedef struct stringtable {
+ GCObject **hash;
+ lu_int32 nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** informations about a call
+*/
+typedef struct CallInfo {
+ StkId base; /* base for this function */
+ StkId func; /* function index in the stack */
+ StkId top; /* top for this function */
+ const Instruction *savedpc;
+ int nresults; /* expected number of results from this function */
+ int tailcalls; /* number of tail calls lost under this entry */
+} CallInfo;
+
+
+
+#define curr_func(L) (clvalue(L->ci->func))
+#define ci_func(ci) (clvalue((ci)->func))
+#define f_isLua(ci) (!ci_func(ci)->c.isC)
+#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
+
+
+/*
+** `global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ stringtable strt; /* hash table for strings */
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to `frealloc' */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ int sweepstrgc; /* position of sweep in `strt' */
+ GCObject *rootgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* position of sweep in `rootgc' */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of weak tables (to be cleared) */
+ GCObject *tmudata; /* last element of list of userdata to be GC */
+ Mbuffer buff; /* temporary buffer for string concatentation */
+ lu_mem GCthreshold;
+ lu_mem totalbytes; /* number of bytes currently allocated */
+ lu_mem estimate; /* an estimate of number of bytes actually in use */
+ lu_mem gcdept; /* how much GC is `behind schedule' */
+ int gcpause; /* size of pause between successive GCs */
+ int gcstepmul; /* GC `granularity' */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ TValue l_registry;
+ struct lua_State *mainthread;
+ UpVal uvhead; /* head of double-linked list of all open upvalues */
+ struct Table *mt[NUM_TAGS]; /* metatables for basic types */
+ TString *tmname[TM_N]; /* array with tag-method names */
+} global_State;
+
+
+/*
+** `per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ StkId top; /* first free slot in the stack */
+ StkId base; /* base of current function */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ const Instruction *savedpc; /* `savedpc' of current function */
+ StkId stack_last; /* last free slot in the stack */
+ StkId stack; /* stack base */
+ CallInfo *end_ci; /* points after end of ci array*/
+ CallInfo *base_ci; /* array of CallInfo's */
+ int stacksize;
+ int size_ci; /* size of array `base_ci' */
+ unsigned short nCcalls; /* number of nested C calls */
+ unsigned short baseCcalls; /* nested C calls when resuming coroutine */
+ lu_byte hookmask;
+ lu_byte allowhook;
+ int basehookcount;
+ int hookcount;
+ lua_Hook hook;
+ TValue l_gt; /* table of globals */
+ TValue env; /* temporary place for environments */
+ GCObject *openupval; /* list of open upvalues in this stack */
+ GCObject *gclist;
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+};
+
+
+#define G(L) (L->l_G)
+
+
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+ GCheader gch;
+ union TString ts;
+ union Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct UpVal uv;
+ struct lua_State th; /* thread */
+};
+
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define gco2ts(o) (&rawgco2ts(o)->tsv)
+#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
+#define gco2u(o) (&rawgco2u(o)->uv)
+#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
+#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
+#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define ngcotouv(o) \
+ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
+
+/* macro to convert any Lua object into a GCObject */
+#define obj2gco(v) (cast(GCObject *, (v)))
+
+
+LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+
+#endif
+
diff --git a/engines/sword25/util/lua/src/lstring.c b/engines/sword25/util/lua/src/lstring.c
new file mode 100755
index 0000000000..49113151cc
--- /dev/null
+++ b/engines/sword25/util/lua/src/lstring.c
@@ -0,0 +1,111 @@
+/*
+** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lstring_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+
+void luaS_resize (lua_State *L, int newsize) {
+ GCObject **newhash;
+ stringtable *tb;
+ int i;
+ if (G(L)->gcstate == GCSsweepstring)
+ return; /* cannot resize during GC traverse */
+ newhash = luaM_newvector(L, newsize, GCObject *);
+ tb = &G(L)->strt;
+ for (i=0; i<newsize; i++) newhash[i] = NULL;
+ /* rehash */
+ for (i=0; i<tb->size; i++) {
+ GCObject *p = tb->hash[i];
+ while (p) { /* for each node in the list */
+ GCObject *next = p->gch.next; /* save next */
+ unsigned int h = gco2ts(p)->hash;
+ int h1 = lmod(h, newsize); /* new position */
+ lua_assert(cast_int(h%newsize) == lmod(h, newsize));
+ p->gch.next = newhash[h1]; /* chain it */
+ newhash[h1] = p;
+ p = next;
+ }
+ }
+ luaM_freearray(L, tb->hash, tb->size, TString *);
+ tb->size = newsize;
+ tb->hash = newhash;
+}
+
+
+static TString *newlstr (lua_State *L, const char *str, size_t l,
+ unsigned int h) {
+ TString *ts;
+ stringtable *tb;
+ if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
+ luaM_toobig(L);
+ ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
+ ts->tsv.len = l;
+ ts->tsv.hash = h;
+ ts->tsv.marked = luaC_white(G(L));
+ ts->tsv.tt = LUA_TSTRING;
+ ts->tsv.reserved = 0;
+ memcpy(ts+1, str, l*sizeof(char));
+ ((char *)(ts+1))[l] = '\0'; /* ending 0 */
+ tb = &G(L)->strt;
+ h = lmod(h, tb->size);
+ ts->tsv.next = tb->hash[h]; /* chain new entry */
+ tb->hash[h] = obj2gco(ts);
+ tb->nuse++;
+ if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
+ luaS_resize(L, tb->size*2); /* too crowded */
+ return ts;
+}
+
+
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ GCObject *o;
+ unsigned int h = cast(unsigned int, l); /* seed */
+ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */
+ size_t l1;
+ for (l1=l; l1>=step; l1-=step) /* compute hash */
+ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
+ for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
+ o != NULL;
+ o = o->gch.next) {
+ TString *ts = rawgco2ts(o);
+ if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
+ /* string may be dead */
+ if (isdead(G(L), o)) changewhite(o);
+ return ts;
+ }
+ }
+ return newlstr(L, str, l, h); /* not found */
+}
+
+
+Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {
+ Udata *u;
+ if (s > MAX_SIZET - sizeof(Udata))
+ luaM_toobig(L);
+ u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));
+ u->uv.marked = luaC_white(G(L)); /* is not finalized */
+ u->uv.tt = LUA_TUSERDATA;
+ u->uv.len = s;
+ u->uv.metatable = NULL;
+ u->uv.env = e;
+ /* chain it on udata list (after main thread) */
+ u->uv.next = G(L)->mainthread->next;
+ G(L)->mainthread->next = obj2gco(u);
+ return u;
+}
+
diff --git a/engines/sword25/util/lua/src/lstring.h b/engines/sword25/util/lua/src/lstring.h
new file mode 100755
index 0000000000..73a2ff8b38
--- /dev/null
+++ b/engines/sword25/util/lua/src/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
+
+#define sizeudata(u) (sizeof(union Udata)+(u)->len)
+
+#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
+
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/lstrlib.c b/engines/sword25/util/lua/src/lstrlib.c
new file mode 100755
index 0000000000..ca333ba168
--- /dev/null
+++ b/engines/sword25/util/lua/src/lstrlib.c
@@ -0,0 +1,868 @@
+/*
+** $Id: lstrlib.c,v 1.132.1.3 2007/12/28 15:32:23 roberto Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/* macro to `unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, l);
+ return 1;
+}
+
+
+static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
+ /* relative string position: negative means back from end */
+ return (pos>=0) ? pos : (ptrdiff_t)len+pos+1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
+ ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
+ if (start <= end)
+ lua_pushlstring(L, s+start-1, end-start+1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int str_reverse (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ while (l--) luaL_addchar(&b, s[l]);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_addchar(&b, tolower(uchar(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_addchar(&b, toupper(uchar(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static int str_rep (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ int n = luaL_checkint(L, 2);
+ luaL_buffinit(L, &b);
+ while (n-- > 0)
+ luaL_addlstring(&b, s, l);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
+ ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
+ int n, i;
+ if (posi <= 0) posi = 1;
+ if ((size_t)pose > l) pose = l;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ n = (int)(pose - posi + 1);
+ if (posi + n <= pose) /* overflow? */
+ luaL_error(L, "string slice too long");
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i<n; i++)
+ lua_pushinteger(L, uchar(s[posi+i-1]));
+ return n;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i=1; i<=n; i++) {
+ int c = luaL_checkint(L, i);
+ luaL_argcheck(L, uchar(c) == c, i, "invalid value");
+ luaL_addchar(&b, uchar(c));
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int writer (lua_State *L, const void* b, size_t size, void* B) {
+ (void)L;
+ luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
+ return 0;
+}
+
+
+static int str_dump (lua_State *L) {
+ luaL_Buffer b;
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 1);
+ luaL_buffinit(L,&b);
+ if (lua_dump(L, writer, &b) != 0)
+ luaL_error(L, "unable to dump given function");
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+
+#define CAP_UNFINISHED (-1)
+#define CAP_POSITION (-2)
+
+typedef struct MatchState {
+ const char *src_init; /* init of source string */
+ const char *src_end; /* end (`\0') of source string */
+ lua_State *L;
+ int level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ ptrdiff_t len;
+ } capture[LUA_MAXCAPTURES];
+} MatchState;
+
+
+#define L_ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (MatchState *ms, int l) {
+ l -= '1';
+ if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
+ return luaL_error(ms->L, "invalid capture index");
+ return l;
+}
+
+
+static int capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a `]' */
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
+ if (*(p++) == L_ESC && *p != '\0')
+ p++; /* skip escapes (e.g. `%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break;
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the `^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int singlematch (int c, const char *p, const char *ep) {
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+static const char *matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (*p == 0 || *(p+1) == 0)
+ luaL_error(ms->L, "unbalanced pattern");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p) {
+ init: /* using goto's to optimize tail recursion */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p+1) == ')') /* position capture? */
+ return start_capture(ms, s, p+2, CAP_POSITION);
+ else
+ return start_capture(ms, s, p+1, CAP_UNFINISHED);
+ }
+ case ')': { /* end capture */
+ return end_capture(ms, s, p+1);
+ }
+ case L_ESC: {
+ switch (*(p+1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p+2);
+ if (s == NULL) return NULL;
+ p+=4; goto init; /* else return match(ms, s, p+4); */
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (*p != '[')
+ luaL_error(ms->L, "missing " LUA_QL("[") " after "
+ LUA_QL("%%f") " in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s-1);
+ if (matchbracketclass(uchar(previous), p, ep-1) ||
+ !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
+ p=ep; goto init; /* else return match(ms, s, ep); */
+ }
+ default: {
+ if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p+1)));
+ if (s == NULL) return NULL;
+ p+=2; goto init; /* else return match(ms, s, p+2) */
+ }
+ goto dflt; /* case default */
+ }
+ }
+ }
+ case '\0': { /* end of pattern */
+ return s; /* match succeeded */
+ }
+ case '$': {
+ if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
+ return (s == ms->src_end) ? s : NULL; /* check end of string */
+ else goto dflt;
+ }
+ default: dflt: { /* it is a pattern item */
+ const char *ep = classend(ms, p); /* points to what is next */
+ int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
+ switch (*ep) {
+ case '?': { /* optional */
+ const char *res;
+ if (m && ((res=match(ms, s+1, ep+1)) != NULL))
+ return res;
+ p=ep+1; goto init; /* else return match(ms, s, ep+1); */
+ }
+ case '*': { /* 0 or more repetitions */
+ return max_expand(ms, s, p, ep);
+ }
+ case '+': { /* 1 or more repetitions */
+ return (m ? max_expand(ms, s+1, p, ep) : NULL);
+ }
+ case '-': { /* 0 or more repetitions (minimum) */
+ return min_expand(ms, s, p, ep);
+ }
+ default: {
+ if (!m) return NULL;
+ s++; p=ep; goto init; /* else return match(ms, s+1, ep); */
+ }
+ }
+ }
+ }
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative `l1' */
+ else {
+ const char *init; /* to search for a `*s2' inside `s1' */
+ l2--; /* 1st char will be checked by `memchr' */
+ l1 = l1-l2; /* `s2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct `l1' and `s1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static void push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ if (i >= ms->level) {
+ if (i == 0) /* ms->level == 0, too */
+ lua_pushlstring(ms->L, s, e - s); /* add whole match */
+ else
+ luaL_error(ms->L, "invalid capture index");
+ }
+ else {
+ ptrdiff_t l = ms->capture[i].len;
+ if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
+ if (l == CAP_POSITION)
+ lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
+ else
+ lua_pushlstring(ms->L, ms->capture[i].init, l);
+ }
+}
+
+
+static int push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+static int str_find_aux (lua_State *L, int find) {
+ size_t l1, l2;
+ const char *s = luaL_checklstring(L, 1, &l1);
+ const char *p = luaL_checklstring(L, 2, &l2);
+ ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
+ if (init < 0) init = 0;
+ else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
+ if (find && (lua_toboolean(L, 4) || /* explicit request? */
+ strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
+ /* do a plain search */
+ const char *s2 = lmemfind(s+init, l1-init, p, l2);
+ if (s2) {
+ lua_pushinteger(L, s2-s+1);
+ lua_pushinteger(L, s2-s+l2);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ const char *s1=s+init;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+l1;
+ do {
+ const char *res;
+ ms.level = 0;
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, s1-s+1); /* start */
+ lua_pushinteger(L, res-s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static int str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+static int gmatch_aux (lua_State *L) {
+ MatchState ms;
+ size_t ls;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ const char *p = lua_tostring(L, lua_upvalueindex(2));
+ const char *src;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+ls;
+ for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
+ src <= ms.src_end;
+ src++) {
+ const char *e;
+ ms.level = 0;
+ if ((e = match(&ms, src, p)) != NULL) {
+ lua_Integer newstart = e-s;
+ if (e == src) newstart++; /* empty match? go at least one position */
+ lua_pushinteger(L, newstart);
+ lua_replace(L, lua_upvalueindex(3));
+ return push_captures(&ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int gmatch (lua_State *L) {
+ luaL_checkstring(L, 1);
+ luaL_checkstring(L, 2);
+ lua_settop(L, 2);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+
+static int gfind_nodef (lua_State *L) {
+ return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
+ LUA_QL("string.gmatch"));
+}
+
+
+static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l, i;
+ const char *news = lua_tolstring(ms->L, 3, &l);
+ for (i = 0; i < l; i++) {
+ if (news[i] != L_ESC)
+ luaL_addchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit(uchar(news[i])))
+ luaL_addchar(b, news[i]);
+ else if (news[i] == '0')
+ luaL_addlstring(b, s, e - s);
+ else {
+ push_onecapture(ms, news[i] - '1', s, e);
+ luaL_addvalue(b); /* add capture to accumulated result */
+ }
+ }
+ }
+}
+
+
+static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ lua_State *L = ms->L;
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING: {
+ add_s(ms, b, s, e);
+ return;
+ }
+ case LUA_TFUNCTION: {
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(ms, s, e);
+ lua_call(L, n, 1);
+ break;
+ }
+ case LUA_TTABLE: {
+ push_onecapture(ms, 0, s, e);
+ lua_gettable(L, 3);
+ break;
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1);
+ lua_pushlstring(L, s, e - s); /* keep original text */
+ }
+ else if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
+ luaL_addvalue(b); /* add result to accumulator */
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl;
+ const char *src = luaL_checklstring(L, 1, &srcl);
+ const char *p = luaL_checkstring(L, 2);
+ int tr = lua_type(L, 3);
+ int max_s = luaL_optint(L, 4, srcl+1);
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ int n = 0;
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
+ "string/function/table expected");
+ luaL_buffinit(L, &b);
+ ms.L = L;
+ ms.src_init = src;
+ ms.src_end = src+srcl;
+ while (n < max_s) {
+ const char *e;
+ ms.level = 0;
+ e = match(&ms, src, p);
+ if (e) {
+ n++;
+ add_value(&ms, &b, src, e);
+ }
+ if (e && e>src) /* non empty match? */
+ src = e; /* skip it */
+ else if (src < ms.src_end)
+ luaL_addchar(&b, *src++);
+ else break;
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
+#define MAX_ITEM 512
+/* valid flags in a format specification */
+#define FLAGS "-+ #0"
+/*
+** maximum size of each format specification (such as '%-099.99d')
+** (+10 accounts for %99.99x plus margin of error)
+*/
+#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
+
+
+static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ luaL_addchar(b, '"');
+ while (l--) {
+ switch (*s) {
+ case '"': case '\\': case '\n': {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ break;
+ }
+ case '\r': {
+ luaL_addlstring(b, "\\r", 2);
+ break;
+ }
+ case '\0': {
+ luaL_addlstring(b, "\\000", 4);
+ break;
+ }
+ default: {
+ luaL_addchar(b, *s);
+ break;
+ }
+ }
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
+ const char *p = strfrmt;
+ while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
+ if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
+ luaL_error(L, "invalid format (repeated flags)");
+ if (isdigit(uchar(*p))) p++; /* skip width */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ if (*p == '.') {
+ p++;
+ if (isdigit(uchar(*p))) p++; /* skip precision */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ }
+ if (isdigit(uchar(*p)))
+ luaL_error(L, "invalid format (width or precision too long)");
+ *(form++) = '%';
+ strncpy(form, strfrmt, p - strfrmt + 1);
+ form += p - strfrmt + 1;
+ *form = '\0';
+ return p;
+}
+
+
+static void addintlen (char *form) {
+ size_t l = strlen(form);
+ char spec = form[l - 1];
+ strcpy(form + l - 1, LUA_INTFRMLEN);
+ form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
+ form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
+}
+
+
+static int str_format (lua_State *L) {
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format (`%...') */
+ char buff[MAX_ITEM]; /* to store the formatted item */
+ arg++;
+ strfrmt = scanformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ sprintf(buff, form, (int)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'd': case 'i': {
+ addintlen(form);
+ sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'o': case 'u': case 'x': case 'X': {
+ addintlen(form);
+ sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G': {
+ sprintf(buff, form, (double)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'q': {
+ addquoted(L, &b, arg);
+ continue; /* skip the 'addsize' at the end */
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ if (!strchr(form, '.') && l >= 100) {
+ /* no precision and string is too long to be formatted;
+ keep original string */
+ lua_pushvalue(L, arg);
+ luaL_addvalue(&b);
+ continue; /* skip the `addsize' at the end */
+ }
+ else {
+ sprintf(buff, form, s);
+ break;
+ }
+ }
+ default: { /* also treat cases `pnLlh' */
+ return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
+ LUA_QL("format"), *(strfrmt - 1));
+ }
+ }
+ luaL_addlstring(&b, buff, strlen(buff));
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static const luaL_Reg strlib[] = {
+ {"byte", str_byte},
+ {"char", str_char},
+ {"dump", str_dump},
+ {"find", str_find},
+ {"format", str_format},
+ {"gfind", gfind_nodef},
+ {"gmatch", gmatch},
+ {"gsub", str_gsub},
+ {"len", str_len},
+ {"lower", str_lower},
+ {"match", str_match},
+ {"rep", str_rep},
+ {"reverse", str_reverse},
+ {"sub", str_sub},
+ {"upper", str_upper},
+ {NULL, NULL}
+};
+
+
+static void createmetatable (lua_State *L) {
+ lua_createtable(L, 0, 1); /* create metatable for strings */
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2);
+ lua_setmetatable(L, -2); /* set string metatable */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* string library... */
+ lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** Open string library
+*/
+LUALIB_API int luaopen_string (lua_State *L) {
+ luaL_register(L, LUA_STRLIBNAME, strlib);
+#if defined(LUA_COMPAT_GFIND)
+ lua_getfield(L, -1, "gmatch");
+ lua_setfield(L, -2, "gfind");
+#endif
+ createmetatable(L);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/ltable.c b/engines/sword25/util/lua/src/ltable.c
new file mode 100755
index 0000000000..ec84f4fabc
--- /dev/null
+++ b/engines/sword25/util/lua/src/ltable.c
@@ -0,0 +1,588 @@
+/*
+** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables).
+** Tables keep its elements in two parts: an array part and a hash part.
+** Non-negative integer keys are all candidates to be kept in the array
+** part. The actual size of the array is the largest `n' such that at
+** least half the slots between 0 and n are in use.
+** Hash uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the `original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** Hence even when the load factor reaches 100%, performance remains good.
+*/
+
+#include <math.h>
+#include <string.h>
+
+#define ltable_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "ltable.h"
+
+
+/*
+** max size of array part is 2^MAXBITS
+*/
+#if LUAI_BITSINT > 26
+#define MAXBITS 26
+#else
+#define MAXBITS (LUAI_BITSINT-2)
+#endif
+
+#define MAXASIZE (1 << MAXBITS)
+
+
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+
+#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
+#define hashboolean(t,p) hashpow2(t, p)
+
+
+/*
+** for some types, it is better to avoid modulus by power of 2, as
+** they tend to have many 2 factors.
+*/
+#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+
+#define hashpointer(t,p) hashmod(t, IntPoint(p))
+
+
+/*
+** number of ints inside a lua_Number
+*/
+#define numints cast_int(sizeof(lua_Number)/sizeof(int))
+
+
+
+#define dummynode (&dummynode_)
+
+static const Node dummynode_ = {
+ {{NULL}, LUA_TNIL}, /* value */
+ {{{NULL}, LUA_TNIL, NULL}} /* key */
+};
+
+
+/*
+** hash for lua_Numbers
+*/
+static Node *hashnum (const Table *t, lua_Number n) {
+ unsigned int a[numints];
+ int i;
+ if (luai_numeq(n, 0)) /* avoid problems with -0 */
+ return gnode(t, 0);
+ memcpy(a, &n, sizeof(a));
+ for (i = 1; i < numints; i++) a[0] += a[i];
+ return hashmod(t, a[0]);
+}
+
+
+
+/*
+** returns the `main' position of an element in a table (that is, the index
+** of its hash value)
+*/
+static Node *mainposition (const Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNUMBER:
+ return hashnum(t, nvalue(key));
+ case LUA_TSTRING:
+ return hashstr(t, rawtsvalue(key));
+ case LUA_TBOOLEAN:
+ return hashboolean(t, bvalue(key));
+ case LUA_TLIGHTUSERDATA:
+ return hashpointer(t, pvalue(key));
+ default:
+ return hashpointer(t, gcvalue(key));
+ }
+}
+
+
+/*
+** returns the index for `key' if `key' is an appropriate key to live in
+** the array part of the table, -1 otherwise.
+*/
+static int arrayindex (const TValue *key) {
+ if (ttisnumber(key)) {
+ lua_Number n = nvalue(key);
+ int k;
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), n))
+ return k;
+ }
+ return -1; /* `key' did not match some condition */
+}
+
+
+/*
+** returns the index of a `key' for table traversals. First goes all
+** elements in the array part, then elements in the hash part. The
+** beginning of a traversal is signalled by -1.
+*/
+static int findindex (lua_State *L, Table *t, StkId key) {
+ int i;
+ if (ttisnil(key)) return -1; /* first iteration */
+ i = arrayindex(key);
+ if (0 < i && i <= t->sizearray) /* is `key' inside array part? */
+ return i-1; /* yes; that's the index (corrected to C) */
+ else {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ /* key may be dead already, but it is ok to use it in `next' */
+ if (luaO_rawequalObj(key2tval(n), key) ||
+ (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) &&
+ gcvalue(gkey(n)) == gcvalue(key))) {
+ i = cast_int(n - gnode(t, 0)); /* key index in hash table */
+ /* hash elements are numbered after array ones */
+ return i + t->sizearray;
+ }
+ else n = gnext(n);
+ } while (n);
+ luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */
+ return 0; /* to avoid warnings */
+ }
+}
+
+
+int luaH_next (lua_State *L, Table *t, StkId key) {
+ int i = findindex(L, t, key); /* find original element */
+ for (i++; i < t->sizearray; i++) { /* try first array part */
+ if (!ttisnil(&t->array[i])) { /* a non-nil value? */
+ setnvalue(key, cast_num(i+1));
+ setobj2s(L, key+1, &t->array[i]);
+ return 1;
+ }
+ }
+ for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */
+ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
+ setobj2s(L, key, key2tval(gnode(t, i)));
+ setobj2s(L, key+1, gval(gnode(t, i)));
+ return 1;
+ }
+ }
+ return 0; /* no more elements */
+}
+
+
+/*
+** {=============================================================
+** Rehash
+** ==============================================================
+*/
+
+
+static int computesizes (int nums[], int *narray) {
+ int i;
+ int twotoi; /* 2^i */
+ int a = 0; /* number of elements smaller than 2^i */
+ int na = 0; /* number of elements to go to array part */
+ int n = 0; /* optimal size for array part */
+ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
+ if (nums[i] > 0) {
+ a += nums[i];
+ if (a > twotoi/2) { /* more than half elements present? */
+ n = twotoi; /* optimal size (till now) */
+ na = a; /* all elements smaller than n will go to array part */
+ }
+ }
+ if (a == *narray) break; /* all elements already counted */
+ }
+ *narray = n;
+ lua_assert(*narray/2 <= na && na <= *narray);
+ return na;
+}
+
+
+static int countint (const TValue *key, int *nums) {
+ int k = arrayindex(key);
+ if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */
+ nums[ceillog2(k)]++; /* count as such */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+static int numusearray (const Table *t, int *nums) {
+ int lg;
+ int ttlg; /* 2^lg */
+ int ause = 0; /* summation of `nums' */
+ int i = 1; /* count to traverse all array keys */
+ for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */
+ int lc = 0; /* counter */
+ int lim = ttlg;
+ if (lim > t->sizearray) {
+ lim = t->sizearray; /* adjust upper limit */
+ if (i > lim)
+ break; /* no more elements to count */
+ }
+ /* count elements in range (2^(lg-1), 2^lg] */
+ for (; i <= lim; i++) {
+ if (!ttisnil(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+
+static int numusehash (const Table *t, int *nums, int *pnasize) {
+ int totaluse = 0; /* total number of elements */
+ int ause = 0; /* summation of `nums' */
+ int i = sizenode(t);
+ while (i--) {
+ Node *n = &t->node[i];
+ if (!ttisnil(gval(n))) {
+ ause += countint(key2tval(n), nums);
+ totaluse++;
+ }
+ }
+ *pnasize += ause;
+ return totaluse;
+}
+
+
+static void setarrayvector (lua_State *L, Table *t, int size) {
+ int i;
+ luaM_reallocvector(L, t->array, t->sizearray, size, TValue);
+ for (i=t->sizearray; i<size; i++)
+ setnilvalue(&t->array[i]);
+ t->sizearray = size;
+}
+
+
+static void setnodevector (lua_State *L, Table *t, int size) {
+ int lsize;
+ if (size == 0) { /* no elements to hash part? */
+ t->node = cast(Node *, dummynode); /* use common `dummynode' */
+ lsize = 0;
+ }
+ else {
+ int i;
+ lsize = ceillog2(size);
+ if (lsize > MAXBITS)
+ luaG_runerror(L, "table overflow");
+ size = twoto(lsize);
+ t->node = luaM_newvector(L, size, Node);
+ for (i=0; i<size; i++) {
+ Node *n = gnode(t, i);
+ gnext(n) = NULL;
+ setnilvalue(gkey(n));
+ setnilvalue(gval(n));
+ }
+ }
+ t->lsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, size); /* all positions are free */
+}
+
+
+static void resize (lua_State *L, Table *t, int nasize, int nhsize) {
+ int i;
+ int oldasize = t->sizearray;
+ int oldhsize = t->lsizenode;
+ Node *nold = t->node; /* save old hash ... */
+ if (nasize > oldasize) /* array part must grow? */
+ setarrayvector(L, t, nasize);
+ /* create new hash part with appropriate size */
+ setnodevector(L, t, nhsize);
+ if (nasize < oldasize) { /* array part must shrink? */
+ t->sizearray = nasize;
+ /* re-insert elements from vanishing slice */
+ for (i=nasize; i<oldasize; i++) {
+ if (!ttisnil(&t->array[i]))
+ setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]);
+ }
+ /* shrink array */
+ luaM_reallocvector(L, t->array, oldasize, nasize, TValue);
+ }
+ /* re-insert elements from hash part */
+ for (i = twoto(oldhsize) - 1; i >= 0; i--) {
+ Node *old = nold+i;
+ if (!ttisnil(gval(old)))
+ setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old));
+ }
+ if (nold != dummynode)
+ luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */
+}
+
+
+void luaH_resizearray (lua_State *L, Table *t, int nasize) {
+ int nsize = (t->node == dummynode) ? 0 : sizenode(t);
+ resize(L, t, nasize, nsize);
+}
+
+
+static void rehash (lua_State *L, Table *t, const TValue *ek) {
+ int nasize, na;
+ int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */
+ int i;
+ int totaluse;
+ for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */
+ nasize = numusearray(t, nums); /* count keys in array part */
+ totaluse = nasize; /* all those keys are integer keys */
+ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */
+ /* count extra key */
+ nasize += countint(ek, nums);
+ totaluse++;
+ /* compute new size for array part */
+ na = computesizes(nums, &nasize);
+ /* resize the table to new computed sizes */
+ resize(L, t, nasize, totaluse - na);
+}
+
+
+
+/*
+** }=============================================================
+*/
+
+
+Table *luaH_new (lua_State *L, int narray, int nhash) {
+ Table *t = luaM_new(L, Table);
+ luaC_link(L, obj2gco(t), LUA_TTABLE);
+ t->metatable = NULL;
+ t->flags = cast_byte(~0);
+ /* temporary values (kept only if some malloc fails) */
+ t->array = NULL;
+ t->sizearray = 0;
+ t->lsizenode = 0;
+ t->node = cast(Node *, dummynode);
+ setarrayvector(L, t, narray);
+ setnodevector(L, t, nhash);
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Table *t) {
+ if (t->node != dummynode)
+ luaM_freearray(L, t->node, sizenode(t), Node);
+ luaM_freearray(L, t->array, t->sizearray, TValue);
+ luaM_free(L, t);
+}
+
+
+static Node *getfreepos (Table *t) {
+ while (t->lastfree-- > t->node) {
+ if (ttisnil(gkey(t->lastfree)))
+ return t->lastfree;
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+
+/*
+** inserts a new key into a hash table; first, check whether key's main
+** position is free. If not, check whether colliding node is in its main
+** position or not: if it is not, move colliding node to an empty place and
+** put new key in its main position; otherwise (colliding node is in its main
+** position), new key goes to an empty position.
+*/
+static TValue *newkey (lua_State *L, Table *t, const TValue *key) {
+ Node *mp = mainposition(t, key);
+ if (!ttisnil(gval(mp)) || mp == dummynode) {
+ Node *othern;
+ Node *n = getfreepos(t); /* get a free place */
+ if (n == NULL) { /* cannot find a free place? */
+ rehash(L, t, key); /* grow table */
+ return luaH_set(L, t, key); /* re-insert key into grown table */
+ }
+ lua_assert(n != dummynode);
+ othern = mainposition(t, key2tval(mp));
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (gnext(othern) != mp) othern = gnext(othern); /* find previous */
+ gnext(othern) = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ gnext(mp) = NULL; /* now `mp' is free */
+ setnilvalue(gval(mp));
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ gnext(n) = gnext(mp); /* chain new position */
+ gnext(mp) = n;
+ mp = n;
+ }
+ }
+ gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;
+ luaC_barriert(L, t, key);
+ lua_assert(ttisnil(gval(mp)));
+ return gval(mp);
+}
+
+
+/*
+** search function for integers
+*/
+const TValue *luaH_getnum (Table *t, int key) {
+ /* (1 <= key && key <= t->sizearray) */
+ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
+ return &t->array[key-1];
+ else {
+ lua_Number nk = cast_num(key);
+ Node *n = hashnum(t, nk);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+}
+
+
+/*
+** search function for strings
+*/
+const TValue *luaH_getstr (Table *t, TString *key) {
+ Node *n = hashstr(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+}
+
+
+/*
+** main search function
+*/
+const TValue *luaH_get (Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNIL: return luaO_nilobject;
+ case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));
+ case LUA_TNUMBER: {
+ int k;
+ lua_Number n = nvalue(key);
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
+ return luaH_getnum(t, k); /* use specialized version */
+ /* else go through */
+ }
+ default: {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (luaO_rawequalObj(key2tval(n), key))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+ }
+}
+
+
+TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
+ const TValue *p = luaH_get(t, key);
+ t->flags = 0;
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ if (ttisnil(key)) luaG_runerror(L, "table index is nil");
+ else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
+ luaG_runerror(L, "table index is NaN");
+ return newkey(L, t, key);
+ }
+}
+
+
+TValue *luaH_setnum (lua_State *L, Table *t, int key) {
+ const TValue *p = luaH_getnum(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setnvalue(&k, cast_num(key));
+ return newkey(L, t, &k);
+ }
+}
+
+
+TValue *luaH_setstr (lua_State *L, Table *t, TString *key) {
+ const TValue *p = luaH_getstr(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setsvalue(L, &k, key);
+ return newkey(L, t, &k);
+ }
+}
+
+
+static int unbound_search (Table *t, unsigned int j) {
+ unsigned int i = j; /* i is zero or a present index */
+ j++;
+ /* find `i' and `j' such that i is present and j is not */
+ while (!ttisnil(luaH_getnum(t, j))) {
+ i = j;
+ j *= 2;
+ if (j > cast(unsigned int, MAX_INT)) { /* overflow? */
+ /* table was built with bad purposes: resort to linear search */
+ i = 1;
+ while (!ttisnil(luaH_getnum(t, i))) i++;
+ return i - 1;
+ }
+ }
+ /* now do a binary search between them */
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(luaH_getnum(t, m))) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+/*
+** Try to find a boundary in table `t'. A `boundary' is an integer index
+** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
+*/
+int luaH_getn (Table *t) {
+ unsigned int j = t->sizearray;
+ if (j > 0 && ttisnil(&t->array[j - 1])) {
+ /* there is a boundary in the array part: (binary) search for it */
+ unsigned int i = 0;
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(&t->array[m - 1])) j = m;
+ else i = m;
+ }
+ return i;
+ }
+ /* else must find a boundary in hash part */
+ else if (t->node == dummynode) /* hash part is empty? */
+ return j; /* that is easy... */
+ else return unbound_search(t, j);
+}
+
+
+
+#if defined(LUA_DEBUG)
+
+Node *luaH_mainposition (const Table *t, const TValue *key) {
+ return mainposition(t, key);
+}
+
+int luaH_isdummy (Node *n) { return n == dummynode; }
+
+#endif
diff --git a/engines/sword25/util/lua/src/ltable.h b/engines/sword25/util/lua/src/ltable.h
new file mode 100755
index 0000000000..f5b9d5ead0
--- /dev/null
+++ b/engines/sword25/util/lua/src/ltable.h
@@ -0,0 +1,40 @@
+/*
+** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define gnode(t,i) (&(t)->node[i])
+#define gkey(n) (&(n)->i_key.nk)
+#define gval(n) (&(n)->i_val)
+#define gnext(n) ((n)->i_key.nk.next)
+
+#define key2tval(n) (&(n)->i_key.tvk)
+
+
+LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
+LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);
+LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
+LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
+LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);
+LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);
+LUAI_FUNC void luaH_free (lua_State *L, Table *t);
+LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
+LUAI_FUNC int luaH_getn (Table *t);
+
+
+#if defined(LUA_DEBUG)
+LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
+LUAI_FUNC int luaH_isdummy (Node *n);
+#endif
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/ltablib.c b/engines/sword25/util/lua/src/ltablib.c
new file mode 100755
index 0000000000..06f1c37be1
--- /dev/null
+++ b/engines/sword25/util/lua/src/ltablib.c
@@ -0,0 +1,279 @@
+/*
+** $Id: ltablib.c,v 1.38.1.2 2007/12/28 15:32:23 roberto Exp $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))
+
+
+static int foreachi (lua_State *L) {
+ int i;
+ int n = aux_getn(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ for (i=1; i <= n; i++) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushinteger(L, i); /* 1st argument */
+ lua_rawgeti(L, 1, i); /* 2nd argument */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 1); /* remove nil result */
+ }
+ return 0;
+}
+
+
+static int foreach (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushvalue(L, -3); /* key */
+ lua_pushvalue(L, -3); /* value */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 2); /* remove value and result */
+ }
+ return 0;
+}
+
+
+static int maxn (lua_State *L) {
+ lua_Number max = 0;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pop(L, 1); /* remove value */
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ lua_Number v = lua_tonumber(L, -1);
+ if (v > max) max = v;
+ }
+ }
+ lua_pushnumber(L, max);
+ return 1;
+}
+
+
+static int getn (lua_State *L) {
+ lua_pushinteger(L, aux_getn(L, 1));
+ return 1;
+}
+
+
+static int setn (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+#ifndef luaL_setn
+ luaL_setn(L, 1, luaL_checkint(L, 2));
+#else
+ luaL_error(L, LUA_QL("setn") " is obsolete");
+#endif
+ lua_pushvalue(L, 1);
+ return 1;
+}
+
+
+static int tinsert (lua_State *L) {
+ int e = aux_getn(L, 1) + 1; /* first empty element */
+ int pos; /* where to insert new element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ int i;
+ pos = luaL_checkint(L, 2); /* 2nd argument is the position */
+ if (pos > e) e = pos; /* `grow' array if necessary */
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_rawgeti(L, 1, i-1);
+ lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
+ }
+ }
+ luaL_setn(L, 1, e); /* new size */
+ lua_rawseti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int tremove (lua_State *L) {
+ int e = aux_getn(L, 1);
+ int pos = luaL_optint(L, 2, e);
+ if (!(1 <= pos && pos <= e)) /* position is outside bounds? */
+ return 0; /* nothing to remove */
+ luaL_setn(L, 1, e - 1); /* t.n = n-1 */
+ lua_rawgeti(L, 1, pos); /* result = t[pos] */
+ for ( ;pos<e; pos++) {
+ lua_rawgeti(L, 1, pos+1);
+ lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */
+ }
+ lua_pushnil(L);
+ lua_rawseti(L, 1, e); /* t[e] = nil */
+ return 1;
+}
+
+
+static int tconcat (lua_State *L) {
+ luaL_Buffer b;
+ size_t lsep;
+ int i, last;
+ const char *sep = luaL_optlstring(L, 2, "", &lsep);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i = luaL_optint(L, 3, 1);
+ last = luaL_opt(L, luaL_checkint, 4, luaL_getn(L, 1));
+ luaL_buffinit(L, &b);
+ for (; i <= last; i++) {
+ lua_rawgeti(L, 1, i);
+ luaL_argcheck(L, lua_isstring(L, -1), 1, "table contains non-strings");
+ luaL_addvalue(&b);
+ if (i != last)
+ luaL_addlstring(&b, sep, lsep);
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on `Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+*/
+
+
+static void set2 (lua_State *L, int i, int j) {
+ lua_rawseti(L, 1, i);
+ lua_rawseti(L, 1, j);
+}
+
+static int sort_comp (lua_State *L, int a, int b) {
+ if (!lua_isnil(L, 2)) { /* function? */
+ int res;
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
+ lua_call(L, 2, 1);
+ res = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+ }
+ else /* a < b? */
+ return lua_lessthan(L, a, b);
+}
+
+static void auxsort (lua_State *L, int l, int u) {
+ while (l < u) { /* for tail recursion */
+ int i, j;
+ /* sort elements a[l], a[(l+u)/2] and a[u] */
+ lua_rawgeti(L, 1, l);
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
+ set2(L, l, u); /* swap a[l] - a[u] */
+ else
+ lua_pop(L, 2);
+ if (u-l == 1) break; /* only 2 elements */
+ i = (l+u)/2;
+ lua_rawgeti(L, 1, i);
+ lua_rawgeti(L, 1, l);
+ if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
+ set2(L, i, l);
+ else {
+ lua_pop(L, 1); /* remove a[l] */
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
+ set2(L, i, u);
+ else
+ lua_pop(L, 2);
+ }
+ if (u-l == 2) break; /* only 3 elements */
+ lua_rawgeti(L, 1, i); /* Pivot */
+ lua_pushvalue(L, -1);
+ lua_rawgeti(L, 1, u-1);
+ set2(L, i, u-1);
+ /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
+ i = l; j = u-1;
+ for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
+ /* repeat ++i until a[i] >= P */
+ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i>u) luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* repeat --j until a[j] <= P */
+ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (j<l) luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ if (j<i) {
+ lua_pop(L, 3); /* pop pivot, a[i], a[j] */
+ break;
+ }
+ set2(L, i, j);
+ }
+ lua_rawgeti(L, 1, u-1);
+ lua_rawgeti(L, 1, i);
+ set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
+ /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
+ /* adjust so that smaller half is in [j..i] and larger one in [l..u] */
+ if (i-l < u-i) {
+ j=l; i=i-1; l=i+2;
+ }
+ else {
+ j=i+1; i=u; u=j-2;
+ }
+ auxsort(L, j, i); /* call recursively the smaller one */
+ } /* repeat the routine for the larger one */
+}
+
+static int sort (lua_State *L) {
+ int n = aux_getn(L, 1);
+ luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */
+ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_settop(L, 2); /* make sure there is two arguments */
+ auxsort(L, 1, n);
+ return 0;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg tab_funcs[] = {
+ {"concat", tconcat},
+ {"foreach", foreach},
+ {"foreachi", foreachi},
+ {"getn", getn},
+ {"maxn", maxn},
+ {"insert", tinsert},
+ {"remove", tremove},
+ {"setn", setn},
+ {"sort", sort},
+ {NULL, NULL}
+};
+
+
+LUALIB_API int luaopen_table (lua_State *L) {
+ luaL_register(L, LUA_TABLIBNAME, tab_funcs);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/src/ltm.c b/engines/sword25/util/lua/src/ltm.c
new file mode 100755
index 0000000000..c27f0f6fab
--- /dev/null
+++ b/engines/sword25/util/lua/src/ltm.c
@@ -0,0 +1,75 @@
+/*
+** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define ltm_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+
+const char *const luaT_typenames[] = {
+ "nil", "boolean", "userdata", "number",
+ "string", "table", "function", "userdata", "thread",
+ "proto", "upval"
+};
+
+
+void luaT_init (lua_State *L) {
+ static const char *const luaT_eventname[] = { /* ORDER TM */
+ "__index", "__newindex",
+ "__gc", "__mode", "__eq",
+ "__add", "__sub", "__mul", "__div", "__mod",
+ "__pow", "__unm", "__len", "__lt", "__le",
+ "__concat", "__call"
+ };
+ int i;
+ for (i=0; i<TM_N; i++) {
+ G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
+ luaS_fix(G(L)->tmname[i]); /* never collect these names */
+ }
+}
+
+
+/*
+** function to be used with macro "fasttm": optimized for absence of
+** tag methods
+*/
+const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
+ const TValue *tm = luaH_getstr(events, ename);
+ lua_assert(event <= TM_EQ);
+ if (ttisnil(tm)) { /* no tag method? */
+ events->flags |= cast_byte(1u<<event); /* cache this fact */
+ return NULL;
+ }
+ else return tm;
+}
+
+
+const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
+ Table *mt;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ mt = hvalue(o)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(o)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(o)];
+ }
+ return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);
+}
+
diff --git a/engines/sword25/util/lua/src/ltm.h b/engines/sword25/util/lua/src/ltm.h
new file mode 100755
index 0000000000..64343b781b
--- /dev/null
+++ b/engines/sword25/util/lua/src/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_EQ, /* last tag method with `fast' access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_MOD,
+ TM_POW,
+ TM_UNM,
+ TM_LEN,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+LUAI_DATA const char *const luaT_typenames[];
+
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+#endif
diff --git a/engines/sword25/util/lua/src/lua.c b/engines/sword25/util/lua/src/lua.c
new file mode 100755
index 0000000000..3a46609328
--- /dev/null
+++ b/engines/sword25/util/lua/src/lua.c
@@ -0,0 +1,392 @@
+/*
+** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lua_c
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static lua_State *globalL = NULL;
+
+static const char *progname = LUA_PROGNAME;
+
+
+
+static void lstop (lua_State *L, lua_Debug *ar) {
+ (void)ar; /* unused arg. */
+ lua_sethook(L, NULL, 0, 0);
+ luaL_error(L, "interrupted!");
+}
+
+
+static void laction (int i) {
+ signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
+ terminate process (default action) */
+ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+
+
+static void print_usage (void) {
+ fprintf(stderr,
+ "usage: %s [options] [script [args]].\n"
+ "Available options are:\n"
+ " -e stat execute string " LUA_QL("stat") "\n"
+ " -l name require library " LUA_QL("name") "\n"
+ " -i enter interactive mode after executing " LUA_QL("script") "\n"
+ " -v show version information\n"
+ " -- stop handling options\n"
+ " - execute stdin and stop handling options\n"
+ ,
+ progname);
+ fflush(stderr);
+}
+
+
+static void l_message (const char *pname, const char *msg) {
+ if (pname) fprintf(stderr, "%s: ", pname);
+ fprintf(stderr, "%s\n", msg);
+ fflush(stderr);
+}
+
+
+static int report (lua_State *L, int status) {
+ if (status && !lua_isnil(L, -1)) {
+ const char *msg = lua_tostring(L, -1);
+ if (msg == NULL) msg = "(error object is not a string)";
+ l_message(progname, msg);
+ lua_pop(L, 1);
+ }
+ return status;
+}
+
+
+static int traceback (lua_State *L) {
+ if (!lua_isstring(L, 1)) /* 'message' not a string? */
+ return 1; /* keep it intact */
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue(L, 1); /* pass error message */
+ lua_pushinteger(L, 2); /* skip this function and traceback */
+ lua_call(L, 2, 1); /* call debug.traceback */
+ return 1;
+}
+
+
+static int docall (lua_State *L, int narg, int clear) {
+ int status;
+ int base = lua_gettop(L) - narg; /* function index */
+ lua_pushcfunction(L, traceback); /* push traceback function */
+ lua_insert(L, base); /* put it under chunk and args */
+ signal(SIGINT, laction);
+ status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
+ signal(SIGINT, SIG_DFL);
+ lua_remove(L, base); /* remove traceback function */
+ /* force a complete garbage collection in case of errors */
+ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+ return status;
+}
+
+
+static void print_version (void) {
+ l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT);
+}
+
+
+static int getargs (lua_State *L, char **argv, int n) {
+ int narg;
+ int i;
+ int argc = 0;
+ while (argv[argc]) argc++; /* count total number of arguments */
+ narg = argc - (n + 1); /* number of arguments to the script */
+ luaL_checkstack(L, narg + 3, "too many arguments to script");
+ for (i=n+1; i < argc; i++)
+ lua_pushstring(L, argv[i]);
+ lua_createtable(L, narg, n + 1);
+ for (i=0; i < argc; i++) {
+ lua_pushstring(L, argv[i]);
+ lua_rawseti(L, -2, i - n);
+ }
+ return narg;
+}
+
+
+static int dofile (lua_State *L, const char *name) {
+ int status = luaL_loadfile(L, name) || docall(L, 0, 1);
+ return report(L, status);
+}
+
+
+static int dostring (lua_State *L, const char *s, const char *name) {
+ int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
+ return report(L, status);
+}
+
+
+static int dolibrary (lua_State *L, const char *name) {
+ lua_getglobal(L, "require");
+ lua_pushstring(L, name);
+ return report(L, docall(L, 1, 1));
+}
+
+
+static const char *get_prompt (lua_State *L, int firstline) {
+ const char *p;
+ lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
+ p = lua_tostring(L, -1);
+ if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
+ lua_pop(L, 1); /* remove global */
+ return p;
+}
+
+
+static int incomplete (lua_State *L, int status) {
+ if (status == LUA_ERRSYNTAX) {
+ size_t lmsg;
+ const char *msg = lua_tolstring(L, -1, &lmsg);
+ const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
+ if (strstr(msg, LUA_QL("<eof>")) == tp) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ return 0; /* else... */
+}
+
+
+static int pushline (lua_State *L, int firstline) {
+ char buffer[LUA_MAXINPUT];
+ char *b = buffer;
+ size_t l;
+ const char *prmt = get_prompt(L, firstline);
+ if (lua_readline(L, b, prmt) == 0)
+ return 0; /* no input */
+ l = strlen(b);
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[l-1] = '\0'; /* remove it */
+ if (firstline && b[0] == '=') /* first line starts with `=' ? */
+ lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
+ else
+ lua_pushstring(L, b);
+ lua_freeline(L, b);
+ return 1;
+}
+
+
+static int loadline (lua_State *L) {
+ int status;
+ lua_settop(L, 0);
+ if (!pushline(L, 1))
+ return -1; /* no input */
+ for (;;) { /* repeat until gets a complete line */
+ status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+ if (!incomplete(L, status)) break; /* cannot try to add lines? */
+ if (!pushline(L, 0)) /* no more input? */
+ return -1;
+ lua_pushliteral(L, "\n"); /* add a new line... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+ lua_saveline(L, 1);
+ lua_remove(L, 1); /* remove line */
+ return status;
+}
+
+
+static void dotty (lua_State *L) {
+ int status;
+ const char *oldprogname = progname;
+ progname = NULL;
+ while ((status = loadline(L)) != -1) {
+ if (status == 0) status = docall(L, 0, 0);
+ report(L, status);
+ if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+ l_message(progname, lua_pushfstring(L,
+ "error calling " LUA_QL("print") " (%s)",
+ lua_tostring(L, -1)));
+ }
+ }
+ lua_settop(L, 0); /* clear stack */
+ fputs("\n", stdout);
+ fflush(stdout);
+ progname = oldprogname;
+}
+
+
+static int handle_script (lua_State *L, char **argv, int n) {
+ int status;
+ const char *fname;
+ int narg = getargs(L, argv, n); /* collect arguments */
+ lua_setglobal(L, "arg");
+ fname = argv[n];
+ if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
+ fname = NULL; /* stdin */
+ status = luaL_loadfile(L, fname);
+ lua_insert(L, -(narg+1));
+ if (status == 0)
+ status = docall(L, narg, 0);
+ else
+ lua_pop(L, narg);
+ return report(L, status);
+}
+
+
+/* check that argument has no extra characters at the end */
+#define notail(x) {if ((x)[2] != '\0') return -1;}
+
+
+static int collectargs (char **argv, int *pi, int *pv, int *pe) {
+ int i;
+ for (i = 1; argv[i] != NULL; i++) {
+ if (argv[i][0] != '-') /* not an option? */
+ return i;
+ switch (argv[i][1]) { /* option */
+ case '-':
+ notail(argv[i]);
+ return (argv[i+1] != NULL ? i+1 : 0);
+ case '\0':
+ return i;
+ case 'i':
+ notail(argv[i]);
+ *pi = 1; /* go through */
+ case 'v':
+ notail(argv[i]);
+ *pv = 1;
+ break;
+ case 'e':
+ *pe = 1; /* go through */
+ case 'l':
+ if (argv[i][2] == '\0') {
+ i++;
+ if (argv[i] == NULL) return -1;
+ }
+ break;
+ default: return -1; /* invalid option */
+ }
+ }
+ return 0;
+}
+
+
+static int runargs (lua_State *L, char **argv, int n) {
+ int i;
+ for (i = 1; i < n; i++) {
+ if (argv[i] == NULL) continue;
+ lua_assert(argv[i][0] == '-');
+ switch (argv[i][1]) { /* option */
+ case 'e': {
+ const char *chunk = argv[i] + 2;
+ if (*chunk == '\0') chunk = argv[++i];
+ lua_assert(chunk != NULL);
+ if (dostring(L, chunk, "=(command line)") != 0)
+ return 1;
+ break;
+ }
+ case 'l': {
+ const char *filename = argv[i] + 2;
+ if (*filename == '\0') filename = argv[++i];
+ lua_assert(filename != NULL);
+ if (dolibrary(L, filename))
+ return 1; /* stop if file fails */
+ break;
+ }
+ default: break;
+ }
+ }
+ return 0;
+}
+
+
+static int handle_luainit (lua_State *L) {
+ const char *init = getenv(LUA_INIT);
+ if (init == NULL) return 0; /* status OK */
+ else if (init[0] == '@')
+ return dofile(L, init+1);
+ else
+ return dostring(L, init, "=" LUA_INIT);
+}
+
+
+struct Smain {
+ int argc;
+ char **argv;
+ int status;
+};
+
+
+static int pmain (lua_State *L) {
+ struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
+ char **argv = s->argv;
+ int script;
+ int has_i = 0, has_v = 0, has_e = 0;
+ globalL = L;
+ if (argv[0] && argv[0][0]) progname = argv[0];
+ lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
+ luaL_openlibs(L); /* open libraries */
+ lua_gc(L, LUA_GCRESTART, 0);
+ s->status = handle_luainit(L);
+ if (s->status != 0) return 0;
+ script = collectargs(argv, &has_i, &has_v, &has_e);
+ if (script < 0) { /* invalid args? */
+ print_usage();
+ s->status = 1;
+ return 0;
+ }
+ if (has_v) print_version();
+ s->status = runargs(L, argv, (script > 0) ? script : s->argc);
+ if (s->status != 0) return 0;
+ if (script)
+ s->status = handle_script(L, argv, script);
+ if (s->status != 0) return 0;
+ if (has_i)
+ dotty(L);
+ else if (script == 0 && !has_e && !has_v) {
+ if (lua_stdin_is_tty()) {
+ print_version();
+ dotty(L);
+ }
+ else dofile(L, NULL); /* executes stdin as a file */
+ }
+ return 0;
+}
+
+
+int main (int argc, char **argv) {
+ int status;
+ struct Smain s;
+ lua_State *L = lua_open(); /* create state */
+ if (L == NULL) {
+ l_message(argv[0], "cannot create state: not enough memory");
+ return EXIT_FAILURE;
+ }
+ s.argc = argc;
+ s.argv = argv;
+ status = lua_cpcall(L, &pmain, &s);
+ report(L, status);
+ lua_close(L);
+ return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/engines/sword25/util/lua/src/lua.h b/engines/sword25/util/lua/src/lua.h
new file mode 100755
index 0000000000..5bc97b746f
--- /dev/null
+++ b/engines/sword25/util/lua/src/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** Lua - An Extensible Extension Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION "Lua 5.1"
+#define LUA_RELEASE "Lua 5.1.3"
+#define LUA_VERSION_NUM 501
+#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
+
+
+/* mark for precompiled code (`<esc>Lua') */
+#define LUA_SIGNATURE "\033Lua"
+
+/* option for multiple returns in `lua_pcall' and `lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** pseudo-indices
+*/
+#define LUA_REGISTRYINDEX (-10000)
+#define LUA_ENVIRONINDEX (-10001)
+#define LUA_GLOBALSINDEX (-10002)
+#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
+
+
+/* thread status; 0 is OK */
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+
+/*
+** functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
+
+
+/*
+** prototype for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_remove) (lua_State *L, int idx);
+LUA_API void (lua_insert) (lua_State *L, int idx);
+LUA_API void (lua_replace) (lua_State *L, int idx);
+LUA_API int (lua_checkstack) (lua_State *L, int sz);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
+
+LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
+LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_objlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void (lua_gettable) (lua_State *L, int idx);
+LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawget) (lua_State *L, int idx);
+LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_getfenv) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setfenv) (lua_State *L, int idx);
+
+
+/*
+** `load' and `call' functions (load and run Lua code)
+*/
+LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
+LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
+LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yield) (lua_State *L, int nresults);
+LUA_API int (lua_resume) (lua_State *L, int narg);
+LUA_API int (lua_status) (lua_State *L);
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_strlen(L,i) lua_objlen(L, (i))
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) \
+ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
+
+#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
+#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+
+/*
+** compatibility macros and functions
+*/
+
+#define lua_open() luaL_newstate()
+
+#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
+
+#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
+
+#define lua_Chunkreader lua_Reader
+#define lua_Chunkwriter lua_Writer
+
+
+/* hack */
+LUA_API void lua_setlevel (lua_State *from, lua_State *to);
+
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILRET 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debuger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
+
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook lua_gethook (lua_State *L);
+LUA_API int lua_gethookmask (lua_State *L);
+LUA_API int lua_gethookcount (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `local', `field', `method' */
+ const char *what; /* (S) `Lua', `C', `main', `tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ int i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
+*
+* 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.
+******************************************************************************/
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/luac.c b/engines/sword25/util/lua/src/luac.c
new file mode 100755
index 0000000000..d07017391b
--- /dev/null
+++ b/engines/sword25/util/lua/src/luac.c
@@ -0,0 +1,200 @@
+/*
+** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $
+** Lua compiler (saves bytecodes to files; also list bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#define PROGNAME "luac" /* default program name */
+#define OUTPUT PROGNAME ".out" /* default output file */
+
+static int listing=0; /* list bytecodes? */
+static int dumping=1; /* dump bytecodes? */
+static int stripping=0; /* strip debug information? */
+static char Output[]={ OUTPUT }; /* default output file name */
+static const char* output=Output; /* actual output file name */
+static const char* progname=PROGNAME; /* actual program name */
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+ fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
+ else
+ fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+ "usage: %s [options] [filenames].\n"
+ "Available options are:\n"
+ " - process stdin\n"
+ " -l list\n"
+ " -o name output to file " LUA_QL("name") " (default is \"%s\")\n"
+ " -p parse only\n"
+ " -s strip debug information\n"
+ " -v show version information\n"
+ " -- stop handling options\n",
+ progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s) (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+ if (*argv[i]!='-') /* end of options; keep it */
+ break;
+ else if (IS("--")) /* end of options; skip it */
+ {
+ ++i;
+ if (version) ++version;
+ break;
+ }
+ else if (IS("-")) /* end of options; use stdin */
+ break;
+ else if (IS("-l")) /* list */
+ ++listing;
+ else if (IS("-o")) /* output file */
+ {
+ output=argv[++i];
+ if (output==NULL || *output==0) usage(LUA_QL("-o") " needs argument");
+ if (IS("-")) output=NULL;
+ }
+ else if (IS("-p")) /* parse only */
+ dumping=0;
+ else if (IS("-s")) /* strip debug information */
+ stripping=1;
+ else if (IS("-v")) /* show version */
+ ++version;
+ else /* unknown option */
+ usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+ dumping=0;
+ argv[--i]=Output;
+ }
+ if (version)
+ {
+ printf("%s %s\n",LUA_RELEASE,LUA_COPYRIGHT);
+ if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+#define toproto(L,i) (clvalue(L->top+(i))->l.p)
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+ return toproto(L,-1);
+ else
+ {
+ int i,pc;
+ Proto* f=luaF_newproto(L);
+ setptvalue2s(L,L->top,f); incr_top(L);
+ f->source=luaS_newliteral(L,"=(" PROGNAME ")");
+ f->maxstacksize=1;
+ pc=2*n+1;
+ f->code=luaM_newvector(L,pc,Instruction);
+ f->sizecode=pc;
+ f->p=luaM_newvector(L,n,Proto*);
+ f->sizep=n;
+ pc=0;
+ for (i=0; i<n; i++)
+ {
+ f->p[i]=toproto(L,i-n-1);
+ f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
+ f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
+ }
+ f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
+ return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+struct Smain {
+ int argc;
+ char** argv;
+};
+
+static int pmain(lua_State* L)
+{
+ struct Smain* s = (struct Smain*)lua_touserdata(L, 1);
+ int argc=s->argc;
+ char** argv=s->argv;
+ const Proto* f;
+ int i;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i<argc; i++)
+ {
+ const char* filename=IS("-") ? NULL : argv[i];
+ if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+ FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+ if (D==NULL) cannot("open");
+ lua_lock(L);
+ luaU_dump(L,f,writer,D,stripping);
+ lua_unlock(L);
+ if (ferror(D)) cannot("write");
+ if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ struct Smain s;
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=lua_open();
+ if (L==NULL) fatal("not enough memory for state");
+ s.argc=argc;
+ s.argv=argv;
+ if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
diff --git a/engines/sword25/util/lua/src/luaconf.h b/engines/sword25/util/lua/src/luaconf.h
new file mode 100755
index 0000000000..803a3f9e7b
--- /dev/null
+++ b/engines/sword25/util/lua/src/luaconf.h
@@ -0,0 +1,763 @@
+/*
+** $Id: luaconf.h,v 1.82.1.6 2008/01/18 17:07:48 roberto Exp $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lconfig_h
+#define lconfig_h
+
+#include <limits.h>
+#include <stddef.h>
+
+
+/*
+** ==================================================================
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+@@ LUA_ANSI controls the use of non-ansi features.
+** CHANGE it (define it) if you want Lua to avoid the use of any
+** non-ansi feature or library.
+*/
+#if defined(__STRICT_ANSI__)
+#define LUA_ANSI
+#endif
+
+
+#if !defined(LUA_ANSI) && defined(_WIN32)
+#define LUA_WIN
+#endif
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#define LUA_USE_READLINE /* needs some extra libraries */
+#endif
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_DL_DYLD /* does not need extra library */
+#endif
+
+
+
+/*
+@@ LUA_USE_POSIX includes all functionallity listed as X/Open System
+@* Interfaces Extension (XSI).
+** CHANGE it (define it) if your system is XSI compatible.
+*/
+#if defined(LUA_USE_POSIX)
+#define LUA_USE_MKSTEMP
+#define LUA_USE_ISATTY
+#define LUA_USE_POPEN
+#define LUA_USE_ULONGJMP
+#endif
+
+
+/*
+@@ LUA_PATH and LUA_CPATH are the names of the environment variables that
+@* Lua check to set its paths.
+@@ LUA_INIT is the name of the environment variable that Lua
+@* checks for initialization code.
+** CHANGE them if you want different names.
+*/
+#define LUA_PATH "LUA_PATH"
+#define LUA_CPATH "LUA_CPATH"
+#define LUA_INIT "LUA_INIT"
+
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+@* Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+@* C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+#if defined(_WIN32)
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+#define LUA_PATH_DEFAULT \
+ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
+#define LUA_CPATH_DEFAULT \
+ ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
+
+#else
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/5.1/"
+#define LUA_CDIR LUA_ROOT "lib/lua/5.1/"
+#define LUA_PATH_DEFAULT \
+ "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua"
+#define LUA_CPATH_DEFAULT \
+ "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
+#endif
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+
+/*
+@@ LUA_PATHSEP is the character that separates templates in a path.
+@@ LUA_PATH_MARK is the string that marks the substitution points in a
+@* template.
+@@ LUA_EXECDIR in a Windows path is replaced by the executable's
+@* directory.
+@@ LUA_IGMARK is a mark to ignore all before it when bulding the
+@* luaopen_ function name.
+** CHANGE them if for some reason your system cannot use those
+** characters. (E.g., if one of those characters is a common character
+** in file/directory names.) Probably you do not need to change them.
+*/
+#define LUA_PATHSEP ";"
+#define LUA_PATH_MARK "?"
+#define LUA_EXECDIR "!"
+#define LUA_IGMARK "-"
+
+
+/*
+@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger.
+** CHANGE that if ptrdiff_t is not adequate on your machine. (On most
+** machines, ptrdiff_t gives a good choice between int or long.)
+*/
+#define LUA_INTEGER ptrdiff_t
+
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all standard library functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL)
+
+#if defined(LUA_CORE) || defined(LUA_LIB)
+#define LUA_API __declspec(dllexport)
+#else
+#define LUA_API __declspec(dllimport)
+#endif
+
+#else
+
+#define LUA_API extern
+
+#endif
+
+/* more often than not the libs go together with the core */
+#define LUALIB_API LUA_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+@* exported to outside modules.
+@@ LUAI_DATA is a mark for all extern (const) variables that are not to
+@* be exported to outside modules.
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library.
+*/
+#if defined(luaall_c)
+#define LUAI_FUNC static
+#define LUAI_DATA /* empty */
+
+#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__)
+#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
+#define LUAI_DATA LUAI_FUNC
+
+#else
+#define LUAI_FUNC extern
+#define LUAI_DATA extern
+#endif
+
+
+
+/*
+@@ LUA_QL describes how error messages quote program elements.
+** CHANGE it if you want a different appearance.
+*/
+#define LUA_QL(x) "'" x "'"
+#define LUA_QS LUA_QL("%s")
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+@* of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+** {==================================================================
+** Stand-alone configuration
+** ===================================================================
+*/
+
+#if defined(lua_c) || defined(luaall_c)
+
+/*
+@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+@* is, whether we're running lua interactively).
+** CHANGE it if you have a better definition for non-POSIX/non-Windows
+** systems.
+*/
+#if defined(LUA_USE_ISATTY)
+#include <unistd.h>
+#define lua_stdin_is_tty() isatty(0)
+#elif defined(LUA_WIN)
+#include <io.h>
+#include <stdio.h>
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+#else
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+#endif
+
+
+/*
+@@ LUA_PROMPT is the default prompt used by stand-alone Lua.
+@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.
+** CHANGE them if you want different prompts. (You can also change the
+** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)
+*/
+#define LUA_PROMPT "> "
+#define LUA_PROMPT2 ">> "
+
+
+/*
+@@ LUA_PROGNAME is the default name for the stand-alone Lua program.
+** CHANGE it if your stand-alone interpreter has a different name and
+** your system is not able to detect that name automatically.
+*/
+#define LUA_PROGNAME "lua"
+
+
+/*
+@@ LUA_MAXINPUT is the maximum length for an input line in the
+@* stand-alone interpreter.
+** CHANGE it if you need longer lines.
+*/
+#define LUA_MAXINPUT 512
+
+
+/*
+@@ lua_readline defines how to show a prompt and then read a line from
+@* the standard input.
+@@ lua_saveline defines how to "save" a read line in a "history".
+@@ lua_freeline defines how to free a line read by lua_readline.
+** CHANGE them if you want to improve this functionality (e.g., by using
+** GNU readline and history facilities).
+*/
+#if defined(LUA_USE_READLINE)
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,idx) \
+ if (lua_strlen(L,idx) > 0) /* non-empty line? */ \
+ add_history(lua_tostring(L, idx)); /* add it to history */
+#define lua_freeline(L,b) ((void)L, free(b))
+#else
+#define lua_readline(L,b,p) \
+ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
+ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
+#define lua_saveline(L,idx) { (void)L; (void)idx; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+#endif
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles
+@* as a percentage.
+** CHANGE it if you want the GC to run faster or slower (higher values
+** mean larger pauses which mean slower collection.) You can also change
+** this value dynamically.
+*/
+#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */
+
+
+/*
+@@ LUAI_GCMUL defines the default speed of garbage collection relative to
+@* memory allocation as a percentage.
+** CHANGE it if you want to change the granularity of the garbage
+** collection. (Higher values mean coarser collections. 0 represents
+** infinity, where each step performs a full collection.) You can also
+** change this value dynamically.
+*/
+#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */
+
+
+
+/*
+@@ LUA_COMPAT_GETN controls compatibility with old getn behavior.
+** CHANGE it (define it) if you want exact compatibility with the
+** behavior of setn/getn in Lua 5.0.
+*/
+#define LUA_COMPAT_GETN // BS25 #undef LUA_COMPAT_GETN
+
+/*
+@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib.
+** CHANGE it to undefined as soon as you do not need a global 'loadlib'
+** function (the function is still available as 'package.loadlib').
+*/
+#undef LUA_COMPAT_LOADLIB
+
+/*
+@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature.
+** CHANGE it to undefined as soon as your programs use only '...' to
+** access vararg parameters (instead of the old 'arg' table).
+*/
+#define LUA_COMPAT_VARARG
+
+/*
+@@ LUA_COMPAT_MOD controls compatibility with old math.mod function.
+** CHANGE it to undefined as soon as your programs use 'math.fmod' or
+** the new '%' operator instead of 'math.mod'.
+*/
+#define LUA_COMPAT_MOD
+
+/*
+@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting
+@* facility.
+** CHANGE it to 2 if you want the old behaviour, or undefine it to turn
+** off the advisory error when nesting [[...]].
+*/
+#define LUA_COMPAT_LSTR 1
+
+/*
+@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name.
+** CHANGE it to undefined as soon as you rename 'string.gfind' to
+** 'string.gmatch'.
+*/
+#define LUA_COMPAT_GFIND
+
+/*
+@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib'
+@* behavior.
+** CHANGE it to undefined as soon as you replace to 'luaL_register'
+** your uses of 'luaL_openlib'
+*/
+#define LUA_COMPAT_OPENLIB
+
+
+
+/*
+@@ luai_apicheck is the assert macro used by the Lua-C API.
+** CHANGE luai_apicheck if you want Lua to perform some checks in the
+** parameters it gets from API calls. This may slow down the interpreter
+** a bit, but may be quite useful when debugging C code that interfaces
+** with Lua. A useful redefinition is to use assert.h.
+*/
+#if defined(LUA_USE_APICHECK)
+#include <assert.h>
+#define luai_apicheck(L,o) { (void)L; assert(o); }
+#else
+#define luai_apicheck(L,o) { (void)L; }
+#endif
+
+
+/*
+@@ LUAI_BITSINT defines the number of bits in an int.
+** CHANGE here if Lua cannot automatically detect the number of bits of
+** your machine. Probably you do not need to change this.
+*/
+/* avoid overflows in comparison */
+#if INT_MAX-20 < 32760
+#define LUAI_BITSINT 16
+#elif INT_MAX > 2147483640L
+/* int has at least 32 bits */
+#define LUAI_BITSINT 32
+#else
+#error "you must define LUA_BITSINT with number of bits in an integer"
+#endif
+
+
+/*
+@@ LUAI_UINT32 is an unsigned integer with at least 32 bits.
+@@ LUAI_INT32 is an signed integer with at least 32 bits.
+@@ LUAI_UMEM is an unsigned integer big enough to count the total
+@* memory used by Lua.
+@@ LUAI_MEM is a signed integer big enough to count the total memory
+@* used by Lua.
+** CHANGE here if for some weird reason the default definitions are not
+** good enough for your machine. (The definitions in the 'else'
+** part always works, but may waste space on machines with 64-bit
+** longs.) Probably you do not need to change this.
+*/
+#if LUAI_BITSINT >= 32
+#define LUAI_UINT32 unsigned int
+#define LUAI_INT32 int
+#define LUAI_MAXINT32 INT_MAX
+#define LUAI_UMEM size_t
+#define LUAI_MEM ptrdiff_t
+#else
+/* 16-bit ints */
+#define LUAI_UINT32 unsigned long
+#define LUAI_INT32 long
+#define LUAI_MAXINT32 LONG_MAX
+#define LUAI_UMEM unsigned long
+#define LUAI_MEM long
+#endif
+
+
+/*
+@@ LUAI_MAXCALLS limits the number of nested calls.
+** CHANGE it if you need really deep recursive calls. This limit is
+** arbitrary; its only purpose is to stop infinite recursion before
+** exhausting memory.
+*/
+#define LUAI_MAXCALLS 20000
+
+
+/*
+@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function
+@* can use.
+** CHANGE it if you need lots of (Lua) stack space for your C
+** functions. This limit is arbitrary; its only purpose is to stop C
+** functions to consume unlimited stack space.
+*/
+#define LUAI_MCS_AUX ((int)(INT_MAX / (4*sizeof(LUA_NUMBER))))
+#define LUAI_MAXCSTACK (LUAI_MCS_AUX > SHRT_MAX ? SHRT_MAX : LUAI_MCS_AUX)
+
+
+
+/*
+** {==================================================================
+** CHANGE (to smaller values) the following definitions if your system
+** has a small C stack. (Or you may want to change them to larger
+** values if your system has a large C stack and these limits are
+** too rigid for you.) Some of these constants control the size of
+** stack-allocated arrays used by the compiler or the interpreter, while
+** others limit the maximum number of recursive calls that the compiler
+** or the interpreter can perform. Values too large may cause a C stack
+** overflow for some forms of deep constructs.
+** ===================================================================
+*/
+
+
+/*
+@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and
+@* syntactical nested non-terminals in a program.
+*/
+#define LUAI_MAXCCALLS 200
+
+
+/*
+@@ LUAI_MAXVARS is the maximum number of local variables per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXVARS 200
+
+
+/*
+@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXUPVALUES 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
+*/
+#define LUAL_BUFFERSIZE BUFSIZ
+
+/* }================================================================== */
+
+
+
+
+/*
+** {==================================================================
+@@ LUA_NUMBER is the type of numbers in Lua.
+** CHANGE the following definitions only if you want to build Lua
+** with a number type different from double. You may also need to
+** change lua_number2int & lua_number2integer.
+** ===================================================================
+*/
+
+#define LUA_NUMBER_DOUBLE
+#define LUA_NUMBER double
+
+/*
+@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'
+@* over a number.
+*/
+#define LUAI_UACNUMBER double
+
+
+/*
+@@ LUA_NUMBER_SCAN is the format for reading numbers.
+@@ LUA_NUMBER_FMT is the format for writing numbers.
+@@ lua_number2str converts a number to a string.
+@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion.
+@@ lua_str2number converts a string to a number.
+*/
+#define LUA_NUMBER_SCAN "%lf"
+#define LUA_NUMBER_FMT "%.14g"
+#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n))
+#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */
+#define lua_str2number(s,p) strtod((s), (p))
+
+
+/*
+@@ The luai_num* macros define the primitive operations over numbers.
+*/
+#if defined(LUA_CORE)
+#include <math.h>
+#define luai_numadd(a,b) ((a)+(b))
+#define luai_numsub(a,b) ((a)-(b))
+#define luai_nummul(a,b) ((a)*(b))
+#define luai_numdiv(a,b) ((a)/(b))
+#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
+#define luai_numpow(a,b) (pow(a,b))
+#define luai_numunm(a) (-(a))
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numlt(a,b) ((a)<(b))
+#define luai_numle(a,b) ((a)<=(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+#endif
+
+
+/*
+@@ lua_number2int is a macro to convert lua_Number to int.
+@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.
+** CHANGE them if you know a faster way to convert a lua_Number to
+** int (with any rounding method and without throwing errors) in your
+** system. In Pentium machines, a naive typecast from double to int
+** in C is extremely slow, so any alternative is worth trying.
+*/
+
+/* On a Pentium, resort to a trick */
+#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \
+ (defined(__i386) || defined (_M_IX86) || defined(__i386__))
+
+/* On a Microsoft compiler, use assembler */
+#if defined(_MSC_VER)
+
+#define lua_number2int(i,d) __asm fld d __asm fistp i
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+/* the next trick should work on any Pentium, but sometimes clashes
+ with a DirectX idiosyncrasy */
+#else
+
+union luai_Cast { double l_d; long l_l; };
+#define lua_number2int(i,d) \
+ { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+#endif
+
+
+/* this option always works, but may be slow */
+#else
+#define lua_number2int(i,d) ((i)=(int)(d))
+#define lua_number2integer(i,d) ((i)=(lua_Integer)(d))
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
+** CHANGE it if your system requires alignments larger than double. (For
+** instance, if your system supports long doubles and they must be
+** aligned in 16-byte boundaries, then you should add long double in the
+** union.) Probably you do not need to change this.
+*/
+#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; }
+
+
+/*
+@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.
+** CHANGE them if you prefer to use longjmp/setjmp even with C++
+** or if want/don't to use _longjmp/_setjmp instead of regular
+** longjmp/setjmp. By default, Lua handles errors with exceptions when
+** compiling as C++ code, with _longjmp/_setjmp when asked to use them,
+** and with longjmp/setjmp otherwise.
+*/
+#if defined(__cplusplus)
+/* C++ exceptions */
+#define LUAI_THROW(L,c) throw(c)
+#define LUAI_TRY(L,c,a) try { a } catch(...) \
+ { if ((c)->status == 0) (c)->status = -1; }
+#define luai_jmpbuf int /* dummy variable */
+
+#elif defined(LUA_USE_ULONGJMP)
+/* in Unix, try _longjmp/_setjmp (more efficient) */
+#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#else
+/* default handling with long jumps */
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#endif
+
+
+/*
+@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern
+@* can do during pattern-matching.
+** CHANGE it if you need more captures. This limit is arbitrary.
+*/
+#define LUA_MAXCAPTURES 32
+
+
+/*
+@@ lua_tmpnam is the function that the OS library uses to create a
+@* temporary name.
+@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam.
+** CHANGE them if you have an alternative to tmpnam (which is considered
+** insecure) or if you want the original tmpnam anyway. By default, Lua
+** uses tmpnam except when POSIX is available, where it uses mkstemp.
+*/
+#if defined(loslib_c) || defined(luaall_c)
+
+#if defined(LUA_USE_MKSTEMP)
+#include <unistd.h>
+#define LUA_TMPNAMBUFSIZE 32
+#define lua_tmpnam(b,e) { \
+ strcpy(b, "/tmp/lua_XXXXXX"); \
+ e = mkstemp(b); \
+ if (e != -1) close(e); \
+ e = (e == -1); }
+
+#else
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+#endif
+
+#endif
+
+
+/*
+@@ lua_popen spawns a new process connected to the current one through
+@* the file streams.
+** CHANGE it if you have a way to implement it in your system.
+*/
+#if defined(LUA_USE_POPEN)
+
+#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m))
+#define lua_pclose(L,file) ((void)L, (pclose(file) != -1))
+
+#elif defined(LUA_WIN)
+
+#define lua_popen(L,c,m) ((void)L, _popen(c,m))
+#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1))
+
+#else
+
+#define lua_popen(L,c,m) ((void)((void)c, m), \
+ luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0)
+#define lua_pclose(L,file) ((void)((void)L, file), 0)
+
+#endif
+
+/*
+@@ LUA_DL_* define which dynamic-library system Lua should use.
+** CHANGE here if Lua has problems choosing the appropriate
+** dynamic-library system for your platform (either Windows' DLL, Mac's
+** dyld, or Unix's dlopen). If your system is some kind of Unix, there
+** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for
+** it. To use dlopen you also need to adapt the src/Makefile (probably
+** adding -ldl to the linker options), so Lua does not select it
+** automatically. (When you change the makefile to add -ldl, you must
+** also add -DLUA_USE_DLOPEN.)
+** If you do not want any kind of dynamic library, undefine all these
+** options.
+** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD.
+*/
+#if defined(LUA_USE_DLOPEN)
+#define LUA_DL_DLOPEN
+#endif
+
+#if defined(LUA_WIN)
+#define LUA_DL_DLL
+#endif
+
+
+/*
+@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State
+@* (the data goes just *before* the lua_State pointer).
+** CHANGE (define) this if you really need that. This value must be
+** a multiple of the maximum alignment required for your machine.
+*/
+#define LUAI_EXTRASPACE 0
+
+
+/*
+@@ luai_userstate* allow user-specific actions on threads.
+** CHANGE them if you defined LUAI_EXTRASPACE and need to do something
+** extra when a thread is created/deleted/resumed/yielded.
+*/
+#define luai_userstateopen(L) ((void)L)
+#define luai_userstateclose(L) ((void)L)
+#define luai_userstatethread(L,L1) ((void)L)
+#define luai_userstatefree(L) ((void)L)
+#define luai_userstateresume(L,n) ((void)L)
+#define luai_userstateyield(L,n) ((void)L)
+
+
+/*
+@@ LUA_INTFRMLEN is the length modifier for integer conversions
+@* in 'string.format'.
+@@ LUA_INTFRM_T is the integer type correspoding to the previous length
+@* modifier.
+** CHANGE them if your system supports long long or does not support long.
+*/
+
+#if defined(LUA_USELONGLONG)
+
+#define LUA_INTFRMLEN "ll"
+#define LUA_INTFRM_T long long
+
+#else
+
+#define LUA_INTFRMLEN "l"
+#define LUA_INTFRM_T long
+
+#endif
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+
+
+#endif
+
diff --git a/engines/sword25/util/lua/src/lualib.h b/engines/sword25/util/lua/src/lualib.h
new file mode 100755
index 0000000000..469417f670
--- /dev/null
+++ b/engines/sword25/util/lua/src/lualib.h
@@ -0,0 +1,53 @@
+/*
+** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+/* Key to file-handle type */
+#define LUA_FILEHANDLE "FILE*"
+
+
+#define LUA_COLIBNAME "coroutine"
+LUALIB_API int (luaopen_base) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUALIB_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUALIB_API int (luaopen_io) (lua_State *L);
+
+#define LUA_OSLIBNAME "os"
+LUALIB_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUALIB_API int (luaopen_string) (lua_State *L);
+
+#define LUA_MATHLIBNAME "math"
+LUALIB_API int (luaopen_math) (lua_State *L);
+
+#define LUA_DBLIBNAME "debug"
+LUALIB_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUALIB_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+
+#ifndef lua_assert
+#define lua_assert(x) ((void)0)
+#endif
+
+
+#endif
diff --git a/engines/sword25/util/lua/src/lundump.c b/engines/sword25/util/lua/src/lundump.c
new file mode 100755
index 0000000000..731c064553
--- /dev/null
+++ b/engines/sword25/util/lua/src/lundump.c
@@ -0,0 +1,225 @@
+/*
+** $Id: lundump.c,v 2.7.1.2 2008/01/18 16:39:11 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#include <string.h>
+
+#define lundump_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstring.h"
+#include "lundump.h"
+#include "lzio.h"
+
+typedef struct {
+ lua_State* L;
+ ZIO* Z;
+ Mbuffer* b;
+ const char* name;
+} LoadState;
+
+#ifdef LUAC_TRUST_BINARIES
+#define IF(c,s)
+#define error(S,s)
+#else
+#define IF(c,s) if (c) error(S,s)
+
+static void error(LoadState* S, const char* why)
+{
+ luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why);
+ luaD_throw(S->L,LUA_ERRSYNTAX);
+}
+#endif
+
+#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size))
+#define LoadByte(S) (lu_byte)LoadChar(S)
+#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x))
+#define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
+
+static void LoadBlock(LoadState* S, void* b, size_t size)
+{
+ size_t r=luaZ_read(S->Z,b,size);
+ UNUSED(r);
+ IF (r!=0, "unexpected end");
+}
+
+static int LoadChar(LoadState* S)
+{
+ char x;
+ LoadVar(S,x);
+ return x;
+}
+
+static int LoadInt(LoadState* S)
+{
+ int x;
+ LoadVar(S,x);
+ IF (x<0, "bad integer");
+ return x;
+}
+
+static lua_Number LoadNumber(LoadState* S)
+{
+ lua_Number x;
+ LoadVar(S,x);
+ return x;
+}
+
+static TString* LoadString(LoadState* S)
+{
+ size_t size;
+ LoadVar(S,size);
+ if (size==0)
+ return NULL;
+ else
+ {
+ char* s=luaZ_openspace(S->L,S->b,size);
+ LoadBlock(S,s,size);
+ return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */
+ }
+}
+
+static void LoadCode(LoadState* S, Proto* f)
+{
+ int n=LoadInt(S);
+ f->code=luaM_newvector(S->L,n,Instruction);
+ f->sizecode=n;
+ LoadVector(S,f->code,n,sizeof(Instruction));
+}
+
+static Proto* LoadFunction(LoadState* S, TString* p);
+
+static void LoadConstants(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ f->k=luaM_newvector(S->L,n,TValue);
+ f->sizek=n;
+ for (i=0; i<n; i++) setnilvalue(&f->k[i]);
+ for (i=0; i<n; i++)
+ {
+ TValue* o=&f->k[i];
+ int t=LoadChar(S);
+ switch (t)
+ {
+ case LUA_TNIL:
+ setnilvalue(o);
+ break;
+ case LUA_TBOOLEAN:
+ setbvalue(o,LoadChar(S));
+ break;
+ case LUA_TNUMBER:
+ setnvalue(o,LoadNumber(S));
+ break;
+ case LUA_TSTRING:
+ setsvalue2n(S->L,o,LoadString(S));
+ break;
+ default:
+ error(S,"bad constant");
+ break;
+ }
+ }
+ n=LoadInt(S);
+ f->p=luaM_newvector(S->L,n,Proto*);
+ f->sizep=n;
+ for (i=0; i<n; i++) f->p[i]=NULL;
+ for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);
+}
+
+static void LoadDebug(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ f->lineinfo=luaM_newvector(S->L,n,int);
+ f->sizelineinfo=n;
+ LoadVector(S,f->lineinfo,n,sizeof(int));
+ n=LoadInt(S);
+ f->locvars=luaM_newvector(S->L,n,LocVar);
+ f->sizelocvars=n;
+ for (i=0; i<n; i++) f->locvars[i].varname=NULL;
+ for (i=0; i<n; i++)
+ {
+ f->locvars[i].varname=LoadString(S);
+ f->locvars[i].startpc=LoadInt(S);
+ f->locvars[i].endpc=LoadInt(S);
+ }
+ n=LoadInt(S);
+ f->upvalues=luaM_newvector(S->L,n,TString*);
+ f->sizeupvalues=n;
+ for (i=0; i<n; i++) f->upvalues[i]=NULL;
+ for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);
+}
+
+static Proto* LoadFunction(LoadState* S, TString* p)
+{
+ Proto* f=luaF_newproto(S->L);
+ setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
+ f->source=LoadString(S); if (f->source==NULL) f->source=p;
+ f->linedefined=LoadInt(S);
+ f->lastlinedefined=LoadInt(S);
+ f->nups=LoadByte(S);
+ f->numparams=LoadByte(S);
+ f->is_vararg=LoadByte(S);
+ f->maxstacksize=LoadByte(S);
+ LoadCode(S,f);
+ LoadConstants(S,f);
+ LoadDebug(S,f);
+ IF (!luaG_checkcode(f), "bad code");
+ S->L->top--;
+ return f;
+}
+
+static void LoadHeader(LoadState* S)
+{
+ char h[LUAC_HEADERSIZE];
+ char s[LUAC_HEADERSIZE];
+ luaU_header(h);
+ LoadBlock(S,s,LUAC_HEADERSIZE);
+ IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header");
+}
+
+/*
+** load precompiled chunk
+*/
+Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
+{
+ LoadState S;
+ if (*name=='@' || *name=='=')
+ S.name=name+1;
+ else if (*name==LUA_SIGNATURE[0])
+ S.name="binary string";
+ else
+ S.name=name;
+ S.L=L;
+ S.Z=Z;
+ S.b=buff;
+ LoadHeader(&S);
+ return LoadFunction(&S,luaS_newliteral(L,"=?"));
+}
+
+/*
+* make header
+*/
+void luaU_header (char* h)
+{
+ int x=1;
+ memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
+ h+=sizeof(LUA_SIGNATURE)-1;
+ *h++=(char)LUAC_VERSION;
+ *h++=(char)LUAC_FORMAT;
+ *h++=(char)*(char*)&x; /* endianness */
+ *h++=(char)sizeof(int);
+ *h++=(char)sizeof(size_t);
+ *h++=(char)sizeof(Instruction);
+ *h++=(char)sizeof(lua_Number);
+ *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */
+}
diff --git a/engines/sword25/util/lua/src/lundump.h b/engines/sword25/util/lua/src/lundump.h
new file mode 100755
index 0000000000..c80189dbff
--- /dev/null
+++ b/engines/sword25/util/lua/src/lundump.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+/* load one chunk; from lundump.c */
+LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
+
+/* make header; from lundump.c */
+LUAI_FUNC void luaU_header (char* h);
+
+/* dump one chunk; from ldump.c */
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip);
+
+#ifdef luac_c
+/* print one chunk; from print.c */
+LUAI_FUNC void luaU_print (const Proto* f, int full);
+#endif
+
+/* for header of binary files -- this is Lua 5.1 */
+#define LUAC_VERSION 0x51
+
+/* for header of binary files -- this is the official format */
+#define LUAC_FORMAT 0
+
+/* size of header of binary files */
+#define LUAC_HEADERSIZE 12
+
+#endif
diff --git a/engines/sword25/util/lua/src/lvm.c b/engines/sword25/util/lua/src/lvm.c
new file mode 100755
index 0000000000..ee3256ab94
--- /dev/null
+++ b/engines/sword25/util/lua/src/lvm.c
@@ -0,0 +1,763 @@
+/*
+** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lvm_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+/* limit for table tag-method chains (to avoid loops) */
+#define MAXTAGLOOP 100
+
+
+const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
+ lua_Number num;
+ if (ttisnumber(obj)) return obj;
+ if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
+ setnvalue(n, num);
+ return n;
+ }
+ else
+ return NULL;
+}
+
+
+int luaV_tostring (lua_State *L, StkId obj) {
+ if (!ttisnumber(obj))
+ return 0;
+ else {
+ char s[LUAI_MAXNUMBER2STR];
+ lua_Number n = nvalue(obj);
+ lua_number2str(s, n);
+ setsvalue2s(L, obj, luaS_new(L, s));
+ return 1;
+ }
+}
+
+
+static void traceexec (lua_State *L, const Instruction *pc) {
+ lu_byte mask = L->hookmask;
+ const Instruction *oldpc = L->savedpc;
+ L->savedpc = pc;
+ if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
+ resethookcount(L);
+ luaD_callhook(L, LUA_HOOKCOUNT, -1);
+ }
+ if (mask & LUA_MASKLINE) {
+ Proto *p = ci_func(L->ci)->l.p;
+ int npc = pcRel(pc, p);
+ int newline = getline(p, npc);
+ /* call linehook when enter a new function, when jump back (loop),
+ or when enter a new line */
+ if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))
+ luaD_callhook(L, LUA_HOOKLINE, newline);
+ }
+}
+
+
+static void callTMres (lua_State *L, StkId res, const TValue *f,
+ const TValue *p1, const TValue *p2) {
+ ptrdiff_t result = savestack(L, res);
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ luaD_checkstack(L, 3);
+ L->top += 3;
+ luaD_call(L, L->top - 3, 1);
+ res = restorestack(L, result);
+ L->top--;
+ setobjs2s(L, res, L->top);
+}
+
+
+
+static void callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3) {
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ setobj2s(L, L->top+3, p3); /* 3th argument */
+ luaD_checkstack(L, 4);
+ L->top += 4;
+ luaD_call(L, L->top - 4, 0);
+}
+
+
+void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t)) { /* `t' is a table? */
+ Table *h = hvalue(t);
+ const TValue *res = luaH_get(h, key); /* do a primitive get */
+ if (!ttisnil(res) || /* result is no nil? */
+ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
+ setobj2s(L, val, res);
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm)) {
+ callTMres(L, val, tm, t, key);
+ return;
+ }
+ t = tm; /* else repeat with `tm' */
+ }
+ luaG_runerror(L, "loop in gettable");
+}
+
+
+void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t)) { /* `t' is a table? */
+ Table *h = hvalue(t);
+ TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
+ if (!ttisnil(oldval) || /* result is no nil? */
+ (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
+ setobj2t(L, oldval, val);
+ luaC_barriert(L, h, val);
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm)) {
+ callTM(L, tm, t, key, val);
+ return;
+ }
+ t = tm; /* else repeat with `tm' */
+ }
+ luaG_runerror(L, "loop in settable");
+}
+
+
+static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
+ if (ttisnil(tm))
+ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
+ if (ttisnil(tm)) return 0;
+ callTMres(L, res, tm, p1, p2);
+ return 1;
+}
+
+
+static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,
+ TMS event) {
+ const TValue *tm1 = fasttm(L, mt1, event);
+ const TValue *tm2;
+ if (tm1 == NULL) return NULL; /* no metamethod */
+ if (mt1 == mt2) return tm1; /* same metatables => same metamethods */
+ tm2 = fasttm(L, mt2, event);
+ if (tm2 == NULL) return NULL; /* no metamethod */
+ if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
+ return tm1;
+ return NULL;
+}
+
+
+static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
+ TMS event) {
+ const TValue *tm1 = luaT_gettmbyobj(L, p1, event);
+ const TValue *tm2;
+ if (ttisnil(tm1)) return -1; /* no metamethod? */
+ tm2 = luaT_gettmbyobj(L, p2, event);
+ if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
+ return -1;
+ callTMres(L, L->top, tm1, p1, p2);
+ return !l_isfalse(L->top);
+}
+
+
+static int l_strcmp (const TString *ls, const TString *rs) {
+ const char *l = getstr(ls);
+ size_t ll = ls->tsv.len;
+ const char *r = getstr(rs);
+ size_t lr = rs->tsv.len;
+ for (;;) {
+ int temp = strcoll(l, r);
+ if (temp != 0) return temp;
+ else { /* strings are equal up to a `\0' */
+ size_t len = strlen(l); /* index of first `\0' in both strings */
+ if (len == lr) /* r is finished? */
+ return (len == ll) ? 0 : 1;
+ else if (len == ll) /* l is finished? */
+ return -1; /* l is smaller than r (because r is not finished) */
+ /* both strings longer than `len'; go on comparing (after the `\0') */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numlt(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
+ else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
+ return res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+static int lessequal (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numle(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
+ else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */
+ return res;
+ else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */
+ return !res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
+ const TValue *tm;
+ lua_assert(ttype(t1) == ttype(t2));
+ switch (ttype(t1)) {
+ case LUA_TNIL: return 1;
+ case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
+ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
+ case LUA_TUSERDATA: {
+ if (uvalue(t1) == uvalue(t2)) return 1;
+ tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,
+ TM_EQ);
+ break; /* will try TM */
+ }
+ case LUA_TTABLE: {
+ if (hvalue(t1) == hvalue(t2)) return 1;
+ tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ default: return gcvalue(t1) == gcvalue(t2);
+ }
+ if (tm == NULL) return 0; /* no TM? */
+ callTMres(L, L->top, tm, t1, t2); /* call TM */
+ return !l_isfalse(L->top);
+}
+
+
+void luaV_concat (lua_State *L, int total, int last) {
+ do {
+ StkId top = L->base + last + 1;
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
+ if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
+ luaG_concaterror(L, top-2, top-1);
+ } else if (tsvalue(top-1)->len == 0) /* second op is empty? */
+ (void)tostring(L, top - 2); /* result is first op (as string) */
+ else {
+ /* at least two string values; get as many as possible */
+ size_t tl = tsvalue(top-1)->len;
+ char *buffer;
+ int i;
+ /* collect total length */
+ for (n = 1; n < total && tostring(L, top-n-1); n++) {
+ size_t l = tsvalue(top-n-1)->len;
+ if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
+ tl += l;
+ }
+ buffer = luaZ_openspace(L, &G(L)->buff, tl);
+ tl = 0;
+ for (i=n; i>0; i--) { /* concat all strings */
+ size_t l = tsvalue(top-i)->len;
+ memcpy(buffer+tl, svalue(top-i), l);
+ tl += l;
+ }
+ setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
+ }
+ total -= n-1; /* got `n' strings to create 1 new */
+ last -= n-1;
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+static void Arith (lua_State *L, StkId ra, const TValue *rb,
+ const TValue *rc, TMS op) {
+ TValue tempb, tempc;
+ const TValue *b, *c;
+ if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
+ (c = luaV_tonumber(rc, &tempc)) != NULL) {
+ lua_Number nb = nvalue(b), nc = nvalue(c);
+ switch (op) {
+ case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
+ case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
+ case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
+ case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;
+ case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;
+ case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
+ case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
+ default: lua_assert(0); break;
+ }
+ }
+ else if (!call_binTM(L, rb, rc, ra, op))
+ luaG_aritherror(L, rb, rc);
+}
+
+
+
+/*
+** some macros for common tasks in `luaV_execute'
+*/
+
+#define runtime_check(L, c) { if (!(c)) break; }
+
+#define RA(i) (base+GETARG_A(i))
+/* to be used after possible stack reallocation */
+#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
+#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
+#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
+#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
+#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))
+
+
+#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);}
+
+
+#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
+
+
+#define arith_op(op,tm) { \
+ TValue *rb = RKB(i); \
+ TValue *rc = RKC(i); \
+ if (ttisnumber(rb) && ttisnumber(rc)) { \
+ lua_Number nb = nvalue(rb), nc = nvalue(rc); \
+ setnvalue(ra, op(nb, nc)); \
+ } \
+ else \
+ Protect(Arith(L, ra, rb, rc, tm)); \
+ }
+
+
+
+void luaV_execute (lua_State *L, int nexeccalls) {
+ LClosure *cl;
+ StkId base;
+ TValue *k;
+ const Instruction *pc;
+ reentry: /* entry point */
+ lua_assert(isLua(L->ci));
+ pc = L->savedpc;
+ cl = &clvalue(L->ci->func)->l;
+ base = L->base;
+ k = cl->p->k;
+ /* main loop of interpreter */
+ for (;;) {
+ const Instruction i = *pc++;
+ StkId ra;
+ if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
+ (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
+ traceexec(L, pc);
+ if (L->status == LUA_YIELD) { /* did hook yield? */
+ L->savedpc = pc - 1;
+ return;
+ }
+ base = L->base;
+ }
+ /* warning!! several calls may realloc the stack and invalidate `ra' */
+ ra = RA(i);
+ lua_assert(base == L->base && L->base == L->ci->base);
+ lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
+ lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
+ switch (GET_OPCODE(i)) {
+ case OP_MOVE: {
+ setobjs2s(L, ra, RB(i));
+ continue;
+ }
+ case OP_LOADK: {
+ setobj2s(L, ra, KBx(i));
+ continue;
+ }
+ case OP_LOADBOOL: {
+ setbvalue(ra, GETARG_B(i));
+ if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
+ continue;
+ }
+ case OP_LOADNIL: {
+ TValue *rb = RB(i);
+ do {
+ setnilvalue(rb--);
+ } while (rb >= ra);
+ continue;
+ }
+ case OP_GETUPVAL: {
+ int b = GETARG_B(i);
+ setobj2s(L, ra, cl->upvals[b]->v);
+ continue;
+ }
+ case OP_GETGLOBAL: {
+ TValue g;
+ TValue *rb = KBx(i);
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(rb));
+ Protect(luaV_gettable(L, &g, rb, ra));
+ continue;
+ }
+ case OP_GETTABLE: {
+ Protect(luaV_gettable(L, RB(i), RKC(i), ra));
+ continue;
+ }
+ case OP_SETGLOBAL: {
+ TValue g;
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(KBx(i)));
+ Protect(luaV_settable(L, &g, KBx(i), ra));
+ continue;
+ }
+ case OP_SETUPVAL: {
+ UpVal *uv = cl->upvals[GETARG_B(i)];
+ setobj(L, uv->v, ra);
+ luaC_barrier(L, uv, ra);
+ continue;
+ }
+ case OP_SETTABLE: {
+ Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
+ continue;
+ }
+ case OP_NEWTABLE: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_SELF: {
+ StkId rb = RB(i);
+ setobjs2s(L, ra+1, rb);
+ Protect(luaV_gettable(L, rb, RKC(i), ra));
+ continue;
+ }
+ case OP_ADD: {
+ arith_op(luai_numadd, TM_ADD);
+ continue;
+ }
+ case OP_SUB: {
+ arith_op(luai_numsub, TM_SUB);
+ continue;
+ }
+ case OP_MUL: {
+ arith_op(luai_nummul, TM_MUL);
+ continue;
+ }
+ case OP_DIV: {
+ arith_op(luai_numdiv, TM_DIV);
+ continue;
+ }
+ case OP_MOD: {
+ arith_op(luai_nummod, TM_MOD);
+ continue;
+ }
+ case OP_POW: {
+ arith_op(luai_numpow, TM_POW);
+ continue;
+ }
+ case OP_UNM: {
+ TValue *rb = RB(i);
+ if (ttisnumber(rb)) {
+ lua_Number nb = nvalue(rb);
+ setnvalue(ra, luai_numunm(nb));
+ }
+ else {
+ Protect(Arith(L, ra, rb, rb, TM_UNM));
+ }
+ continue;
+ }
+ case OP_NOT: {
+ int res = l_isfalse(RB(i)); /* next assignment may change this value */
+ setbvalue(ra, res);
+ continue;
+ }
+ case OP_LEN: {
+ const TValue *rb = RB(i);
+ switch (ttype(rb)) {
+ case LUA_TTABLE: {
+ setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
+ break;
+ }
+ case LUA_TSTRING: {
+ setnvalue(ra, cast_num(tsvalue(rb)->len));
+ break;
+ }
+ default: { /* try metamethod */
+ Protect(
+ if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
+ luaG_typeerror(L, rb, "get length of");
+ )
+ }
+ }
+ continue;
+ }
+ case OP_CONCAT: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));
+ setobjs2s(L, RA(i), base+b);
+ continue;
+ }
+ case OP_JMP: {
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_EQ: {
+ TValue *rb = RKB(i);
+ TValue *rc = RKC(i);
+ Protect(
+ if (equalobj(L, rb, rc) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LT: {
+ Protect(
+ if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LE: {
+ Protect(
+ if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_TEST: {
+ if (l_isfalse(ra) != GETARG_C(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ pc++;
+ continue;
+ }
+ case OP_TESTSET: {
+ TValue *rb = RB(i);
+ if (l_isfalse(rb) != GETARG_C(i)) {
+ setobjs2s(L, ra, rb);
+ dojump(L, pc, GETARG_sBx(*pc));
+ }
+ pc++;
+ continue;
+ }
+ case OP_CALL: {
+ int b = GETARG_B(i);
+ int nresults = GETARG_C(i) - 1;
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ switch (luaD_precall(L, ra, nresults)) {
+ case PCRLUA: {
+ nexeccalls++;
+ goto reentry; /* restart luaV_execute over new Lua function */
+ }
+ case PCRC: {
+ /* it was a C function (`precall' called it); adjust results */
+ if (nresults >= 0) L->top = L->ci->top;
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_TAILCALL: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
+ switch (luaD_precall(L, ra, LUA_MULTRET)) {
+ case PCRLUA: {
+ /* tail call: put new frame in place of previous one */
+ CallInfo *ci = L->ci - 1; /* previous frame */
+ int aux;
+ StkId func = ci->func;
+ StkId pfunc = (ci+1)->func; /* previous function index */
+ if (L->openupval) luaF_close(L, ci->base);
+ L->base = ci->base = ci->func + ((ci+1)->base - pfunc);
+ for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */
+ setobjs2s(L, func+aux, pfunc+aux);
+ ci->top = L->top = func+aux; /* correct top */
+ lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);
+ ci->savedpc = L->savedpc;
+ ci->tailcalls++; /* one more call lost */
+ L->ci--; /* remove new frame */
+ goto reentry;
+ }
+ case PCRC: { /* it was a C function (`precall' called it) */
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_RETURN: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b-1;
+ if (L->openupval) luaF_close(L, base);
+ L->savedpc = pc;
+ b = luaD_poscall(L, ra);
+ if (--nexeccalls == 0) /* was previous function running `here'? */
+ return; /* no: return */
+ else { /* yes: continue its execution */
+ if (b) L->top = L->ci->top;
+ lua_assert(isLua(L->ci));
+ lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);
+ goto reentry;
+ }
+ }
+ case OP_FORLOOP: {
+ lua_Number step = nvalue(ra+2);
+ lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
+ lua_Number limit = nvalue(ra+1);
+ if (luai_numlt(0, step) ? luai_numle(idx, limit)
+ : luai_numle(limit, idx)) {
+ dojump(L, pc, GETARG_sBx(i)); /* jump back */
+ setnvalue(ra, idx); /* update internal index... */
+ setnvalue(ra+3, idx); /* ...and external index */
+ }
+ continue;
+ }
+ case OP_FORPREP: {
+ const TValue *init = ra;
+ const TValue *plimit = ra+1;
+ const TValue *pstep = ra+2;
+ L->savedpc = pc; /* next steps may throw errors */
+ if (!tonumber(init, ra))
+ luaG_runerror(L, LUA_QL("for") " initial value must be a number");
+ else if (!tonumber(plimit, ra+1))
+ luaG_runerror(L, LUA_QL("for") " limit must be a number");
+ else if (!tonumber(pstep, ra+2))
+ luaG_runerror(L, LUA_QL("for") " step must be a number");
+ setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_TFORLOOP: {
+ StkId cb = ra + 3; /* call base */
+ setobjs2s(L, cb+2, ra+2);
+ setobjs2s(L, cb+1, ra+1);
+ setobjs2s(L, cb, ra);
+ L->top = cb+3; /* func. + 2 args (state and index) */
+ Protect(luaD_call(L, cb, GETARG_C(i)));
+ L->top = L->ci->top;
+ cb = RA(i) + 3; /* previous call may change the stack */
+ if (!ttisnil(cb)) { /* continue loop? */
+ setobjs2s(L, cb-1, cb); /* save control variable */
+ dojump(L, pc, GETARG_sBx(*pc)); /* jump back */
+ }
+ pc++;
+ continue;
+ }
+ case OP_SETLIST: {
+ int n = GETARG_B(i);
+ int c = GETARG_C(i);
+ int last;
+ Table *h;
+ if (n == 0) {
+ n = cast_int(L->top - ra) - 1;
+ L->top = L->ci->top;
+ }
+ if (c == 0) c = cast_int(*pc++);
+ runtime_check(L, ttistable(ra));
+ h = hvalue(ra);
+ last = ((c-1)*LFIELDS_PER_FLUSH) + n;
+ if (last > h->sizearray) /* needs more space? */
+ luaH_resizearray(L, h, last); /* pre-alloc it at once */
+ for (; n > 0; n--) {
+ TValue *val = ra+n;
+ setobj2t(L, luaH_setnum(L, h, last--), val);
+ luaC_barriert(L, h, val);
+ }
+ continue;
+ }
+ case OP_CLOSE: {
+ luaF_close(L, ra);
+ continue;
+ }
+ case OP_CLOSURE: {
+ Proto *p;
+ Closure *ncl;
+ int nup, j;
+ p = cl->p->p[GETARG_Bx(i)];
+ nup = p->nups;
+ ncl = luaF_newLclosure(L, nup, cl->env);
+ ncl->l.p = p;
+ for (j=0; j<nup; j++, pc++) {
+ if (GET_OPCODE(*pc) == OP_GETUPVAL)
+ ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
+ else {
+ lua_assert(GET_OPCODE(*pc) == OP_MOVE);
+ ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
+ }
+ }
+ setclvalue(L, ra, ncl);
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_VARARG: {
+ int b = GETARG_B(i) - 1;
+ int j;
+ CallInfo *ci = L->ci;
+ int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;
+ if (b == LUA_MULTRET) {
+ Protect(luaD_checkstack(L, n));
+ ra = RA(i); /* previous call may change the stack */
+ b = n;
+ L->top = ra + n;
+ }
+ for (j = 0; j < b; j++) {
+ if (j < n) {
+ setobjs2s(L, ra + j, ci->base - n + j);
+ }
+ else {
+ setnilvalue(ra + j);
+ }
+ }
+ continue;
+ }
+ }
+ }
+}
+
diff --git a/engines/sword25/util/lua/src/lvm.h b/engines/sword25/util/lua/src/lvm.h
new file mode 100755
index 0000000000..bfe4f5678d
--- /dev/null
+++ b/engines/sword25/util/lua/src/lvm.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))
+
+#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \
+ (((o) = luaV_tonumber(o,n)) != NULL))
+
+#define equalobj(L,o1,o2) \
+ (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))
+
+
+LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);
+LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);
+LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);
+LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);
+LUAI_FUNC void luaV_concat (lua_State *L, int total, int last);
+
+#endif
diff --git a/engines/sword25/util/lua/src/lzio.c b/engines/sword25/util/lua/src/lzio.c
new file mode 100755
index 0000000000..293edd59b0
--- /dev/null
+++ b/engines/sword25/util/lua/src/lzio.c
@@ -0,0 +1,82 @@
+/*
+** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lzio_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "llimits.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+int luaZ_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0) return EOZ;
+ z->n = size - 1;
+ z->p = buff;
+ return char2int(*(z->p++));
+}
+
+
+int luaZ_lookahead (ZIO *z) {
+ if (z->n == 0) {
+ if (luaZ_fill(z) == EOZ)
+ return EOZ;
+ else {
+ z->n++; /* luaZ_fill removed first byte; put back it */
+ z->p--;
+ }
+ }
+ return char2int(*z->p);
+}
+
+
+void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t luaZ_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (luaZ_lookahead(z) == EOZ)
+ return n; /* return number of missing bytes */
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {
+ if (n > buff->buffsize) {
+ if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
+ luaZ_resizebuffer(L, buff, n);
+ }
+ return buff->buffer;
+}
+
+
diff --git a/engines/sword25/util/lua/src/lzio.h b/engines/sword25/util/lua/src/lzio.h
new file mode 100755
index 0000000000..51d695d8c1
--- /dev/null
+++ b/engines/sword25/util/lua/src/lzio.h
@@ -0,0 +1,67 @@
+/*
+** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+#include "lmem.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define char2int(c) cast(int, cast(unsigned char, (c)))
+
+#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z))
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define luaZ_buffer(buff) ((buff)->buffer)
+#define luaZ_sizebuffer(buff) ((buff)->buffsize)
+#define luaZ_bufflen(buff) ((buff)->n)
+
+#define luaZ_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define luaZ_resizebuffer(L, buff, size) \
+ (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
+ (buff)->buffsize = size)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);
+LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */
+LUAI_FUNC int luaZ_lookahead (ZIO *z);
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader;
+ void* data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int luaZ_fill (ZIO *z);
+
+#endif
diff --git a/engines/sword25/util/lua/src/print.c b/engines/sword25/util/lua/src/print.c
new file mode 100755
index 0000000000..e240cfc3c6
--- /dev/null
+++ b/engines/sword25/util/lua/src/print.c
@@ -0,0 +1,227 @@
+/*
+** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $
+** print bytecodes
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lundump.h"
+
+#define PrintFunction luaU_print
+
+#define Sizeof(x) ((int)sizeof(x))
+#define VOID(p) ((const void*)(p))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=ts->tsv.len;
+ putchar('"');
+ for (i=0; i<n; i++)
+ {
+ int c=s[i];
+ switch (c)
+ {
+ case '"': printf("\\\""); break;
+ case '\\': printf("\\\\"); break;
+ case '\a': printf("\\a"); break;
+ case '\b': printf("\\b"); break;
+ case '\f': printf("\\f"); break;
+ case '\n': printf("\\n"); break;
+ case '\r': printf("\\r"); break;
+ case '\t': printf("\\t"); break;
+ case '\v': printf("\\v"); break;
+ default: if (isprint((unsigned char)c))
+ putchar(c);
+ else
+ printf("\\%03u",(unsigned char)c);
+ }
+ }
+ putchar('"');
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttype(o))
+ {
+ case LUA_TNIL:
+ printf("nil");
+ break;
+ case LUA_TBOOLEAN:
+ printf(bvalue(o) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf(LUA_NUMBER_FMT,nvalue(o));
+ break;
+ case LUA_TSTRING:
+ PrintString(rawtsvalue(o));
+ break;
+ default: /* cannot happen */
+ printf("? type=%d",ttype(o));
+ break;
+ }
+}
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+ Instruction i=code[pc];
+ OpCode o=GET_OPCODE(i);
+ int a=GETARG_A(i);
+ int b=GETARG_B(i);
+ int c=GETARG_C(i);
+ int bx=GETARG_Bx(i);
+ int sbx=GETARG_sBx(i);
+ int line=getline(f,pc);
+ printf("\t%d\t",pc+1);
+ if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+ printf("%-9s\t",luaP_opnames[o]);
+ switch (getOpMode(o))
+ {
+ case iABC:
+ printf("%d",a);
+ if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b);
+ if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c);
+ break;
+ case iABx:
+ if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx);
+ break;
+ case iAsBx:
+ if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx);
+ break;
+ }
+ switch (o)
+ {
+ case OP_LOADK:
+ printf("\t; "); PrintConstant(f,bx);
+ break;
+ case OP_GETUPVAL:
+ case OP_SETUPVAL:
+ printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-");
+ break;
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL:
+ printf("\t; %s",svalue(&f->k[bx]));
+ break;
+ case OP_GETTABLE:
+ case OP_SELF:
+ if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
+ break;
+ case OP_SETTABLE:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_POW:
+ case OP_EQ:
+ case OP_LT:
+ case OP_LE:
+ if (ISK(b) || ISK(c))
+ {
+ printf("\t; ");
+ if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
+ printf(" ");
+ if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
+ }
+ break;
+ case OP_JMP:
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ printf("\t; to %d",sbx+pc+2);
+ break;
+ case OP_CLOSURE:
+ printf("\t; %p",VOID(f->p[bx]));
+ break;
+ case OP_SETLIST:
+ if (c==0) printf("\t; %d",(int)code[++pc]);
+ else printf("\t; %d",c);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+}
+
+#define SS(x) (x==1)?"":"s"
+#define S(x) x,SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=getstr(f->source);
+ if (*s=='@' || *s=='=')
+ s++;
+ else if (*s==LUA_SIGNATURE[0])
+ s="(bstring)";
+ else
+ s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n",
+ (f->linedefined==0)?"main":"function",s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ f->numparams,f->is_vararg?"+":"",SS(f->numparams),
+ S(f->maxstacksize),S(f->nups));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintConstants(const Proto* f)
+{
+ int i,n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t",i+1);
+ PrintConstant(f,i);
+ printf("\n");
+ }
+}
+
+static void PrintLocals(const Proto* f)
+{
+ int i,n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+}
+
+static void PrintUpvalues(const Proto* f)
+{
+ int i,n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ if (f->upvalues==NULL) return;
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\n",i,getstr(f->upvalues[i]));
+ }
+}
+
+void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full)
+ {
+ PrintConstants(f);
+ PrintLocals(f);
+ PrintUpvalues(f);
+ }
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}
diff --git a/engines/sword25/util/lua/test/README b/engines/sword25/util/lua/test/README
new file mode 100755
index 0000000000..0c7f38bc25
--- /dev/null
+++ b/engines/sword25/util/lua/test/README
@@ -0,0 +1,26 @@
+These are simple tests for Lua. Some of them contain useful code.
+They are meant to be run to make sure Lua is built correctly and also
+to be read, to see how Lua programs look.
+
+Here is a one-line summary of each program:
+
+ bisect.lua bisection method for solving non-linear equations
+ cf.lua temperature conversion table (celsius to farenheit)
+ echo.lua echo command line arguments
+ env.lua environment variables as automatic global variables
+ factorial.lua factorial without recursion
+ fib.lua fibonacci function with cache
+ fibfor.lua fibonacci numbers with coroutines and generators
+ globals.lua report global variable usage
+ hello.lua the first program in every language
+ life.lua Conway's Game of Life
+ luac.lua bare-bones luac
+ printf.lua an implementation of printf
+ readonly.lua make global variables readonly
+ sieve.lua the sieve of of Eratosthenes programmed with coroutines
+ sort.lua two implementations of a sort function
+ table.lua make table, grouping all data for the same item
+ trace-calls.lua trace calls
+ trace-globals.lua trace assigments to global variables
+ xd.lua hex dump
+
diff --git a/engines/sword25/util/lua/test/bisect.lua b/engines/sword25/util/lua/test/bisect.lua
new file mode 100755
index 0000000000..f91e69bfba
--- /dev/null
+++ b/engines/sword25/util/lua/test/bisect.lua
@@ -0,0 +1,27 @@
+-- bisection method for solving non-linear equations
+
+delta=1e-6 -- tolerance
+
+function bisect(f,a,b,fa,fb)
+ local c=(a+b)/2
+ io.write(n," c=",c," a=",a," b=",b,"\n")
+ if c==a or c==b or math.abs(a-b)<delta then return c,b-a end
+ n=n+1
+ local fc=f(c)
+ if fa*fc<0 then return bisect(f,a,c,fa,fc) else return bisect(f,c,b,fc,fb) end
+end
+
+-- find root of f in the inverval [a,b]. needs f(a)*f(b)<0
+function solve(f,a,b)
+ n=0
+ local z,e=bisect(f,a,b,f(a),f(b))
+ io.write(string.format("after %d steps, root is %.17g with error %.1e, f=%.1e\n",n,z,e,f(z)))
+end
+
+-- our function
+function f(x)
+ return x*x*x-x-1
+end
+
+-- find zero in [1,2]
+solve(f,1,2)
diff --git a/engines/sword25/util/lua/test/cf.lua b/engines/sword25/util/lua/test/cf.lua
new file mode 100755
index 0000000000..8cda54b981
--- /dev/null
+++ b/engines/sword25/util/lua/test/cf.lua
@@ -0,0 +1,16 @@
+-- temperature conversion table (celsius to farenheit)
+
+for c0=-20,50-1,10 do
+ io.write("C ")
+ for c=c0,c0+10-1 do
+ io.write(string.format("%3.0f ",c))
+ end
+ io.write("\n")
+
+ io.write("F ")
+ for c=c0,c0+10-1 do
+ f=(9/5)*c+32
+ io.write(string.format("%3.0f ",f))
+ end
+ io.write("\n\n")
+end
diff --git a/engines/sword25/util/lua/test/echo.lua b/engines/sword25/util/lua/test/echo.lua
new file mode 100755
index 0000000000..4313439a85
--- /dev/null
+++ b/engines/sword25/util/lua/test/echo.lua
@@ -0,0 +1,5 @@
+-- echo command line arguments
+
+for i=0,table.getn(arg) do
+ print(i,arg[i])
+end
diff --git a/engines/sword25/util/lua/test/env.lua b/engines/sword25/util/lua/test/env.lua
new file mode 100755
index 0000000000..9e62a57fbf
--- /dev/null
+++ b/engines/sword25/util/lua/test/env.lua
@@ -0,0 +1,7 @@
+-- read environment variables as if they were global variables
+
+local f=function (t,i) return os.getenv(i) end
+setmetatable(getfenv(),{__index=f})
+
+-- an example
+print(a,USER,PATH)
diff --git a/engines/sword25/util/lua/test/factorial.lua b/engines/sword25/util/lua/test/factorial.lua
new file mode 100755
index 0000000000..7c4cf0fa45
--- /dev/null
+++ b/engines/sword25/util/lua/test/factorial.lua
@@ -0,0 +1,32 @@
+-- function closures are powerful
+
+-- traditional fixed-point operator from functional programming
+Y = function (g)
+ local a = function (f) return f(f) end
+ return a(function (f)
+ return g(function (x)
+ local c=f(f)
+ return c(x)
+ end)
+ end)
+end
+
+
+-- factorial without recursion
+F = function (f)
+ return function (n)
+ if n == 0 then return 1
+ else return n*f(n-1) end
+ end
+ end
+
+factorial = Y(F) -- factorial is the fixed point of F
+
+-- now test it
+function test(x)
+ io.write(x,"! = ",factorial(x),"\n")
+end
+
+for n=0,16 do
+ test(n)
+end
diff --git a/engines/sword25/util/lua/test/fib.lua b/engines/sword25/util/lua/test/fib.lua
new file mode 100755
index 0000000000..97a921b132
--- /dev/null
+++ b/engines/sword25/util/lua/test/fib.lua
@@ -0,0 +1,40 @@
+-- fibonacci function with cache
+
+-- very inefficient fibonacci function
+function fib(n)
+ N=N+1
+ if n<2 then
+ return n
+ else
+ return fib(n-1)+fib(n-2)
+ end
+end
+
+-- a general-purpose value cache
+function cache(f)
+ local c={}
+ return function (x)
+ local y=c[x]
+ if not y then
+ y=f(x)
+ c[x]=y
+ end
+ return y
+ end
+end
+
+-- run and time it
+function test(s,f)
+ N=0
+ local c=os.clock()
+ local v=f(n)
+ local t=os.clock()-c
+ print(s,n,v,t,N)
+end
+
+n=arg[1] or 24 -- for other values, do lua fib.lua XX
+n=tonumber(n)
+print("","n","value","time","evals")
+test("plain",fib)
+fib=cache(fib)
+test("cached",fib)
diff --git a/engines/sword25/util/lua/test/fibfor.lua b/engines/sword25/util/lua/test/fibfor.lua
new file mode 100755
index 0000000000..8bbba39cd7
--- /dev/null
+++ b/engines/sword25/util/lua/test/fibfor.lua
@@ -0,0 +1,13 @@
+-- example of for with generator functions
+
+function generatefib (n)
+ return coroutine.wrap(function ()
+ local a,b = 1, 1
+ while a <= n do
+ coroutine.yield(a)
+ a, b = b, a+b
+ end
+ end)
+end
+
+for i in generatefib(1000) do print(i) end
diff --git a/engines/sword25/util/lua/test/globals.lua b/engines/sword25/util/lua/test/globals.lua
new file mode 100755
index 0000000000..d4c20e1565
--- /dev/null
+++ b/engines/sword25/util/lua/test/globals.lua
@@ -0,0 +1,13 @@
+-- reads luac listings and reports global variable usage
+-- lines where a global is written to are marked with "*"
+-- typical usage: luac -p -l file.lua | lua globals.lua | sort | lua table.lua
+
+while 1 do
+ local s=io.read()
+ if s==nil then break end
+ local ok,_,l,op,g=string.find(s,"%[%-?(%d*)%]%s*([GS])ETGLOBAL.-;%s+(.*)$")
+ if ok then
+ if op=="S" then op="*" else op="" end
+ io.write(g,"\t",l,op,"\n")
+ end
+end
diff --git a/engines/sword25/util/lua/test/hello.lua b/engines/sword25/util/lua/test/hello.lua
new file mode 100755
index 0000000000..0925498f21
--- /dev/null
+++ b/engines/sword25/util/lua/test/hello.lua
@@ -0,0 +1,3 @@
+-- the first program in every language
+
+io.write("Hello world, from ",_VERSION,"!\n")
diff --git a/engines/sword25/util/lua/test/life.lua b/engines/sword25/util/lua/test/life.lua
new file mode 100755
index 0000000000..911d9fe177
--- /dev/null
+++ b/engines/sword25/util/lua/test/life.lua
@@ -0,0 +1,111 @@
+-- life.lua
+-- original by Dave Bollinger <DBollinger@compuserve.com> posted to lua-l
+-- modified to use ANSI terminal escape sequences
+-- modified to use for instead of while
+
+local write=io.write
+
+ALIVE="¥" DEAD="þ"
+ALIVE="O" DEAD="-"
+
+function delay() -- NOTE: SYSTEM-DEPENDENT, adjust as necessary
+ for i=1,10000 do end
+ -- local i=os.clock()+1 while(os.clock()<i) do end
+end
+
+function ARRAY2D(w,h)
+ local t = {w=w,h=h}
+ for y=1,h do
+ t[y] = {}
+ for x=1,w do
+ t[y][x]=0
+ end
+ end
+ return t
+end
+
+_CELLS = {}
+
+-- give birth to a "shape" within the cell array
+function _CELLS:spawn(shape,left,top)
+ for y=0,shape.h-1 do
+ for x=0,shape.w-1 do
+ self[top+y][left+x] = shape[y*shape.w+x+1]
+ end
+ end
+end
+
+-- run the CA and produce the next generation
+function _CELLS:evolve(next)
+ local ym1,y,yp1,yi=self.h-1,self.h,1,self.h
+ while yi > 0 do
+ local xm1,x,xp1,xi=self.w-1,self.w,1,self.w
+ while xi > 0 do
+ local sum = self[ym1][xm1] + self[ym1][x] + self[ym1][xp1] +
+ self[y][xm1] + self[y][xp1] +
+ self[yp1][xm1] + self[yp1][x] + self[yp1][xp1]
+ next[y][x] = ((sum==2) and self[y][x]) or ((sum==3) and 1) or 0
+ xm1,x,xp1,xi = x,xp1,xp1+1,xi-1
+ end
+ ym1,y,yp1,yi = y,yp1,yp1+1,yi-1
+ end
+end
+
+-- output the array to screen
+function _CELLS:draw()
+ local out="" -- accumulate to reduce flicker
+ for y=1,self.h do
+ for x=1,self.w do
+ out=out..(((self[y][x]>0) and ALIVE) or DEAD)
+ end
+ out=out.."\n"
+ end
+ write(out)
+end
+
+-- constructor
+function CELLS(w,h)
+ local c = ARRAY2D(w,h)
+ c.spawn = _CELLS.spawn
+ c.evolve = _CELLS.evolve
+ c.draw = _CELLS.draw
+ return c
+end
+
+--
+-- shapes suitable for use with spawn() above
+--
+HEART = { 1,0,1,1,0,1,1,1,1; w=3,h=3 }
+GLIDER = { 0,0,1,1,0,1,0,1,1; w=3,h=3 }
+EXPLODE = { 0,1,0,1,1,1,1,0,1,0,1,0; w=3,h=4 }
+FISH = { 0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,1,0; w=5,h=4 }
+BUTTERFLY = { 1,0,0,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1; w=5,h=5 }
+
+-- the main routine
+function LIFE(w,h)
+ -- create two arrays
+ local thisgen = CELLS(w,h)
+ local nextgen = CELLS(w,h)
+
+ -- create some life
+ -- about 1000 generations of fun, then a glider steady-state
+ thisgen:spawn(GLIDER,5,4)
+ thisgen:spawn(EXPLODE,25,10)
+ thisgen:spawn(FISH,4,12)
+
+ -- run until break
+ local gen=1
+ write("\027[2J") -- ANSI clear screen
+ while 1 do
+ thisgen:evolve(nextgen)
+ thisgen,nextgen = nextgen,thisgen
+ write("\027[H") -- ANSI home cursor
+ thisgen:draw()
+ write("Life - generation ",gen,"\n")
+ gen=gen+1
+ if gen>2000 then break end
+ --delay() -- no delay
+ end
+end
+
+LIFE(40,20)
diff --git a/engines/sword25/util/lua/test/luac b/engines/sword25/util/lua/test/luac
new file mode 100755
index 0000000000..e69de29bb2
--- /dev/null
+++ b/engines/sword25/util/lua/test/luac
diff --git a/engines/sword25/util/lua/test/luac.lua b/engines/sword25/util/lua/test/luac.lua
new file mode 100755
index 0000000000..96a0a97ce7
--- /dev/null
+++ b/engines/sword25/util/lua/test/luac.lua
@@ -0,0 +1,7 @@
+-- bare-bones luac in Lua
+-- usage: lua luac.lua file.lua
+
+assert(arg[1]~=nil and arg[2]==nil,"usage: lua luac.lua file.lua")
+f=assert(io.open("luac.out","wb"))
+assert(f:write(string.dump(assert(loadfile(arg[1])))))
+assert(f:close())
diff --git a/engines/sword25/util/lua/test/printf.lua b/engines/sword25/util/lua/test/printf.lua
new file mode 100755
index 0000000000..58c63ff518
--- /dev/null
+++ b/engines/sword25/util/lua/test/printf.lua
@@ -0,0 +1,7 @@
+-- an implementation of printf
+
+function printf(...)
+ io.write(string.format(...))
+end
+
+printf("Hello %s from %s on %s\n",os.getenv"USER" or "there",_VERSION,os.date())
diff --git a/engines/sword25/util/lua/test/readonly.lua b/engines/sword25/util/lua/test/readonly.lua
new file mode 100755
index 0000000000..85c0b4e013
--- /dev/null
+++ b/engines/sword25/util/lua/test/readonly.lua
@@ -0,0 +1,12 @@
+-- make global variables readonly
+
+local f=function (t,i) error("cannot redefine global variable `"..i.."'",2) end
+local g={}
+local G=getfenv()
+setmetatable(g,{__index=G,__newindex=f})
+setfenv(1,g)
+
+-- an example
+rawset(g,"x",3)
+x=2
+y=1 -- cannot redefine `y'
diff --git a/engines/sword25/util/lua/test/sieve.lua b/engines/sword25/util/lua/test/sieve.lua
new file mode 100755
index 0000000000..0871bb2125
--- /dev/null
+++ b/engines/sword25/util/lua/test/sieve.lua
@@ -0,0 +1,29 @@
+-- the sieve of of Eratosthenes programmed with coroutines
+-- typical usage: lua -e N=1000 sieve.lua | column
+
+-- generate all the numbers from 2 to n
+function gen (n)
+ return coroutine.wrap(function ()
+ for i=2,n do coroutine.yield(i) end
+ end)
+end
+
+-- filter the numbers generated by `g', removing multiples of `p'
+function filter (p, g)
+ return coroutine.wrap(function ()
+ while 1 do
+ local n = g()
+ if n == nil then return end
+ if math.mod(n, p) ~= 0 then coroutine.yield(n) end
+ end
+ end)
+end
+
+N=N or 1000 -- from command line
+x = gen(N) -- generate primes up to N
+while 1 do
+ local n = x() -- pick a number until done
+ if n == nil then break end
+ print(n) -- must be a prime number
+ x = filter(n, x) -- now remove its multiples
+end
diff --git a/engines/sword25/util/lua/test/sort.lua b/engines/sword25/util/lua/test/sort.lua
new file mode 100755
index 0000000000..0bcb15f837
--- /dev/null
+++ b/engines/sword25/util/lua/test/sort.lua
@@ -0,0 +1,66 @@
+-- two implementations of a sort function
+-- this is an example only. Lua has now a built-in function "sort"
+
+-- extracted from Programming Pearls, page 110
+function qsort(x,l,u,f)
+ if l<u then
+ local m=math.random(u-(l-1))+l-1 -- choose a random pivot in range l..u
+ x[l],x[m]=x[m],x[l] -- swap pivot to first position
+ local t=x[l] -- pivot value
+ m=l
+ local i=l+1
+ while i<=u do
+ -- invariant: x[l+1..m] < t <= x[m+1..i-1]
+ if f(x[i],t) then
+ m=m+1
+ x[m],x[i]=x[i],x[m] -- swap x[i] and x[m]
+ end
+ i=i+1
+ end
+ x[l],x[m]=x[m],x[l] -- swap pivot to a valid place
+ -- x[l+1..m-1] < x[m] <= x[m+1..u]
+ qsort(x,l,m-1,f)
+ qsort(x,m+1,u,f)
+ end
+end
+
+function selectionsort(x,n,f)
+ local i=1
+ while i<=n do
+ local m,j=i,i+1
+ while j<=n do
+ if f(x[j],x[m]) then m=j end
+ j=j+1
+ end
+ x[i],x[m]=x[m],x[i] -- swap x[i] and x[m]
+ i=i+1
+ end
+end
+
+function show(m,x)
+ io.write(m,"\n\t")
+ local i=1
+ while x[i] do
+ io.write(x[i])
+ i=i+1
+ if x[i] then io.write(",") end
+ end
+ io.write("\n")
+end
+
+function testsorts(x)
+ local n=1
+ while x[n] do n=n+1 end; n=n-1 -- count elements
+ show("original",x)
+ qsort(x,1,n,function (x,y) return x<y end)
+ show("after quicksort",x)
+ selectionsort(x,n,function (x,y) return x>y end)
+ show("after reverse selection sort",x)
+ qsort(x,1,n,function (x,y) return x<y end)
+ show("after quicksort again",x)
+end
+
+-- array to be sorted
+x={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}
+
+testsorts(x)
diff --git a/engines/sword25/util/lua/test/table.lua b/engines/sword25/util/lua/test/table.lua
new file mode 100755
index 0000000000..235089c04b
--- /dev/null
+++ b/engines/sword25/util/lua/test/table.lua
@@ -0,0 +1,12 @@
+-- make table, grouping all data for the same item
+-- input is 2 columns (item, data)
+
+local A
+while 1 do
+ local l=io.read()
+ if l==nil then break end
+ local _,_,a,b=string.find(l,'"?([_%w]+)"?%s*(.*)$')
+ if a~=A then A=a io.write("\n",a,":") end
+ io.write(" ",b)
+end
+io.write("\n")
diff --git a/engines/sword25/util/lua/test/trace-calls.lua b/engines/sword25/util/lua/test/trace-calls.lua
new file mode 100755
index 0000000000..6d7a7b3b43
--- /dev/null
+++ b/engines/sword25/util/lua/test/trace-calls.lua
@@ -0,0 +1,32 @@
+-- trace calls
+-- example: lua -ltrace-calls bisect.lua
+
+local level=0
+
+local function hook(event)
+ local t=debug.getinfo(3)
+ io.write(level," >>> ",string.rep(" ",level))
+ if t~=nil and t.currentline>=0 then io.write(t.short_src,":",t.currentline," ") end
+ t=debug.getinfo(2)
+ if event=="call" then
+ level=level+1
+ else
+ level=level-1 if level<0 then level=0 end
+ end
+ if t.what=="main" then
+ if event=="call" then
+ io.write("begin ",t.short_src)
+ else
+ io.write("end ",t.short_src)
+ end
+ elseif t.what=="Lua" then
+-- table.foreach(t,print)
+ io.write(event," ",t.name or "(Lua)"," <",t.linedefined,":",t.short_src,">")
+ else
+ io.write(event," ",t.name or "(C)"," [",t.what,"] ")
+ end
+ io.write("\n")
+end
+
+debug.sethook(hook,"cr")
+level=0
diff --git a/engines/sword25/util/lua/test/trace-globals.lua b/engines/sword25/util/lua/test/trace-globals.lua
new file mode 100755
index 0000000000..295e670caa
--- /dev/null
+++ b/engines/sword25/util/lua/test/trace-globals.lua
@@ -0,0 +1,38 @@
+-- trace assigments to global variables
+
+do
+ -- a tostring that quotes strings. note the use of the original tostring.
+ local _tostring=tostring
+ local tostring=function(a)
+ if type(a)=="string" then
+ return string.format("%q",a)
+ else
+ return _tostring(a)
+ end
+ end
+
+ local log=function (name,old,new)
+ local t=debug.getinfo(3,"Sl")
+ local line=t.currentline
+ io.write(t.short_src)
+ if line>=0 then io.write(":",line) end
+ io.write(": ",name," is now ",tostring(new)," (was ",tostring(old),")","\n")
+ end
+
+ local g={}
+ local set=function (t,name,value)
+ log(name,g[name],value)
+ g[name]=value
+ end
+ setmetatable(getfenv(),{__index=g,__newindex=set})
+end
+
+-- an example
+
+a=1
+b=2
+a=10
+b=20
+b=nil
+b=200
+print(a,b,c)
diff --git a/engines/sword25/util/lua/test/xd.lua b/engines/sword25/util/lua/test/xd.lua
new file mode 100755
index 0000000000..ebc3effc06
--- /dev/null
+++ b/engines/sword25/util/lua/test/xd.lua
@@ -0,0 +1,14 @@
+-- hex dump
+-- usage: lua xd.lua < file
+
+local offset=0
+while true do
+ local s=io.read(16)
+ if s==nil then return end
+ io.write(string.format("%08X ",offset))
+ string.gsub(s,"(.)",
+ function (c) io.write(string.format("%02X ",string.byte(c))) end)
+ io.write(string.rep(" ",3*(16-string.len(s))))
+ io.write(" ",string.gsub(s,"%c","."),"\n")
+ offset=offset+16
+end
diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG
new file mode 100755
index 0000000000..e31ed26044
--- /dev/null
+++ b/engines/sword25/util/pluto/CHANGELOG
@@ -0,0 +1,38 @@
+$Id$
+
+-- 2.4 --
+* Changed upval unboxing to allow upvals which contain func-housed cycles
+* Added stack checking to all stack-growing functions
+* Serialized debug information for functions
+
+-- 2.3 --
+* Added LUALIB_API declaration for luaopen_pluto
+
+-- 2.2 --
+* Rolled all internal Lua dependencies into the Pluto distribution
+* Made the unit tests depend on dynamically loading Pluto
+
+-- 2.1 --
+* Various fixes to make the GC happy
+* stack size always expanded where necessary
+* fixed some memory leaks
+* GC disabled during unpersist
+* callstack initialized for traversal
+
+This changelog is maintained as of version 2.0alpha1.
+Earlier versions are changelogged on the LuaForge site.
+
+-- 2.0 --
+* Fixed a few format changes to 5.1.3
+* Fixed myriad warnings
+* GCC compliance: not incrementing cast results
+* Fix for self-referring upvals
+* Renamed loading function to work with Lua module system
+* Loading tables with __newindex works
+* unpersist makes buffer copy
+
+-- 2.0alpha1 --
+* Fixed all outstanding 5.0->5.1 conversion issues
+* Made heavier use of size_t in preference to int
+* Fixed GC/Upval issue (thanks to Eric Jacobs)
+
diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT
new file mode 100755
index 0000000000..b3f10ee603
--- /dev/null
+++ b/engines/sword25/util/pluto/FILEFORMAT
@@ -0,0 +1,168 @@
+$Id$
+
+pluto_persist() produces a "hunk" of objects. Here's the file format adhered
+to by the function, and expected by pluto_unpersist().
+
+As a developer, I feel that where file format information is given it is of
+utmost importance that that information precisely and accurately reflects the
+actual operation of the application. Therefore, if you find any discrepancy
+between this and actual operation, please lambast me thoroughly over email.
+
+Pseudo-C is used to express the file format. Padding is assumed to be
+nonexistent. The keyword "one_of" is used to express a concept similar to
+"union", except that its size is the size of the actual datatype chosen. Thus,
+objects which contain, directly or indirectly, a one_of, may vary in size.
+
+
+struct Object {
+ int firstTime; /* Whether this is the first time the object
+ is being referenced */
+ one_of {
+ RealObject o; /* if firstTime == 1 */
+ Reference r; /* if firstTime == 0 */
+ };
+};
+
+struct Reference {
+ int ref; /* The index the object was registered with */
+};
+
+struct RealObject {
+ int type; /* The type of the object */
+ one_of {
+ Boolean b; /* If type == LUA_TBOOLEAN */
+ LightUserData l; /* If type == LUA_TLIGHTUSERDATA */
+ Number n; /* If type == LUA_TNUMBER */
+ String s; /* If type == LUA_TSTRING */
+ Table t; /* If type == LUA_TTABLE */
+ Function f; /* If type == LUA_TFUNCTION */
+ Userdata u; /* If type == LUA_TUSERDATA */
+ Thread th; /* If type == LUA_TTHREAD */
+ Proto p; /* If type == LUA_TPROTO (from lobject.h) */
+ Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */
+ }; /* The actual object */
+};
+
+struct Boolean {
+ int32 bvalue; /* 0 for false, 1 for true */
+};
+
+struct LightUserData {
+ void* luvalue; /* The actual, literal pointer */
+};
+
+struct Number {
+ lua_Number nvalue; /* The actual number */
+};
+
+struct String {
+ int length; /* The length of the string */
+ char str[length]; /* The actual string (not null terminated) */
+};
+
+struct Table {
+ int isspecial; /* 1 if SP is used; 0 otherwise */
+ one_of {
+ Closure c; /* if isspecial == 1; closure to refill the table */
+ LiteralTable t; /* if isspecial == 0; literal table info */
+ };
+};
+
+struct LiteralTable {
+ Object metatable; /* nil for default metatable */
+ Pair p[]; /* key/value pairs */
+ Object nil = nil; /* Nil reference to terminate */
+};
+
+struct Pair {
+ Object key;
+ Object value;
+};
+
+struct Function { /* Actually a closure */
+ lu_byte nups; /* Number of upvalues the function uses */
+ Object proto; /* The proto this function uses */
+ Object upvals[nups]; /* All upvalues */
+ Object fenv; /* The FEnv (nil for the global table)
+};
+
+struct Upval {
+ Object obj; /* The object this upval refers to */
+}
+
+struct Userdata {
+ int isSpecial; /* 1 for special persistence, 0 for literal
+ one_of {
+ LiteralUserdata lu; /* if is_special is 0 */
+ SpecialUserdata su; /* if is_special is 1 */
+ };
+};
+
+struct LiteralUserdata {
+ Object metatable; /* The metatable (nil for default) */
+ int length; /* Size of the data */
+ char data[length]; /* The actual data */
+};
+
+struct SpecialUserdata {
+ int length; /* The size of the data */
+ Object func; /* The closure used to fill the userdata */
+};
+
+struct Thread {
+ int stacksize; /* The size of the stack filled with objects,
+ * including the "nil" that is hidden below
+ * the bottom of the stack visible to C */
+ Object stack[stacksize];/* Indices of all stack values, bottom up */
+ int callinfosize; /* Number of elements in the CallInfo stack */
+ CallInfo callinfostack[callinfosize]; /* The CallInfo stack */
+ int base; /* base = L->base - L->stack; */
+ int top; /* top = L->top - L->stack; */
+ OpenUpval openupvals[]; /* Upvalues to open */
+ Object nil = nil; /* To terminate the open upvalues list */
+};
+
+struct OpenUpval {
+ Object upval; /* The upvalue */
+ int stackpos; /* The stack position to "reopen" it to */
+
+};
+
+struct CallInfo {
+ int base; /* base = ci->base - L->stack; */
+ int top; /* top = ci->top - L->stack; */
+ int pc; /* pc = ci->pc - proto->code; */
+ int state; /* flags used by the CallInfo */
+};
+
+struct Proto {
+ int sizek; /* Number of constants referenced */
+ Object k[sizek]; /* Constants referenced */
+ int sizep; /* Number of inner Protos referenced */
+ Object p[sizep]; /* Inner Protos referenced */
+ int sizecode; /* Number of instructions in code */
+ Instruction code[sizecode]; /* The proto's code */
+ ProtoDebug debuginfo; /* Debug information for the proto */
+ lu_byte nups; /* Number of upvalues used */
+ lu_byte numparams; /* Number of parameters taken */
+ lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */
+ lu_byte maxstacksize; /* Size of stack reserved for the function */
+};
+
+struct ProtoDebug {
+ int sizeupvals; /* Number of upvalue names */
+ Object upvals; /* Upvalue names */
+ int sizelocvars; /* Number of local variable names */
+ LocVar[sizelocvars]; /* Local variable names */
+ Object source; /* The source code */
+ int sizelineinfo; /* Number of opcode-line mappings */
+ int lineinfo[sizelineinfo]; /* opcode-line mappings */
+ int linedefined; /* Start of line range */
+ int lastlinedefined; /* End of line range */
+};
+
+struct LocVar {
+ Object name; /* Name of the local variable */
+ int startpc; /* Point where variable is active */
+ int endpc; /* Point where variable is dead */
+}; \ No newline at end of file
diff --git a/engines/sword25/util/pluto/Makefile b/engines/sword25/util/pluto/Makefile
new file mode 100755
index 0000000000..611ecc83d2
--- /dev/null
+++ b/engines/sword25/util/pluto/Makefile
@@ -0,0 +1,29 @@
+LDLIBS= -lm -ldl -llua
+LDFLAGS = -rdynamic # -L../lua-5.1.3/src
+# CFLAGS= -g3 -Wall -fprofile-arcs -ftest-coverage
+CFLAGS= -g3 -Wall -ansi -pedantic
+
+LIBTOOL=libtool --tag=CC
+
+default: pluto.so pptest puptest
+
+%.lo: %.c
+ $(LIBTOOL) --mode=compile cc $(CFLAGS) -c $<
+
+pluto.so: pluto.lo pdep.lo lzio.lo
+ $(LIBTOOL) --mode=link cc -rpath /usr/local/lib/lua/5.1 -o libpluto.la $^
+ mv .libs/libpluto.so.0.0.0 $@
+
+test: pptest puptest pptest.lua puptest.lua pluto.so
+ ./pptest
+ ./puptest
+
+pptest: pptest.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+puptest: puptest.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+clean:
+ -rm -r *.so *.la *.lo .libs *.a *.o *.bb *.bbg *.da *.gcov pptest puptest test.plh
+
diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README
new file mode 100755
index 0000000000..838fce498b
--- /dev/null
+++ b/engines/sword25/util/pluto/README
@@ -0,0 +1,133 @@
+$Id$
+
+PLUTO - Heavy duty persistence for Lua
+
+Pluto is a library which allows users to write arbitrarily large portions
+of the "Lua universe" into a flat file, and later read them back into the
+same or a different Lua universe. Object references are appropriately
+handled, such that the file contains everything needed to recreate the
+objects in question.
+
+Pluto has the following major features:
+* Can persist any Lua function
+* Can persist threads
+* Works with any Lua chunkreader/chunkwriter
+* Support for "invariant" permanent objects, of all datatypes
+* Can invoke metafunctions for custom persistence of tables and userdata
+
+Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua
+5.0, please use version 1.2 of Pluto.
+
+Starting with version 2.2, Pluto no longer depends on the Lua sources.
+Instead, it subsumes the required headers into its own codebase.
+As a result, it may not work properly with Lua version 5.1.4 or later.
+
+Pluto may have bugs. Users are advised to define lua_assert in
+luaconf.h to something useful when compiling in debug mode, to catch
+assertions by Pluto and Lua.
+
+The Pluto library consists of two public functions.
+
+int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud)
+
+This function recursively persists the Lua object in stack position 2
+and all other objects which are directly or indirectly referenced by
+it, except those referenced in the permanent object table. The data
+is written using the chunk-writer given, and that writer is passed
+the arbitrary pointer value ud.
+
+The Lua stack must contain exactly and only these two items, in order:
+
+1. A table of permanent objects, that should not be persisted. For each
+permanent object, the object itself should be the key, and a unique
+object of any type should be the value. Likely candidates for this table
+include Lua functions (including those in the Lua libraries) that are
+loaded at load-time. It must include all non-persistable objects that
+are referenced by the object to be persisted. The table is not modified
+by the function. Objects in this table are considered "opaque" and are
+not examined or descended into. Objects should not appear in the table
+multiple times; the result of doing this is undefined (though probably
+harmless). NOTE: If you are planning to persist threads, keep in mind
+that all yielded threads have coroutine.yield on the tops of their
+stacks. Since it's a C function, it should be put here. For complex
+permanents, it may be a good idea to use the __index meta-function of
+the permanents table to "search" for permanents.
+
+2. The single object to be persisted. In many cases, this will be the
+global table. For more flexibility, however, it may be something like a
+table built for the occasion, with various values to keep track of. The
+object may not be nil.
+
+
+int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud)
+
+This function loads in a Lua object and places it on top of the stack. All
+objects directly or indirectly referenced by it are also loaded.
+
+The Lua stack must contain, as its top value, a table of permanent
+objects. This table should be like the permanent object table used when
+persisting, but with the key and value of each pair reversed. These
+objects are used as substitutes for those referenced in their positions
+when persisting, and under most circumstances should be identical objects
+to those referenced in the permanents table used for persisting. It's
+okay for multiple keys to refer to the same object.
+
+
+RUNNING PLUTO FROM LUA:
+It is also possible to invoke pluto from a Lua script. The C function
+pluto_open() will register pluto.persist and pluto.unpersist, lua functions
+which operate on strings. The first takes a permanents table and a root
+object, and returns a string; the second takes a permanents table and a
+string, and returns the root object.
+
+An error will be raised if pluto.persist is called from a thread which is
+itself referenced by the root object.
+
+SPECIAL PERSISTENCE:
+Tables and userdata have special persistence semantics. These semantics are
+keyed to the value of the object's metatable's __persist member, if any. This
+member may be any of the following four values:
+1. Boolean "true": The table or userdata is persisted literally; tables are
+persisted member-by-member, and userdata are written out as literal data.
+2. Boolean "false": An error is returned, indicating that the object cannot
+be persisted.
+3. A function: This function should take one argument, the object in question,
+and return one result, a closure. This "fixup closure", in turn, will be
+persisted, and during unpersistence will be called. The closure will be
+responsible for recreating the object with the appropriate data, based on
+its upvalues.
+4. Nil, or no metatable. In the case of tables, the table is literally
+persisted. In the case of userdata, an error is returned.
+
+Here's an example of special persistence for a simple 3d vector object:
+
+vec = { x = 2, y = 1, z = 4 }
+setmetatable(vec, { __persist = function(oldtbl)
+ local x = oldtbl.x
+ local y = oldtbl.y
+ local z = oldtbl.z
+ local mt = getmetatable(oldtbl)
+ return function()
+ newtbl = {}
+ newtbl.x = x
+ newtbl.y = y
+ newtbl.z = z
+ setmetatable(newtbl, mt)
+ return newtbl
+ end
+end })
+
+Note how x, y, z, and the mt are explicitly pulled out of the table. It is
+important that the fixup closure returned not reference the original table
+directly, as that table would again be persisted as an upvalue, leading to an
+infinite loop. Also note that the object's metatable is NOT automatically
+persisted; it is necessary for the fixup closure to reset it, if it wants.
+
+LIMITATIONS/TODO:
+* Light userdata are persisted literally, as their pointer values. This
+may or may not be what you want.
+* Closures of C functions may not be persisted. Once it becomes possible
+to specify a C function "proto" as a permanent object, this restriction
+will be relaxed.
+
+BUGS: None known. Emphasis on the 'known'.
diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS
new file mode 100755
index 0000000000..fea3595dbf
--- /dev/null
+++ b/engines/sword25/util/pluto/THANKS
@@ -0,0 +1,10 @@
+Pluto is surprisingly robust and useful. This would not be the case without
+the hard work and helpfulness of the following people, mentioned in no
+particular order:
+
+Ivko Stanilov
+Goran Adrinek
+Eric Jacobs
+Anolan Milanes
+Malte Thiesen
+
diff --git a/engines/sword25/util/pluto/pdep.c b/engines/sword25/util/pluto/pdep.c
new file mode 100755
index 0000000000..a32c43b42d
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep.c
@@ -0,0 +1,112 @@
+/* This file is derived from the Lua source code. Please see lua.h for
+the copyright statement.
+*/
+
+#include "pdep/pdep.h"
+
+#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;}
+
+void pdep_pushobject (lua_State *L, const TValue *o) {
+ setobj2s(L, L->top, o);
+ api_incr_top(L);
+}
+
+void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ block = (*g->frealloc)(g->ud, block, osize, nsize);
+ lua_assert((nsize == 0) == (block == NULL));
+ g->totalbytes = (g->totalbytes - osize) + nsize;
+ return block;
+}
+
+void pdep_link (lua_State *L, GCObject *o, lu_byte tt) {
+ global_State *g = G(L);
+ o->gch.next = g->rootgc;
+ g->rootgc = o;
+ o->gch.marked = luaC_white(g);
+ o->gch.tt = tt;
+}
+
+Proto *pdep_newproto (lua_State *L) {
+ Proto *f = pdep_new(L, Proto);
+ pdep_link(L, obj2gco(f), LUA_TPROTO);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->sizelineinfo = 0;
+ f->sizeupvalues = 0;
+ f->nups = 0;
+ f->upvalues = NULL;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->lineinfo = NULL;
+ f->sizelocvars = 0;
+ f->locvars = NULL;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems)));
+ pdep_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->l.isC = 0;
+ c->l.env = e;
+ c->l.nupvalues = cast_byte(nelems);
+ while (nelems--) c->l.upvals[nelems] = NULL;
+ return c;
+}
+
+static void correctstack (lua_State *L, TValue *oldstack) {
+ CallInfo *ci;
+ GCObject *up;
+ L->top = (L->top - oldstack) + L->stack;
+ for (up = L->openupval; up != NULL; up = up->gch.next)
+ gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
+ for (ci = L->base_ci; ci <= L->ci; ci++) {
+ ci->top = (ci->top - oldstack) + L->stack;
+ ci->base = (ci->base - oldstack) + L->stack;
+ ci->func = (ci->func - oldstack) + L->stack;
+ }
+ L->base = (L->base - oldstack) + L->stack;
+}
+
+
+void pdep_reallocstack (lua_State *L, int newsize) {
+ TValue *oldstack = L->stack;
+ int realsize = newsize + 1 + EXTRA_STACK;
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
+ L->stacksize = realsize;
+ L->stack_last = L->stack+newsize;
+ correctstack(L, oldstack);
+}
+
+void pdep_growstack (lua_State *L, int n) {
+ if (n <= L->stacksize) /* double size is enough? */
+ pdep_reallocstack(L, 2*L->stacksize);
+ else
+ pdep_reallocstack(L, L->stacksize + n);
+}
+
+void pdep_reallocCI (lua_State *L, int newsize) {
+ CallInfo *oldci = L->base_ci;
+ pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
+ L->size_ci = newsize;
+ L->ci = (L->ci - oldci) + L->base_ci;
+ L->end_ci = L->base_ci + L->size_ci - 1;
+}
+
+TString *pdep_newlstr (lua_State *L, const char *str, size_t l) {
+ TString *res;
+ lua_pushlstring(L, str, l);
+ res = rawtsvalue(L->top-1);
+ lua_pop(L, 1);
+ return res;
+}
diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README
new file mode 100755
index 0000000000..3592754da0
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/README
@@ -0,0 +1,5 @@
+These files are directly copied from the Lua distribution, with the
+exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed.
+
+As such, unlike the rest of Pluto, they are released under the
+same terms as Lua. See "lua.h" for the copyright notice.
diff --git a/engines/sword25/util/pluto/pdep/lauxlib.h b/engines/sword25/util/pluto/pdep/lauxlib.h
new file mode 100755
index 0000000000..34258235db
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#if defined(LUA_COMPAT_GETN)
+LUALIB_API int (luaL_getn) (lua_State *L, int t);
+LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
+#else
+#define luaL_getn(L,i) ((int)lua_objlen(L, i))
+#define luaL_setn(L,i,j) ((void)0) /* no op! */
+#endif
+
+#if defined(LUA_COMPAT_OPENLIB)
+#define luaI_openlib luaL_openlib
+#endif
+
+
+/* extra error code for `luaL_load' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+
+LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup);
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l);
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
+LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
+LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
+ const char *name);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
+ const char *r);
+
+LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
+ const char *fname, int szhint);
+
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_argcheck(L, cond,numarg,extramsg) \
+ ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int lvl; /* number of strings in the stack (level) */
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+/* compatibility only */
+#define luaL_putchar(B,c) luaL_addchar(B,c)
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+/* compatibility with ref system */
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
+ (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
+
+#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
+
+#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
+
+
+#define luaL_reg luaL_Reg
+
+#endif
+
+
diff --git a/engines/sword25/util/pluto/pdep/ldo.h b/engines/sword25/util/pluto/pdep/ldo.h
new file mode 100755
index 0000000000..98fddac59f
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+#define luaD_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ luaD_growstack(L, n); \
+ else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
+
+
+#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
+
+#define savestack(L,p) ((char *)(p) - (char *)L->stack)
+#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
+
+#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
+#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
+
+
+/* results from luaD_precall */
+#define PCRLUA 0 /* initiated a call to a Lua function */
+#define PCRC 1 /* did a call to a C function */
+#define PCRYIELD 2 /* C funtion yielded */
+
+
+/* type of protected functions, to be ran by `runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
+LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
+LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
+LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
+LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
+LUAI_FUNC void luaD_growstack (lua_State *L, int n);
+
+LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lfunc.h b/engines/sword25/util/pluto/pdep/lfunc.h
new file mode 100755
index 0000000000..a68cf5151c
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
+ cast(int, sizeof(TValue)*((n)-1)))
+
+#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
+ cast(int, sizeof(TValue *)*((n)-1)))
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_close (lua_State *L, StkId level);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
+LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lgc.h b/engines/sword25/util/pluto/pdep/lgc.h
new file mode 100755
index 0000000000..5a8dc605b3
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpause 0
+#define GCSpropagate 1
+#define GCSsweepstring 2
+#define GCSsweep 3
+#define GCSfinalize 4
+
+
+/*
+** some userful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
+#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
+#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
+
+
+
+/*
+** Layout for bit use in `marked' field:
+** bit 0 - object is white (type 0)
+** bit 1 - object is white (type 1)
+** bit 2 - object is black
+** bit 3 - for userdata: has been finalized
+** bit 3 - for tables: has weak keys
+** bit 4 - for tables: has weak values
+** bit 5 - object is fixed (should not be collected)
+** bit 6 - object is "super" fixed (only the main thread)
+*/
+
+
+#define WHITE0BIT 0
+#define WHITE1BIT 1
+#define BLACKBIT 2
+#define FINALIZEDBIT 3
+#define KEYWEAKBIT 3
+#define VALUEWEAKBIT 4
+#define FIXEDBIT 5
+#define SFIXEDBIT 6
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
+#define isgray(x) (!isblack(x) && !iswhite(x))
+
+#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
+#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
+
+#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
+#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
+
+
+#define luaC_checkGC(L) { \
+ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
+ if (G(L)->totalbytes >= G(L)->GCthreshold) \
+ luaC_step(L); }
+
+
+#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
+
+#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
+ luaC_barrierback(L,t); }
+
+#define luaC_objbarrier(L,p,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
+
+#define luaC_objbarriert(L,t,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
+
+LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
+LUAI_FUNC void luaC_callGCTM (lua_State *L);
+LUAI_FUNC void luaC_freeall (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_fullgc (lua_State *L);
+LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
+LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
+LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/llimits.h b/engines/sword25/util/pluto/pdep/llimits.h
new file mode 100755
index 0000000000..ca8dcb7224
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** Limits, basic types, and some other `installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+typedef LUAI_UINT32 lu_int32;
+
+typedef LUAI_UMEM lu_mem;
+
+typedef LUAI_MEM l_mem;
+
+
+
+/* chars used as small naturals (so that `char' is reserved for characters) */
+typedef unsigned char lu_byte;
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to integer
+** this is for hashing only; there is no problem if the integer
+** cannot hold the whole pointer value
+*/
+#define IntPoint(p) ((unsigned int)(lu_mem)(p))
+
+
+
+/* type to ensure maximum alignment */
+typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
+
+
+/* result of a `usual argument conversion' over lua_Number */
+typedef LUAI_UACNUMBER l_uacNumber;
+
+
+/* internal assertions for in-house debugging */
+#ifdef lua_assert
+
+#define check_exp(c,e) (lua_assert(c), (e))
+#define api_check(l,e) lua_assert(e)
+
+#else
+
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define api_check luai_apicheck
+
+#endif
+
+
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+#ifndef cast
+#define cast(t, exp) ((t)(exp))
+#endif
+
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+typedef lu_int32 Instruction;
+
+
+
+/* maximum stack for a Lua function */
+#define MAXSTACK 250
+
+
+
+/* minimum size for the string table (must be power of 2) */
+#ifndef MINSTRTABSIZE
+#define MINSTRTABSIZE 32
+#endif
+
+
+/* minimum size for string buffer */
+#ifndef LUA_MINBUFFER
+#define LUA_MINBUFFER 32
+#endif
+
+
+#ifndef lua_lock
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+#ifndef luai_threadyield
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#ifndef HARDSTACKTESTS
+#define condhardstacktests(x) ((void)0)
+#else
+#define condhardstacktests(x) x
+#endif
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lobject.h b/engines/sword25/util/pluto/pdep/lobject.h
new file mode 100755
index 0000000000..e7199dfc68
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/* tags for values visible from Lua */
+#define LAST_TAG LUA_TTHREAD
+
+#define NUM_TAGS (LAST_TAG+1)
+
+
+/*
+** Extra tags for non-values
+*/
+#define LUA_TPROTO (LAST_TAG+1)
+#define LUA_TUPVAL (LAST_TAG+2)
+#define LUA_TDEADKEY (LAST_TAG+3)
+
+
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
+
+
+/*
+** Common header in struct form
+*/
+typedef struct GCheader {
+ CommonHeader;
+} GCheader;
+
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union {
+ GCObject *gc;
+ void *p;
+ lua_Number n;
+ int b;
+} Value;
+
+
+/*
+** Tagged Values
+*/
+
+#define TValuefields Value value; int tt
+
+typedef struct lua_TValue {
+ TValuefields;
+} TValue;
+
+
+/* Macros to test type */
+#define ttisnil(o) (ttype(o) == LUA_TNIL)
+#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
+#define ttisstring(o) (ttype(o) == LUA_TSTRING)
+#define ttistable(o) (ttype(o) == LUA_TTABLE)
+#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
+#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
+#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
+#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
+
+/* Macros to access values */
+#define ttype(o) ((o)->tt)
+#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
+#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
+#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
+#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
+#define tsvalue(o) (&rawtsvalue(o)->tsv)
+#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
+#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
+#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
+
+#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
+
+/*
+** for internal debug only
+*/
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
+
+
+/* Macros to set values */
+#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
+
+#define setnvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
+
+#define setpvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
+
+#define setbvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
+
+#define setsvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; o1->tt=o2->tt; \
+ checkliveness(G(L),o1); }
+
+
+/*
+** different types of sets, according to destination
+*/
+
+/* from stack to (same) stack */
+#define setobjs2s setobj
+/* to stack (not from same stack) */
+#define setobj2s setobj
+#define setsvalue2s setsvalue
+#define sethvalue2s sethvalue
+#define setptvalue2s setptvalue
+/* from table to same table */
+#define setobjt2t setobj
+/* to table */
+#define setobj2t setobj
+/* to new object */
+#define setobj2n setobj
+#define setsvalue2n setsvalue
+
+#define setttype(obj, tt) (ttype(obj) = (tt))
+
+
+#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
+
+
+
+typedef TValue *StkId; /* index to stack elements */
+
+
+/*
+** String headers for string table
+*/
+typedef union TString {
+ L_Umaxalign dummy; /* ensures maximum alignment for strings */
+ struct {
+ CommonHeader;
+ lu_byte reserved;
+ unsigned int hash;
+ size_t len;
+ } tsv;
+} TString;
+
+
+#define getstr(ts) cast(const char *, (ts) + 1)
+#define svalue(o) getstr(tsvalue(o))
+
+
+
+typedef union Udata {
+ L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+ struct {
+ CommonHeader;
+ struct Table *metatable;
+ struct Table *env;
+ size_t len;
+ } uv;
+} Udata;
+
+
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ TValue *k; /* constants used by the function */
+ Instruction *code;
+ struct Proto **p; /* functions defined inside the function */
+ int *lineinfo; /* map from opcodes to source lines */
+ struct LocVar *locvars; /* information about local variables */
+ TString **upvalues; /* upvalue names */
+ TString *source;
+ int sizeupvalues;
+ int sizek; /* size of `k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of `p' */
+ int sizelocvars;
+ int linedefined;
+ int lastlinedefined;
+ GCObject *gclist;
+ lu_byte nups; /* number of upvalues */
+ lu_byte numparams;
+ lu_byte is_vararg;
+ lu_byte maxstacksize;
+} Proto;
+
+
+/* masks for new-style vararg */
+#define VARARG_HASARG 1
+#define VARARG_ISVARARG 2
+#define VARARG_NEEDSARG 4
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+
+/*
+** Upvalues
+*/
+
+typedef struct UpVal {
+ CommonHeader;
+ TValue *v; /* points to stack or to its own value */
+ union {
+ TValue value; /* the value (when closed) */
+ struct { /* double linked list (when open) */
+ struct UpVal *prev;
+ struct UpVal *next;
+ } l;
+ } u;
+} UpVal;
+
+
+/*
+** Closures
+*/
+
+#define ClosureHeader \
+ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
+ struct Table *env
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1];
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1];
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
+#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
+
+
+/*
+** Tables
+*/
+
+typedef union TKey {
+ struct {
+ TValuefields;
+ struct Node *next; /* for chaining */
+ } nk;
+ TValue tvk;
+} TKey;
+
+
+typedef struct Node {
+ TValue i_val;
+ TKey i_key;
+} Node;
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of `node' array */
+ struct Table *metatable;
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ GCObject *gclist;
+ int sizearray; /* size of `array' array */
+} Table;
+
+
+
+/*
+** `module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+#define luaO_nilobject (&luaO_nilobject_)
+
+LUAI_DATA const TValue luaO_nilobject_;
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+LUAI_FUNC int luaO_log2 (unsigned int x);
+LUAI_FUNC int luaO_int2fb (unsigned int x);
+LUAI_FUNC int luaO_fb2int (int x);
+LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
+
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lopcodes.h b/engines/sword25/util/pluto/pdep/lopcodes.h
new file mode 100755
index 0000000000..41224d6ee1
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits.
+ Instructions can have the following fields:
+ `A' : 8 bits
+ `B' : 9 bits
+ `C' : 9 bits
+ `Bx' : 18 bits (`B' and `C' together)
+ `sBx' : signed Bx
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 9
+#define SIZE_B 9
+#define SIZE_Bx (SIZE_C + SIZE_B)
+#define SIZE_A 8
+
+#define SIZE_OP 6
+
+#define POS_OP 0
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_C (POS_A + SIZE_A)
+#define POS_B (POS_C + SIZE_C)
+#define POS_Bx POS_C
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+*/
+#if SIZE_Bx < LUAI_BITSINT-1
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
+#else
+#define MAXARG_Bx MAX_INT
+#define MAXARG_sBx MAX_INT
+#endif
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
+#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
+
+#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
+
+#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
+#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
+ ((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
+
+#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
+#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
+ ((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
+
+#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
+
+
+#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+
+/*
+** Macros to operate RK indices
+*/
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x) ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r) ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x) ((x) | BITRK)
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R(x) - register
+** Kst(x) - constant (in constant table)
+** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+*/
+
+
+/*
+** grep "ORDER OP" if you change these enums
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R(A) := R(B) */
+OP_LOADK,/* A Bx R(A) := Kst(Bx) */
+OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
+OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
+OP_GETUPVAL,/* A B R(A) := UpValue[B] */
+
+OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
+OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
+
+OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
+OP_SETUPVAL,/* A B UpValue[B] := R(A) */
+OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
+
+OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
+
+OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
+
+OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
+OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
+OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
+OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
+OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
+OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
+OP_UNM,/* A B R(A) := -R(B) */
+OP_NOT,/* A B R(A) := not R(B) */
+OP_LEN,/* A B R(A) := length of R(B) */
+
+OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
+
+OP_JMP,/* sBx pc+=sBx */
+
+OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
+OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
+OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
+
+OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
+OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
+
+OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
+
+OP_FORLOOP,/* A sBx R(A)+=R(A+2);
+ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
+
+OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+ if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
+OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
+
+OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
+OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
+
+OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
+} OpCode;
+
+
+#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+ (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
+ and can be 0: OP_CALL then sets `top' to last_result+1, so
+ next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
+
+ (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to `top'
+
+ (*) In OP_SETLIST, if (B == 0) then B = `top';
+ if (C == 0) then next `instruction' is real C
+
+ (*) For comparisons, A specifies what condition the test should accept
+ (true or false).
+
+ (*) All `skips' (pc++) assume that next instruction is a jump
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-1: op mode
+** bits 2-3: C arg mode
+** bits 4-5: B arg mode
+** bit 6: instruction set register A
+** bit 7: operator is a test
+*/
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
+#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
+#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
+
+
+LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lstate.h b/engines/sword25/util/pluto/pdep/lstate.h
new file mode 100755
index 0000000000..3bc575b6bc
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/* table of globals */
+#define gt(L) (&L->l_gt)
+
+/* registry */
+#define registry(L) (&G(L)->l_registry)
+
+
+/* extra stack space to handle TM calls and some other extras */
+#define EXTRA_STACK 5
+
+
+#define BASIC_CI_SIZE 8
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+
+
+typedef struct stringtable {
+ GCObject **hash;
+ lu_int32 nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** informations about a call
+*/
+typedef struct CallInfo {
+ StkId base; /* base for this function */
+ StkId func; /* function index in the stack */
+ StkId top; /* top for this function */
+ const Instruction *savedpc;
+ int nresults; /* expected number of results from this function */
+ int tailcalls; /* number of tail calls lost under this entry */
+} CallInfo;
+
+
+
+#define curr_func(L) (clvalue(L->ci->func))
+#define ci_func(ci) (clvalue((ci)->func))
+#define f_isLua(ci) (!ci_func(ci)->c.isC)
+#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
+
+
+/*
+** `global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ stringtable strt; /* hash table for strings */
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to `frealloc' */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ int sweepstrgc; /* position of sweep in `strt' */
+ GCObject *rootgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* position of sweep in `rootgc' */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of weak tables (to be cleared) */
+ GCObject *tmudata; /* last element of list of userdata to be GC */
+ Mbuffer buff; /* temporary buffer for string concatentation */
+ lu_mem GCthreshold;
+ lu_mem totalbytes; /* number of bytes currently allocated */
+ lu_mem estimate; /* an estimate of number of bytes actually in use */
+ lu_mem gcdept; /* how much GC is `behind schedule' */
+ int gcpause; /* size of pause between successive GCs */
+ int gcstepmul; /* GC `granularity' */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ TValue l_registry;
+ struct lua_State *mainthread;
+ UpVal uvhead; /* head of double-linked list of all open upvalues */
+ struct Table *mt[NUM_TAGS]; /* metatables for basic types */
+ TString *tmname[TM_N]; /* array with tag-method names */
+} global_State;
+
+
+/*
+** `per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ StkId top; /* first free slot in the stack */
+ StkId base; /* base of current function */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ const Instruction *savedpc; /* `savedpc' of current function */
+ StkId stack_last; /* last free slot in the stack */
+ StkId stack; /* stack base */
+ CallInfo *end_ci; /* points after end of ci array*/
+ CallInfo *base_ci; /* array of CallInfo's */
+ int stacksize;
+ int size_ci; /* size of array `base_ci' */
+ unsigned short nCcalls; /* number of nested C calls */
+ unsigned short baseCcalls; /* nested C calls when resuming coroutine */
+ lu_byte hookmask;
+ lu_byte allowhook;
+ int basehookcount;
+ int hookcount;
+ lua_Hook hook;
+ TValue l_gt; /* table of globals */
+ TValue env; /* temporary place for environments */
+ GCObject *openupval; /* list of open upvalues in this stack */
+ GCObject *gclist;
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+};
+
+
+#define G(L) (L->l_G)
+
+
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+ GCheader gch;
+ union TString ts;
+ union Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct UpVal uv;
+ struct lua_State th; /* thread */
+};
+
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define gco2ts(o) (&rawgco2ts(o)->tsv)
+#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
+#define gco2u(o) (&rawgco2u(o)->uv)
+#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
+#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
+#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define ngcotouv(o) \
+ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
+
+/* macro to convert any Lua object into a GCObject */
+#define obj2gco(v) (cast(GCObject *, (v)))
+
+
+LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lstring.h b/engines/sword25/util/pluto/pdep/lstring.h
new file mode 100755
index 0000000000..73a2ff8b38
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
+
+#define sizeudata(u) (sizeof(union Udata)+(u)->len)
+
+#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
+
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/ltm.h b/engines/sword25/util/pluto/pdep/ltm.h
new file mode 100755
index 0000000000..64343b781b
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_EQ, /* last tag method with `fast' access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_MOD,
+ TM_POW,
+ TM_UNM,
+ TM_LEN,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+LUAI_DATA const char *const luaT_typenames[];
+
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lua.h b/engines/sword25/util/pluto/pdep/lua.h
new file mode 100755
index 0000000000..5bc97b746f
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** Lua - An Extensible Extension Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION "Lua 5.1"
+#define LUA_RELEASE "Lua 5.1.3"
+#define LUA_VERSION_NUM 501
+#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
+
+
+/* mark for precompiled code (`<esc>Lua') */
+#define LUA_SIGNATURE "\033Lua"
+
+/* option for multiple returns in `lua_pcall' and `lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** pseudo-indices
+*/
+#define LUA_REGISTRYINDEX (-10000)
+#define LUA_ENVIRONINDEX (-10001)
+#define LUA_GLOBALSINDEX (-10002)
+#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
+
+
+/* thread status; 0 is OK */
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+
+/*
+** functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
+
+
+/*
+** prototype for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_remove) (lua_State *L, int idx);
+LUA_API void (lua_insert) (lua_State *L, int idx);
+LUA_API void (lua_replace) (lua_State *L, int idx);
+LUA_API int (lua_checkstack) (lua_State *L, int sz);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
+
+LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
+LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_objlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void (lua_gettable) (lua_State *L, int idx);
+LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawget) (lua_State *L, int idx);
+LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_getfenv) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setfenv) (lua_State *L, int idx);
+
+
+/*
+** `load' and `call' functions (load and run Lua code)
+*/
+LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
+LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
+LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yield) (lua_State *L, int nresults);
+LUA_API int (lua_resume) (lua_State *L, int narg);
+LUA_API int (lua_status) (lua_State *L);
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_strlen(L,i) lua_objlen(L, (i))
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) \
+ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
+
+#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
+#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+
+/*
+** compatibility macros and functions
+*/
+
+#define lua_open() luaL_newstate()
+
+#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
+
+#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
+
+#define lua_Chunkreader lua_Reader
+#define lua_Chunkwriter lua_Writer
+
+
+/* hack */
+LUA_API void lua_setlevel (lua_State *from, lua_State *to);
+
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILRET 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debuger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
+
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook lua_gethook (lua_State *L);
+LUA_API int lua_gethookmask (lua_State *L);
+LUA_API int lua_gethookcount (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `local', `field', `method' */
+ const char *what; /* (S) `Lua', `C', `main', `tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ int i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
+*
+* 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.
+******************************************************************************/
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h
new file mode 100755
index 0000000000..4e654a52c9
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lzio.h
@@ -0,0 +1,65 @@
+/*
+** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define char2int(c) cast(int, cast(unsigned char, (c)))
+
+#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z))
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define pdep_buffer(buff) ((buff)->buffer)
+#define pdep_sizebuffer(buff) ((buff)->buffsize)
+#define pdep_bufflen(buff) ((buff)->n)
+
+#define pdep_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define pdep_resizebuffer(L, buff, size) \
+ (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
+ (buff)->buffsize = size)
+
+#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n);
+LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */
+LUAI_FUNC int pdep_lookahead (ZIO *z);
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader;
+ void* data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int pdep_fill (ZIO *z);
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h
new file mode 100755
index 0000000000..c26f4566c5
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/pdep.h
@@ -0,0 +1,41 @@
+#ifndef PDEP_H
+#define PDEP_H
+
+#include "lua.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llimits.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lauxlib.h"
+
+
+#define pdep_reallocv(L,b,on,n,e) \
+ pdep_realloc_(L, (b), (on)*(e), (n)*(e))
+#define pdep_reallocvector(L, v,oldn,n,t) \
+ ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t))))
+#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t))
+#define pdep_newvector(L,n,t) \
+ cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t)))
+#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t)))
+#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t))
+#define pdep_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ pdep_growstack(L, n); \
+ else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1);
+
+
+void pdep_pushobject (lua_State *L, const TValue *o);
+void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize);
+void pdep_link (lua_State *L, GCObject *o, lu_byte tt);
+Proto *pdep_newproto (lua_State *L);
+Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e);
+void pdep_reallocstack (lua_State *L, int newsize);
+void pdep_growstack (lua_State *L, int n);
+void pdep_reallocCI (lua_State *L, int newsize);
+TString *pdep_newlstr (lua_State *L, const char *str, size_t l);
+
+#endif
diff --git a/engines/sword25/util/pluto/pluto.c b/engines/sword25/util/pluto/pluto.c
new file mode 100755
index 0000000000..8abf3293b2
--- /dev/null
+++ b/engines/sword25/util/pluto/pluto.c
@@ -0,0 +1,1658 @@
+/* $Id$ */
+
+/* Pluto - Heavy-duty persistence for Lua
+ * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public
+ * domain. People making use of this software as part of an application
+ * are politely requested to email the author at sneftel@gmail.com
+ * with a brief description of the application, primarily to satisfy his
+ * curiosity.
+ *
+ * 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.
+ */
+
+#include "lua.h"
+#include "pluto.h"
+
+#define USE_PDEP
+
+#ifdef USE_PDEP
+#include "pdep/pdep.h"
+#define LIF(prefix, name) pdep ## _ ## name
+#else
+#include "lapi.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llimits.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lauxlib.h"
+#define LIF(prefix, name) lua ## prefix ## _ ## name
+#endif
+
+#include <string.h>
+
+
+
+/* #define PLUTO_DEBUG */
+
+
+
+
+#ifdef PLUTO_DEBUG
+#include <stdio.h>
+#endif
+
+#define PLUTO_TPERMANENT 101
+
+#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); }
+
+typedef struct PersistInfo_t {
+ lua_State *L;
+ int counter;
+ lua_Chunkwriter writer;
+ void *ud;
+#ifdef PLUTO_DEBUG
+ int level;
+#endif
+} PersistInfo;
+
+#ifdef PLUTO_DEBUG
+void printindent(int indent)
+{
+ int il;
+ for(il=0; il<indent; il++) {
+ printf(" ");
+ }
+}
+#endif
+
+/* Mutual recursion requires prototype */
+static void persist(PersistInfo *pi);
+
+/* A simple reimplementation of the unfortunately static function luaA_index.
+ * Does not support the global table, registry, or upvalues. */
+static StkId getobject(lua_State *L, int stackpos)
+{
+ if(stackpos > 0) {
+ lua_assert(L->base+stackpos-1 < L->top);
+ return L->base+stackpos-1;
+ } else {
+ lua_assert(L->top-stackpos >= L->base);
+ return L->top+stackpos;
+ }
+}
+
+/* Choose whether to do a regular or special persistence based on an object's
+ * metatable. "default" is whether the object, if it doesn't have a __persist
+ * entry, is literally persistable or not.
+ * Pushes the unpersist closure and returns true if special persistence is
+ * used. */
+static int persistspecialobject(PersistInfo *pi, int defaction)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(pi->L, 4);
+ /* Check whether we should persist literally, or via the __persist
+ * metafunction */
+ if(!lua_getmetatable(pi->L, -1)) {
+ if(defaction) {
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Type not literally persistable by default");
+ lua_error(pi->L);
+ }
+ }
+ /* perms reftbl sptbl ... obj mt */
+ lua_pushstring(pi->L, "__persist");
+ /* perms reftbl sptbl ... obj mt "__persist" */
+ lua_rawget(pi->L, -2);
+ /* perms reftbl sptbl ... obj mt __persist? */
+ if(lua_isnil(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt nil */
+ lua_pop(pi->L, 2);
+ /* perms reftbl sptbl ... obj */
+ if(defaction) {
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Type not literally persistable by default");
+ lua_error(pi->L);
+ return 0; /* not reached */
+ }
+ } else if(lua_isboolean(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt bool */
+ if(lua_toboolean(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt true */
+ lua_pop(pi->L, 2);
+ /* perms reftbl sptbl ... obj */
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Metatable forbade persistence");
+ lua_error(pi->L);
+ return 0; /* not reached */
+ }
+ } else if(!lua_isfunction(pi->L, -1)) {
+ lua_pushstring(pi->L, "__persist not nil, boolean, or function");
+ lua_error(pi->L);
+ }
+ /* perms reftbl ... obj mt __persist */
+ lua_pushvalue(pi->L, -3);
+ /* perms reftbl ... obj mt __persist obj */
+#ifdef PLUTO_PASS_USERDATA_TO_PERSIST
+ lua_pushlightuserdata(pi->L, (void*)pi->writer);
+ lua_pushlightuserdata(pi->L, pi->ud);
+ /* perms reftbl ... obj mt __persist obj ud */
+ lua_call(pi->L, 3, 1);
+ /* perms reftbl ... obj mt func? */
+#else
+ lua_call(pi->L, 1, 1);
+ /* perms reftbl ... obj mt func? */
+#endif
+ /* perms reftbl ... obj mt func? */
+ if(!lua_isfunction(pi->L, -1)) {
+ lua_pushstring(pi->L, "__persist function did not return a function");
+ lua_error(pi->L);
+ }
+ /* perms reftbl ... obj mt func */
+ {
+ int one = 1;
+ pi->writer(pi->L, &one, sizeof(int), pi->ud);
+ }
+ persist(pi);
+ /* perms reftbl ... obj mt func */
+ lua_pop(pi->L, 2);
+ /* perms reftbl ... obj */
+ return 1;
+}
+
+static void persisttable(PersistInfo *pi)
+{
+ /* perms reftbl ... tbl */
+ lua_checkstack(pi->L, 3);
+ if(persistspecialobject(pi, 1)) {
+ /* perms reftbl ... tbl */
+ return;
+ }
+ /* perms reftbl ... tbl */
+ /* First, persist the metatable (if any) */
+ if(!lua_getmetatable(pi->L, -1)) {
+ lua_pushnil(pi->L);
+ }
+ /* perms reftbl ... tbl mt/nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl */
+
+ /* Now, persist all k/v pairs */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... tbl nil */
+ while(lua_next(pi->L, -2)) {
+ /* perms reftbl ... tbl k v */
+ lua_pushvalue(pi->L, -2);
+ /* perms reftbl ... tbl k v k */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl k v */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl k */
+ }
+ /* perms reftbl ... tbl */
+ /* Terminate list */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... tbl nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl */
+}
+
+static void persistuserdata(PersistInfo *pi) {
+ /* perms reftbl ... udata */
+ lua_checkstack(pi->L, 2);
+ if(persistspecialobject(pi, 0)) {
+ /* perms reftbl ... udata */
+ return;
+ } else {
+ /* Use literal persistence */
+ size_t length = uvalue(getobject(pi->L, -1))->len;
+ pi->writer(pi->L, &length, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, lua_touserdata(pi->L, -1), length, pi->ud);
+ if(!lua_getmetatable(pi->L, -1)) {
+ /* perms reftbl ... udata */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... udata mt/nil */
+ }
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... udata */
+ }
+}
+
+
+static Proto *toproto(lua_State *L, int stackpos)
+{
+ return gco2p(getobject(L, stackpos)->value.gc);
+}
+
+static UpVal *toupval(lua_State *L, int stackpos)
+{
+ lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL);
+ return gco2uv(getobject(L, stackpos)->value.gc);
+}
+
+static void pushproto(lua_State *L, Proto *proto)
+{
+ TValue o;
+ setptvalue(L, &o, proto);
+ LIF(A,pushobject)(L, &o);
+}
+
+#define setuvvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \
+ checkliveness(G(L),i_o); }
+
+static void pushupval(lua_State *L, UpVal *upval)
+{
+ TValue o;
+ setuvvalue(L, &o, upval);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void pushclosure(lua_State *L, Closure *closure)
+{
+ TValue o;
+ setclvalue(L, &o, closure);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void pushstring(lua_State *L, TString *s)
+{
+ TValue o;
+ setsvalue(L, &o, s);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void persistfunction(PersistInfo *pi)
+{
+ /* perms reftbl ... func */
+ Closure *cl = clvalue(getobject(pi->L, -1));
+ lua_checkstack(pi->L, 2);
+ if(cl->c.isC) {
+ /* It's a C function. For now, we aren't going to allow
+ * persistence of C closures, even if the "C proto" is
+ * already in the permanents table. */
+ lua_pushstring(pi->L, "Attempt to persist a C function");
+ lua_error(pi->L);
+ } else {
+ /* It's a Lua closure. */
+ {
+ /* We don't really _NEED_ the number of upvals,
+ * but it'll simplify things a bit */
+ pi->writer(pi->L, &cl->l.p->nups, sizeof(lu_byte), pi->ud);
+ }
+ /* Persist prototype */
+ {
+ pushproto(pi->L, cl->l.p);
+ /* perms reftbl ... func proto */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* Persist upvalue values (not the upvalue objects
+ * themselves) */
+ {
+ int i;
+ for(i=0; i<cl->l.p->nups; i++) {
+ /* perms reftbl ... func */
+ pushupval(pi->L, cl->l.upvals[i]);
+ /* perms reftbl ... func upval */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+ }
+ /* Persist function environment */
+ {
+ lua_getfenv(pi->L, -1);
+ /* perms reftbl ... func fenv */
+ if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) {
+ /* Function has the default fenv */
+ /* perms reftbl ... func _G */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... func nil */
+ }
+ /* perms reftbl ... func fenv/nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ }
+}
+
+
+/* Upvalues are tricky. Here's why.
+ *
+ * A particular upvalue may be either "open", in which case its member v
+ * points into a thread's stack, or "closed" in which case it points to the
+ * upvalue itself. An upvalue is closed under any of the following conditions:
+ * -- The function that initially declared the variable "local" returns
+ * -- The thread in which the closure was created is garbage collected
+ *
+ * To make things wackier, just because a thread is reachable by Lua doesn't
+ * mean it's in our root set. We need to be able to treat an open upvalue
+ * from an unreachable thread as a closed upvalue.
+ *
+ * The solution:
+ * (a) For the purposes of persisting, don't indicate whether an upvalue is
+ * closed or not.
+ * (b) When unpersisting, pretend that all upvalues are closed.
+ * (c) When persisting, persist all open upvalues referenced by a thread
+ * that is persisted, and tag each one with the corresponding stack position
+ * (d) When unpersisting, "reopen" each of these upvalues as the thread is
+ * unpersisted
+ */
+static void persistupval(PersistInfo *pi)
+{
+ /* perms reftbl ... upval */
+ UpVal *uv = toupval(pi->L, -1);
+ lua_checkstack(pi->L, 1);
+
+ /* We can't permit the upval to linger around on the stack, as Lua
+ * will bail if its GC finds it. */
+
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... */
+ LIF(A,pushobject)(pi->L, uv->v);
+ /* perms reftbl ... obj */
+ persist(pi);
+ /* perms reftbl ... obj */
+}
+
+static void persistproto(PersistInfo *pi)
+{
+ /* perms reftbl ... proto */
+ Proto *p = toproto(pi->L, -1);
+ lua_checkstack(pi->L, 2);
+
+ /* Persist constant refs */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizek, sizeof(int), pi->ud);
+ for(i=0; i<p->sizek; i++) {
+ LIF(A,pushobject)(pi->L, &p->k[i]);
+ /* perms reftbl ... proto const */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ }
+ /* perms reftbl ... proto */
+
+ /* serialize inner Proto refs */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizep, sizeof(int), pi->ud);
+ for(i=0; i<p->sizep; i++)
+ {
+ pushproto(pi->L, p->p[i]);
+ /* perms reftbl ... proto subproto */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ }
+ /* perms reftbl ... proto */
+
+ /* Serialize code */
+ {
+ pi->writer(pi->L, &p->sizecode, sizeof(int), pi->ud);
+ pi->writer(pi->L, p->code, sizeof(Instruction) * p->sizecode, pi->ud);
+ }
+
+ /* Serialize upvalue names */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizeupvalues, sizeof(int), pi->ud);
+ for(i=0; i<p->sizeupvalues; i++)
+ {
+ pushstring(pi->L, p->upvalues[i]);
+ persist(pi);
+ lua_pop(pi->L, 1);
+ }
+ }
+ /* Serialize local variable infos */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizelocvars, sizeof(int), pi->ud);
+ for(i=0; i<p->sizelocvars; i++)
+ {
+ pushstring(pi->L, p->locvars[i].varname);
+ persist(pi);
+ lua_pop(pi->L, 1);
+
+ pi->writer(pi->L, &p->locvars[i].startpc, sizeof(int), pi->ud);
+ pi->writer(pi->L, &p->locvars[i].endpc, sizeof(int), pi->ud);
+ }
+ }
+
+ /* Serialize source string */
+ pushstring(pi->L, p->source);
+ persist(pi);
+ lua_pop(pi->L, 1);
+
+ /* Serialize line numbers */
+ {
+ pi->writer(pi->L, &p->sizelineinfo, sizeof(int), pi->ud);
+ if (p->sizelineinfo)
+ {
+ pi->writer(pi->L, p->lineinfo, sizeof(int) * p->sizelineinfo, pi->ud);
+ }
+ }
+
+ /* Serialize linedefined and lastlinedefined */
+ pi->writer(pi->L, &p->linedefined, sizeof(int), pi->ud);
+ pi->writer(pi->L, &p->lastlinedefined, sizeof(int), pi->ud);
+
+ /* Serialize misc values */
+ {
+ pi->writer(pi->L, &p->nups, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->numparams, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->is_vararg, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->maxstacksize, sizeof(lu_byte), pi->ud);
+ }
+ /* We do not currently persist upvalue names, local variable names,
+ * variable lifetimes, line info, or source code. */
+}
+
+/* Copies a stack, but the stack is reversed in the process
+ */
+static size_t revappendstack(lua_State *from, lua_State *to)
+{
+ StkId o;
+ for(o=from->top-1; o>=from->stack; o--) {
+ setobj2s(to, to->top, o);
+ to->top++;
+ }
+ return from->top - from->stack;
+}
+
+/* Persist all stack members
+ */
+static void persistthread(PersistInfo *pi)
+{
+ size_t posremaining;
+ lua_State *L2;
+ /* perms reftbl ... thr */
+ L2 = lua_tothread(pi->L, -1);
+ lua_checkstack(pi->L, L2->top - L2->stack + 1);
+ if(pi->L == L2) {
+ lua_pushstring(pi->L, "Can't persist currently running thread");
+ lua_error(pi->L);
+ return; /* not reached */
+ }
+
+ /* Persist the stack */
+ posremaining = revappendstack(L2, pi->L);
+ /* perms reftbl ... thr (rev'ed contents of L2) */
+ pi->writer(pi->L, &posremaining, sizeof(size_t), pi->ud);
+ for(; posremaining > 0; posremaining--) {
+ persist(pi);
+ lua_pop(pi->L, 1);
+ }
+ /* perms reftbl ... thr */
+ /* Now, persist the CallInfo stack. */
+ {
+ size_t i, numframes = (L2->ci - L2->base_ci) + 1;
+ pi->writer(pi->L, &numframes, sizeof(size_t), pi->ud);
+ for(i=0; i<numframes; i++) {
+ CallInfo *ci = L2->base_ci + i;
+ size_t stackbase = ci->base - L2->stack;
+ size_t stackfunc = ci->func - L2->stack;
+ size_t stacktop = ci->top - L2->stack;
+ size_t savedpc = (ci != L2->base_ci) ?
+ ci->savedpc - ci_func(ci)->l.p->code :
+ 0;
+ pi->writer(pi->L, &stackbase, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stackfunc, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stacktop, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &ci->nresults, sizeof(int), pi->ud);
+ pi->writer(pi->L, &savedpc, sizeof(size_t), pi->ud);
+ }
+ }
+
+ /* Serialize the state's other parameters, with the exception of upval stuff */
+ {
+ size_t stackbase = L2->base - L2->stack;
+ size_t stacktop = L2->top - L2->stack;
+ lua_assert(L2->nCcalls <= 1);
+ pi->writer(pi->L, &L2->status, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &stackbase, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stacktop, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &L2->errfunc, sizeof(ptrdiff_t), pi->ud);
+ }
+
+ /* Finally, record upvalues which need to be reopened */
+ /* See the comment above persistupval() for why we do this */
+ {
+ GCObject *gco;
+ UpVal *uv;
+ /* perms reftbl ... thr */
+ for(gco = L2->openupval; gco != NULL; gco = uv->next) {
+ size_t stackpos;
+ uv = gco2uv(gco);
+
+ /* Make sure upvalue is really open */
+ lua_assert(uv->v != &uv->u.value);
+ pushupval(pi->L, uv);
+ /* perms reftbl ... thr uv */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... thr */
+ stackpos = uv->v - L2->stack;
+ pi->writer(pi->L, &stackpos, sizeof(size_t), pi->ud);
+ }
+ /* perms reftbl ... thr */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... thr nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... thr */
+ }
+ /* perms reftbl ... thr */
+}
+
+static void persistboolean(PersistInfo *pi)
+{
+ int b = lua_toboolean(pi->L, -1);
+ pi->writer(pi->L, &b, sizeof(int), pi->ud);
+}
+
+static void persistlightuserdata(PersistInfo *pi)
+{
+ void *p = lua_touserdata(pi->L, -1);
+ pi->writer(pi->L, &p, sizeof(void *), pi->ud);
+}
+
+static void persistnumber(PersistInfo *pi)
+{
+ lua_Number n = lua_tonumber(pi->L, -1);
+ pi->writer(pi->L, &n, sizeof(lua_Number), pi->ud);
+}
+
+static void persiststring(PersistInfo *pi)
+{
+ size_t length = lua_strlen(pi->L, -1);
+ pi->writer(pi->L, &length, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, lua_tostring(pi->L, -1), length, pi->ud);
+}
+
+/* Top-level delegating persist function
+ */
+static void persist(PersistInfo *pi)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(pi->L, 2);
+ /* If the object has already been written, write a reference to it */
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_rawget(pi->L, 2);
+ /* perms reftbl ... obj ref? */
+ if(!lua_isnil(pi->L, -1)) {
+ /* perms reftbl ... obj ref */
+ int zero = 0;
+ int ref = (int)lua_touserdata(pi->L, -1);
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ pi->writer(pi->L, &ref, sizeof(int), pi->ud);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj ref */
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("0 %d\n", ref);
+#endif
+ return;
+ }
+ /* perms reftbl ... obj nil */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+ /* If the object is nil, write the pseudoreference 0 */
+ if(lua_isnil(pi->L, -1)) {
+ int zero = 0;
+ /* firsttime */
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ /* ref */
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("0 0\n");
+#endif
+ return;
+ }
+ {
+ /* indicate that it's the first time */
+ int one = 1;
+ pi->writer(pi->L, &one, sizeof(int), pi->ud);
+ }
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_pushlightuserdata(pi->L, (void*)(++(pi->counter)));
+ /* perms reftbl ... obj obj ref */
+ lua_rawset(pi->L, 2);
+ /* perms reftbl ... obj */
+
+ pi->writer(pi->L, &pi->counter, sizeof(int), pi->ud);
+
+
+ /* At this point, we'll give the permanents table a chance to play. */
+ {
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_gettable(pi->L, 1);
+ /* perms reftbl ... obj permkey? */
+ if(!lua_isnil(pi->L, -1)) {
+ /* perms reftbl ... obj permkey */
+ int type = PLUTO_TPERMANENT;
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("1 %d PERM\n", pi->counter);
+ pi->level++;
+#endif
+ pi->writer(pi->L, &type, sizeof(int), pi->ud);
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+#ifdef PLUTO_DEBUG
+ pi->level--;
+#endif
+ return;
+ } else {
+ /* perms reftbl ... obj nil */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+ }
+ /* perms reftbl ... obj */
+ }
+ {
+ int type = lua_type(pi->L, -1);
+ pi->writer(pi->L, &type, sizeof(int), pi->ud);
+
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("1 %d %d\n", pi->counter, type);
+ pi->level++;
+#endif
+ }
+
+ switch(lua_type(pi->L, -1)) {
+ case LUA_TBOOLEAN:
+ persistboolean(pi);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ persistlightuserdata(pi);
+ break;
+ case LUA_TNUMBER:
+ persistnumber(pi);
+ break;
+ case LUA_TSTRING:
+ persiststring(pi);
+ break;
+ case LUA_TTABLE:
+ persisttable(pi);
+ break;
+ case LUA_TFUNCTION:
+ persistfunction(pi);
+ break;
+ case LUA_TTHREAD:
+ persistthread(pi);
+ break;
+ case LUA_TPROTO:
+ persistproto(pi);
+ break;
+ case LUA_TUPVAL:
+ persistupval(pi);
+ break;
+ case LUA_TUSERDATA:
+ persistuserdata(pi);
+ break;
+ default:
+ lua_assert(0);
+ }
+#ifdef PLUTO_DEBUG
+ pi->level--;
+#endif
+}
+
+void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud)
+{
+ PersistInfo pi;
+
+ pi.counter = 0;
+ pi.L = L;
+ pi.writer = writer;
+ pi.ud = ud;
+#ifdef PLUTO_DEBUG
+ pi.level = 0;
+#endif
+
+ lua_checkstack(L, 4);
+ /* perms? rootobj? ...? */
+ lua_assert(lua_gettop(L) == 2);
+ /* perms rootobj */
+ lua_assert(!lua_isnil(L, 2));
+ /* perms rootobj */
+ lua_newtable(L);
+ /* perms rootobj reftbl */
+
+ /* Now we're going to make the table weakly keyed. This prevents the
+ * GC from visiting it and trying to mark things it doesn't want to
+ * mark in tables, e.g. upvalues. All objects in the table are
+ * a priori reachable, so it doesn't matter that we do this. */
+ lua_newtable(L);
+ /* perms rootobj reftbl mt */
+ lua_pushstring(L, "__mode");
+ /* perms rootobj reftbl mt "__mode" */
+ lua_pushstring(L, "k");
+ /* perms rootobj reftbl mt "__mode" "k" */
+ lua_settable(L, 4);
+ /* perms rootobj reftbl mt */
+ lua_setmetatable(L, 3);
+ /* perms rootobj reftbl */
+ lua_insert(L, 2);
+ /* perms reftbl rootobj */
+ persist(&pi);
+ /* perms reftbl rootobj */
+ lua_remove(L, 2);
+ /* perms rootobj */
+}
+
+typedef struct WriterInfo_t {
+ char* buf;
+ size_t buflen;
+} WriterInfo;
+
+static int bufwriter (lua_State *L, const void* p, size_t sz, void* ud) {
+ const char* cp = (const char*)p;
+ WriterInfo *wi = (WriterInfo *)ud;
+
+ LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char);
+ while(sz)
+ {
+ /* how dearly I love ugly C pointer twiddling */
+ wi->buf[wi->buflen++] = *cp++;
+ sz--;
+ }
+ return 0;
+}
+
+int persist_l(lua_State *L)
+{
+ /* perms? rootobj? ...? */
+ WriterInfo wi;
+
+ wi.buf = NULL;
+ wi.buflen = 0;
+
+ lua_settop(L, 2);
+ /* perms? rootobj? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms rootobj? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms rootobj */
+
+ pluto_persist(L, bufwriter, &wi);
+
+ lua_settop(L, 0);
+ /* (empty) */
+ lua_pushlstring(L, wi.buf, wi.buflen);
+ /* str */
+ pdep_freearray(L, wi.buf, wi.buflen, char);
+ return 1;
+}
+
+typedef struct UnpersistInfo_t {
+ lua_State *L;
+ ZIO zio;
+#ifdef PLUTO_DEBUG
+ int level;
+#endif
+} UnpersistInfo;
+
+static void unpersist(UnpersistInfo *upi);
+
+/* The object is left on the stack. This is primarily used by unpersist, but
+ * may be used by GCed objects that may incur cycles in order to preregister
+ * the object. */
+static void registerobject(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(upi->L, 2);
+ lua_pushlightuserdata(upi->L, (void*)ref);
+ /* perms reftbl ... obj ref */
+ lua_pushvalue(upi->L, -2);
+ /* perms reftbl ... obj ref obj */
+ lua_settable(upi->L, 2);
+ /* perms reftbl ... obj */
+}
+
+static void unpersistboolean(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int b;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0);
+ lua_pushboolean(upi->L, b);
+ /* perms reftbl ... bool */
+}
+
+static void unpersistlightuserdata(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ void *p;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0);
+ lua_pushlightuserdata(upi->L, p);
+ /* perms reftbl ... ludata */
+}
+
+static void unpersistnumber(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_Number n;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0);
+ lua_pushnumber(upi->L, n);
+ /* perms reftbl ... num */
+}
+
+static void unpersiststring(UnpersistInfo *upi)
+{
+ /* perms reftbl sptbl ref */
+ int length;
+ char* string;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);
+ string = pdep_newvector(upi->L, length, char);
+ verify(LIF(Z,read)(&upi->zio, string, length) == 0);
+ lua_pushlstring(upi->L, string, length);
+ /* perms reftbl sptbl ref str */
+ pdep_freearray(upi->L, string, length, char);
+}
+
+static void unpersistspecialtable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 1);
+ unpersist(upi);
+ /* perms reftbl ... spfunc? */
+ lua_assert(lua_isfunction(upi->L, -1));
+ /* perms reftbl ... spfunc */
+ lua_call(upi->L, 0, 1);
+ /* perms reftbl ... tbl? */
+ lua_assert(lua_istable(upi->L, -1));
+ /* perms reftbl ... tbl */
+}
+
+static void unpersistliteraltable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 3);
+ /* Preregister table for handling of cycles */
+ lua_newtable(upi->L);
+ /* perms reftbl ... tbl */
+ registerobject(ref, upi);
+ /* perms reftbl ... tbl */
+ /* Unpersist metatable */
+ {
+ unpersist(upi);
+ /* perms reftbl ... tbl mt/nil? */
+ if(lua_istable(upi->L, -1)) {
+ /* perms reftbl ... tbl mt */
+ lua_setmetatable(upi->L, -2);
+ /* perms reftbl ... tbl */
+ } else {
+ /* perms reftbl ... tbl nil? */
+ lua_assert(lua_isnil(upi->L, -1));
+ /* perms reftbl ... tbl nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... tbl */
+ }
+ /* perms reftbl ... tbl */
+ }
+
+ while(1)
+ {
+ /* perms reftbl ... tbl */
+ unpersist(upi);
+ /* perms reftbl ... tbl key/nil */
+ if(lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... tbl nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... tbl */
+ break;
+ }
+ /* perms reftbl ... tbl key */
+ unpersist(upi);
+ /* perms reftbl ... tbl key value? */
+ lua_assert(!lua_isnil(upi->L, -1));
+ /* perms reftbl ... tbl key value */
+ lua_rawset(upi->L, -3);
+ /* perms reftbl ... tbl */
+ }
+}
+
+static void unpersisttable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 1);
+ {
+ int isspecial;
+ verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0);
+ if(isspecial) {
+ unpersistspecialtable(ref, upi);
+ /* perms reftbl ... tbl */
+ } else {
+ unpersistliteraltable(ref, upi);
+ /* perms reftbl ... tbl */
+ }
+ /* perms reftbl ... tbl */
+ }
+}
+
+static UpVal *makeupval(lua_State *L, int stackpos)
+{
+ UpVal *uv = pdep_new(L, UpVal);
+ pdep_link(L, (GCObject*)uv, LUA_TUPVAL);
+ uv->tt = LUA_TUPVAL;
+ uv->v = &uv->u.value;
+ uv->u.l.prev = NULL;
+ uv->u.l.next = NULL;
+ setobj(L, uv->v, getobject(L, stackpos));
+ return uv;
+}
+
+static Proto *makefakeproto(lua_State *L, lu_byte nups)
+{
+ Proto *p = pdep_newproto(L);
+ p->sizelineinfo = 1;
+ p->lineinfo = pdep_newvector(L, 1, int);
+ p->lineinfo[0] = 1;
+ p->sizecode = 1;
+ p->code = pdep_newvector(L, 1, Instruction);
+ p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0);
+ p->source = pdep_newlstr(L, "", 0);
+ p->maxstacksize = 2;
+ p->nups = nups;
+ p->sizek = 0;
+ p->sizep = 0;
+
+ return p;
+}
+
+/* The GC is not fond of finding upvalues in tables. We get around this
+ * during persistence using a weakly keyed table, so that the GC doesn't
+ * bother to mark them. This won't work in unpersisting, however, since
+ * if we make the values weak they'll be collected (since nothing else
+ * references them). Our solution, during unpersisting, is to represent
+ * upvalues as dummy functions, each with one upvalue. */
+static void boxupval_start(lua_State *L)
+{
+ LClosure *lcl;
+ lcl = (LClosure*)pdep_newLclosure(L, 1, hvalue(&L->l_gt));
+ pushclosure(L, (Closure*)lcl);
+ /* ... func */
+ lcl->p = makefakeproto(L, 1);
+
+ /* Temporarily initialize the upvalue to nil */
+
+ lua_pushnil(L);
+ lcl->upvals[0] = makeupval(L, -1);
+ lua_pop(L, 1);
+}
+
+static void boxupval_finish(lua_State *L)
+{
+ /* ... func obj */
+ LClosure *lcl = (LClosure *) clvalue(getobject(L, -2));
+
+ lcl->upvals[0]->u.value = *getobject(L, -1);
+ lua_pop(L, 1);
+}
+
+
+static void unboxupval(lua_State *L)
+{
+ /* ... func */
+ LClosure *lcl;
+ UpVal *uv;
+
+ lcl = (LClosure*)clvalue(getobject(L, -1));
+ uv = lcl->upvals[0];
+ lua_pop(L, 1);
+ /* ... */
+ pushupval(L, uv);
+ /* ... upval */
+}
+
+static void unpersistfunction(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ LClosure *lcl;
+ int i;
+ lu_byte nupvalues;
+ lua_checkstack(upi->L, 2);
+
+ verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0);
+
+ lcl = (LClosure*)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt));
+ pushclosure(upi->L, (Closure*)lcl);
+
+ /* perms reftbl ... func */
+ /* Put *some* proto in the closure, before the GC can find it */
+ lcl->p = makefakeproto(upi->L, nupvalues);
+
+ /* Also, we need to temporarily fill the upvalues */
+ lua_pushnil(upi->L);
+ /* perms reftbl ... func nil */
+ for(i=0; i<nupvalues; i++) {
+ lcl->upvals[i] = makeupval(upi->L, -1);
+ }
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+
+ /* I can't see offhand how a function would ever get to be self-
+ * referential, but just in case let's register it early */
+ registerobject(ref, upi);
+
+ /* Now that it's safe, we can get the real proto */
+ unpersist(upi);
+ /* perms reftbl ... func proto? */
+ lua_assert(lua_type(upi->L, -1) == LUA_TPROTO);
+ /* perms reftbl ... func proto */
+ lcl->p = toproto(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+
+ for(i=0; i<nupvalues; i++) {
+ /* perms reftbl ... func */
+ unpersist(upi);
+ /* perms reftbl ... func func2 */
+ unboxupval(upi->L);
+ /* perms reftbl ... func upval */
+ lcl->upvals[i] = toupval(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+
+ /* Finally, the fenv */
+ unpersist(upi);
+ /* perms reftbl ... func fenv/nil? */
+ lua_assert(lua_type(upi->L, -1) == LUA_TNIL ||
+ lua_type(upi->L, -1) == LUA_TTABLE);
+ /* perms reftbl ... func fenv/nil */
+ if(!lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... func fenv */
+ lua_setfenv(upi->L, -2);
+ /* perms reftbl ... func */
+ } else {
+ /* perms reftbl ... func nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+}
+
+static void unpersistupval(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 2);
+
+ boxupval_start(upi->L);
+ /* perms reftbl ... func */
+ registerobject(ref, upi);
+
+ unpersist(upi);
+ /* perms reftbl ... func obj */
+ boxupval_finish(upi->L);
+ /* perms reftbl ... func */
+}
+
+static void unpersistproto(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ Proto *p;
+ int i;
+ int sizep, sizek;
+
+ /* We have to be careful. The GC expects a lot out of protos. In
+ * particular, we need to give the function a valid string for its
+ * source, and valid code, even before we actually read in the real
+ * code. */
+ TString *source = pdep_newlstr(upi->L, "", 0);
+ p = pdep_newproto(upi->L);
+ p->source = source;
+ p->sizecode=1;
+ p->code = pdep_newvector(upi->L, 1, Instruction);
+ p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0);
+ p->maxstacksize = 2;
+ p->sizek = 0;
+ p->sizep = 0;
+
+ lua_checkstack(upi->L, 2);
+
+ pushproto(upi->L, p);
+ /* perms reftbl ... proto */
+ /* We don't need to register early, since protos can never ever be
+ * involved in cyclic references */
+
+ /* Read in constant references */
+ {
+ verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue);
+ for(i=0; i<sizek; i++) {
+ /* perms reftbl ... proto */
+ unpersist(upi);
+ /* perms reftbl ... proto k */
+ setobj2s(upi->L, &p->k[i], getobject(upi->L, -1));
+ p->sizek++;
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ /* perms reftbl ... proto */
+ }
+ /* Read in sub-proto references */
+ {
+ verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*);
+ for(i=0; i<sizep; i++) {
+ /* perms reftbl ... proto */
+ unpersist(upi);
+ /* perms reftbl ... proto subproto */
+ p->p[i] = toproto(upi->L, -1);
+ p->sizep++;
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ /* perms reftbl ... proto */
+ }
+
+ /* Read in code */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction);
+ verify(LIF(Z,read)(&upi->zio, p->code,
+ sizeof(Instruction) * p->sizecode) == 0);
+ }
+
+ /* Read in upvalue names */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0);
+ if (p->sizeupvalues)
+ {
+ LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *);
+ for(i=0; i<p->sizeupvalues; i++)
+ {
+ unpersist(upi);
+ p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+ }
+ }
+ }
+
+ /* Read in local variable infos */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0);
+ if (p->sizelocvars)
+ {
+ LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar);
+ for(i=0; i<p->sizelocvars; i++)
+ {
+ unpersist(upi);
+ p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+
+ verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0);
+ }
+ }
+ }
+
+ /* Read in source string*/
+ unpersist(upi);
+ p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+
+ /* Read in line numbers */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0);
+ if (p->sizelineinfo)
+ {
+ LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int);
+ verify(LIF(Z,read)(&upi->zio, p->lineinfo,
+ sizeof(int) * p->sizelineinfo) == 0);
+ }
+ }
+
+ /* Read in linedefined and lastlinedefined */
+ verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0);
+
+ /* Read in misc values */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0);
+ }
+}
+
+
+/* Does basically the opposite of luaC_link().
+ * Right now this function is rather inefficient; it requires traversing the
+ * entire root GC set in order to find one object. If the GC list were doubly
+ * linked this would be much easier, but there's no reason for Lua to have
+ * that. */
+static void gcunlink(lua_State *L, GCObject *gco)
+{
+ GCObject *prevslot;
+ if(G(L)->rootgc == gco) {
+ G(L)->rootgc = G(L)->rootgc->gch.next;
+ return;
+ }
+
+ prevslot = G(L)->rootgc;
+ while(prevslot->gch.next != gco) {
+ lua_assert(prevslot->gch.next != NULL);
+ prevslot = prevslot->gch.next;
+ }
+
+ prevslot->gch.next = prevslot->gch.next->gch.next;
+}
+
+/* FIXME __ALL__ field ordering */
+static void unpersistthread(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_State *L2;
+ size_t stacklimit = 0;
+ L2 = lua_newthread(upi->L);
+ lua_checkstack(upi->L, 3);
+ /* L1: perms reftbl ... thr */
+ /* L2: (empty) */
+ registerobject(ref, upi);
+
+ /* First, deserialize the object stack. */
+ {
+ size_t i, stacksize;
+ verify(LIF(Z,read)(&upi->zio, &stacksize, sizeof(size_t)) == 0);
+ LIF(D,growstack)(L2, (int)stacksize);
+ /* Make sure that the first stack element (a nil, representing
+ * the imaginary top-level C function) is written to the very,
+ * very bottom of the stack */
+ L2->top--;
+ for(i=0; i<stacksize; i++) {
+ unpersist(upi);
+ /* L1: perms reftbl ... thr obj* */
+ }
+ lua_xmove(upi->L, L2, stacksize);
+ /* L1: perms reftbl ... thr */
+ /* L2: obj* */
+ }
+ /* (hereafter, stacks refer to L1) */
+
+ /* Now, deserialize the CallInfo stack. */
+ {
+ size_t i, numframes;
+ verify(LIF(Z,read)(&upi->zio, &numframes, sizeof(size_t)) == 0);
+ LIF(D,reallocCI)(L2,numframes*2);
+ for(i=0; i<numframes; i++) {
+ CallInfo *ci = L2->base_ci + i;
+ size_t stackbase, stackfunc, stacktop, savedpc;
+ verify(LIF(Z,read)(&upi->zio, &stackbase, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stackfunc, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stacktop, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &savedpc, sizeof(size_t)) == 0);
+
+ if(stacklimit < stacktop)
+ stacklimit = stacktop;
+
+ ci->base = L2->stack+stackbase;
+ ci->func = L2->stack+stackfunc;
+ ci->top = L2->stack+stacktop;
+ ci->savedpc = (ci != L2->base_ci) ?
+ ci_func(ci)->l.p->code+savedpc :
+ 0;
+ ci->tailcalls = 0;
+ /* Update the pointer each time, to keep the GC
+ * happy*/
+ L2->ci = ci;
+ }
+ }
+ /* perms reftbl ... thr */
+ /* Deserialize the state's other parameters, with the exception of upval stuff */
+ {
+ size_t stackbase, stacktop;
+ L2->savedpc = L2->ci->savedpc;
+ verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stackbase, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stacktop, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0);
+ L2->base = L2->stack + stackbase;
+ L2->top = L2->stack + stacktop;
+ }
+ /* Finally, "reopen" upvalues (see persistupval() for why) */
+ {
+ UpVal* uv;
+ GCObject **nextslot = &L2->openupval;
+ global_State *g = G(L2);
+ while(1) {
+ size_t stackpos;
+ unpersist(upi);
+ /* perms reftbl ... thr uv/nil */
+ if(lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... thr nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... thr */
+ break;
+ }
+ /* perms reftbl ... thr boxeduv */
+ unboxupval(upi->L);
+ /* perms reftbl ... thr uv */
+ uv = toupval(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... thr */
+
+ verify(LIF(Z,read)(&upi->zio, &stackpos, sizeof(size_t)) == 0);
+ uv->v = L2->stack + stackpos;
+ gcunlink(upi->L, (GCObject*)uv);
+ uv->marked = luaC_white(g);
+ *nextslot = (GCObject*)uv;
+ nextslot = &uv->next;
+ uv->u.l.prev = &G(L2)->uvhead;
+ uv->u.l.next = G(L2)->uvhead.u.l.next;
+ uv->u.l.next->u.l.prev = uv;
+ G(L2)->uvhead.u.l.next = uv;
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ }
+ *nextslot = NULL;
+ }
+
+ /* The stack must be valid at least to the highest value among the CallInfos */
+ /* 'top' and the values up to there must be filled with 'nil' */
+ {
+ StkId o;
+ LIF(D,checkstack)(L2, (int)stacklimit);
+ for (o = L2->top; o <= L2->top + stacklimit; o++)
+ setnilvalue(o);
+ }
+}
+
+static void unpersistuserdata(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int isspecial;
+ lua_checkstack(upi->L, 2);
+ verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0);
+ if(isspecial) {
+ unpersist(upi);
+ /* perms reftbl ... spfunc? */
+ lua_assert(lua_isfunction(upi->L, -1));
+ /* perms reftbl ... spfunc */
+#ifdef PLUTO_PASS_USERDATA_TO_PERSIST
+ lua_pushlightuserdata(upi->L, &upi->zio);
+ lua_call(upi->L, 1, 1);
+#else
+ lua_call(upi->L, 0, 1);
+#endif
+ /* perms reftbl ... udata? */
+/* This assertion might not be necessary; it's conceivable, for
+ * example, that the SP function might decide to return a table
+ * with equivalent functionality. For the time being, we'll
+ * ignore this possibility in favor of stricter and more testable
+ * requirements. */
+ lua_assert(lua_isuserdata(upi->L, -1));
+ /* perms reftbl ... udata */
+ } else {
+ size_t length;
+ verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);
+
+ lua_newuserdata(upi->L, length);
+ /* perms reftbl ... udata */
+ registerobject(ref, upi);
+ verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0);
+
+ unpersist(upi);
+ /* perms reftbl ... udata mt/nil? */
+ lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1));
+ /* perms reftbl ... udata mt/nil */
+ lua_setmetatable(upi->L, -2);
+ /* perms reftbl ... udata */
+ }
+ /* perms reftbl ... udata */
+}
+
+static void unpersistpermanent(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 2);
+ unpersist(upi);
+ /* perms reftbl permkey */
+ lua_gettable(upi->L, 1);
+ /* perms reftbl perm? */
+ /* We assume currently that the substituted permanent value
+ * shouldn't be nil. This may be a bad assumption. Real-life
+ * experience is needed to evaluate this. */
+ lua_assert(!lua_isnil(upi->L, -1));
+ /* perms reftbl perm */
+}
+
+/* For debugging only; not called when lua_assert is empty */
+static int inreftable(lua_State *L, int ref)
+{
+ int res;
+ lua_checkstack(L, 1);
+ /* perms reftbl ... */
+ lua_pushlightuserdata(L, (void*)ref);
+ /* perms reftbl ... ref */
+ lua_gettable(L, 2);
+ /* perms reftbl ... obj? */
+ res = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ /* perms reftbl ... */
+ return res;
+}
+
+static void unpersist(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int firstTime;
+ int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */
+ lua_checkstack(upi->L, 2);
+ LIF(Z,read)(&upi->zio, &firstTime, sizeof(int));
+ if(firstTime) {
+ int ref;
+ int type;
+ LIF(Z,read)(&upi->zio, &ref, sizeof(int));
+ lua_assert(!inreftable(upi->L, ref));
+ LIF(Z,read)(&upi->zio, &type, sizeof(int));
+#ifdef PLUTO_DEBUG
+ printindent(upi->level);
+ printf("1 %d %d\n", ref, type);
+ upi->level++;
+#endif
+ switch(type) {
+ case LUA_TBOOLEAN:
+ unpersistboolean(upi);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ unpersistlightuserdata(upi);
+ break;
+ case LUA_TNUMBER:
+ unpersistnumber(upi);
+ break;
+ case LUA_TSTRING:
+ unpersiststring(upi);
+ break;
+ case LUA_TTABLE:
+ unpersisttable(ref, upi);
+ break;
+ case LUA_TFUNCTION:
+ unpersistfunction(ref, upi);
+ break;
+ case LUA_TTHREAD:
+ unpersistthread(ref, upi);
+ break;
+ case LUA_TPROTO:
+ unpersistproto(ref, upi);
+ break;
+ case LUA_TUPVAL:
+ unpersistupval(ref, upi);
+ break;
+ case LUA_TUSERDATA:
+ unpersistuserdata(ref, upi);
+ break;
+ case PLUTO_TPERMANENT:
+ unpersistpermanent(ref, upi);
+ break;
+ default:
+ lua_assert(0);
+ }
+ /* perms reftbl ... obj */
+ lua_assert(lua_type(upi->L, -1) == type ||
+ type == PLUTO_TPERMANENT ||
+ /* Remember, upvalues get a special dispensation, as
+ * described in boxupval */
+ (lua_type(upi->L, -1) == LUA_TFUNCTION &&
+ type == LUA_TUPVAL));
+ registerobject(ref, upi);
+ /* perms reftbl ... obj */
+#ifdef PLUTO_DEBUG
+ upi->level--;
+#endif
+ } else {
+ int ref;
+ LIF(Z,read)(&upi->zio, &ref, sizeof(int));
+#ifdef PLUTO_DEBUG
+ printindent(upi->level);
+ printf("0 %d\n", ref);
+#endif
+ if(ref == 0) {
+ lua_pushnil(upi->L);
+ /* perms reftbl ... nil */
+ } else {
+ lua_pushlightuserdata(upi->L, (void*)ref);
+ /* perms reftbl ... ref */
+ lua_gettable(upi->L, 2);
+ /* perms reftbl ... obj? */
+ lua_assert(!lua_isnil(upi->L, -1));
+ }
+ /* perms reftbl ... obj/nil */
+ }
+ /* perms reftbl ... obj/nil */
+ lua_assert(lua_gettop(upi->L) == stacksize + 1);
+}
+
+void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud)
+{
+ /* We use the graciously provided ZIO (what the heck does the Z stand
+ * for?) library so that we don't have to deal with the reader directly.
+ * Letting the reader function decide how much data to return can be
+ * very unpleasant.
+ */
+ UnpersistInfo upi;
+ upi.L = L;
+#ifdef PLUTO_DEBUG
+ upi.level = 0;
+#endif
+
+ lua_checkstack(L, 3);
+ LIF(Z,init)(L, &upi.zio, reader, ud);
+
+ /* perms */
+ lua_newtable(L);
+ /* perms reftbl */
+ lua_gc(L, LUA_GCSTOP, 0);
+ unpersist(&upi);
+ lua_gc(L, LUA_GCRESTART, 0);
+ /* perms reftbl rootobj */
+ lua_replace(L, 2);
+ /* perms rootobj */
+}
+
+typedef struct LoadInfo_t {
+ char *buf;
+ size_t size;
+} LoadInfo;
+
+
+static const char *bufreader(lua_State *L, void *ud, size_t *sz) {
+ LoadInfo *li = (LoadInfo *)ud;
+ if(li->size == 0) {
+ return NULL;
+ }
+ *sz = li->size;
+ li->size = 0;
+ return li->buf;
+}
+
+int unpersist_l(lua_State *L)
+{
+ LoadInfo li;
+ char const *origbuf;
+ char *tempbuf;
+ size_t bufsize;
+ /* perms? str? ...? */
+ lua_settop(L, 2);
+ /* perms? str? */
+ origbuf = luaL_checklstring(L, 2, &bufsize);
+ tempbuf = LIF(M,newvector)(L, bufsize, char);
+ memcpy(tempbuf, origbuf, bufsize);
+
+ li.buf = tempbuf;
+ li.size = bufsize;
+
+ /* perms? str */
+ lua_pop(L, 1);
+ /* perms? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms */
+ pluto_unpersist(L, bufreader, &li);
+ /* perms rootobj */
+ LIF(M,freearray)(L, tempbuf, bufsize, char);
+ return 1;
+}
+
+static luaL_reg pluto_reg[] = {
+ { "persist", persist_l },
+ { "unpersist", unpersist_l },
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_pluto(lua_State *L) {
+ luaL_openlib(L, "pluto", pluto_reg, 0);
+ return 1;
+}
diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h
new file mode 100755
index 0000000000..3674842d44
--- /dev/null
+++ b/engines/sword25/util/pluto/pluto.h
@@ -0,0 +1,25 @@
+/* $Id$ */
+
+/* Pluto - Heavy-duty persistence for Lua
+ * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public
+ * domain. People making use of this software as part of an application
+ * are politely requested to email the author at sneftel@gmail.com
+ * with a brief description of the application, primarily to satisfy his
+ * curiosity.
+ *
+ * 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.
+ */
+
+/* lua.h must be included before this file */
+
+void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud);
+
+void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud);
+
+LUALIB_API int luaopen_pluto(lua_State *L);
diff --git a/engines/sword25/util/pluto/plzio.c b/engines/sword25/util/pluto/plzio.c
new file mode 100755
index 0000000000..7c5ab3b773
--- /dev/null
+++ b/engines/sword25/util/pluto/plzio.c
@@ -0,0 +1,76 @@
+/*
+** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lzio_c
+#define LUA_CORE
+
+#include "pdep/pdep.h"
+
+int pdep_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0) return EOZ;
+ z->n = size - 1;
+ z->p = buff;
+ return char2int(*(z->p++));
+}
+
+
+int pdep_lookahead (ZIO *z) {
+ if (z->n == 0) {
+ if (pdep_fill(z) == EOZ)
+ return EOZ;
+ else {
+ z->n++; /* pdep_fill removed first byte; put back it */
+ z->p--;
+ }
+ }
+ return char2int(*z->p);
+}
+
+
+void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t pdep_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (pdep_lookahead(z) == EOZ)
+ return n; /* return number of missing bytes */
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) {
+ if (n > buff->buffsize) {
+ if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
+ pdep_resizebuffer(L, buff, n);
+ }
+ return buff->buffer;
+}
+
+
diff --git a/engines/sword25/util/pluto/pptest.c b/engines/sword25/util/pluto/pptest.c
new file mode 100755
index 0000000000..1bfecf2b75
--- /dev/null
+++ b/engines/sword25/util/pluto/pptest.c
@@ -0,0 +1,95 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+static int LUAF_createludata(lua_State *L)
+{
+ lua_pushlightuserdata(L, (void*)321);
+ return 1;
+}
+
+/* A userdata that may be literally persisted */
+static int LUAF_boxinteger(lua_State *L)
+{
+ /* num */
+ int* ptr = lua_newuserdata(L, sizeof(int));
+ /* num udata */
+ *ptr = luaL_checkint(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_pushboolean(L, 1);
+ /* num udata mt "__persist" true */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_boxboolean(lua_State *L)
+{
+ /* bool */
+ char* ptr = lua_newuserdata(L, sizeof(char));
+ /* bool udata */
+ *ptr = lua_toboolean(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_getglobal(L, "booleanpersist");
+ /* num udata mt "__persist" booleanpersist */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_unboxboolean(lua_State *L)
+{
+ /* udata */
+ lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
+ /* udata bool */
+ return 1;
+}
+
+static int LUAF_onerror(lua_State *L)
+{
+
+ const char* str = 0;
+ if(lua_gettop(L) != 0)
+ {
+ str = lua_tostring(L, -1);
+ printf("%s\n",str);
+ }
+ return 0;
+}
+
+int main()
+{
+ lua_State* L = lua_open();
+
+ luaL_openlibs(L);
+ lua_settop(L, 0);
+
+ lua_register(L, "createludata", LUAF_createludata);
+ lua_register(L, "boxinteger", LUAF_boxinteger);
+ lua_register(L, "boxboolean", LUAF_boxboolean);
+ lua_register(L, "unboxboolean", LUAF_unboxboolean);
+ lua_register(L, "onerror", LUAF_onerror);
+
+ lua_pushcfunction(L, LUAF_onerror);
+ luaL_loadfile(L, "pptest.lua");
+ lua_pcall(L,0,0,1);
+
+ lua_close(L);
+
+ return 0;
+}
diff --git a/engines/sword25/util/pluto/pptest.lua b/engines/sword25/util/pluto/pptest.lua
new file mode 100755
index 0000000000..144da3ee80
--- /dev/null
+++ b/engines/sword25/util/pluto/pptest.lua
@@ -0,0 +1,168 @@
+-- $Id$
+
+require "pluto"
+
+permtable = { 1234 }
+
+perms = { [coroutine.yield] = 1, [permtable] = 2 }
+
+twithmt = {}
+setmetatable( twithmt, { __call = function() return 21 end } )
+
+function testfenv()
+ return abc
+end
+
+setfenv(testfenv, { abc = 456 })
+
+function fa(i)
+ local ia = i + 1
+ return fb(ia)
+end
+
+function fb(i)
+ local ib = i + 1
+ ib = ib + fc(ib)
+ return ib
+end
+
+function fc(i)
+ local ic = i + 1
+ coroutine.yield()
+ return ic*2
+end
+
+function func()
+ return 4
+end
+
+thr = coroutine.create(fa)
+coroutine.resume(thr, 2)
+
+testtbl = { a = 2, [2] = 4 }
+
+function funcreturningclosure(n)
+ return function()
+ return n
+ end
+end
+
+function nestedfunc(n)
+ return (function(m) return m+2 end)(n+3)
+end
+
+testloopa = {}
+testloopb = { testloopa = testloopa }
+testloopa.testloopb = testloopb
+
+sharedref = {}
+refa = {sharedref = sharedref}
+refb = {sharedref = sharedref}
+
+sptable = { a = 3 }
+
+setmetatable(sptable, {
+ __persist = function(tbl)
+ local a = tbl.a
+ return function()
+ return { a = a+3 }
+ end
+ end
+})
+
+literaludata = boxinteger(71)
+
+function booleanpersist(udata)
+ local b = unboxboolean(udata)
+ return function()
+ return boxboolean(b)
+ end
+end
+
+function makecounter()
+ local a = 0
+ return {
+ inc = function() a = a + 1 end,
+ cur = function() return a end
+ }
+end
+
+function uvinthreadfunc()
+ local a = 1
+ local b = function()
+ a = a+1
+ coroutine.yield()
+ a = a+1
+ end
+ a = a+1
+ b()
+ a = a+1
+ return a
+end
+
+uvinthread = coroutine.create(uvinthreadfunc)
+coroutine.resume(uvinthread)
+
+niinmt = { a = 3 }
+setmetatable(niinmt, {__newindex = function(key, val) end })
+
+
+
+
+local function GenerateObjects()
+ local Table = {}
+
+ function Table:Func()
+ return { Table, self }
+ end
+
+ function uvcycle()
+ return Table:Func()
+ end
+end
+
+GenerateObjects()
+
+
+
+function debuginfo(foo)
+ foo = foo + foo
+ return debug.getlocal(1,1)
+end
+
+rootobj = {
+ testfalse = false,
+ testtrue = true,
+ testseven = 7,
+ testfoobar = "foobar",
+ testfuncreturnsfour = func,
+ testnil = nil,
+ testthread = thr,
+ testperm = permtable,
+ testmt = twithmt,
+ testtbl = testtbl,
+ testfenv = testfenv,
+ testclosure = funcreturningclosure(11),
+ testnilclosure = funcreturningclosure(nil),
+ testnest = nestedfunc,
+ testludata = createludata(),
+ testlooptable = testloopa,
+ testsharedrefa = refa,
+ testsharedrefb = refb,
+ testsptable = sptable,
+ testliteraludata = literaludata,
+ testspudata1 = boxboolean(true),
+ testspudata2 = boxboolean(false),
+ testsharedupval = makecounter(),
+ testuvinthread = uvinthread,
+ testniinmt = niinmt,
+ testuvcycle = uvcycle,
+ testdebuginfo = debuginfo
+}
+
+buf = pluto.persist(perms, rootobj)
+
+onerror()
+outfile = io.open("test.plh", "wb")
+outfile:write(buf)
+outfile:close()
diff --git a/engines/sword25/util/pluto/puptest.c b/engines/sword25/util/pluto/puptest.c
new file mode 100755
index 0000000000..e9aa7ea305
--- /dev/null
+++ b/engines/sword25/util/pluto/puptest.c
@@ -0,0 +1,81 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+static int LUAF_checkludata(lua_State *L)
+{
+ lua_pushboolean(L, lua_touserdata(L, -1) == (void*)321);
+ return 1;
+}
+
+static int LUAF_unboxinteger(lua_State *L)
+{
+ lua_pushnumber(L, *((int*)lua_touserdata(L, -1)));
+ return 1;
+}
+
+static int LUAF_unboxboolean(lua_State *L)
+{
+ /* udata */
+ lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
+ /* udata bool */
+ return 1;
+}
+
+static int LUAF_boxboolean(lua_State *L)
+{
+ /* bool */
+ char* ptr = lua_newuserdata(L, sizeof(char));
+ /* bool udata */
+ *ptr = lua_toboolean(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_getglobal(L, "booleanpersist");
+ /* num udata mt "__persist" booleanpersist */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_onerror(lua_State *L)
+{
+
+ const char* str = 0;
+ if(lua_gettop(L) != 0)
+ {
+ str = lua_tostring(L, -1);
+ printf("%s\n",str);
+ }
+ return 0;
+}
+
+int main()
+{
+ lua_State* L = lua_open();
+
+ luaL_openlibs(L);
+ lua_settop(L, 0);
+
+ lua_register(L, "checkludata", LUAF_checkludata);
+ lua_register(L, "unboxinteger", LUAF_unboxinteger);
+ lua_register(L, "boxboolean", LUAF_boxboolean);
+ lua_register(L, "unboxboolean", LUAF_unboxboolean);
+ lua_register(L, "onerror", LUAF_onerror);
+
+ lua_pushcfunction(L, LUAF_onerror);
+ luaL_loadfile(L, "puptest.lua");
+ lua_pcall(L,0,0,1);
+
+ lua_close(L);
+
+ return 0;
+}
diff --git a/engines/sword25/util/pluto/puptest.lua b/engines/sword25/util/pluto/puptest.lua
new file mode 100755
index 0000000000..e5ccdd64bd
--- /dev/null
+++ b/engines/sword25/util/pluto/puptest.lua
@@ -0,0 +1,93 @@
+-- $Id$
+
+require "pluto"
+
+permtable = { 1234 }
+
+perms = { [1] = coroutine.yield, [2] = permtable }
+
+function testcounter(counter)
+ local a = counter.cur()
+ counter.inc()
+ return counter.cur() == a+1
+end
+
+function testuvinthread(func)
+ local success, result = coroutine.resume(func)
+ return success and result == 5
+end
+
+
+function test(rootobj)
+ local passed = 0
+ local total = 0
+ local dotest = function(name,cond)
+ total = total+1
+ if cond then
+ print(name, " PASSED")
+ passed = passed + 1
+ else
+ print(name, "* FAILED")
+ end
+ end
+
+
+ dotest("Boolean FALSE ", rootobj.testfalse == false)
+ dotest("Boolean TRUE ", rootobj.testtrue == true)
+ dotest("Number 7 ", rootobj.testseven == 7)
+ dotest("String 'foobar' ", rootobj.testfoobar == "foobar")
+ dotest("Func returning 4 ", rootobj.testfuncreturnsfour() == 4)
+ dotest("Nil value ", rootobj.testnil == nil)
+ dotest("Thread resume ", coroutine.resume(rootobj.testthread) == true,14)
+ dotest("Table ", rootobj.testtbl.a == 2 and rootobj.testtbl[2] == 4);
+ dotest("Permanent table ", rootobj.testperm == permtable)
+ dotest("Table metatable ", rootobj.testmt() == 21)
+ dotest("Function env ", rootobj.testfenv() == 456)
+ dotest("Lua closure ", rootobj.testclosure() == 11)
+ dotest("Nil in closure ", rootobj.testnilclosure() == nil)
+ dotest("Nested func ", rootobj.testnest(1) == 6)
+ dotest("Light userdata ", checkludata(rootobj.testludata))
+ dotest("Looped tables ",
+ rootobj.testlooptable.testloopb.testloopa ==
+ rootobj.testlooptable)
+ dotest("Shared reference ", rootobj.testsharedrefa.sharedref ==
+ rootobj.testsharedrefb.sharedref)
+ dotest("Identical tables ", rootobj.testsharedrefa ~=
+ rootobj.testsharedrefb)
+ dotest("Table special persist", rootobj.testsptable.a == 6)
+ dotest("Udata literal persist",
+ unboxinteger(rootobj.testliteraludata) == 71)
+ dotest("Udata special persist",
+ unboxboolean(rootobj.testspudata1) == true and
+ unboxboolean(rootobj.testspudata2) == false)
+ dotest("Shared upvalues ",
+ testcounter(rootobj.testsharedupval))
+ dotest("Open upvalues ",
+ testuvinthread(rootobj.testuvinthread))
+ dotest("Upvalue cycles ",
+ rootobj.testuvcycle()[1] == rootobj.testuvcycle()[2])
+ dotest("__newindex metamethod", rootobj.testniinmt.a == 3)
+ dotest("Debug info ", (rootobj.testdebuginfo(2)) == "foo")
+ print()
+ if passed == total then
+ print("All tests passed.")
+ else
+ print(passed .. "/" .. total .. " tests passed.")
+ end
+end
+
+infile, err = io.open("test.plh", "rb")
+if infile == nil then
+ error("While opening: " .. (err or "no error"))
+end
+
+buf, err = infile:read("*a")
+if buf == nil then
+ error("While reading: " .. (err or "no error"))
+end
+
+infile:close()
+
+rootobj = pluto.unpersist(perms, buf)
+
+test(rootobj)