From: Matthias Braun Date: Thu, 20 Apr 2006 18:44:10 +0000 (+0000) Subject: - make sure all log messages get displayed, even when the console is not X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=690ac9e5920c8d3dd0a8a14a7bd163eaf52ce689;p=supertux.git - make sure all log messages get displayed, even when the console is not completely intiailized yet - first attempt to include sqdbg into supertux (commented out again for now) - some work on worldmap switching - new editor images for ghostparticels and scripttrigger SVN-Revision: 3379 --- diff --git a/configure.ac b/configure.ac index 21cc76504..be44942b8 100644 --- a/configure.ac +++ b/configure.ac @@ -59,38 +59,40 @@ AC_CHECK_HEADERS(unistd.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST +AC_C_BIGENDIAN dnl =========================================================================== dnl Give advanced users some options to play with VARIANT=optimize -AC_MSG_CHECKING(for gprof mode) +AC_MSG_CHECKING([for build variant]) + AC_ARG_ENABLE(gprof, AC_HELP_STRING([--enable-gprof], [enable GNU profiling support]), [enable_gprof=$enableval], [enable_gprof=no]) if test "$enable_gprof" = "yes"; then - #CXXFLAGS="$CXXFLAGS -pg" VARIANT=profile - AC_MSG_RESULT([enabled]) -else - AC_MSG_RESULT([disabled]) fi -AC_MSG_CHECKING(for debug mode) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable debugging mode]), [enable_debug=$enableval], [enable_debug=no]) if test "$enable_debug" = "yes"; then AC_DEFINE([DEBUG], 1, [define to compile in debug checks]) VARIANT=debug - AC_MSG_RESULT([enabled]) else VARIANT=optimize - AC_MSG_RESULT([disabled]) fi AC_SUBST([VARIANT]) - -AC_C_BIGENDIAN() +AC_MSG_RESULT([$VARIANT]) + +AC_ARG_ENABLE(sqdbg, + AC_HELP_STRING([--enable-sqdbg], [enable squirrel remote debugger]), + [enable_sqdbg=$enableval], [enable_sqdbg=no]) +if test "$enable_sqdbg" = "yes"; then + AC_DEFINE([ENABLE_SQDBG], 1, [define if sqdbg should be enabled]) + AC_SUBST([enable_sqdbg]) +fi AM_ICONV AC_SUBST([ICONV_LIBS], [$LIBICONV]) @@ -145,12 +147,6 @@ AC_CONFIG_FILES([Jamconfig]) AC_OUTPUT echo "" -echo "Features:" -echo "=========" -echo " Profile Mode: $enable_gprof" -echo " Debug Mode: $enable_debug" -echo "" -echo "" echo " NOTE: This project uses jam (and not make) as build tool" echo "" diff --git a/data/images/engine/editor/ghostparticles.png b/data/images/engine/editor/ghostparticles.png new file mode 100644 index 000000000..4f1afedd8 Binary files /dev/null and b/data/images/engine/editor/ghostparticles.png differ diff --git a/data/images/engine/editor/ghostparticles.xcf b/data/images/engine/editor/ghostparticles.xcf new file mode 100644 index 000000000..1ced9d377 Binary files /dev/null and b/data/images/engine/editor/ghostparticles.xcf differ diff --git a/data/images/engine/editor/scripttrigger.png b/data/images/engine/editor/scripttrigger.png new file mode 100644 index 000000000..b6bdabd09 Binary files /dev/null and b/data/images/engine/editor/scripttrigger.png differ diff --git a/data/images/engine/editor/scripttrigger.xcf b/data/images/engine/editor/scripttrigger.xcf new file mode 100644 index 000000000..841f50b42 Binary files /dev/null and b/data/images/engine/editor/scripttrigger.xcf differ diff --git a/data/levels/world1/world.nut b/data/levels/world1/world.nut index c9e608b31..c83bcc3c2 100644 --- a/data/levels/world1/world.nut +++ b/data/levels/world1/world.nut @@ -10,7 +10,19 @@ if(! ("world" in state)) { state.world <- "levels/world1/worldmap.stwm"; save_state(); } + +// load worldmap and wait till it is displayed load_worldmap(state.world); fadeout_screen(0.5); wait_for_screenswitch(); save_state(); + +worldthread <- get_current_thread(); +// wait for worldchanges +while(true) { + ::suspend(); + exit_screen(); + load_worldmap(state.world); + save_state(); +} + diff --git a/data/levels/world1/worldmap.stwm b/data/levels/world1/worldmap.stwm index 647b7f210..7062bce8b 100644 --- a/data/levels/world1/worldmap.stwm +++ b/data/levels/world1/worldmap.stwm @@ -184,11 +184,11 @@ ) (special-tile (invisible-tile #t) - (script "state.world <- \"levels/world2/worldmap.stwm\"; -save_state(); - -exit_screen(); -fadeout_screen(2.5); + (script " +state.world <- \"levels/world2/worldmap.stwm\"; +fadeout_screen(2); +wait(2); +worldthread.wakeup(); ") (x 38) (y 21) diff --git a/data/scripts/console.nut b/data/scripts/console.nut index 9170e458d..ff411c04c 100644 --- a/data/scripts/console.nut +++ b/data/scripts/console.nut @@ -14,12 +14,6 @@ function finish() Level.finish(true); } -function println(val) -{ - print(val); - print("\n"); -} - /** * Display a list of functions in the roottable (or in the table specified) */ @@ -40,4 +34,3 @@ function functions(...) } } - diff --git a/data/scripts/default.nut b/data/scripts/default.nut index fbfc8a875..f3a262e03 100644 --- a/data/scripts/default.nut +++ b/data/scripts/default.nut @@ -2,7 +2,6 @@ * This script gets loaded into the squirrel root vm in supertux. So functions * and variables you define here can be used in all threads */ - function end_level() { Sound.play_music("music/leveldone.ogg"); @@ -21,3 +20,9 @@ function levelflip() Effect.fade_in(1); } +function println(val) +{ + print(val); + print("\n"); +} + diff --git a/mk/jam/objects.jam b/mk/jam/objects.jam index 03b006b94..0449b434b 100644 --- a/mk/jam/objects.jam +++ b/mk/jam/objects.jam @@ -148,8 +148,8 @@ rule UseHeaderFile { return ; } -RegisterFileType UseHeaderFile : .h .hpp ; -RegisterHeaderRule HeaderRule : $(HDRPATTERN) : .h .hpp .inc ; +RegisterFileType UseHeaderFile : .h .hpp .inc .inl ; +RegisterHeaderRule HeaderRule : $(HDRPATTERN) : .h .hpp .inc .inl ; ## SearchSource ## Sets search path of the sourcefiles to the current SUBDIR and sets a diff --git a/src/Jamfile b/src/Jamfile index b1ca0c4af..69f7b2621 100644 --- a/src/Jamfile +++ b/src/Jamfile @@ -27,5 +27,5 @@ C++Flags supertux : -DAPPDATADIR='\"$(appdatadir)\"' ; LinkWith supertux : squirrel ; ExternalLibs supertux : SDL SDLIMAGE GL OPENAL VORBIS VORBISFILE OGG ICONV PHYSFS BINRELOC ; Help supertux : "Build the supertux executable" ; -IncludeDir supertux : squirrel/include ; +IncludeDir supertux : squirrel/include squirrel ; diff --git a/src/console.cpp b/src/console.cpp index 7e7b38f76..9ac545c0f 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -38,10 +38,7 @@ Console::Console() : vm(NULL), backgroundOffset(0), height(0), alpha(1.0), offset(0), focused(false), stayOpen(0) { - font.reset(new Font("images/engine/fonts/white-small.png", - "images/engine/fonts/shadow-small.png", 8, 9, 1)); - background.reset(new Surface("images/engine/console.png")); - background2.reset(new Surface("images/engine/console2.png")); + fontheight = 8; } Console::~Console() @@ -51,6 +48,16 @@ Console::~Console() } } +void +Console::init_graphics() +{ + font.reset(new Font("images/engine/fonts/white-small.png", + "images/engine/fonts/shadow-small.png", 8, 9, 1)); + fontheight = font->get_height(); + background.reset(new Surface("images/engine/console.png")); + background2.reset(new Surface("images/engine/console2.png")); +} + void Console::flush(ConsoleStreamBuffer* buffer) { @@ -192,7 +199,7 @@ Console::addLine(std::string s) if (height < 64) { if(height < 4) height = 4; - height += font->get_height(); + height += fontheight; } alpha = 1.0; diff --git a/src/console.hpp b/src/console.hpp index ff9357126..1194a7fe4 100644 --- a/src/console.hpp +++ b/src/console.hpp @@ -46,6 +46,8 @@ public: static std::ostream input; /**< stream of keyboard input to send to the console. Do not forget to send std::endl or to flush the stream. */ static std::ostream output; /**< stream of characters to output to the console. Do not forget to send std::endl or to flush the stream. */ + void init_graphics(); + void backspace(); /**< delete last character sent to the input stream */ void scroll(int offset); /**< scroll console text up or down by @c offset lines */ void autocomplete(); /**< autocomplete current command */ @@ -98,6 +100,7 @@ private: int offset; /**< decrease to scroll text up */ bool focused; /**< true if console has input focus */ std::auto_ptr font; + float fontheight; /**< height of the font (this is a separate var, because the font could not be initialized yet but is needed in the addLine message */ float stayOpen; diff --git a/src/main.cpp b/src/main.cpp index 8f4569e1c..5543ea3d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -483,12 +483,11 @@ int main(int argc, char** argv) int result = 0; try { + Console::instance = new Console(); srand(time(0)); init_physfs(argv[0]); init_sdl(); - log_fatal << "Test" << std::endl; - timelog("controller"); main_controller = new JoystickKeyboardController(); timelog("config"); @@ -502,7 +501,7 @@ int main(int argc, char** argv) init_audio(); timelog("video"); init_video(); - Console::instance = new Console(); + Console::instance->init_graphics(); timelog("scripting"); init_scripting(); diff --git a/src/mainloop.cpp b/src/mainloop.cpp index 4e7199d40..65da30ba4 100644 --- a/src/mainloop.cpp +++ b/src/mainloop.cpp @@ -60,6 +60,10 @@ MainLoop::push_screen(Screen* screen, ScreenFade* screen_fade) { this->next_screen.reset(screen); this->screen_fade.reset(screen_fade); + if(nextpop) + nextpush = false; + else + nextpush = true; nextpop = false; speed = 1.0; } @@ -70,6 +74,7 @@ MainLoop::exit_screen(ScreenFade* screen_fade) next_screen.reset(NULL); this->screen_fade.reset(screen_fade); nextpop = true; + nextpush = false; } void @@ -134,7 +139,8 @@ MainLoop::run() screen_stack.pop_back(); nextpop = false; speed = 1.0; - } else if(current_screen.get() != NULL) { + } + if(nextpush && current_screen.get() != NULL) { screen_stack.push_back(current_screen.release()); } diff --git a/src/mainloop.hpp b/src/mainloop.hpp index 5e51d2df1..db961e06b 100644 --- a/src/mainloop.hpp +++ b/src/mainloop.hpp @@ -48,6 +48,7 @@ private: bool running; float speed; bool nextpop; + bool nextpush; std::auto_ptr next_screen; std::auto_ptr current_screen; std::auto_ptr console; diff --git a/src/script_manager.cpp b/src/script_manager.cpp index 14e659949..da3f6064a 100644 --- a/src/script_manager.cpp +++ b/src/script_manager.cpp @@ -60,6 +60,22 @@ ScriptManager::ScriptManager() throw std::runtime_error("Couldn't initialize squirrel vm"); sq_setforeignptr(vm, (SQUserPointer) this); +#ifdef ENABLE_SQDBG + debugger = NULL; + /* + debugger = sq_rdbg_init(vm, 1234, SQFalse); + if(debugger == NULL) + throw SquirrelError(vm, "Couldn't initialize suirrel remote debugger"); + + sq_enabledebuginfo(vm, SQTrue); + log_info << "Waiting for debug client..." << std::endl; + if(!SQ_SUCCEEDED(sq_rdbg_waitforconnections(debugger))) { + throw SquirrelError(vm, "Waiting for debug clients failed"); + } + log_info << "debug client connected." << std::endl; + */ +#endif + // register squirrel libs sq_pushroottable(vm); if(sqstd_register_bloblib(vm) < 0) @@ -89,6 +105,9 @@ ScriptManager::ScriptManager() ScriptManager::ScriptManager(ScriptManager* parent) { +#ifdef ENABLE_SQDBG + debugger = NULL; +#endif this->parent = parent; vm = parent->vm; parent->childs.push_back(this); @@ -105,6 +124,9 @@ ScriptManager::~ScriptManager() std::remove(parent->childs.begin(), parent->childs.end(), this), parent->childs.end()); } else { +#ifdef ENABLE_SQDBG + sq_rdbg_shutdown(debugger); +#endif sq_close(vm); } } @@ -136,12 +158,21 @@ ScriptManager::create_thread(bool leave_thread_on_stack) void ScriptManager::update() { +#ifdef ENABLE_SQDBG + if(debugger != NULL) + sq_rdbg_update(debugger); +#endif + for(SquirrelVMs::iterator i = squirrel_vms.begin(); i != squirrel_vms.end(); ) { SquirrelVM& squirrel_vm = *i; int vm_state = sq_getvmstate(squirrel_vm.vm); - if(vm_state == SQ_VMSTATE_SUSPENDED && squirrel_vm.wakeup_time > 0 && game_time >= squirrel_vm.wakeup_time) { + if(vm_state == SQ_VMSTATE_SUSPENDED + && squirrel_vm.wakeup_time > 0 + && game_time >= squirrel_vm.wakeup_time) { squirrel_vm.waiting_for_events = WakeupData(NO_EVENT); + squirrel_vm.wakeup_time = 0; + try { if(SQ_FAILED(sq_wakeupvm(squirrel_vm.vm, false, false))) { throw SquirrelError(squirrel_vm.vm, "Couldn't resume script"); diff --git a/src/script_manager.hpp b/src/script_manager.hpp index 77d74d212..e0f297852 100644 --- a/src/script_manager.hpp +++ b/src/script_manager.hpp @@ -25,6 +25,9 @@ #include #include #include "timer.hpp" +#ifdef ENABLE_SQDBG +#include +#endif class GameObject; @@ -98,6 +101,9 @@ private: HSQUIRRELVM vm; ScriptManager* parent; std::vector childs; +#ifdef ENABLE_SQDBG + HSQREMOTEDBG debugger; +#endif }; #endif diff --git a/src/scripting/functions.cpp b/src/scripting/functions.cpp index f1a3715cb..fc946db97 100644 --- a/src/scripting/functions.cpp +++ b/src/scripting/functions.cpp @@ -58,6 +58,22 @@ int display(HSQUIRRELVM vm) return 0; } +void print_stacktrace(HSQUIRRELVM vm) +{ + print_squirrel_stack(vm); +} + +int get_current_thread(HSQUIRRELVM vm) +{ + SQObject object; + sq_resetobject(&object); + object._unVal.pThread = vm; + object._type = OT_THREAD; + sq_pushobject(vm, object); + + return 1; +} + void wait(HSQUIRRELVM vm, float seconds) { SQUserPointer ptr = sq_getforeignptr(vm); diff --git a/src/scripting/functions.hpp b/src/scripting/functions.hpp index 9222c0ed1..2b690630c 100644 --- a/src/scripting/functions.hpp +++ b/src/scripting/functions.hpp @@ -43,6 +43,16 @@ static const int KEY_GOLD = 0x010; int display(HSQUIRRELVM vm) __custom; /** + * Displays contents of the current stack + */ +void print_stacktrace(HSQUIRRELVM vm); + +/** + * returns the currently running thread + */ +int get_current_thread(HSQUIRRELVM vm) __custom; + +/** * Display a text file and scrolls it over the screen (on next screenswitch) */ void display_text_file(const std::string& filename); diff --git a/src/scripting/wrapper.cpp b/src/scripting/wrapper.cpp index 4f2592e20..910d6bc77 100644 --- a/src/scripting/wrapper.cpp +++ b/src/scripting/wrapper.cpp @@ -1481,6 +1481,30 @@ static int display_wrapper(HSQUIRRELVM vm) return Scripting::display(vm); } +static int print_stacktrace_wrapper(HSQUIRRELVM vm) +{ + HSQUIRRELVM arg0 = vm; + + try { + Scripting::print_stacktrace(arg0); + + return 0; + + } catch(std::exception& e) { + sq_throwerror(vm, e.what()); + return SQ_ERROR; + } catch(...) { + sq_throwerror(vm, _SC("Unexpected exception while executing function 'print_stacktrace'")); + return SQ_ERROR; + } + +} + +static int get_current_thread_wrapper(HSQUIRRELVM vm) +{ + return Scripting::get_current_thread(vm); +} + static int display_text_file_wrapper(HSQUIRRELVM vm) { const char* arg0; @@ -2321,6 +2345,18 @@ void register_supertux_wrapper(HSQUIRRELVM v) throw SquirrelError(v, "Couldn't register function 'display'"); } + sq_pushstring(v, "print_stacktrace", -1); + sq_newclosure(v, &print_stacktrace_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'print_stacktrace'"); + } + + sq_pushstring(v, "get_current_thread", -1); + sq_newclosure(v, &get_current_thread_wrapper, 0); + if(SQ_FAILED(sq_createslot(v, -3))) { + throw SquirrelError(v, "Couldn't register function 'get_current_thread'"); + } + sq_pushstring(v, "display_text_file", -1); sq_newclosure(v, &display_text_file_wrapper, 0); if(SQ_FAILED(sq_createslot(v, -3))) { diff --git a/src/squirrel/Jamfile b/src/squirrel/Jamfile index 9db250272..3a3c36fed 100644 --- a/src/squirrel/Jamfile +++ b/src/squirrel/Jamfile @@ -1,14 +1,21 @@ SubDir TOP src squirrel ; +SQDBG_SOURCES = [ Wildcard sqdbg : *.cpp *.h *.inl ] ; +if $(enable_sqdbg) = "yes" { + EXTRA_SOURCES = $(SQDBG_SOURCES) ; +} else { + Package $(SQDBG_SOURCES) ; +} + Library squirrel : [ Wildcard squirrel : *.cpp *.h ] [ Wildcard sqstdlib : *.cpp *.c *.h ] + $(EXTRA_SOURCES) : noinstall ; -for i in $(squirrel_OBJECTS) -{ - CXXFLAGS on $(i) = [ Filter [ on $(i) GetVar CXXFLAGS ] : -Wall -W -Werror ] ; - CFLAGS on $(i) = [ Filter [ on $(i) GetVar CFLAGS ] : -Wall -W -Werror ] ; +for i in $(squirrel_OBJECTS) { + CXXFLAGS on $(i) = [ Filter [ on $(i) GetVar CXXFLAGS ] : -Wall -W -Werror ] ; + CFLAGS on $(i) = [ Filter [ on $(i) GetVar CFLAGS ] : -Wall -W -Werror ] ; } IncludeDir squirrel : include ; diff --git a/src/squirrel/README b/src/squirrel/README index a0776e9f6..d6c047157 100644 --- a/src/squirrel/README +++ b/src/squirrel/README @@ -1,3 +1,66 @@ -This directory contains the SQUIRREL programming language version 2.0.1 from -http://squirrel.sourceforge.net -Originally on zlib/png license. +~~~~~~~~ +Squirrel +~~~~~~~~ + +This directory contains the SQUIRREL programming language as found on +http://www.squirrel-lang.org with some minor fixes to make it compile warning +free on 64bit architectures. + +Squirrel Copyright: +Copyright (c) 2003-2006 Alberto Demichelis + +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. +----------------------------------------------------- +END OF COPYRIGHT + + +~~~~~~~~~~~~~~~~~~~~~~~~ +Squirrel Remote Debugger +~~~~~~~~~~~~~~~~~~~~~~~~ + +The subdirectory sqdbg contains the sqdbg library by Alberto Demichelis. + +SQDBG License: + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use +of this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim +that you wrote the original software. If you use this software in a product, an +acknowledgment in the product documentation would be appreciated but is not +required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + diff --git a/src/squirrel/sqdbg/serialize_state.inl b/src/squirrel/sqdbg/serialize_state.inl new file mode 100644 index 000000000..347ee63c8 --- /dev/null +++ b/src/squirrel/sqdbg/serialize_state.inl @@ -0,0 +1,416 @@ +static const SQChar serialize_state_nut[] = { +0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x6c, 0x6f, +0x63, 0x61, 0x6c, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, +0x3d, 0x7b, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x3d, 0x30, 0x2c, 0x72, 0x65, +0x66, 0x73, 0x3d, 0x7b, 0x7d, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x63, 0x6f, +0x6d, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, +0x3c, 0x2d, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62, +0x6c, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, +0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x5d, +0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, +0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, +0x75, 0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73, +0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, +0x6c, 0x6c, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x77, 0x65, 0x61, 0x6b, +0x72, 0x65, 0x66, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, +0x2c, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, +0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, +0x65, 0x66, 0x73, 0x28, 0x74, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73, +0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, +0x66, 0x28, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74, +0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29, +0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0d, +0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x74, 0x79, 0x70, +0x65, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74, +0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x6f, 0x74, 0x79, 0x70, +0x65, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, +0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, +0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x28, 0x74, 0x20, 0x69, 0x6e, +0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, +0x66, 0x73, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x6f, +0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, +0x5b, 0x74, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, +0x72, 0x65, 0x67, 0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b, +0x0d, 0x0a, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, +0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, +0x74, 0x28, 0x74, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, +0x28, 0x6f, 0x2c, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3a, 0x28, 0x6f, +0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x09, 0x09, +0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20, 0x20, +0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, +0x28, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x20, +0x20, 0x20, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, +0x73, 0x28, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, +0x20, 0x7d, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, +0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, +0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x74, 0x76, +0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, +0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, +0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, +0x65, 0x28, 0x76, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, +0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, +0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, +0x61, 0x72, 0x72, 0x61, 0x79, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, +0x61, 0x73, 0x65, 0x20, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x3a, +0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e, +0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, +0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x73, +0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x76, 0x5d, +0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x3b, +0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x69, 0x6e, +0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x63, +0x61, 0x73, 0x65, 0x20, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22, 0x3a, +0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, +0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, +0x65, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x22, 0x3a, 0x0d, 0x0a, 0x09, +0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, +0x76, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x73, +0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x3b, 0x0d, 0x0a, 0x09, +0x09, 0x63, 0x61, 0x73, 0x65, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, +0x3a, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, +0x75, 0x72, 0x6e, 0x20, 0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x3b, 0x0d, +0x0a, 0x09, 0x09, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x3a, 0x0d, +0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, +0x75, 0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, +0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, +0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, +0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3d, 0x7b, 0x0d, 0x0a, 0x09, 0x5b, +0x22, 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x6e, 0x22, 0x2c, +0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, +0x5d, 0x3d, 0x22, 0x73, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, +0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x22, 0x5d, 0x3d, 0x22, 0x69, 0x22, +0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x22, +0x5d, 0x3d, 0x22, 0x66, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x75, +0x73, 0x65, 0x72, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x3d, 0x22, 0x75, +0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, +0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x3d, 0x22, 0x66, 0x6e, 0x22, 0x2c, 0x0d, +0x0a, 0x09, 0x5b, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x5d, 0x3d, +0x22, 0x74, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x61, 0x72, 0x72, +0x61, 0x79, 0x22, 0x5d, 0x3d, 0x22, 0x61, 0x22, 0x2c, 0x0d, 0x0a, 0x09, +0x5b, 0x22, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, +0x5d, 0x3d, 0x22, 0x67, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x74, +0x68, 0x72, 0x65, 0x61, 0x64, 0x22, 0x5d, 0x3d, 0x22, 0x68, 0x22, 0x2c, +0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, +0x65, 0x22, 0x5d, 0x3d, 0x22, 0x78, 0x22, 0x2c, 0x20, 0x0d, 0x0a, 0x09, +0x5b, 0x22, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x5d, 0x3d, 0x22, 0x79, +0x22, 0x2c, 0x20, 0x20, 0x0d, 0x0a, 0x09, 0x5b, 0x22, 0x62, 0x6f, 0x6f, +0x6c, 0x22, 0x5d, 0x3d, 0x22, 0x62, 0x22, 0x2c, 0x0d, 0x0a, 0x09, 0x5b, +0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x5d, 0x3d, 0x22, +0x77, 0x22, 0x20, 0x20, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, +0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, +0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, 0x29, 0x3a, +0x28, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, +0x73, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x74, +0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, +0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x29, 0x72, 0x65, 0x74, 0x75, +0x72, 0x6e, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x74, 0x79, +0x70, 0x65, 0x73, 0x5b, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x0d, 0x0a, 0x09, +0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0d, +0x0a, 0x7d, 0x20, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, +0x69, 0x6f, 0x6e, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x65, 0x6f, +0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x66, 0x75, +0x6e, 0x63, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, +0x61, 0x6c, 0x20, 0x74, 0x79, 0x20, 0x3d, 0x20, 0x3a, 0x3a, 0x74, 0x79, +0x70, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x69, +0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x69, 0x6e, 0x73, +0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, +0x09, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x54, 0x52, 0x59, +0x20, 0x54, 0x4f, 0x20, 0x55, 0x53, 0x45, 0x20, 0x5f, 0x6e, 0x65, 0x78, +0x74, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, +0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, +0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, 0x0d, 0x0a, 0x09, +0x09, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, +0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64, 0x78, +0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, +0x20, 0x20, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, +0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x65, 0x29, 0x20, 0x7b, 0x0d, 0x0a, +0x09, 0x09, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, +0x28, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, +0x6f, 0x62, 0x6a, 0x2e, 0x67, 0x65, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, +0x28, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7b, 0x0d, +0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, +0x2c, 0x69, 0x64, 0x78, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69, 0x64, 0x78, +0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x7d, 0x0d, +0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x65, +0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x28, 0x74, 0x79, 0x20, 0x3d, 0x3d, +0x20, 0x22, 0x77, 0x65, 0x61, 0x6b, 0x72, 0x65, 0x66, 0x22, 0x29, 0x20, +0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, +0x6a, 0x2c, 0x22, 0x40, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x62, 0x6a, +0x2e, 0x72, 0x65, 0x66, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, +0x0d, 0x0a, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0d, 0x0a, 0x09, +0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x64, 0x78, +0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, 0x29, +0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, +0x20, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x62, 0x6a, 0x2c, 0x69, 0x64, +0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, +0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x7d, +0x0d, 0x0a, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, +0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x28, +0x29, 0x3a, 0x28, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, 0x29, +0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, +0x68, 0x28, 0x69, 0x2c, 0x6f, 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x62, 0x6a, +0x73, 0x5f, 0x72, 0x65, 0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x29, 0x0d, +0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, +0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, +0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x28, 0x69, +0x3d, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74, +0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x3f, 0x22, 0x72, 0x22, 0x3a, 0x70, +0x61, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74, +0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a, +0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x5f, 0x74, 0x79, 0x70, +0x65, 0x6f, 0x66, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, +0x20, 0x69, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x5f, 0x74, +0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x74, +0x79, 0x70, 0x65, 0x28, 0x69, 0x29, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, +0x22, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x22, 0x2c, 0x5f, 0x74, 0x79, +0x70, 0x65, 0x6f, 0x66, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, +0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, +0x28, 0x22, 0x72, 0x65, 0x66, 0x22, 0x2c, 0x6f, 0x2e, 0x74, 0x6f, 0x73, +0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, +0x09, 0x69, 0x66, 0x28, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x3a, 0x3a, 0x67, +0x65, 0x74, 0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, +0x29, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x74, 0x65, 0x72, +0x61, 0x74, 0x65, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x69, 0x2c, +0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6f, 0x62, +0x6a, 0x2c, 0x69, 0x64, 0x78, 0x2c, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x7b, +0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x3a, 0x3a, 0x74, +0x79, 0x70, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x20, 0x3d, 0x3d, 0x20, +0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x0d, +0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, +0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, +0x6e, 0x74, 0x28, 0x22, 0x65, 0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, +0x65, 0x28, 0x22, 0x6b, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x76, 0x22, 0x2c, +0x69, 0x64, 0x78, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, +0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x76, +0x74, 0x22, 0x2c, 0x22, 0x76, 0x22, 0x2c, 0x6f, 0x62, 0x6a, 0x5b, 0x69, +0x64, 0x78, 0x5d, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, +0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x65, +0x22, 0x29, 0x3b, 0x09, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, +0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e, +0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x22, +0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, +0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x76, +0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, +0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x64, 0x2c, 0x65, +0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x29, 0x0d, 0x0a, +0x7b, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, +0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x22, 0x72, 0x65, 0x74, 0x75, +0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, +0x28, 0x22, 0x0d, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, +0x61, 0x72, 0x61, 0x6d, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x0d, 0x0a, 0x09, +0x0d, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70, +0x70, 0x65, 0x6e, 0x64, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x5b, +0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x5d, 0x29, 0x0d, 0x0a, 0x09, 0x6c, +0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x3d, 0x31, +0x3b, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, +0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, +0x73, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x69, 0x21, +0x3d, 0x22, 0x74, 0x68, 0x69, 0x73, 0x22, 0x20, 0x26, 0x26, 0x20, 0x69, +0x5b, 0x30, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x40, 0x27, 0x29, 0x7b, +0x20, 0x2f, 0x2f, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, +0x74, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x73, 0x74, 0x61, +0x72, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x40, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x69, 0x66, 0x28, 0x21, 0x66, 0x69, 0x72, 0x73, 0x74, 0x29, +0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, +0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, +0x2b, 0x22, 0x2c, 0x22, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x0d, 0x0a, +0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x69, 0x72, +0x73, 0x74, 0x3d, 0x6e, 0x75, 0x6c, 0x6c, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, +0x64, 0x28, 0x76, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x75, 0x6e, +0x63, 0x5f, 0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, +0x72, 0x63, 0x2b, 0x69, 0x0d, 0x0a, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, +0x7d, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, +0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x2b, 0x22, 0x29, +0x7b, 0x5c, 0x6e, 0x22, 0x0d, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x5f, +0x73, 0x72, 0x63, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, +0x2b, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x22, 0x2b, +0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2b, 0x22, +0x29, 0x5c, 0x6e, 0x7d, 0x22, 0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x74, +0x72, 0x79, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, +0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x3d, 0x3a, 0x3a, 0x63, 0x6f, 0x6d, +0x70, 0x69, 0x6c, 0x65, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x66, +0x75, 0x6e, 0x63, 0x5f, 0x73, 0x72, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09, +0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, 0x73, 0x74, 0x61, +0x74, 0x75, 0x73, 0x3d, 0x22, 0x6f, 0x6b, 0x22, 0x20, 0x2c, 0x20, 0x76, +0x61, 0x6c, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x29, 0x2e, 0x61, 0x63, +0x61, 0x6c, 0x6c, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x29, 0x7d, +0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x63, 0x61, 0x74, 0x63, +0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, +0x0d, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x7b, +0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f, +0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, +0x0d, 0x0a, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x0d, 0x0a, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, +0x2f, 0x2f, 0x0d, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, +0x20, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x74, +0x79, 0x70, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x76, +0x61, 0x6c, 0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, +0x76, 0x61, 0x6c, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x61, 0x74, +0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x74, 0x79, 0x70, 0x65, +0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x70, 0x61, 0x63, 0x6b, +0x5f, 0x74, 0x79, 0x70, 0x65, 0x28, 0x3a, 0x3a, 0x74, 0x79, 0x70, 0x65, +0x28, 0x76, 0x61, 0x6c, 0x29, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x61, +0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x76, 0x61, 0x6c, +0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x2c, 0x67, 0x65, +0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x76, 0x61, 0x6c, 0x29, 0x2e, +0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, +0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, +0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x3d, 0x5b, 0x5d, 0x0d, 0x0a, 0x6c, +0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x33, +0x3b, 0x0d, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x69, 0x3b, +0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x74, 0x72, 0x79, 0x20, 0x7b, 0x0d, +0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x4e, 0x55, 0x4d, 0x45, 0x52, 0x41, 0x54, +0x45, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x54, 0x41, 0x43, 0x4b, 0x20, +0x57, 0x41, 0x54, 0x43, 0x48, 0x45, 0x53, 0x0d, 0x0a, 0x09, 0x77, 0x68, +0x69, 0x6c, 0x65, 0x28, 0x73, 0x69, 0x3d, 0x3a, 0x3a, 0x67, 0x65, 0x74, +0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x28, 0x6c, +0x65, 0x76, 0x65, 0x6c, 0x29, 0x29, 0x0d, 0x0a, 0x09, 0x7b, 0x0d, 0x0a, +0x09, 0x09, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2e, 0x61, 0x70, 0x70, 0x65, +0x6e, 0x64, 0x28, 0x73, 0x69, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x6c, +0x65, 0x76, 0x65, 0x6c, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, +0x0a, 0x0d, 0x0a, 0x09, 0x2f, 0x2f, 0x45, 0x56, 0x41, 0x4c, 0x55, 0x41, +0x54, 0x45, 0x20, 0x41, 0x4c, 0x4c, 0x20, 0x57, 0x41, 0x54, 0x43, 0x48, +0x45, 0x53, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, +0x67, 0x2e, 0x72, 0x65, 0x66, 0x73, 0x5b, 0x3a, 0x3a, 0x67, 0x65, 0x74, +0x72, 0x6f, 0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x5d, +0x20, 0x3c, 0x2d, 0x20, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, +0x2e, 0x6d, 0x61, 0x78, 0x69, 0x64, 0x2b, 0x2b, 0x3b, 0x0d, 0x0a, 0x09, +0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61, +0x6c, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d, +0x0a, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61, +0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54, 0x49, +0x56, 0x45, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x69, +0x66, 0x28, 0x22, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20, +0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x7b, 0x0d, 0x0a, +0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, +0x68, 0x65, 0x73, 0x20, 0x3c, 0x2d, 0x20, 0x7b, 0x7d, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, +0x2c, 0x77, 0x61, 0x74, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x61, +0x74, 0x63, 0x68, 0x65, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, +0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, +0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63, 0x21, 0x3d, 0x22, 0x4e, 0x41, 0x54, +0x49, 0x56, 0x45, 0x22, 0x29, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, +0x09, 0x09, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, +0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3c, 0x2d, 0x20, 0x65, 0x76, 0x61, 0x6c, +0x75, 0x61, 0x74, 0x65, 0x5f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x28, 0x76, +0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x2c, 0x69, 0x2c, +0x77, 0x61, 0x74, 0x63, 0x68, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, +0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e, 0x73, 0x74, 0x61, +0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, +0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x62, 0x75, +0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, 0x76, 0x61, 0x6c, +0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x2e, +0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, +0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, +0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, 0x6c, +0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x20, +0x3c, 0x2d, 0x20, 0x7b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x22, +0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x76, 0x61, +0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x5b, 0x69, 0x5d, +0x2e, 0x65, 0x78, 0x70, 0x20, 0x3c, 0x2d, 0x20, 0x77, 0x61, 0x74, 0x63, +0x68, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x09, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, +0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, +0x68, 0x28, 0x69, 0x2c, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, +0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, +0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, +0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, +0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, +0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29, 0x3b, 0x0d, +0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, +0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, +0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6f, 0x62, 0x6a, 0x73, 0x22, 0x29, +0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, +0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, +0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x66, 0x6f, 0x72, +0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x61, 0x6c, 0x20, 0x69, +0x6e, 0x20, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x29, 0x0d, 0x0a, 0x09, 0x7b, +0x0d, 0x0a, 0x0d, 0x0a, 0x09, 0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, +0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, +0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, +0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x66, 0x6e, 0x63, 0x22, 0x2c, 0x76, +0x61, 0x6c, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x29, 0x3b, 0x0d, 0x0a, 0x09, +0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, +0x73, 0x72, 0x63, 0x22, 0x2c, 0x76, 0x61, 0x6c, 0x2e, 0x73, 0x72, 0x63, +0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, +0x75, 0x74, 0x65, 0x28, 0x22, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x2c, 0x76, +0x61, 0x6c, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x74, 0x6f, 0x73, 0x74, +0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, +0x66, 0x6f, 0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20, +0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, +0x73, 0x29, 0x0d, 0x0a, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, +0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, +0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x6e, +0x61, 0x6d, 0x65, 0x22, 0x2c, 0x67, 0x65, 0x74, 0x76, 0x61, 0x6c, 0x75, +0x65, 0x28, 0x69, 0x29, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, +0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, +0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x22, 0x74, 0x79, +0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c, 0x22, 0x2c, 0x76, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, +0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x6c, 0x22, 0x29, 0x3b, 0x0d, 0x0a, +0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x28, 0x22, 0x77, +0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x76, +0x61, 0x6c, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x66, 0x6f, +0x72, 0x65, 0x61, 0x63, 0x68, 0x28, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, +0x20, 0x76, 0x61, 0x6c, 0x2e, 0x77, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, +0x29, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, +0x74, 0x28, 0x22, 0x77, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, +0x22, 0x69, 0x64, 0x22, 0x2c, 0x69, 0x2e, 0x74, 0x6f, 0x73, 0x74, 0x72, +0x69, 0x6e, 0x67, 0x28, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, +0x22, 0x65, 0x78, 0x70, 0x22, 0x2c, 0x76, 0x2e, 0x65, 0x78, 0x70, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x61, 0x74, 0x74, 0x72, +0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, +0x73, 0x22, 0x2c, 0x76, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x69, 0x66, 0x28, 0x76, +0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x21, 0x3d, 0x22, 0x65, 0x72, +0x72, 0x6f, 0x72, 0x22, 0x29, 0x20, 0x7b, 0x0d, 0x0a, 0x09, 0x09, 0x09, +0x09, 0x09, 0x09, 0x65, 0x6d, 0x69, 0x74, 0x76, 0x61, 0x6c, 0x75, 0x65, +0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x76, 0x61, 0x6c, +0x22, 0x2c, 0x76, 0x2e, 0x76, 0x61, 0x6c, 0x29, 0x3b, 0x0d, 0x0a, 0x09, +0x09, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x65, +0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x77, +0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x09, 0x7d, 0x0d, 0x0a, 0x09, +0x09, 0x7d, 0x0d, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, +0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x63, 0x61, 0x6c, 0x6c, 0x22, 0x29, +0x3b, 0x0d, 0x0a, 0x09, 0x09, 0x20, 0x0d, 0x0a, 0x09, 0x7d, 0x0d, 0x0a, +0x09, 0x65, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, +0x22, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x22, 0x29, 0x3b, 0x0d, 0x0a, 0x0d, +0x0a, 0x0d, 0x0a, 0x09, 0x6f, 0x62, 0x6a, 0x73, 0x5f, 0x72, 0x65, 0x67, +0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, 0x0d, 0x0a, 0x09, 0x73, +0x74, 0x61, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x3b, +0x0d, 0x0a, 0x09, 0x0d, 0x0a, 0x09, 0x69, 0x66, 0x28, 0x22, 0x63, 0x6f, +0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, +0x22, 0x20, 0x69, 0x6e, 0x20, 0x3a, 0x3a, 0x67, 0x65, 0x74, 0x72, 0x6f, +0x6f, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x29, 0x29, 0x20, 0x3a, +0x3a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x67, 0x61, 0x72, 0x62, +0x61, 0x67, 0x65, 0x28, 0x29, 0x3b, 0x0d, 0x0a, 0x7d, 0x63, 0x61, 0x74, +0x63, 0x68, 0x28, 0x65, 0x29, 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x09, 0x3a, +0x3a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x22, 0x45, 0x52, 0x52, 0x4f, +0x52, 0x22, 0x2b, 0x65, 0x2b, 0x22, 0x5c, 0x6e, 0x22, 0x29, 0x3b, 0x0d, +0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, +}; diff --git a/src/squirrel/sqdbg/sqdbgserver.cpp b/src/squirrel/sqdbg/sqdbgserver.cpp new file mode 100644 index 000000000..7cdbe998f --- /dev/null +++ b/src/squirrel/sqdbg/sqdbgserver.cpp @@ -0,0 +1,634 @@ +#include +#include +#include +#include "sqrdbg.h" +#include "sqdbgserver.h" + + +#ifndef _UNICODE +#define scstrcpy strcpy +#else +#define scstrcpy wcscpy +#endif +struct XMLEscape{ + const SQChar c; + const SQChar *esc; +}; + +#define SQDBG_DEBUG_HOOK _SC("_sqdbg_debug_hook_") +#define SQDBG_ERROR_HANDLER _SC("_sqdbg_error_handler_") + +XMLEscape g_escapes[]={ + {_SC('<'),_SC("<")},{'>',_SC(">")},{_SC('&'),_SC("&")},{_SC('\''),_SC("'")},{_SC('\"'),_SC(""")},{_SC('\n'),_SC(""n")},{_SC('\r'),_SC(""r")},{0, NULL} +}; + +const SQChar *IntToString(int n) +{ + static SQChar temp[256]; + scsprintf(temp,_SC("%d"),n); + return temp; +} + +int debug_hook(HSQUIRRELVM v); +int error_handler(HSQUIRRELVM v); + +int beginelement(HSQUIRRELVM v) +{ + SQUserPointer up; + const SQChar *name; + sq_getuserpointer(v,-1,&up); + SQDbgServer *self = (SQDbgServer*)up; + sq_getuserpointer(v,-1,&up); + sq_getstring(v,2,&name); + self->BeginElement(name); + return 0; +} + +int endelement(HSQUIRRELVM v) +{ + SQUserPointer up; + const SQChar *name; + sq_getuserpointer(v,-1,&up); + SQDbgServer *self = (SQDbgServer*)up; + sq_getuserpointer(v,-1,&up); + sq_getstring(v,2,&name); + self->EndElement(name); + return 0; +} + +int attribute(HSQUIRRELVM v) +{ + SQUserPointer up; + const SQChar *name,*value; + sq_getuserpointer(v,-1,&up); + SQDbgServer *self = (SQDbgServer*)up; + sq_getuserpointer(v,-1,&up); + sq_getstring(v,2,&name); + sq_getstring(v,3,&value); + self->Attribute(name,value); + return 0; +} + +const SQChar *EscapeXMLString(HSQUIRRELVM v,const SQChar *s) +{ + + SQChar *temp=sq_getscratchpad(v,((int)scstrlen(s)*6) + sizeof(SQChar)); + SQChar *dest=temp; + while(*s!=_SC('\0')){ + int i=0; + bool escaped=false; + while(g_escapes[i].esc!=NULL){ + if(*s==g_escapes[i].c){ + scstrcpy(dest,g_escapes[i].esc); + dest+=scstrlen(g_escapes[i].esc); + escaped=true; + break; + } + i++; + } + if(!escaped){*dest=*s;*dest++;} + *s++; + } + *dest=_SC('\0'); + return temp; +} + +SQDbgServer::SQDbgServer(HSQUIRRELVM v) +{ + _ready = false; + _nestedcalls = 0; + _autoupdate = false; + _v = v; + _state = eDBG_Running; + _accept = INVALID_SOCKET; + _endpoint = INVALID_SOCKET; + _maxrecursion = 10; + sq_resetobject(&_debugroot); +} + +SQDbgServer::~SQDbgServer() +{ + sq_release(_v,&_debugroot); + if(_accept != INVALID_SOCKET) + sqdbg_closesocket(_accept); + if(_endpoint != INVALID_SOCKET) + sqdbg_closesocket(_endpoint); +} + +bool SQDbgServer::Init() +{ + //creates an environment table for the debugger + + sq_newtable(_v); + sq_getstackobj(_v,-1,&_debugroot); + sq_addref(_v,&_debugroot); + + //creates a emptyslot to store the watches + sq_pushstring(_v,_SC("watches"),-1); + sq_pushnull(_v); + sq_createslot(_v,-3); + + sq_pushstring(_v,_SC("beginelement"),-1); + sq_pushuserpointer(_v,this); + sq_newclosure(_v,beginelement,1); + sq_setparamscheck(_v,2,_SC(".s")); + sq_createslot(_v,-3); + + sq_pushstring(_v,_SC("endelement"),-1); + sq_pushuserpointer(_v,this); + sq_newclosure(_v,endelement,1); + sq_setparamscheck(_v,2,_SC(".s")); + sq_createslot(_v,-3); + + sq_pushstring(_v,_SC("attribute"),-1); + sq_pushuserpointer(_v,this); + sq_newclosure(_v,attribute,1); + sq_setparamscheck(_v,3,_SC(".ss")); + sq_createslot(_v,-3); + + sq_pop(_v,1); + + //stores debug hook and error handler in the registry + sq_pushregistrytable(_v); + + sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1); + sq_pushuserpointer(_v,this); + sq_newclosure(_v,debug_hook,1); + sq_createslot(_v,-3); + + sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1); + sq_pushuserpointer(_v,this); + sq_newclosure(_v,error_handler,1); + sq_createslot(_v,-3); + + + sq_pop(_v,1); + + //sets the error handlers + SetErrorHandlers(); + return true; +} + +bool SQDbgServer::ReadMsg() +{ + return false; +} + +void SQDbgServer::BusyWait() +{ + while( !ReadMsg() ) + sleep(0); +} + +void SQDbgServer::SendChunk(const SQChar *chunk) +{ + char *buf=NULL; + int buf_len=0; +#ifdef _UNICODE + buf_len=(int)scstrlen(chunk)+1; + buf=(char *)sq_getscratchpad(_v,(buf_len)*3); + wcstombs((char *)buf,chunk,buf_len); +#else + buf_len=(int)scstrlen(chunk); + buf=(char *)chunk; +#endif + send(_endpoint,(const char*)buf,(int)strlen((const char *)buf),0); +} + + +void SQDbgServer::Terminated() +{ + BeginElement(_SC("terminated")); + EndElement(_SC("terminated")); + ::usleep(200); +} + +void SQDbgServer::Hook(int type,int line,const SQChar *src,const SQChar *func) +{ + switch(_state){ + case eDBG_Running: + if(type==_SC('l') && _breakpoints.size()) { + BreakPointSetItor itr = _breakpoints.find(BreakPoint(line,src)); + if(itr != _breakpoints.end()) { + Break(line,src,_SC("breakpoint")); + BreakExecution(); + } + } + break; + case eDBG_Suspended: + _nestedcalls=0; + case eDBG_StepOver: + switch(type){ + case _SC('l'): + if(_nestedcalls==0) { + Break(line,src,_SC("step")); + BreakExecution(); + } + break; + case _SC('c'): + _nestedcalls++; + break; + case _SC('r'): + if(_nestedcalls==0){ + _nestedcalls=0; + + }else{ + _nestedcalls--; + } + break; + } + break; + case eDBG_StepInto: + switch(type){ + case _SC('l'): + _nestedcalls=0; + Break(line,src,_SC("step")); + BreakExecution(); + break; + + } + break; + case eDBG_StepReturn: + switch(type){ + case _SC('l'): + break; + case _SC('c'): + _nestedcalls++; + break; + case _SC('r'): + if(_nestedcalls==0){ + _nestedcalls=0; + _state=eDBG_StepOver; + }else{ + _nestedcalls--; + } + + break; + } + break; + case eDBG_Disabled: + break; + } +} + + +#define MSG_ID(x,y) ((y<<8)|x) +//ab Add Breakpoint +//rb Remove Breakpoint +//sp Suspend +void SQDbgServer::ParseMsg(const char *msg) +{ + + switch(*((unsigned short *)msg)){ + case MSG_ID('a','b'): { + BreakPoint bp; + if(ParseBreakpoint(msg+3,bp)){ + AddBreakpoint(bp); + scprintf(_SC("added bp %d %s\n"),bp._line,bp._src.c_str()); + } + else + scprintf(_SC("error parsing add breakpoint")); + } + break; + case MSG_ID('r','b'): { + BreakPoint bp; + if(ParseBreakpoint(msg+3,bp)){ + RemoveBreakpoint(bp); + scprintf(_SC("removed bp %d %s\n"),bp._line,bp._src.c_str()); + }else + scprintf(_SC("error parsing remove breakpoint")); + } + break; + case MSG_ID('g','o'): + if(_state!=eDBG_Running){ + _state=eDBG_Running; + BeginDocument(); + BeginElement(_SC("resumed")); + EndElement(_SC("resumed")); + EndDocument(); +// Send(_SC("\r\n")); + scprintf(_SC("go (execution resumed)\n")); + } + break; + case MSG_ID('s','p'): + if(_state!=eDBG_Suspended){ + _state=eDBG_Suspended; + scprintf(_SC("suspend\n")); + } + break; + case MSG_ID('s','o'): + if(_state==eDBG_Suspended){ + _state=eDBG_StepOver; + } + break; + case MSG_ID('s','i'): + if(_state==eDBG_Suspended){ + _state=eDBG_StepInto; + scprintf(_SC("step into\n")); + } + break; + case MSG_ID('s','r'): + if(_state==eDBG_Suspended){ + _state=eDBG_StepReturn; + scprintf(_SC("step return\n")); + } + break; + case MSG_ID('d','i'): + if(_state!=eDBG_Disabled){ + _state=eDBG_Disabled; + scprintf(_SC("disabled\n")); + } + break; + case MSG_ID('a','w'): { + Watch w; + if(ParseWatch(msg+3,w)) + { + AddWatch(w); + scprintf(_SC("added watch %d %s\n"),w._id,w._exp.c_str()); + } + else + scprintf(_SC("error parsing add watch")); + } + break; + case MSG_ID('r','w'): { + int id; + if(ParseRemoveWatch(msg+3,id)) + { + RemoveWatch(id); + scprintf(_SC("added watch %d\n"),id); + } + else + scprintf(_SC("error parsing remove watch")); + } + break; + case MSG_ID('t','r'): + scprintf(_SC("terminate from user\n")); + break; + case MSG_ID('r','d'): + scprintf(_SC("ready\n")); + _ready=true; + break; + default: + scprintf(_SC("unknown packet")); + + } +} + +/* + see copyright notice in sqrdbg.h +*/ +bool SQDbgServer::ParseBreakpoint(const char *msg,BreakPoint &out) +{ + static char stemp[MAX_BP_PATH]; + char *ep=NULL; + out._line=strtoul(msg,&ep,16); + if(ep==msg || (*ep)!=':')return false; + + char *dest=stemp; + ep++; + while((*ep)!='\n' && (*ep)!='\0') + { + *dest=*ep; + *dest++;*ep++; + } + *dest='\0'; + *dest++; + *dest='\0'; +#ifdef _UNICODE + int len=(int)strlen(stemp); + SQChar *p=sq_getscratchpad(_v,(SQInteger)(mbstowcs(NULL,stemp,len)+2)*sizeof(SQChar)); + size_t destlen=mbstowcs(p,stemp,len); + p[destlen]=_SC('\0'); + out._src=p; +#else + out._src=stemp; +#endif + return true; +} + +bool SQDbgServer::ParseWatch(const char *msg,Watch &out) +{ + char *ep=NULL; + out._id=strtoul(msg,&ep,16); + if(ep==msg || (*ep)!=':')return false; + + //char *dest=out._src; + ep++; + while((*ep)!='\n' && (*ep)!='\0') + { + out._exp.append(1,*ep); + *ep++; + } + return true; +} + +bool SQDbgServer::ParseRemoveWatch(const char *msg,int &id) +{ + char *ep=NULL; + id=strtoul(msg,&ep,16); + if(ep==msg)return false; + return true; +} + + +void SQDbgServer::BreakExecution() +{ + _state=eDBG_Suspended; + while(_state==eDBG_Suspended){ + if(SQ_FAILED(sq_rdbg_update(this))) + exit(0); + usleep(10); + } +} + +//COMMANDS +void SQDbgServer::AddBreakpoint(BreakPoint &bp) +{ + _breakpoints.insert(bp); + BeginDocument(); + BeginElement(_SC("addbreakpoint")); + Attribute(_SC("line"),IntToString(bp._line)); + Attribute(_SC("src"),bp._src.c_str()); + EndElement(_SC("addbreakpoint")); + EndDocument(); +} + +void SQDbgServer::AddWatch(Watch &w) +{ + _watches.insert(w); +} + +void SQDbgServer::RemoveWatch(int id) +{ + WatchSetItor itor=_watches.find(Watch(id,_SC(""))); + if(itor==_watches.end()){ + BeginDocument(); + BeginElement(_SC("error")); + Attribute(_SC("desc"),_SC("the watch does not exists")); + EndElement(_SC("error")); + EndDocument(); + } + else{ + _watches.erase(itor); + scprintf(_SC("removed watch %d\n"),id); + } +} + +void SQDbgServer::RemoveBreakpoint(BreakPoint &bp) +{ + BreakPointSetItor itor=_breakpoints.find(bp); + if(itor==_breakpoints.end()){ + BeginDocument(); + BeginElement(_SC("break")); + Attribute(_SC("desc"),_SC("the breakpoint doesn't exists")); + EndElement(_SC("break")); + EndDocument(); + } + else{ + BeginDocument(); + BeginElement(_SC("removebreakpoint")); + Attribute(_SC("line"),IntToString(bp._line)); + Attribute(_SC("src"),bp._src.c_str()); + EndElement(_SC("removebreakpoint")); + EndDocument(); + _breakpoints.erase(itor); + } +} + +void SQDbgServer::Break(int line,const SQChar *src,const SQChar *type,const SQChar *error) +{ + if(!error){ + BeginDocument(); + BeginElement(_SC("break")); + Attribute(_SC("line"),IntToString(line)); + Attribute(_SC("src"),src); + Attribute(_SC("type"),type); + SerializeState(); + EndElement(_SC("break")); + EndDocument(); + }else{ + BeginDocument(); + BeginElement(_SC("break")); + Attribute(_SC("line"),IntToString(line)); + Attribute(_SC("src"),src); + Attribute(_SC("type"),type); + Attribute(_SC("error"),error); + SerializeState(); + EndElement(_SC("break")); + EndDocument(); + } +} + +void SQDbgServer::SerializeState() +{ + sq_pushnull(_v); + sq_setdebughook(_v); + sq_pushnull(_v); + sq_seterrorhandler(_v); + const SQChar *sz; + sq_pushobject(_v,_serializefunc); + sq_pushobject(_v,_debugroot); + sq_pushstring(_v,_SC("watches"),-1); + sq_newtable(_v); + for(WatchSetItor i=_watches.begin(); i!=_watches.end(); ++i) + { + sq_pushinteger(_v,i->_id); + sq_pushstring(_v,i->_exp.c_str(),(int)i->_exp.length()); + sq_createslot(_v,-3); + } + sq_rawset(_v,-3); + if(SQ_SUCCEEDED(sq_call(_v,1,SQTrue))){ + if(SQ_SUCCEEDED(sqstd_getblob(_v,-1,(SQUserPointer*)&sz))) + SendChunk(sz); + } + sq_pop(_v,2); + + SetErrorHandlers(); +} + + +void SQDbgServer::SetErrorHandlers() +{ + sq_pushregistrytable(_v); + sq_pushstring(_v,SQDBG_DEBUG_HOOK,-1); + sq_rawget(_v,-2); + sq_setdebughook(_v); + sq_pushstring(_v,SQDBG_ERROR_HANDLER,-1); + sq_rawget(_v,-2); + sq_seterrorhandler(_v); + sq_pop(_v,1); +} + +void SQDbgServer::BeginElement(const SQChar *name) +{ + _xmlcurrentement++; + XMLElementState *self = &xmlstate[_xmlcurrentement]; + scstrcpy(self->name,name); + self->haschildren = false; + if(_xmlcurrentement > 0) { + XMLElementState *parent = &xmlstate[_xmlcurrentement-1]; + if(!parent->haschildren) { + SendChunk(_SC(">")); // closes the parent tag + parent->haschildren = true; + } + } + _scratchstring.resize(2+scstrlen(name)); + scsprintf(&_scratchstring[0],_SC("<%s"),name); + SendChunk(&_scratchstring[0]); +} + +void SQDbgServer::Attribute(const SQChar *name,const SQChar *value) +{ + XMLElementState *self = &xmlstate[_xmlcurrentement]; + assert(!self->haschildren); //cannot have attributes if already has children + const SQChar *escval = escape_xml(value); + _scratchstring.resize(5+scstrlen(name)+scstrlen(escval)); + scsprintf(&_scratchstring[0],_SC(" %s=\"%s\""),name,escval); + SendChunk(&_scratchstring[0]); +} + +void SQDbgServer::EndElement(const SQChar *name) +{ + XMLElementState *self = &xmlstate[_xmlcurrentement]; + assert(scstrcmp(self->name,name) == 0); + if(self->haschildren) { + _scratchstring.resize(4+scstrlen(name)); + scsprintf(&_scratchstring[0],_SC(""),name); + SendChunk(&_scratchstring[0]); + + } + else { + SendChunk(_SC("/>")); + } + _xmlcurrentement--; +} + +void SQDbgServer::EndDocument() +{ + SendChunk(_SC("\r\n")); +} + +//this can be done much better/faster(do we need that?) +const SQChar *SQDbgServer::escape_xml(const SQChar *s) +{ + SQChar *temp=sq_getscratchpad(_v,((int)scstrlen(s)*6) + sizeof(SQChar)); + SQChar *dest=temp; + while(*s!=_SC('\0')){ + int i=0; + bool escaped=false; + while(g_escapes[i].esc!=NULL){ + if(*s==g_escapes[i].c){ + scstrcpy(dest,g_escapes[i].esc); + dest+=scstrlen(g_escapes[i].esc); + escaped=true; + break; + } + i++; + } + if(!escaped){*dest=*s;*dest++;} + *s++; + } + *dest=_SC('\0'); + return temp; + +} diff --git a/src/squirrel/sqdbg/sqdbgserver.h b/src/squirrel/sqdbg/sqdbgserver.h new file mode 100644 index 000000000..dcfa7cf6b --- /dev/null +++ b/src/squirrel/sqdbg/sqdbgserver.h @@ -0,0 +1,159 @@ +#ifndef _SQ_DBGSERVER_H_ +#define _SQ_DBGSERVER_H_ + +#define MAX_BP_PATH 512 +#define MAX_MSG_LEN 2049 + +#include +#include +#include + +#ifdef _WIN32 +#include +#define sqdbg_closesocket(x) closesocket((x)) +typedef socklen_t int; +#else +#include +#include +#include +#include +#include +#include +#include +#include + +#define sqdbg_closesocket(x) close((x)) +typedef int SOCKET; +typedef struct timeval TIMEVAL; +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 +#endif + +typedef std::basic_string SQDBGString; + +inline bool dbg_less(const SQChar *x,const SQChar *y) +{ + int n = 0; + do { + int xl = *x == '\\' ? '/' : tolower(*x); + int yl = *y == '\\' ? '/' : tolower(*y); + int diff = xl - yl; + if(diff != 0) + return diff > 0?true:false; + x++; y++; + }while(*x != 0 && *y != 0); + return false; +} + +struct BreakPoint{ + BreakPoint(){_line=0;} + BreakPoint(int line, const SQChar *src){ _line = line; _src = src; } + BreakPoint(const BreakPoint& bp){ _line = bp._line; _src=bp._src; } + inline bool operator<(const BreakPoint& bp) const + { + if(_line BreakPointSet; +typedef BreakPointSet::iterator BreakPointSetItor; + +typedef std::set WatchSet; +typedef WatchSet::iterator WatchSetItor; + +typedef std::vector SQCharVec; +struct SQDbgServer{ +public: + enum eDbgState{ + eDBG_Running, + eDBG_StepOver, + eDBG_StepInto, + eDBG_StepReturn, + eDBG_Suspended, + eDBG_Disabled, + }; + + SQDbgServer(HSQUIRRELVM v); + ~SQDbgServer(); + bool Init(); + //returns true if a message has been received + bool WaitForClient(); + bool ReadMsg(); + void BusyWait(); + void Hook(int type,int line,const SQChar *src,const SQChar *func); + void ParseMsg(const char *msg); + bool ParseBreakpoint(const char *msg,BreakPoint &out); + bool ParseWatch(const char *msg,Watch &out); + bool ParseRemoveWatch(const char *msg,int &id); + void Terminated(); + // + void BreakExecution(); + void Send(const SQChar *s,...); + void SendChunk(const SQChar *chunk); + void Break(int line,const SQChar *src,const SQChar *type,const SQChar *error=NULL); + + + void SerializeState(); + //COMMANDS + void AddBreakpoint(BreakPoint &bp); + void AddWatch(Watch &w); + void RemoveWatch(int id); + void RemoveBreakpoint(BreakPoint &bp); + + // + void SetErrorHandlers(); + + //XML RELATED STUFF/////////////////////// + #define MAX_NESTING 10 + struct XMLElementState { + SQChar name[256]; + bool haschildren; + }; + + XMLElementState xmlstate[MAX_NESTING]; + int _xmlcurrentement; + + void BeginDocument() { _xmlcurrentement = -1; } + void BeginElement(const SQChar *name); + void Attribute(const SQChar *name, const SQChar *value); + void EndElement(const SQChar *name); + void EndDocument(); + + const SQChar *escape_xml(const SQChar *x); + ////////////////////////////////////////////// + HSQUIRRELVM _v; + HSQOBJECT _debugroot; + eDbgState _state; + SOCKET _accept; + SOCKET _endpoint; + BreakPointSet _breakpoints; + WatchSet _watches; + int _recursionlevel; + int _maxrecursion; + int _nestedcalls; + bool _ready; + bool _autoupdate; + HSQOBJECT _serializefunc; + SQCharVec _scratchstring; + +}; + +#endif //_SQ_DBGSERVER_H_ diff --git a/src/squirrel/sqdbg/sqrdbg.cpp b/src/squirrel/sqdbg/sqrdbg.cpp new file mode 100644 index 000000000..b58a41d01 --- /dev/null +++ b/src/squirrel/sqdbg/sqrdbg.cpp @@ -0,0 +1,166 @@ +/* + see copyright notice in sqrdbg.h +*/ +#include +#include "sqrdbg.h" +#include "sqdbgserver.h" +int debug_hook(HSQUIRRELVM v); +int error_handler(HSQUIRRELVM v); + +#include "serialize_state.inl" + +HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate) +{ + sockaddr_in bindaddr; +#ifdef _WIN32 + WSADATA wsadata; + if (WSAStartup (MAKEWORD(1,1), &wsadata) != 0){ + return NULL; + } +#endif + + SQDbgServer *rdbg = new SQDbgServer(v); + rdbg->_autoupdate = autoupdate?true:false; + rdbg->_accept = socket(AF_INET,SOCK_STREAM,0); + bindaddr.sin_family = AF_INET; + bindaddr.sin_port = htons(port); + bindaddr.sin_addr.s_addr = htonl (INADDR_ANY); + if(bind(rdbg->_accept,(sockaddr*)&bindaddr,sizeof(bindaddr))==SOCKET_ERROR){ + delete rdbg; + sq_throwerror(v,_SC("failed to bind the socket")); + return NULL; + } + if(!rdbg->Init()) { + delete rdbg; + sq_throwerror(v,_SC("failed to initialize the debugger")); + return NULL; + } + + return rdbg; +} + +SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg) +{ + if(SQ_FAILED(sq_compilebuffer(rdbg->_v,serialize_state_nut,(SQInteger)scstrlen(serialize_state_nut),_SC("SERIALIZE_STATE"),SQFalse))) { + sq_throwerror(rdbg->_v,_SC("error compiling the serialization function")); + } + sq_getstackobj(rdbg->_v,-1,&rdbg->_serializefunc); + sq_addref(rdbg->_v,&rdbg->_serializefunc); + sq_pop(rdbg->_v,1); + + sockaddr_in cliaddr; + socklen_t addrlen=sizeof(cliaddr); + if(listen(rdbg->_accept,0)==SOCKET_ERROR) + return sq_throwerror(rdbg->_v,_SC("error on listen(socket)")); + rdbg->_endpoint = accept(rdbg->_accept,(sockaddr*)&cliaddr,&addrlen); + //do not accept any other connection + sqdbg_closesocket(rdbg->_accept); + rdbg->_accept = INVALID_SOCKET; + if(rdbg->_endpoint==INVALID_SOCKET){ + return sq_throwerror(rdbg->_v,_SC("error accept(socket)")); + } + while(!rdbg->_ready){ + sq_rdbg_update(rdbg); + } + return SQ_OK; +} + +SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg) +{ + TIMEVAL time; + time.tv_sec=0; + time.tv_usec=0; + fd_set read_flags; + FD_ZERO(&read_flags); + FD_SET(rdbg->_endpoint, &read_flags); + select(FD_SETSIZE, &read_flags, NULL, NULL, &time); + + if(FD_ISSET(rdbg->_endpoint,&read_flags)){ + char temp[1024]; + int size=0; + char c,prev=0; + memset(&temp,0,sizeof(temp)); + int res; + FD_CLR(rdbg->_endpoint, &read_flags); + while((res = recv(rdbg->_endpoint,&c,1,0))>0){ + + if(c=='\n')break; + if(c!='\r'){ + temp[size]=c; + prev=c; + size++; + } + } + switch(res){ + case 0: + return sq_throwerror(rdbg->_v,_SC("disconnected")); + case SOCKET_ERROR: + return sq_throwerror(rdbg->_v,_SC("socket error")); + } + + temp[size]=0; + temp[size+1]=0; + rdbg->ParseMsg(temp); + } + return SQ_OK; +} + +int debug_hook(HSQUIRRELVM v) +{ + SQUserPointer up; + int event_type,line; + const SQChar *src,*func; + sq_getinteger(v,2,&event_type); + sq_getstring(v,3,&src); + sq_getinteger(v,4,&line); + sq_getstring(v,5,&func); + sq_getuserpointer(v,-1,&up); + HSQREMOTEDBG rdbg = (HSQREMOTEDBG)up; + rdbg->Hook(event_type,line,src,func); + if(rdbg->_autoupdate) { + if(SQ_FAILED(sq_rdbg_update(rdbg))) + return sq_throwerror(v,_SC("socket failed")); + } + return 0; +} + +int error_handler(HSQUIRRELVM v) +{ + SQUserPointer up; + const SQChar *sErr=NULL; + const SQChar *fn=_SC("unknown"); + const SQChar *src=_SC("unknown"); + int line=-1; + SQStackInfos si; + sq_getuserpointer(v,-1,&up); + HSQREMOTEDBG rdbg=(HSQREMOTEDBG)up; + if(SQ_SUCCEEDED(sq_stackinfos(v,1,&si))) + { + if(si.funcname)fn=si.funcname; + if(si.source)src=si.source; + line=si.line; + scprintf(_SC("*FUNCTION [%s] %s line [%d]\n"),fn,src,si.line); + } + if(sq_gettop(v)>=1){ + if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { + scprintf(_SC("\nAN ERROR HAS OCCURED [%s]\n"),sErr); + rdbg->Break(si.line,src,_SC("error"),sErr); + } + else{ + scprintf(_SC("\nAN ERROR HAS OCCURED [unknown]\n")); + rdbg->Break(si.line,src,_SC("error"),_SC("unknown")); + } + } + rdbg->BreakExecution(); + return 0; +} + + +SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg) +{ + delete rdbg; +#ifdef _WIN32 + WSACleanup(); +#endif + return SQ_OK; +} diff --git a/src/squirrel/sqdbg/sqrdbg.h b/src/squirrel/sqdbg/sqrdbg.h new file mode 100644 index 000000000..3d15e7448 --- /dev/null +++ b/src/squirrel/sqdbg/sqrdbg.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2003-2005 Alberto Demichelis + +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. + +*/ +#ifndef _SQ_RDBG_H_ +#define _SQ_RDBG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct SQDbgServer; +typedef SQDbgServer* HSQREMOTEDBG; + +HSQREMOTEDBG sq_rdbg_init(HSQUIRRELVM v,unsigned short port,SQBool autoupdate); +SQRESULT sq_rdbg_waitforconnections(HSQREMOTEDBG rdbg); +SQRESULT sq_rdbg_shutdown(HSQREMOTEDBG rdbg); +SQRESULT sq_rdbg_update(HSQREMOTEDBG rdbg); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif //_SQ_RDBG_H_ +