Browse Source

Add all files

Greg 16 years ago
commit
3c4b6e7f27
100 changed files with 27598 additions and 0 deletions
  1. 219 0
      gmsrc/EditorHighlighters/ConTEXT/Highlighters/GameMonkey.chl
  2. 2 0
      gmsrc/EditorHighlighters/Crimson Editor/Link/extension.gm
  3. 80 0
      gmsrc/EditorHighlighters/Crimson Editor/Spec/gm.key
  4. 34 0
      gmsrc/EditorHighlighters/Crimson Editor/Spec/gm.spc
  5. 76 0
      gmsrc/EditorHighlighters/PSPad editor/Syntax/GameMonkey Script.INI
  6. BIN
      gmsrc/bin/SciLexer.dll
  7. BIN
      gmsrc/bin/StripCR.exe
  8. BIN
      gmsrc/bin/bison.exe
  9. BIN
      gmsrc/bin/flex.exe
  10. 348 0
      gmsrc/doc/ChangeLog.txt
  11. BIN
      gmsrc/doc/GameMonkeyFAQ.pdf
  12. BIN
      gmsrc/doc/GameMonkeyGarbageCollection.pdf
  13. BIN
      gmsrc/doc/GameMonkeyLicense.pdf
  14. BIN
      gmsrc/doc/GameMonkeyScriptReference.pdf
  15. 28 0
      gmsrc/doc/ToDoList.txt
  16. 1689 0
      gmsrc/scripts/Bomber.gm
  17. 691 0
      gmsrc/scripts/BomberRun.gm
  18. 33 0
      gmsrc/scripts/ScriptDescriptions.txt
  19. 230 0
      gmsrc/scripts/benchmarks.gm
  20. 9 0
      gmsrc/scripts/gmDoc/gmdoc.cmd
  21. 333 0
      gmsrc/scripts/gmDoc/gmdoc.gm
  22. 2 0
      gmsrc/scripts/gmDoc/gmdoc.txt
  23. 608 0
      gmsrc/scripts/minesweeper.gm
  24. 696 0
      gmsrc/scripts/snake.gm
  25. 410 0
      gmsrc/src/binds/gmArrayLib.cpp
  26. 95 0
      gmsrc/src/binds/gmArrayLib.h
  27. 13 0
      gmsrc/src/binds/gmCall.cpp
  28. 429 0
      gmsrc/src/binds/gmCall.h
  29. 212 0
      gmsrc/src/binds/gmGCRoot.cpp
  30. 399 0
      gmsrc/src/binds/gmGCRoot.h
  31. 29 0
      gmsrc/src/binds/gmGCRootUtil.cpp
  32. 171 0
      gmsrc/src/binds/gmGCRootUtil.h
  33. 60 0
      gmsrc/src/binds/gmHelpers.cpp
  34. 179 0
      gmsrc/src/binds/gmHelpers.h
  35. 854 0
      gmsrc/src/binds/gmMathLib.cpp
  36. 21 0
      gmsrc/src/binds/gmMathLib.h
  37. 1170 0
      gmsrc/src/binds/gmStringLib.cpp
  38. 21 0
      gmsrc/src/binds/gmStringLib.h
  39. 986 0
      gmsrc/src/binds/gmSystemLib.cpp
  40. 28 0
      gmsrc/src/binds/gmSystemLib.h
  41. 1056 0
      gmsrc/src/binds/gmVector3Lib.cpp
  42. 37 0
      gmsrc/src/binds/gmVector3Lib.h
  43. 473 0
      gmsrc/src/examples/GameObject/App.cpp
  44. 72 0
      gmsrc/src/examples/GameObject/App.h
  45. 317 0
      gmsrc/src/examples/GameObject/GameObj.cpp
  46. 61 0
      gmsrc/src/examples/GameObject/GameObj.h
  47. 470 0
      gmsrc/src/examples/GameObject/GameObject.dsp
  48. 33 0
      gmsrc/src/examples/GameObject/GameObject.dsw
  49. 20 0
      gmsrc/src/examples/GameObject/GameObject.sln
  50. 1383 0
      gmsrc/src/examples/GameObject/GameObject.vcproj
  51. 102 0
      gmsrc/src/examples/GameObject/InputKBWin32.cpp
  52. 90 0
      gmsrc/src/examples/GameObject/InputKBWin32.h
  53. 356 0
      gmsrc/src/examples/GameObject/NetClient.cpp
  54. 30 0
      gmsrc/src/examples/GameObject/NetClient.h
  55. 10 0
      gmsrc/src/examples/GameObject/ReadMe.txt
  56. 326 0
      gmsrc/src/examples/GameObject/ScriptObj.cpp
  57. 165 0
      gmsrc/src/examples/GameObject/ScriptObj.h
  58. 432 0
      gmsrc/src/examples/GameObject/ScriptSys.cpp
  59. 107 0
      gmsrc/src/examples/GameObject/ScriptSys.h
  60. 5 0
      gmsrc/src/examples/GameObject/StdStuff.cpp
  61. 194 0
      gmsrc/src/examples/GameObject/StdStuff.h
  62. 44 0
      gmsrc/src/examples/GameObject/TestGameObj.gm
  63. 30 0
      gmsrc/src/examples/GameObject/main.cpp
  64. 362 0
      gmsrc/src/examples/Minimal/Minimal.dsp
  65. 33 0
      gmsrc/src/examples/Minimal/Minimal.dsw
  66. 20 0
      gmsrc/src/examples/Minimal/Minimal.sln
  67. 1059 0
      gmsrc/src/examples/Minimal/Minimal.vcproj
  68. 78 0
      gmsrc/src/examples/Minimal/main.cpp
  69. 334 0
      gmsrc/src/gm/bison.hairy
  70. 688 0
      gmsrc/src/gm/bison.simple
  71. 1512 0
      gmsrc/src/gm/flex.skl
  72. 15 0
      gmsrc/src/gm/gmArraySimple.cpp
  73. 333 0
      gmsrc/src/gm/gmArraySimple.h
  74. 135 0
      gmsrc/src/gm/gmByteCode.cpp
  75. 106 0
      gmsrc/src/gm/gmByteCode.h
  76. 159 0
      gmsrc/src/gm/gmByteCodeGen.cpp
  77. 49 0
      gmsrc/src/gm/gmByteCodeGen.h
  78. 1595 0
      gmsrc/src/gm/gmCodeGen.cpp
  79. 49 0
      gmsrc/src/gm/gmCodeGen.h
  80. 14 0
      gmsrc/src/gm/gmCodeGenHooks.cpp
  81. 101 0
      gmsrc/src/gm/gmCodeGenHooks.h
  82. 554 0
      gmsrc/src/gm/gmCodeTree.cpp
  83. 255 0
      gmsrc/src/gm/gmCodeTree.h
  84. 109 0
      gmsrc/src/gm/gmConfig.h
  85. 80 0
      gmsrc/src/gm/gmCrc.cpp
  86. 19 0
      gmsrc/src/gm/gmCrc.h
  87. 737 0
      gmsrc/src/gm/gmDebug.cpp
  88. 88 0
      gmsrc/src/gm/gmDebug.h
  89. 321 0
      gmsrc/src/gm/gmDebugger.cpp
  90. 114 0
      gmsrc/src/gm/gmDebugger.h
  91. 282 0
      gmsrc/src/gm/gmFunctionObject.cpp
  92. 163 0
      gmsrc/src/gm/gmFunctionObject.h
  93. 15 0
      gmsrc/src/gm/gmHash.cpp
  94. 348 0
      gmsrc/src/gm/gmHash.h
  95. 835 0
      gmsrc/src/gm/gmIncGC.cpp
  96. 436 0
      gmsrc/src/gm/gmIncGC.h
  97. 51 0
      gmsrc/src/gm/gmIterator.h
  98. 443 0
      gmsrc/src/gm/gmLibHooks.cpp
  99. 124 0
      gmsrc/src/gm/gmLibHooks.h
  100. 14 0
      gmsrc/src/gm/gmListDouble.cpp

+ 219 - 0
gmsrc/EditorHighlighters/ConTEXT/Highlighters/GameMonkey.chl

@@ -0,0 +1,219 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// GameMonkey highlighter written by Matthew Riek and Greg Douglas
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// language name
+
+Language:               GameMonkey Script
+
+
+//////////////////////////////////////////////////////////////////////////////
+// default file filter
+// note: if more than one extension is associated, eg:
+// C/C++ files (*.c,*.cpp,*.h,*.hpp)|*.c;*.cpp;*.h;*.hpp
+
+Filter:                 GameMonkey Script files (*.gm)|*.gm
+
+
+//////////////////////////////////////////////////////////////////////////////
+// help file which will be invokend when F1 is pressed
+
+HelpFile:
+
+
+//////////////////////////////////////////////////////////////////////////////
+// language case sensitivity
+//                      0  - no
+//                      1  - yes
+
+CaseSensitive:          1
+
+
+//////////////////////////////////////////////////////////////////////////////
+// comment type: LineComment - comment to the end of line
+// BlockCommentBeg - block comment begin, it could be
+// multiline
+// BlockCommentEnd - block comment end
+
+LineComment:            //
+BlockCommentBeg:        /*
+BlockCommentEnd:        */
+
+
+//////////////////////////////////////////////////////////////////////////////
+// identifier characters
+// note: characters shouldn't be delimited, except arrays
+// array of chars could be defined as from_char..to_char
+
+IdentifierBegChars:     a..z A..Z _
+IdentifierChars:        a..z A..Z _ 0..9
+
+//////////////////////////////////////////////////////////////////////////////
+// numeric constants begin characters
+// note: characters shouldn't be delimited, except arrays
+// array of chars could be defined as from_char..to_char
+// number always starts with 0..9 except when NumConstBeg
+// defines other
+
+NumConstBegChars:       0..9
+
+
+//////////////////////////////////////////////////////////////////////////////
+// numeric constants characters
+// note: characters shouldn't be delimited, except arrays
+// array of chars could be defined as from_char..to_char
+// number always starts with 0..9 except when NumConstBeg
+// defines other
+
+NumConstChars:          0..9 abcdefxABCDEFX .
+
+
+//////////////////////////////////////////////////////////////////////////////
+// escape character
+
+EscapeChar:
+
+
+//////////////////////////////////////////////////////////////////////////////
+// keyword table
+// note: delimited with spaces, lines could be wrapped
+// you may divide keywords into two groups which can be
+// highlighted differently
+
+// op codes
+
+KeyWords1:  if 
+            else 
+            for 
+            foreach 
+            in 
+            and 
+            or 
+            while 
+            dowhile 
+            function
+            return 
+            continue 
+            break 
+            null 
+            global 
+            local 
+            member 
+            table
+            true 
+            false 
+            this
+
+KeyWords2:
+
+KeyWords3:
+            debug 
+            typeId 
+            typeName 
+            typeRegisterOperator 
+            typeRegisterVariable 
+            sysCollectGarbage 
+            sysGetMemoryUsage 
+            sysGetDesiredMemoryUsageHard 
+            sysGetDesiredMemoryUsageSoft 
+            sysSetDesiredMemoryUsageHard 
+            sysSetDesiredMemoryUsageSoft 
+            sysSetDesiredMemoryUsageAuto 
+            sysTime 
+            doString 
+            globals 
+            threadTime 
+            threadId 
+            threadAllIds 
+            threadKill 
+            threadKillAll 
+            thread 
+            yield 
+            exit 
+            assert 
+            sleep 
+            signal 
+            block 
+            stateSet 
+            stateSetOnThread 
+            stateGet 
+            stateGetLast 
+            stateSetExitFunction 
+            tableCount 
+            tableDuplicate 
+            print 
+            format 
+
+//////////////////////////////////////////////////////////////////////////////
+// string delimiter: StringBegChar - string begin char
+// StringEndChar - string end char
+// MultilineStrings - enables multiline strings, as perl
+// has it
+
+StringBegChar:          "`'
+StringEndChar:          "`'
+MultilineStrings:       0
+
+
+//////////////////////////////////////////////////////////////////////////////
+// use preprocessor: 0 - no
+// 1 - yes
+// note: if yes, '#' and statements after it will be
+// highlighted with Preprocessor defined colors
+
+UsePreprocessor:        0
+
+
+//////////////////////////////////////////////////////////////////////////////
+// highlight line: 0 - no
+// 1 - yes
+// note: if yes, current line will be highlighted
+
+CurrLineHighlighted:    0
+
+
+//////////////////////////////////////////////////////////////////////////////
+// colors
+// note:                first value is foreground, second is background color
+//                        and third (optional) represents font attribute:
+//                        B - bold
+//                        I - italic
+//                        U - underline
+//                        S - strike out
+//                        attributes can be combined: eg. B or BI
+//                      as value, it could be used any standard windows color:
+//                        clBlack, clMaroon, clGreen, clOlive, clNavy,
+//                        clPurple, clTeal, clGray, clSilver, clRed, clLime,
+//                        clYellow, clBlue, clFuchsia, clAqua, clLtGray,
+//                        clDkGray, clWhite, clScrollBar, clBackground,
+//                        clActiveCaption, clInactiveCaption, clMenu, clWindow,
+//                        clWindowFrame, clMenuText, clWindowText, clCaptionText,
+//                        clActiveBorder, clInactiveBorder, clAppWorkSpace,
+//                        clHighlight, clHighlightText, clBtnFace, clBtnShadow,
+//                        clGrayText, clBtnText, clInactiveCaptionText,
+//                        clBtnHighlight, cl3DDkShadow, cl3DLight, clInfoText,
+//                        clInfoBk
+//                      as value, it could be used hex numeric constant too:
+//                        $BBGGRR - BB: blue, GG: green, RR: red, eg: $FF6A00
+
+SpaceCol:               $00D0D0D0 clNavy
+Keyword1Col:            clYellow clNavy
+Keyword2Col:            clYellow clNavy
+Keyword3Col:            $00FFC0C0 clNavy
+IdentifierCol:          $00D0D0D0 clNavy
+CommentCol:             $00C0C000 clNavy
+NumberCol:              clLime clNavy
+StringCol:              clLime clNavy
+SymbolCol:              clWhite clNavy
+PreprocessorCol:        $00FFFF40 clNavy
+SelectionCol:           clNavy $00D0D0D0
+CurrentLineCol:         clBlack clYellow
+
+OverrideTxtFgColor:     0
+BlockAutoindent:        0
+BlockBegStr:
+BlockEndStr:
+MatchedBracesCol:       $008080FF clNavy

+ 2 - 0
gmsrc/EditorHighlighters/Crimson Editor/Link/extension.gm

@@ -0,0 +1,2 @@
+LANGSPEC:GM.SPC
+KEYWORDS:GM.KEY

+ 80 - 0
gmsrc/EditorHighlighters/Crimson Editor/Spec/gm.key

@@ -0,0 +1,80 @@
+[-COMMENT-:GLOBAL]
+# GM GameMonkey LANGUAGE KEYWORDS FILE FOR CRIMSON EDITOR
+# FIRST EDITED BY Greg
+
+[KEYWORDS0:GLOBAL]
+if 
+else 
+for 
+foreach 
+in 
+and 
+or 
+while 
+dowhile 
+function
+return 
+continue 
+break 
+null 
+global 
+local 
+member 
+table
+true 
+false 
+this
+
+[KEYWORDS1:GLOBAL]
+debug
+typeId 
+typeName 
+typeRegisterOperator 
+typeRegisterVariable 
+sysCollectGarbage 
+sysGetMemoryUsage 
+sysSetDesiredMemoryUsageHard 
+sysSetDesiredMemoryUsageSoft 
+sysSetDesiredMemoryUsageAuto 
+sysGetDesiredMemoryUsageHard 
+sysGetDesiredMemoryUsageSoft 
+sysTime 
+doString 
+globals 
+threadTime 
+threadId 
+threadAllIds 
+threadKill 
+threadKillAll 
+thread 
+yield 
+exit 
+assert 
+sleep 
+signal 
+block 
+stateSet 
+stateSetOnThread 
+stateGet 
+stateGetLast 
+stateSetExitFunction 
+tableCount 
+tableDuplicate 
+print 
+format 
+
+[KEYWORDS2:GLOBAL]
+
+[KEYWORDS3:GLOBAL]
+
+[KEYWORDS4:GLOBAL]
+
+[KEYWORDS5:GLOBAL]
+
+[KEYWORDS6:GLOBAL]
+
+[KEYWORDS7:GLOBAL]
+
+[KEYWORDS8:GLOBAL]
+
+[KEYWORDS9:GLOBAL]

+ 34 - 0
gmsrc/EditorHighlighters/Crimson Editor/Spec/gm.spc

@@ -0,0 +1,34 @@
+# GM GameMonkey LANGUAGE SPECIFICATION FILE FOR CRIMSON EDITOR
+# FIRST EDITED BY Greg
+
+$CASESENSITIVE=YES
+$DELIMITERS=~`!@#$%^&*()-+=|\{}[]:;"',.<>/?
+
+# There are currently no preprocessor commands in GM, but there maybe sometime
+# $KEYWORDPREFIX=#
+
+# Bit of a hack to highlight binary constants
+# Will need to set Variable color to match constants
+#$BINARYMARK=0x  Be nice if this was available
+# The below lines don't behave as expected
+#$VARIABLEPREFIX=0b
+#$SPECIALVARIABLECHARS=0b
+
+$HEXADECIMALMARK=0x
+
+$ESCAPECHAR=\
+$QUOTATIONMARK1="
+$QUOTATIONMARK2='
+
+# In the current version of CrimsonEditor (3.51), the $QUOTATIONMARK3 
+# tag isn't supported, but maybe it will be in the future...
+#$QUOTATIONMARK3=`
+
+$LINECOMMENT=//
+$BLOCKCOMMENTON=/*
+$BLOCKCOMMENTOFF=*/
+$INDENTATIONON={
+$INDENTATIONOFF=}
+$PAIRS1=()
+$PAIRS2=[]
+$PAIRS3={}

+ 76 - 0
gmsrc/EditorHighlighters/PSPad editor/Syntax/GameMonkey Script.INI

@@ -0,0 +1,76 @@
+;PSPad user HighLighter definition file
+[Settings]
+Name=GameMonkey Script
+HTMLGroup=0
+Label=0
+FileType=*.gm
+CommentString=
+SlashComment=1
+CComment=1
+SlashComment=1
+IndentChar=
+UnIndentChar=
+TabWidth=2
+CaseSensitive=1
+SingleQuote=1
+DoubleQuote=1
+KeyWordChars=_
+CodeExplorer=ftUnknown
+[KeyWords]
+[ReservedWords]
+and=
+break=
+continue=
+dowhile=
+else=
+false=
+for=
+foreach=
+function=
+global=
+if=
+in=
+local=
+member=
+null=
+or=
+return=
+table=
+this=
+true=
+while=
+[KeyWords2]
+assert=
+block=
+debug=
+doString=
+exit=
+format=
+globals=
+print=
+signal=
+sleep=
+stateGet=
+stateGetLast=
+stateSet=
+stateSetExitFunction=
+stateSetOnThread=
+sysCollectGarbage=
+sysGetDesiredMemoryUsage=
+sysGetMemoryUsage=
+sysSetDesiredMemoryUsage=
+sysTime=
+tableCount=
+tableDuplicate=
+thread=
+threadId=
+threadIds=
+threadKill=
+threadKillAll=
+threadTime=
+typeId=
+typeName=
+typeRegisterOperator=
+typeRegisterVariable=
+yield=
+[KeyWords3]

BIN
gmsrc/bin/SciLexer.dll


BIN
gmsrc/bin/StripCR.exe


BIN
gmsrc/bin/bison.exe


BIN
gmsrc/bin/flex.exe


+ 348 - 0
gmsrc/doc/ChangeLog.txt

@@ -0,0 +1,348 @@
+-----------------------------------------------------------
+GameMonkey Script Change Log
+
+Newer entries are at the bottom.
+Dates are dd/mm/yy
+
+Note that very minor changes like a renamed variable or 
+modified comment may not appear in this list.
+-----------------------------------------------------------
+
+
+22/12/03
+o Package 1.0 ready for public release.
+
+02/01/04
+o Updated script reference doc with system functions, compiler string concat and other.
+
+08/01/04
+o Fixed filename case mismatch between filenames and #includes for Unix and other platform.
+o Renamed and modifed gmNetwork to NetClient and NetServer as they are not gm files.
+o Modified GMD project to show gmDebugger in gm file set and use Win32 #defs for windows specific parts.
+o Removed memory display from snake.gm
+o Added sample script descriptions file.
+o Removed GM_MAX() and GM_MIN() macros and replaced with template versions in gmUtil
+o Added gmLog2ge function as faster version of gmHighestBit, and changed gmArraySimple and gmArrayComplex to suit.
+o Fixed buffer overrun bug with gmVariable::AsString function.
+o Removed all warning level 4 warnings bar the warnings generated from flex and bison files.
+o Added GM_VERSION #define to gmMachine.h.  first version @ "1.1".  added gmVersion script binding.
+o Changed some #ifdef DEBUG to #ifdef GM_DEBUG_BUILD
+
+09/01/04
+o Added constant folding for ints and floats
+o Added +=, -=, *=, /= etc.  Some more optimisation is required on this to avoid reavaluation of complex l-value.
+
+10/01/04
+o Added threadKillAll() binding to allow termination of all threads.
+o Added threadIds() binding to allow access to all threads.
+o Modified BomberRun.gm to allow exit from main menu.
+
+16/01/04
+o Added Minimal code to use GM example.
+
+18/01/04
+o Fixed bug in string operator not.  Incorrectly returned true instead of false.
+
+23/01/04
+o Modified copyright message to be 'GameMonkey Script' as that is the full name.
+
+25/01/04
+o Added GameObject example code.
+
+07/02/04
+o Remove GM_ASSERT from macro GM_CHECK_FLOAT_PARAM.  Was probably left in from some previous debugging.
+
+20/02/04
+o gmMachine::Get/SetDesiredByteMemoryUsage() removed and replaced with Get/SetDesiredByteMemoryUsageHard() and Get/SetDesiredByteMemoryUsageSoft().
+o Changed behaviour of gmMachine::SetDesiredByteMemoryUsage***(). No longer sets auto-memory to false, also does not have auto-memory parameter.
+o New incremental garbage collection logic to govern the inc GC system.
+o New GC stats functions: TODO
+o Set gmConfig.h to use incremental GC by default.  Was using atomic GC.
+o Renamed machine bound function threadIds() to threadAllIds() to make it more different from threadId() and more consistent with other naming.
+
+21/02/04
+o Added paranoid check code to Incremental GC.
+o Fixed serious bug in gmTable with IncGC that caused deleted 'values' to go through write barrier.
+o Added Sleep(0) to WIN32 build of GME to improve windows performance which otherwise suffers from unyielding threads.
+
+26/02/04
+o Changed strtol to strtoul in gmParser.y to convert hex and binary numbers to unsigned ints causing them to not be truncated.  They will appear as full signed ints rather than be clipped to 0-2b range.
+
+03/03/04
+o Fixed bug in gmCodeGen.cpp The array container could resize causing pointers to become invalid.  List is now used to hold function pointers.
+
+06/03/04
+o Generate code target to big/little endian regardless of source platform.
+
+09/03/04
+o Modified gmMachine::CollectGarbage() AND binding sysCollectGarbage() to take a optional parameter to perform force full collection.
+o Improved VM GC logic so garbage collector will always free memory when it has the chance after a collect cycle.
+o Added sysSetDesiredMemoryUsageAuto() binding to enable auto adjust of memory limits (used to be flag in old set memory limit functions).
+
+11/03/04
+o Fixed excess memory bug in gmCodeGen.cpp as result of previous bug fix.
+o Improved VM GC logic so garbage collector auto sizes soft and hard limits better, but realize logic is still poor as long as only soft limit is exceeded.
+
+01/04/04
+o Fixed gmRandomFloat to use RAND_MAX instead of 65535.  Effects some platforms as these were not equivalent.
+
+21/04/04
+o Improved gmThread::Param(), returns reference to gmVariable instead of instance.
+
+18/06/04
+o Removed reseting of error log from gmMachine and gmThread.  Allowed memory limit on log.  Updated examples.
+
+23/08/04
+o Fixed gmMachine::Init().  Global table not initialized after ResetAndFreeMemory().
+
+10/10/04
+o Fixed bugs in gmCodeGen.cpp.  Removed remaining array resize bugs.
+
+24/10/04
+o Remove gmArrayComplex and replaced with gmArraySimple to reduce code as it was not necessary.
+o Removed 'no effect' variables that had been there to remove warning 'unreferenced parameter'.
+o Wrapped new with GM_NEW and GM_PLACEMENT_NEW for simpler replacement eg. debug new.
+o Changed multi char constatns 'abcd' to macro version for better compiler compatability.
+o Aligned memory for allocator in gmLibHooks.cpp for PS2.  Added alignment size #define.
+o Moved #include "assert.h" from gm into gm_Configp.h for better compatability.
+o Other minor changes for compiler compatability or reduction of compiler warnings.
+
+25/10/04
+o Changed behaviour of Block() to yeild if blocked on 'null'.
+o Made functions in gmThread const.
+o Added memory presizing functions to allocators and gmMachine.
+
+27/10/04
+o Changed malloc,free,realloc to new,delete equivalents in bison.simple and flex.skl files.
+o Aligned memory addresses in gmCodeTree, gmLog.
+o Added ToDoList.txt to the \docs folder.  Removed readme.txt from src\gm folder.
+
+02/11/04
+o Added new line to end of files to remove warnings on some compilers.
+o Fixed some non-commented comments on preprocessor commands to remove warnings on some compilers.
+o Changed some includes to later standard: cassert, new, to remove warnings on some compilers.
+o Moved non gm header includes into gmConfig.h with comments. External includes now only in gmConfig.h and gmConfig_p.h.
+o Renamed platform\win32 to win32msvc.  Added platform\win32gcc. Adjusted paths in projects to reflect this change.
+
+11/11/04
+o Fixed commented #define in gmParser.y.
+o Fixed bug in gmCallScript.h.  If C called C bound function via script, thread was not exited correctly.
+o Added gmCallScript::BeginFunction() Generic function call, and AddParamTable().
+
+07/12/04
+o Removed platform specific #defines and checks from gmConfig.h
+
+10/12/04
+o Added #include <errno.h> to flex.skl for Linux compile.
+o Fixed bug in GML.  Code used obsolete non 32bit byte code.
+
+11/12/04
+o Added gmCall.  gmCallScript is now deprecated.
+
+27/12/04
+o Replaced GC related 'workLeftToDo' variable names with 'workLeftToGo' so a simple 'todo' search won't find these.
+o Implemented file.Seek() as it was not implemented.
+
+01/01/05
+o gmfrontend.bat strips CR chars from flex and bison script files before generating compiler files.  This should help Linux & Unix builds.
+
+24/01/05
+o Fixed more compiler warnings.
+
+30/01/05
+o Added newline to end of gmDebugger.cpp.  Will have to watch for this in future to keep gcc happy.
+
+03/02/05
+o Added (experimental) new table creation syntax. eg. t = {1,2,3,}; same as t = table(1,2,3); for simplified config files.
+
+04/02/05
+o Added explicit gmVariable() constructors for gmTableObject and gmFunctionObject completing the default set.
+
+10/02/05
+o Modified gmCall to improve access to return variables.
+
+21/03/05
+o Added ability for gmMachine to handle cpp owned gmObjects and handle GC for them.
+o Changed gmMachine::Presize() (and related functions) to take named params instead of int array for clarity and future expansion.
+o Modified gmMemFixedSet to have more smaller memory sizes.
+
+09/04/05
+o Removed Windows specific hack from gmDebugger.cpp and moved it to the example Windows debugger files.
+o Added gmCall::AddParam() for generic gmVariable params.
+
+14/04/05
+o Added syntax highlighting to debugger. (Actually added back old files I found, that were meant for an IDE never finished.)
+o Added line numbering to debugger.  A quick hack, but maybe useful.
+
+16/04/05
+o Changed gmObject derived class constructors to be non-public.  This forces creation by gmMachine.
+o Minor change to gmListDouble to make some compiler happy.
+
+21/04/05
+o Minor changes for compatability with MSVS 2005.  gmMathLib, gmStringLib, platform header.
+
+04/05/05
+o Added GM_FLOAT_OR_INT_PARAM and GM_CHECK_FLOAT_OR_INT_PARAM to gmThread.h to help with numerical parameters.
+
+09/05/05
+o Changed some #defines in gmThread.h gmIncGC.cpp/.h prefixed with GM_ to reduce potential conflict with other code.
+
+10/05/05
+o Fixed/Changed friend declaration used by gmTableObject and gmFunctionObject non-public constructors, to work with MSVC 6, 2003, 2005.  Handling of friend template functions was different and broken.
+o Added optional UserBreak callback to allow external code to break thread execution. Eg. to exit endless loop when user pressed CTRL-BREAK.
+
+05/08/05
+o Fixed bug in GameObject example, String operator= not always used.
+
+29/09/05
+o Fixed bug in gmTableObject.  GC WriteBarrier was not being called with setting values to null.  Old values, reassigned elsewhere could be lost.
+
+30/10/05
+o Changed location of thread create event so 'this' should be available for query.
+
+14/11/05
+o Removed gmShutdownVector3Lib() from Vector3 binds example.
+
+11/01/06
+o Fixed bug in gmMachine::RemoveCPPOwnedGMObject().  Was not calling WriteBarrier.
+o Added gmMachine::IsCPPOwnedGMObject().
+o Added gmMachine::GetTypeTable() (as per DrEvil's mod).
+o Modified gmMachine::RegisterLib(), Allow append/modify existing table (as per Oli's mod).
+o Removed gmCallScript. (Had been depricated for a long time.)
+o Added experimental gmGCRoot to \binds.  Current implementation uses STL, but allows custom containers.
+
+21/01/06
+o Changed gmMachine, all non-public members are now protected instead of private to allow derived access.
+o Changed project and workspaces from MSVC 6 to MSVC 8 (2005)
+  To compile the Windows based examples & utils, you will need at least 'MS Visual C++ 2005 Express' and the 'Platform SDK', both freely downloadable from Microsoft.
+  To run the Windows examples you will need VC8 compatible runtimes installed, The '.Net Framework 2.0' redistributables or 'Windows Update' will provide these.
+  (Left VC6 projects and workspaces in for now.  Debugger still relies on MFC which is not supported by 2005 Express edition.)
+  
+08/03/06
+o Changed doString and (system.)DoFile bindings to take optional 'this' as parameter
+o Added gmThread Param() with default gmVariable param.
+
+10/03/06
+o Removed CrimsonEdit syntax highlighting for binary numbers as it did not behave as expected.
+
+15/03/06
+o Changed GC to start in 'off' state.
+
+02/04/06
+o Fixed gmMachine anomaly where, when a new thread is created just before yielding, and the yield occured in the last running thread, the new thread was skipped until the next execution cycle.
+
+05/04/06
+o Fixed bug where local (shared) strings waiting for finalization were reused.  Made them revive properly.
+o Removed old write barrier from gmTable, leaving new correct code only.
+
+16/04/06
+o Changed AsString() default for reference types to display pointer in hex instead of decimal.
+
+17/04/06
+o Added more info on binding functions to GameMonkeyScriptReference.pdf
+
+22/04/06
+o Changed gmThread so that final stack frame including 'this' is still valid when MC_THREAD_DESTROY occurs.
+
+20/05/06
+o Added config #define GMMACHINE_REMOVECOMPILER to allow compiler to be removed from gmMachine.  Saves some KBs as well as disables compilation.
+
+03/06/06
+o Added optional divide by zero check in operators to create GM exception rather than OS exception, as per DrEvil's suggestion.
+
+19/06/06
+o Changed gmMachine::AddCPPOwnedGMObject() and RemoveCPPOwnedGMObject() to ignore NULL ptrs rather than ASSERT on them, for caller convenience.
+
+24/07/06
+o Added two gmMachine function registration wrappers to provide alternate interface.
+o Added %u to bound function 'format' to support unsigned ints.
+
+01/08/06
+o Added gmVariable::IsNull().
+
+13/09/06
+o Added a bunch of accessor functions to gmVariable and gmTable for convenience.
+
+15/09/06
+o Added #define-able nullify in gmVariable ctor.  Disabled by default.
+
+26/09/06
+o Added #define _USE_32BIT_TIME_T so system binding 32 bit time functions work with MSVC 8 runtimes.
+
+18/10/06
+o Changed new gmVariable helpers to be const.
+
+26/10/06
+o Added user data to function registering.  This can be useful when binding C/C++ functions, particularly when registering MANY script functions to ONE C function that redirects to MANY C/C++ member functions.
+
+05/12/06
+o Fixed setdot misspelled as getdot in gmOperators.cpp and gmMachineLib.cpp.
+
+04/01/07
+o Modified gmKillThread() to behave as its comment describes.
+
+17/01/07
+o Fixed bug where GC was not correctly handling persistent strings. Thanks Kaz.
+
+20/01/07
+o Improved GC stats to reduce false warnings about bad GC config.
+
+21/01/07
+o Added operators implemented from script.  Thanks Seth 'Nightmare'.
+o Added another GC stats related function.
+o Rearranged pragmas and includes in config headers so order could influence subsequent files.
+o Added 'typename' to some template declarations in gmGCRootUtil.
+
+21/02/07
+o Fixed atan2 binding.  Thanks 'blackstormy'.
+
+20/03/07
+o Modified GC auto calibrate to allow no shrink.
+o Moved GC related settings to gmConfig.h.
+o Added gmMachine::GetAutoMemoryUsage().
+o Changed GME initial memory setting.
+o Added example Vector3 non operator Add for efficiency comparison.
+
+03/05/07
+o Added Experimental user type op_bool test for conditions. Thanks Kaz.
+o Modified killed threads.  Callback from killed state before recycle/delete.
+o Added const to more functions in gmThread.
+
+16/06/07
+o Fix bug relating to thread stack resize gmThread::Touch(). Thanks DrEvil.
+o Build .EXEs with VC2005 SP1, static linked VC runtimes except for Debugger which uses MFC.
+  Get vcredist_x86.exe from http://www.microsoft.com/downloads/details.aspx?familyid=200B2FD9-AE1A-4A14-984D-389C36F85647&displaylang=en or search for "Microsoft Visual C++ 2005 SP1 Redistributable Package" if needed.
+
+22/10/07
+o Added gmMachine::BindLibToFunction() Just a call through to existing gmLibHooks.
+o Changed gmGCRoot<>::Set() to handle null.
+o Removed a_filename from gmMachine::ExecuteFunction() since it wasn't used and could confuse users.
+
+11/11/07
+o Modified all GM_*_PARAM() and added overloads for all gmThread:Param*(), to check if invalid type was present.
+o Modified threadKill() to ignore threads with invalid Id instead of killing current thread.
+
+01/02/08
+o Change gmScanner.l so parser checks for End Of File, to prevent endless loop.
+
+11/04/09
+o Add GM_CHECK_THIS_* macros to gmThread.h.  Thanks Downgraded.
+o Add experimental and optional fork instruction.  Configured as GM_USE_FORK in gmConfig.h. Thanks HiVision.
+
+12/04/09
+o Add experimental foreach support for user types. Configured as GM_USER_FOREACH in gmConfig.h.  Thanks Downgraded.
+
+23/04/09
+o Add gmMachine::GetTypeId() to retrieve type id from string name. Thanks Downgraded.
+o Updated version number, oops.
+
+02/05/09
+o Fixed getind in gmThread.cpp where usertypes influenced the operator chosen to process. Thanks DrEvil.
+
+06/05/09
+o Add experimental endon() allowing a thread to be killed when signalled. Enabled with GM_USE_ENDON in gmConfig.h.  Thanks Downgraded.
+o Fixed (hopefully) endon() kills threads correctly and gets along with other blocks and signals.
+
+05/06/09
+o Fix and improve format() Thanks DrEvil.
+o Add some more gmVariable accessors (similar to gm_ex)

BIN
gmsrc/doc/GameMonkeyFAQ.pdf


BIN
gmsrc/doc/GameMonkeyGarbageCollection.pdf


BIN
gmsrc/doc/GameMonkeyLicense.pdf


BIN
gmsrc/doc/GameMonkeyScriptReference.pdf


+ 28 - 0
gmsrc/doc/ToDoList.txt

@@ -0,0 +1,28 @@
+-----------------------------------------------------------
+GameMonkey Script TODO list
+
+In no particular order unless otherwise specified.  No time 
+frame for changes or features, so effectively a wish list.
+-----------------------------------------------------------
+
+o Fix issue where reported error line is not exact.  Occurs due to instruction pointer increment before exception handling.
+o Fix empty / commented out script from producing parse error. Work around: Add a ';' semicolon to end of source, or ignore known empty scripts.
+o Change overide 'this' syntax from this:func() to func<this>() to support member chains eg. a.b.c<t>()
+o Put ++, -- operators back, but don't let them be used in conditions for consistency.
+
+
+o Make string hash table resize for efficiency in string intensive applications.
+o Make compiler thread safe.  Probably by moving to Lemon parser and Flex++.
+o Make user type Ids sharable between gmMachine instances so identical registration order is not required.
+o Make 64bit compatible.
+o Add exception handling like try / catch blocks.
+o Possibly move code from gmThread into gmMachine.
+o Possibly optimize byte code by moving to register stack machine.
+o Possibly allow extra (eg. variable number of) parameters to be passes as a table to script functions.
+o Optimize thread code to efficiently handle enormous numbers of threads.
+o Expose more to the debugger.
+o Eventually remove classic garbage collector, leaving only incremental collector.
+o Option for unicode strings.
+o Binding to allow user type creation and operator functions from within script.
+o Possibly store the 'color' bit and 'persistent' bit flags for GCObjects in the lower, unused part of the list pointer.
+o Possibly change the double linked list to a single XOR encoded link.  This CPU for memory trade may not be worthwhile.

+ 1689 - 0
gmsrc/scripts/Bomber.gm

@@ -0,0 +1,1689 @@
+//
+// Bomber By Kazys Stepanas
+//
+
+
+//--------------------------------------------------------------------------------------------------
+// Globals.
+//--------------------------------------------------------------------------------------------------
+global bgCol              = CA.B_BLACK;
+global quit               = false;
+global lastTouchedBlock   = -1;
+global player;
+
+global gameLevel          = 1;
+
+global blocks             = table(count = 0);
+global powerups           = table(count = 0);
+global enemies            = table(count = 0);
+global blockDeadZone      = table(x1 = 31, y1 = 8, x2 = 39, y2 = 14);
+global enemyDeadZone      = table(x1 = 22, y1 = 7, x2 = 45, y2 = 16);
+
+// Thing types.
+global T_Space            = 0;
+global T_Player           = 1;
+global T_Bomb             = 2;
+global T_Explosion        = 3;
+global T_Block            = 4;
+global T_Grid             = 5;
+global T_Powerup          = 6;
+global T_Enemy            = 7;
+
+// Powerup types.
+global PT_Bomb            = 1;
+global PT_Size            = 2;
+global PT_Life            = 3;
+
+// Dead zone types.
+global DT_None            = 0;
+global DT_Block           = 1;
+global DT_Enemy           = 2;
+
+//--------------------------------------------------------------------------------------------------
+// Grid control.
+//--------------------------------------------------------------------------------------------------
+global grid = table(startX = 0, startY = 0, spaceX = 1, spaceY = 1, rows = 0, cols = 0);
+
+//--------------------------------------------------------------------------------------------------
+// Constants
+//--------------------------------------------------------------------------------------------------
+global screen             = table(w = 80, h = 23);
+global statusY            = 24;
+
+global bombFuse           = 1.25;
+global explodeTime        = 0.75;
+global explodeCycle       = 0.05;
+global maxPlayerBombs     = 10;
+global maxPlayerSize      = 5;
+global maxPowerups        = 20;
+global maxLives           = 5;
+global startLives         = 3;
+
+global scoreBlock         = 10;
+global scoreNME           = 50;
+
+global nmeMoveTime        = 1.0;
+global powerupChance      = 40;
+global maxLevel           = 10;
+global maxBlockHits       = 3;
+
+//--------------------------------------------------------------------------------------------------
+// Graphics
+//--------------------------------------------------------------------------------------------------
+global playerPix          = "\2";
+global playerDeadPix      = "\5";
+global bombPix            = "\15";
+global expPix             = "+";
+global gridPix            = "\178";
+global block1Pix          = "\176";
+global block2Pix          = "\177";
+global block3Pix          = "\219";
+global nmePix             = "\232";
+global lifePix            = "\3";
+global bombSizePix        = "#";
+global bombExtraPix       = "*";
+global spacePix           = " ";
+
+//--------------------------------------------------------------------------------------------------
+// Colours
+//--------------------------------------------------------------------------------------------------
+global bgColour           = 0;
+global fgColour           = CA.F_RED | CA.F_GREEN;
+
+global playerColour       = CA.F_GREEN | CA.F_BLUE;// | CA.F_INTENSITY;
+global gridColour         = CA.F_RED;
+global blockColour        = CA.F_RED | CA.F_GREEN | CA.F_INTENSITY;
+global nmeColour          = CA.F_RED | CA.F_BLUE;
+global powerupColour      = CA.F_GREEN | CA.F_INTENSITY;
+global puLifeColour       = CA.F_RED;
+
+global statusColour       = CA.F_RED | CA.F_GREEN;
+
+global bombColour         = CA.F_BLUE | CA.F_INTENSITY;
+global expColourCount     = 5;
+global expColours         = array(expColourCount);
+
+global readyColour        = CA.F_RED;
+
+expColours[0] = CA.F_RED | CA.F_INTENSITY;
+expColours[1] = CA.F_BLUE|CA.F_RED;
+expColours[2] = CA.F_RED|CA.F_GREEN | CA.F_INTENSITY;
+expColours[3] = CA.F_RED;
+expColours[4] = CA.F_RED|CA.F_GREEN;
+
+
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+global IsPointWithin = function(a_ptx, a_pty, a_rx1, a_ry1, a_rx2, a_ry2)
+{
+  return (a_ptx >= a_rx1 and a_ptx <= a_rx2 and a_pty >= a_ry1 and a_pty <= a_ry2);
+};
+
+global OverlapRect = function(a_r1x1, a_r1y1, a_r1x2, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2)
+{
+  // Test one rect against the other.
+  if (IsPointWithin(a_r2x1, a_r2y1, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or
+      IsPointWithin(a_r2x1, a_r2y2, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or
+      IsPointWithin(a_r2x2, a_r2y2, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or
+      IsPointWithin(a_r2x2, a_r2y1, a_r1x1, a_r1y1, a_r1x2, a_r1y2))
+  {
+    return true;
+  }
+
+  // Test the other rect.
+  if (IsPointWithin(a_r1x1, a_r1y1, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or
+      IsPointWithin(a_r1x1, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or
+      IsPointWithin(a_r1x2, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or
+      IsPointWithin(a_r1x2, a_r1y1, a_r2x1, a_r2y1, a_r2x2, a_r2y2))
+  {
+    return true;
+  }
+
+  return false;
+};
+
+global ScreenClampX = function(a_x)
+{
+  if (a_x < 0)
+  {
+    return 0;
+  }
+  else if (a_x >= screen.w)
+  {
+    return screen.w-1;
+  }
+
+  return a_x;
+};
+
+global ScreenClampY = function(a_y)
+{
+  if (a_y < 0)
+  {
+    return 0;
+  }
+  else if (a_y >= screen.h)
+  {
+    return screen.h-1;
+  }
+
+  return a_y;
+};
+
+global ScreenClamp = function()
+{
+  .x = ScreenClampX(.x);
+  .y = ScreenClampY(.y);
+};
+
+global BombAt = function(a_x, a_y)
+{
+  for (i = 0; i < player.nextBomb; i=i+1)
+  {
+    bomb = player.bombs[i];
+    if (bomb.x == a_x and bomb.y == a_y)
+    {
+      return true;
+    }
+  }
+  return false;
+};
+
+global PointOverlap = function(a_x1, a_y1, a_x2, a_y2)
+{
+  return IsPointWithin(.x, .y, a_x1, a_y1, a_x2, a_y2);
+};
+
+//--------------------------------------------------------------------------------------------------
+// Player object.
+//--------------------------------------------------------------------------------------------------
+global Player = function()
+{
+  newPlayer = table(x = -1, y = -1);
+  newPlayer.bombSize  = 1;
+  newPlayer.bombFuse  = bombFuse;
+  newPlayer.bombs     = array(maxPlayerBombs);
+  newPlayer.maxBombs  = 1;
+  newPlayer.nextBomb  = 0;
+  newPlayer.threadId  = -1;
+  newPlayer.lastCanMoveThing = null;
+  newPlayer.dead      = false;
+  newPlayer.lives     = startLives;
+  newPlayer.score     = 0;
+  newPlayer.threadId  = -1;
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.Draw = function()
+  {
+    CATTRIB(bgColour | playerColour);
+    if (!.dead)
+    {
+      XYTEXT(.x, .y, playerPix);
+    }
+    else
+    {
+      XYTEXT(.x, .y, playerDeadPix);
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.DrawStatus = function()
+  {
+    CATTRIB(bgColour | puLifeColour | CA.F_INTENSITY);
+    XYTEXT(0,  statusY, format("%s : %d  ", lifePix, .lives));
+    CATTRIB(bgColour | powerupColour);
+    XYTEXT(10, statusY, format("%s : %d  ", bombExtraPix, .maxBombs));
+    XYTEXT(20, statusY, format("%s : %d  ", bombSizePix, .bombSize));
+    CATTRIB(bgColour | statusColour);
+    XYTEXT(50, statusY, format("Score : %d  ", .score));
+  };
+
+  newPlayer.AddScore = function(a_add)
+  {
+    .score = .score + a_add;
+    .DrawStatus();
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.CanMove = function(a_x, a_y)
+  {
+    thing = GetThingAt(a_x, a_y, false);
+    .lastCanMoveThing = thing;
+    return  thing.type == T_Space or thing.type == T_Explosion or
+            thing.type == T_Powerup or thing.type == T_Enemy;
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.Move = function(a_dx, a_dy)
+  {
+    if (!.dead)
+    {
+      oldX  = .x;
+      oldY  = .y;
+      newX  = ScreenClampX(.x + a_dx);
+      newY  = ScreenClampY(.y + a_dy);
+
+      if (.CanMove(newX, newY))
+      {
+        .x = newX;
+        .y = newY;
+        if (.lastCanMoveThing.type == T_Powerup)
+        {
+          .lastCanMoveThing.thing.Pickup();
+        }
+        else if (.lastCanMoveThing.type == T_Explosion or .lastCanMoveThing.type == T_Enemy)
+        {
+          .Kill();
+        }
+
+        RefreshXY(oldX, oldY);
+        RefreshXY(.x, .y);
+      }
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.Kill = function()
+  {
+    .dead = true;
+    RefreshXY(.x, .y);
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.DropBomb = function()
+  {
+    if (!.dead and .nextBomb < .maxBombs and !BombAt(.x, .y))
+    {
+      newBomb = Bomb(.x, .y, .bombSize, .bombFuse, this);
+      .bombs[.nextBomb] = newBomb;
+      .nextBomb = .nextBomb + 1;
+      newBomb.Draw();
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.FreeBomb = function(a_bomb)
+  {
+    found = false;
+    limit = .nextBomb;
+    for (i = 0; i < limit; i=i+1)
+    {
+      if (.bombs[i] == a_bomb)
+      {
+        found = true;
+        .nextBomb = .nextBomb - 1;
+      }
+      if (found)
+      {
+        if (i+1 < limit)
+        {
+          .bombs[i] = .bombs[i+1];
+        }
+        else
+        {
+          .bombs[i] = null;
+        }
+      }
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.StartThread = function()
+  {
+    this:stateSet(.IdleState);
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.IdleState = function()
+  {
+    while (!quit)
+    {
+      pressed = false;
+      if (ISPRESSED(38))
+      {
+        .Move(0, -1);
+        pressed = true;
+      }
+      else if (ISPRESSED(40))
+      {
+        .Move(0, 1);
+        pressed = true;
+      }
+      else if (ISPRESSED(39))
+      {
+        .Move(1, 0);
+        pressed = true;
+      }
+      else if (ISPRESSED(37))
+      {
+        .Move(-1, 0);
+        pressed = true;
+      }
+      else if (ISPRESSED(' '))
+      {
+        .DropBomb();
+        pressed = true;
+      }
+
+      if (pressed)
+      {
+        sleep(0.075);
+      }
+      else if (.dead)
+      {
+        this:stateSet(.DeadState);
+      }
+      else
+      {
+        yield();
+      }
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.DeadState = function()
+  {
+    sleep(explodeTime);
+    if (.lives > 0)
+    {
+      .lives = .lives - 1;
+    }
+    if (.maxBombs > 1)
+    {
+      .maxBombs = .maxBombs - 1;
+    }
+    if (.bombSize > 1)
+    {
+      .bombSize = .bombSize - 1;
+    }
+    .DrawStatus();
+
+    while (!quit)
+    {
+      if (.lives > 0 and ISPRESSED(' '))
+      {
+        while (!quit)
+        {
+          pos = .GetStartPos();
+          thing = GetThingAt(pos.x, pos.y, false);
+          if (thing.type == T_Enemy)
+          {
+            thing.thing.Kill();
+          }
+          sleep(0.2);
+
+          oldX = .x;
+          oldY = .y;
+          .x = pos.x;
+          .y = pos.y;
+
+          .dead = false;
+          RefreshXY(oldX, oldY);
+          RefreshXY(.x, .y);
+          this:stateSet(.IdleState);
+        }
+      }
+      yield();
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.Overlap = PointOverlap;
+
+  //------------------------------------
+  //------------------------------------
+  newPlayer.Cheat = function()
+  {
+    .lives    = 5;
+    .maxBombs = 10;
+    .bombSize = 5;
+    .DrawStatus();
+  };
+
+  newPlayer.GetStartPos = function()
+  {
+    // Position near centre.
+    ptx = grid.startX + (grid.rows/2)*grid.spaceX;
+    pty = grid.startY + (grid.cols/2)*grid.spaceY;
+    return table(x = ptx, y = pty);
+  };
+
+  pos = newPlayer.GetStartPos();
+  newPlayer.x = pos.x;
+  newPlayer.y = pos.y;
+
+  newPlayer.Start = function()
+  {
+    .threadId = this:thread(.StartThread);
+  };
+
+  newPlayer.Stop = function()
+  {
+    threadKill(.threadId);
+  };
+
+  return newPlayer;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Bomb object.
+//--------------------------------------------------------------------------------------------------
+global Bomb = function(a_x, a_y, a_size, a_fuse, a_owner)
+{
+  newBomb = table(x = a_x, y = a_y, size = a_size, fuse = a_fuse, owner = a_owner);
+  newBomb.explode = false;
+  newBomb.expMinX = 0;
+  newBomb.expMaxX = 0;
+  newBomb.expMinY = 0;
+  newBomb.expMaxY = 0;
+  newBomb.visible = true;
+  newBomb.threadId = -1;
+
+  newBomb.CountDown = function()
+  {
+    this:stateSet(.FuseState);
+  };
+
+  newBomb.Explode = function()
+  {
+    this:stateSetOnThread(.threadId, .ExplodeState);
+  };
+
+  newBomb.FuseState = function()
+  {
+    sleep(.fuse);
+    this:stateSet(.ExplodeState);
+  };
+
+  newBomb.ExplodeState = function()
+  {
+    .expMinX = ScreenClampX(.x - .size);
+    .expMaxX = ScreenClampX(.x + .size);
+    .expMinY = ScreenClampY(.y - .size);
+    .expMaxY = ScreenClampY(.y + .size);
+
+    .explode = true;
+    .visible = false;
+    .KillStuff();
+    .visible = true;
+    .Draw();
+    count = 1+g_count;
+
+    inc = explodeCycle/explodeTime;
+    for (i = 0; i < explodeTime; i = i + inc)
+    {
+      sleep(explodeCycle);
+      .Draw();
+    }
+
+    .visible = false;
+    RefreshRect(.expMinX, .expMinY, .expMaxX - .expMinX + 1, .expMaxY - .expMinY + 1);
+    if (.owner != null)
+    {
+      .owner.FreeBomb(this);
+    }
+    exit();
+  };
+
+  newBomb.KillStuff = function()
+  {
+    // Kill stuff.
+    for (x = .x; x >= .expMinX; x=x-1)
+    {
+      if (!.TryKill(GetThingAt(x, .y, true)))
+      {
+        .expMinX = x;
+        break;
+      }
+    }
+    for (x = .x+1; x <= .expMaxX; x=x+1)
+    {
+      if (!.TryKill(GetThingAt(x, .y, true)))
+      {
+        .expMaxX = x;
+        break;
+      }
+    }
+
+    for (y = .y; y >= .expMinY; y=y-1)
+    {
+      if (!.TryKill(GetThingAt(.x, y, true)))
+      {
+        .expMinY = y;
+        break;
+      }
+    }
+    for (y = .y+1; y <= .expMaxY; y=y+1)
+    {
+      if (!.TryKill(GetThingAt(.x, y, true)))
+      {
+        .expMaxY = y;
+        break;
+      }
+    }
+  };
+
+  newBomb.TryKill = function(a_thing)
+  {
+    if (a_thing.type == T_Block)
+    {
+      a_thing.thing.Kill();
+      return false;
+    }
+    else if (a_thing.type == T_Bomb and a_thing.thing != this)
+    {
+      a_thing.thing.Explode();
+    }
+    else if (a_thing.type == T_Player or a_thing.type == T_Enemy)
+    {
+      a_thing.thing.Kill();
+    }
+    return true;
+  };
+
+  newBomb.Draw = function()
+  {
+    if (.visible)
+    {
+      if (.explode)
+      {
+        CATTRIB(bgColour | expColours[randint(0, expColourCount)]);
+        for (x = .expMinX; x <= .expMaxX; x=x+1)
+        {
+          if (IsSpaceOnGrid(x, .y))
+          {
+            XYTEXT(x, .y, expPix);
+          }
+        }
+
+        for (y = .expMinY; y <= .expMaxY; y=y+1)
+        {
+          if (IsSpaceOnGrid(.x, y))
+          {
+            XYTEXT(.x, y, expPix);
+          }
+        }
+      }
+      else
+      {
+        CATTRIB(bgColour | bombColour);
+        XYTEXT(.x, .y, bombPix);
+      }
+    }
+  };
+
+  newBomb.Overlap = function(a_x1, a_y1, a_x2, a_y2)
+  {
+    if (.explode)
+    {
+      return OverlapRect(.expMinX, .y, .expMaxX, .y+1, a_x1, a_y1, a_x2, a_y2) and OverlapRect(.x, .expMinY, .x+1, .expMaxY, a_x1, a_y1, a_x2, a_y2);
+    }
+    return IsPointWithin(.x, .y, a_x1, a_y1, a_x2, a_y2);
+  };
+
+  newBomb.FuseThread = function()
+  {
+    sleep(.fuse);
+    .Explode();
+  };
+
+  newBomb.threadId = newBomb:thread(newBomb.CountDown);
+  newBomb:thread(newBomb.FuseThread);
+  return newBomb;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Block code.
+//--------------------------------------------------------------------------------------------------
+global Block = function(a_x, a_y)
+{
+  newBlock = table(x = a_x, y = a_y, visible = true);
+  newBlock.hits = 1;
+
+  newBlock.Draw = function()
+  {
+    if (.visible)
+    {
+      CATTRIB(bgColour | blockColour);
+      if (.hits == 3)
+      {
+        XYTEXT(.x, .y, block3Pix);
+      }
+      else if (.hits == 2)
+      {
+        XYTEXT(.x, .y, block2Pix);
+      }
+      else
+      {
+        XYTEXT(.x, .y, block1Pix);
+      }
+    }
+  };
+
+  newBlock.Overlap = PointOverlap;
+
+  newBlock.KillThread = function()
+  {
+    yield();
+    yield();
+    if (.visible)
+    {
+      .visible = false;
+      player.AddScore(scoreBlock);
+      if (randint(0, 100) < powerupChance)
+      {
+        AddPowerup(.x, .y);
+      }
+    }
+  };
+
+  newBlock.Kill = function()
+  {
+    .hits = .hits - 1;
+    if (.hits == 0)
+    {
+      this:thread(.KillThread);
+    }
+    RefreshXY(.x, .y);
+  };
+
+  return newBlock;
+};
+
+global CreateBlocks = function(a_num)
+{
+  global maxBlockHits;
+  global blocks = table(count = 0, blocks = array(a_num));
+  for (i = 0; i < a_num; i=i+1)
+  {
+    pos = GetRandomFreePos(DT_Block, true);
+    if (pos.x != -1 and pos.y != -1)
+    {
+      thing = GetThingAt(pos.x, pos.y, false);
+      if (thing.type == DT_Block)
+      {
+        if (thing.thing.hits < maxBlockHits)
+        {
+          thing.thing.hits = thing.thing.hits + 1;
+        }
+      }
+      else
+      {
+        blocks.blocks[i] = Block(pos.x, pos.y);
+        blocks.count = i;
+      }
+    }
+  }
+  blocks.count = a_num;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Powerup.
+//--------------------------------------------------------------------------------------------------
+global Powerup = function(a_x, a_y, a_type)
+{
+  newPu = table(x = a_x, y = a_y, type = a_type);
+
+  if (a_type == PT_Bomb)
+  {
+    newPu.Draw = function()
+    {
+      CATTRIB(bgColour | powerupColour);
+      XYTEXT(.x, .y, bombExtraPix);
+    };
+
+    newPu.Pickup = function()
+    {
+      if (player.maxBombs < maxPlayerBombs)
+      {
+        player.maxBombs = player.maxBombs + 1;
+      }
+      player.DrawStatus();
+      RemovePowerup(this);
+    };
+  }
+  else if (a_type == PT_Size)
+  {
+    newPu.Draw = function()
+    {
+      CATTRIB(bgColour | powerupColour);
+      XYTEXT(.x, .y, bombSizePix);
+    };
+
+    newPu.Pickup = function()
+    {
+      if (player.bombSize < maxPlayerSize)
+      {
+        player.bombSize = player.bombSize + 1;
+      }
+      player.DrawStatus();
+      RemovePowerup(this);
+    };
+  }
+  else if (a_type == PT_Life)
+  {
+    newPu.Draw = function()
+    {
+      CATTRIB(bgColour | puLifeColour);
+      XYTEXT(.x, .y, lifePix);
+    };
+
+    newPu.Pickup = function()
+    {
+      if (player.lives < maxLives)
+      {
+        player.lives = player.lives + 1;
+      }
+      player.DrawStatus();
+      RemovePowerup(this);
+    };
+  }
+  else
+  {
+    newPu.Draw = function()
+    {
+    };
+
+    newPu.Pickup = function()
+    {
+      RemovePowerup(this);
+    };
+  }
+
+  newPu.Overlap = PointOverlap;
+
+  return newPu;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Powerup management.
+//--------------------------------------------------------------------------------------------------
+global RandomPowerupType = function()
+{
+  int = randint(0, 100);
+
+  if (int < 49)
+  {
+    return PT_Bomb;
+  }
+  if (int < 98)
+  {
+    return PT_Size;
+  }
+
+  return PT_Life;
+};
+
+global AddPowerup = function(a_x, a_y)
+{
+  if (powerups.count < maxPowerups)
+  {
+    powerups.powerups[powerups.count] = Powerup(a_x, a_y, RandomPowerupType());
+    powerups.count = powerups.count + 1;
+    RefreshXY(a_x, a_y);
+  }
+};
+
+global RemovePowerup = function(a_pu)
+{
+  found = false;
+  count = powerups.count;
+  for (i = 0; i < count; i = i + 1)
+  {
+    if (powerups.powerups[i] == a_pu)
+    {
+      found = true;
+      powerups.powerups[i] = null;
+      powerups.count = powerups.count - 1;
+    }
+    if (found)
+    {
+      if (i+1 < count)
+      {
+        powerups.powerups[i] = powerups.powerups[i+1];
+      }
+      else
+      {
+        powerups.powerups[i] = null;
+      }
+    }
+  }
+};
+
+global InitPowerups = function()
+{
+  global powerups = table(count = 0, powerups = array(maxPowerups));
+};
+
+//--------------------------------------------------------------------------------------------------
+// Enemies.
+//--------------------------------------------------------------------------------------------------
+global CreateEnemies = function(a_num)
+{
+  global enemies = table(count = 0, enemies = array(a_num));
+
+  for (i = 0; i < a_num; i=i+1)
+  {
+    pos = GetRandomFreePos(DT_Enemy, false);
+    if (pos.x != -1 and pos.y != -1)
+    {
+      enemies.enemies[i] = Enemy(pos.x, pos.y);
+      enemies.count = i+1;
+    }
+  }
+};
+
+global Enemy = function(a_x, a_y)
+{
+  newEnemy = table(x = a_x, y = a_y, dead = false);
+  newEnemy.lastCanMoveThing = null;
+  newEnemy.lastPos          = table(x = a_x, y = a_y);
+  newEnemy.threadId         = -1;
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.Draw = function()
+  {
+    if (!.dead)
+    {
+      CATTRIB(bgColour | nmeColour);
+      XYTEXT(.x, .y, nmePix);
+    }
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.Kill = function()
+  {
+    player.AddScore(scoreNME);
+    .dead = true;
+    if (randint(0, 100) < powerupChance)
+    {
+      AddPowerup(.x, .y);
+    }
+    RefreshXY(.x, .y);
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.Overlap = PointOverlap;
+
+  //------------------------------------
+  // Movement.
+  //------------------------------------
+  newEnemy.CanMove = function(a_x, a_y)
+  {
+    if (a_x < 0 or a_x >= screen.w or a_y < 0 or a_y > screen.h)
+    {
+      return false;
+    }
+
+    thing = GetThingAt(a_x, a_y, false);
+    .lastCanMoveThing = thing;
+    return  thing.type == T_Space or thing.type == T_Exlposion or
+            thing.type == T_Powerup or thing.type == T_Player;
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.MoveX = function()
+  {
+    // Is the player on the same column?
+    if (.y == player.y)
+    {
+      // Are we within a grid spacing?
+      if (player.x - .x <= grid.spaceX + 1)
+      {
+        if (.x < player.x)
+        {
+          start = .x+1;
+          end   = player.x+1;
+          moveX = start;
+        }
+        else
+        {
+          start = player.x+1;
+          end   = .x;
+          moveX = end-1;
+        }
+        // Do we have a clear path.
+        moveOk = true;
+        for (x = start; moveOk and x < end; x=x+1)
+        {
+          if (!.CanMove(x, .y))
+          {
+            moveOk = false;
+          }
+        }
+        if (moveOk and .CanMove(moveX, .y))
+        {
+          .MoveTo(moveX, .y);
+          return true;
+        }
+      }
+    }
+
+    mx = 1;
+    if (player.x < .x)
+    {
+      mx = -1;
+    }
+    if ((.x+mx != .lastPos.x or .y != .lastPos.y) and .CanMove(.x+mx, .y))
+    {
+      .MoveTo(.x+mx, .y);
+      return true;
+    }
+    return false;
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.MoveY = function()
+  {
+    // Is the player on the same row?
+    if (.x == player.x)
+    {
+      // Are we within a grid spacing?
+      if (player.y - .y <= grid.spaceY + 1)
+      {
+        if (.y < player.y)
+        {
+          start = .y+1;
+          end   = player.y+1;
+          moveY = start;
+        }
+        else
+        {
+          start = player.y+1;
+          end   = .y;
+          moveY = end-1;
+        }
+        // Do we have a clear path.
+        moveOk = true;
+        for (y = start; moveOk and y < end; y=y+1)
+        {
+          if (!.CanMove(.x, y))
+          {
+            moveOk = false;
+          }
+        }
+        if (moveOk and .CanMove(.x, moveY))
+        {
+          .MoveTo(.x, moveY);
+          return true;
+        }
+      }
+    }
+
+    my = 1;
+    if (player.y < .y)
+    {
+      my = -1;
+    }
+    if ((.x != .lastPos.x or .y+my != .lastPos.y) and .CanMove(.x, .y+my))
+    {
+      .MoveTo(.x, .y+my);
+      return true;
+    }
+    return false;
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.MoveTo = function(a_x, a_y)
+  {
+    .lastPos.x  = .x;
+    .lastPos.y  = .y;
+
+    thing = GetThingAt(a_x, a_y, false);
+
+    .x          = a_x;
+    .y          = a_y;
+
+    if (thing.type == T_Player)
+    {
+      player.Kill();
+    }
+    else if (thing.type == T_Explosion)
+    {
+      .Kill();
+    }
+
+    RefreshXY(.lastPos.x, .lastPos.y);
+    RefreshXY(.x, .y);
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.Move = function()
+  {
+    moved = false;
+    if (!player.dead)
+    {
+      // Try move towards the player.
+      preferX = abs(player.x - .x) > abs(player.y - .y);
+      if (preferX)
+      {
+        moved = .MoveX();
+        if (!moved)
+        {
+          moved = .MoveY();
+        }
+      }
+      else
+      {
+        moved = .MoveY();
+        if (!moved)
+        {
+          moved = .MoveX();
+        }
+      }
+
+      // Try move away from the last position.
+      if (!moved)
+      {
+        testX = 2*.x - .lastPos.x;
+        testY = 2*.y - .lastPos.y;
+        if (.CanMove(testX, testY))
+        {
+          .MoveTo(testX, testY);
+          moved = true;
+        }
+      }
+    }
+
+    if (!moved and (.lastPos.x != .x or .lastPos.y != .y) and .CanMove(.lastPos.x, .lastPos.y))
+    {
+      .MoveTo(.lastPos.x, .lastPos.y);
+    }
+  };
+
+  //------------------------------------
+  // Thread functions and states.
+  //------------------------------------
+  newEnemy.Start = function()
+  {
+    .threadId = this:thread(.IdleState);
+  };
+
+  //------------------------------------
+  //------------------------------------
+  newEnemy.IdleState = function()
+  {
+    while (!quit and !.dead)
+    {
+      sleep(nmeMoveTime);
+      .Move();
+    }
+  };
+
+  newEnemy.Start();
+
+  return newEnemy;
+};
+
+//--------------------------------------------------------------------------------------------------
+// Refresh functions.
+//--------------------------------------------------------------------------------------------------
+global Refresh = function()
+{
+  CATTRIB(bgColour | fgColour);
+  CLS();
+
+  // Draw blocks.
+  for (b = 0; b < blocks.count; b=b+1)
+  {
+    blocks.blocks[b].Draw();
+  }
+
+  // Draw powerups.
+  for (p = 0; p < powerups.count; p=p+1)
+  {
+    powerups.powerups[p].Draw();
+  }
+
+  // Draw enemies.
+  for (e = 0; e < enemies.count; e=e+1)
+  {
+    enemies.enemies[e].Draw();
+  }
+
+  // Draw bombs.
+  for (b = 0; b < player.nextBomb; b=b+1)
+  {
+    player.bombs[b].Draw();
+  }
+
+  player.Draw();
+  player.DrawStatus();
+
+  DrawGrid();
+};
+
+global RefreshXY = function(a_x, a_y)
+{
+  RefreshRect(a_x, a_y, 1, 1);
+};
+
+global RefreshRect = function(a_x, a_y, a_w, a_h)
+{
+  // Clear the area.
+  xLimit = a_x + a_w;
+  yLimit = a_y + a_h;
+  CATTRIB(bgColour | fgColour);
+  for (x = a_x; x < xLimit; x=x+1)
+  {
+    for (y = a_y; y < yLimit; y=y+1)
+    {
+      XYTEXT(x, y, spacePix);
+    }
+  }
+
+  // Draw blocks.
+  for (b = 0; b < blocks.count; b=b+1)
+  {
+    blk = blocks.blocks[b];
+    if (blk.Overlap(a_x, a_y, xLimit, yLimit))
+    {
+      blk.Draw();
+    }
+  }
+
+  // Draw powerups.
+  for (p = 0; p < powerups.count; p=p+1)
+  {
+    pu = powerups.powerups[p];
+    if (pu.Overlap(a_x, a_y, xLimit, yLimit))
+    {
+      pu.Draw();
+    }
+  }
+
+  // Draw enemies.
+  for (e = 0; e < enemies.count; e=e+1)
+  {
+    nme = enemies.enemies[e];
+    if (nme.Overlap(a_x, a_y, xLimit, yLimit))
+    {
+      nme.Draw();
+    }
+  }
+
+  // Draw bombs.
+  for (b = 0; b < player.nextBomb; b=b+1)
+  {
+    bomb = player.bombs[b];
+    if (bomb.Overlap(a_x, a_y, xLimit, yLimit))
+    {
+      bomb.Draw();
+    }
+  }
+
+  if (player.Overlap(a_x, a_y, xLimit, yLimit))
+  {
+    player.Draw();
+  }
+
+  DrawGridRect(a_x, a_y, a_w, a_h);
+};
+
+//--------------------------------------------------------------------------------------------------
+// Grid control.
+//--------------------------------------------------------------------------------------------------
+global Grid = function(a_startX, a_startY, a_spaceX, a_spaceY)
+{
+  grid.startX = a_startX;
+  grid.startY = a_startY;
+  grid.spaceX = a_spaceX;
+  grid.spaceY = a_spaceY;
+
+  grid.rows   = (screen.w-grid.startX) / (grid.spaceX + 1);
+  grid.cols   = (screen.h-grid.startY) / (grid.spaceY + 1);
+
+  if ((screen.w-grid.startX) % (grid.spaceX + 1) != 0)
+  {
+    grid.rows = grid.rows + 1;
+  }
+
+  if ((screen.h-grid.startY) % (grid.spaceY + 1) != 0)
+  {
+    grid.cols = grid.cols + 1;
+  }
+};
+
+global DrawGrid = function()
+{
+  DrawGridRect(0, 0, screen.w, screen.h);
+};
+
+global DrawGridRect = function(a_startX, a_startY, a_w, a_h)
+{
+  xLimit = a_startX + a_w;
+  yLimit = a_startY + a_h;
+  CATTRIB(bgColour | gridColour);
+  for (x = a_startX; x < xLimit; x=x+1)
+  {
+    for (y = a_startY; y < yLimit; y=y+1)
+    {
+      if (!IsSpaceOnGrid(x, y))
+      {
+        XYTEXT(x, y, gridPix);
+      }
+    }
+  }
+};
+
+global IsSpaceOnGrid = function(a_x, a_y)
+{
+  return (a_x - grid.startX) % (grid.spaceX+1) == 0 or (a_y - grid.startY) % (grid.spaceY+1) == 0;
+};
+
+global IsFreePos = function(a_x, a_y, a_allowBlocks)
+{
+  thing = GetThingAt(a_x, a_y, true);
+  return thing.type == T_Space or (a_allowBlocks and thing.type == T_Block);
+};
+
+global GetThingAt = function(a_x, a_y, a_ignoreExplosion)
+{
+  if (!IsSpaceOnGrid(a_x, a_y))
+  {
+    return table(thing = grid, type = T_Grid);
+  }
+
+  // Test blocks
+  for (b = 0; b < blocks.count; b=b+1)
+  {
+    blk = blocks.blocks[b];
+    if (blk.visible and blk.x == a_x and blk.y == a_y)
+    {
+      return table(thing = blk, type = T_Block);
+    }
+  }
+
+  // Test enemies.
+  for (e = 0; e < enemies.count; e=e+1)
+  {
+    nme = enemies.enemies[e];
+    if (!nme.dead and nme.x == a_x and nme.y == a_y)
+    {
+      return table(thing = nme, type = T_Enemy);
+    }
+  }
+
+  // Test player pos.
+  if (!player.dead and a_x == player.x and a_y == player.y)
+  {
+    return table(thing = player, type = T_Player);
+  }
+
+  // Test bombs.
+  for (b = 0; b < player.nextBomb; b=b+1)
+  {
+    bomb = player.bombs[b];
+    if (!bomb.explode)
+    {
+      if (bomb.x == a_x and bomb.y == a_y)
+      {
+        return table(thing = bomb, type = T_Bomb);
+      }
+    }
+    else if (!a_ignoreExplosion and bomb.Overlap(a_x, a_y, a_x+1, a_y+1))
+    {
+      return table(thing = bomb, type = T_Explosion);
+    }
+  }
+
+  // Test powerups.
+  for (p = 0; p < powerups.count; p=p+1)
+  {
+    pu = powerups.powerups[p];
+    if (pu.x == a_x and pu.y == a_y)
+    {
+      return table(thing = pu, type = T_Powerup);
+    }
+  }
+
+  return table(thing = null, type = T_Space);
+};
+
+global InDeadZone = function(a_dtType, a_x, a_y)
+{
+  if (a_dtType == DT_Block)
+  {
+    return IsPointWithin(a_x, a_y, blockDeadZone.x1, blockDeadZone.y1, blockDeadZone.x2, blockDeadZone.y2);
+  }
+  if (a_dtType == DT_Enemy)
+  {
+    return IsPointWithin(a_x, a_y, enemyDeadZone.x1, enemyDeadZone.y1, enemyDeadZone.x2, enemyDeadZone.y2);
+  }
+  return false;
+};
+
+global GetRandomFreePos1 = function(a_deadZoneType, a_allowBlocks, a_tries)
+{
+  row = randint(0, grid.rows);
+  col = randint(0, grid.cols);
+
+  px  = row*grid.spaceX + grid.startX;
+  py  = col*grid.spaceY + grid.startY;
+
+  if (a_deadZoneType != DT_None and InDeadZone(a_deadZoneType, px, py))
+  {
+    return GetRandomFreePos1(a_deadZoneType, a_tries);
+  }
+
+  if (IsFreePos(px, py))
+  {
+    return table(x = px, y = py);
+  }
+
+  while (py < screen.h)
+  {
+    py=py+1;
+    if ((a_deadZoneType == DT_None or !InDeadZone(a_deadZoneType, px, py)) and IsFreePos(px, py))
+    {
+      return table(x = px, y = py);
+    }
+  }
+
+  if (tries < 5)
+  {
+    return GetRandomFreePos1(a_tries+1);
+  }
+  return table(x = -1, y = -1);
+};
+
+global GetRandomFreePos = function(a_deadZoneType, a_allowBlocks)
+{
+  return GetRandomFreePos1(a_deadZoneType, a_allowBlocks, 0);
+};
+
+//--------------------------------------------------------------------------------------------------
+// Initialisation.
+//--------------------------------------------------------------------------------------------------
+RenderRect = function(a_rect)
+{
+  CATTRIB(bgColour | gridColour);
+  for (i = a_rect.x1; i <= a_rect.x2; i=i+1)
+  {
+    for (j = a_rect.y1; j <= a_rect.y2; j=j+1)
+    {
+      XYTEXT(i, j, "o");
+    }
+  }
+};
+
+MemThread = function()
+{
+  while (!quit)
+  {
+    CATTRIB(bgColour | statusColour);
+    XYTEXT(65, statusY, format("Mem: %d   ", sysGetMemoryUsage()));
+    sleep(1.0);
+  }
+};
+
+//--------------------------------------------------------------------------------------------------
+// Level thread.
+//--------------------------------------------------------------------------------------------------
+global GameOver = function()
+{
+  CATTRIB(bgColour | statusColour);
+  CLS();
+
+  local score = player.score;
+  KillAll();
+
+  sleep(0.3);
+
+  global quit;
+  while (!quit)
+  {
+    CATTRIB(bgColour | expColours[randint(0, expColourCount)]);
+    x = 8;
+    y = 8;
+    XYTEXT(x, y, "__________________________________________________________"); y=y+1;
+    XYTEXT(x, y, "      __                             __                   "); y=y+1;
+    XYTEXT(x, y, "    /    )                         /    )                 "); y=y+1;
+    XYTEXT(x, y, "   /         __   _  _    __      /    /         __   )__ "); y=y+1;
+    XYTEXT(x, y, "  /  --,   /   ) / /  ) /___)    /    /   | /  /___) /   )"); y=y+1;
+    XYTEXT(x, y, "_(____/___(___(_/_/__/_(___ ____(____/____|/__(___ _/_____"); y=y+1;
+
+    CATTRIB(bgColour | statusColour);
+    XYTEXT(0, statusY, format("Score : %d", score));
+
+    if (threadTime() < 1000 and ISPRESSED(' '))
+    {
+      stateSet(TitleScreenState);
+    }
+    else if (ISPRESSED(27))
+    {
+      quit = true;
+    }
+
+    sleep(randfloat(0.03, 0.3));
+  }
+
+  global player  = null;
+  global blocks  = null;
+  global enemies = null;
+
+  stateSet(TitleScreenState);
+};
+
+global GameState = function()
+{
+  global maxLevel;
+  global player;
+  global blocks;
+  global enemies;
+  global gameLevel;
+  Grid(3, 2, 5, 5);
+  if (player == null or player.lives <= 0)
+  {
+    player = Player();
+  }
+  pos = player.GetStartPos();
+  player.x = pos.x;
+  player.y = pos.y;
+
+  global nmeMoveTime = 1.0 - gameLevel*0.1;
+
+  InitPowerups();
+  CreateBlocks(min(10 + 10*gameLevel, 99));
+  CreateEnemies(5*gameLevel);
+  yield();
+  Refresh();
+
+  yield();
+  player.Start();
+
+  local done = false;
+  while (!quit and !done)
+  {
+    local count;
+
+    done = true;
+    if (ISPRESSED(27))
+    {
+      global quit = true;
+      break;
+    }
+/*
+    // Check blocks.
+    count = blocks.count;
+    for (i = 0; done and i < count; i=i+1)
+    {
+      blk = blocks.blocks[i];
+      if (blk.visible)
+      {
+        done = false;
+      }
+    }
+*/
+    // Check enemies.
+    count = enemies.count;
+    for (i = 0; done and i < count; i=i+1)
+    {
+      nme = enemies.enemies[i];
+      if (!nme.dead)
+      {
+        done = false;
+      }
+    }
+
+    // Check bombs.
+    done = done and player.nextBomb == 0;
+
+    // Check for lives.
+    done = done or player.lives <= 0;
+
+    sleep(0.3);
+  }
+
+  player.Stop();
+
+  if (!quit)
+  {
+    if (player.lives <= 0)
+    {
+      stateSet(GameOver);
+    }
+    global gameLevel;
+    gameLevel=gameLevel+1;
+    stateSet(LevelState);
+  }
+
+  KillAll();
+};
+
+global LevelState = function()
+{
+  global maxLevel;
+  global gameLevel;
+
+  if (gameLevel > maxLevel)
+  {
+    gameLevel = maxLevel;
+  }
+
+  CATTRIB(bgColour | statusColour);
+  CLS();
+
+  sleep(0.3);
+
+  global quit;
+  while (!quit)
+  {
+    CATTRIB(bgColour | expColours[randint(0, expColourCount)]);
+    x = 17;
+    y = 8;
+    XYTEXT(x, y, "___________________________________________"); y=y+1;
+    XYTEXT(x, y, "    ____                              ___  "); y=y+1;
+    XYTEXT(x, y, "    /    )                   /      (    ) "); y=y+1;
+    XYTEXT(x, y, "   /___ /    __    __    __ /          /   "); y=y+1;
+    XYTEXT(x, y, "  /    |   /___) /   ) /   /   /   /  /    "); y=y+1;
+    XYTEXT(x, y, "_/_____|__(___ _(___(_(___/___(___/__o_____"); y=y+1;
+    XYTEXT(x, y, "                                 /         "); y=y+1;
+    XYTEXT(x, y, "                             (_ /          "); y=y+1;
+
+    if (threadTime() > 1000 and ISPRESSED(' '))
+    {
+      CLS();
+      sleep(0.5);
+      stateSet(GameState);
+    }
+    else if (ISPRESSED(27))
+    {
+      quit = true;
+    }
+
+    sleep(randfloat(0.03, 0.3));
+  }
+
+  KillAll();
+};
+
+global KillAll = function()
+{
+  // Kill all threads.
+  global player;
+  global enemies;
+  global blocks;
+
+  if (player)
+  {
+    local count = player.nextBomb;
+    for (i = 0; i < count; i=i+1)
+    {
+      threadKill(player.bombs[i].threadId);
+    }
+    threadKill(player.threadId);
+  }
+
+  if (enemies)
+  {
+    count = enemies.count;
+    for (i = 0; i < count; i=i+1)
+    {
+      threadKill(enemies.enemies[i].threadId);
+    }
+  }
+};
+
+global TitleScreenState = function()
+{
+  CATTRIB(bgColour | statusColour);
+  CLS();
+  sleep(0.3);
+  while (!quit)
+  {
+    CATTRIB(bgColour | expColours[randint(0, expColourCount)]);
+    x = 17;
+    y = 8;
+    XYTEXT(x, y, "_________________________________________"); y=y+1;
+    XYTEXT(x, y, "    ____                                 "); y=y+1;
+    XYTEXT(x, y, "    /   )                 /              "); y=y+1;
+    XYTEXT(x, y, "   /__ /     __   _  _   /__    __   )__ "); y=y+1;
+    XYTEXT(x, y, "  /    )   /   ) / /  ) /   ) /___) /   )"); y=y+1;
+    XYTEXT(x, y, "_/____/___(___/_/_/__/_(___/_(___ _/_____"); y=y+1;
+    XYTEXT(x, y, "_________________________________________"); y=y+1;
+
+    if (ISPRESSED(27))
+    {
+      global quit = true;
+      break;
+    }
+    else if (ISPRESSED(' '))
+    {
+      global gameLevel = 1;
+      stateSet(LevelState);
+    }
+    sleep(randfloat(0.03, 0.3));
+  }
+
+  KillAll();
+  exit();
+};
+
+CURSOR(0, 0); // bool visible, percentage visible
+CATTRIB(fgColour | bgColour);
+
+thread(MemThread);
+
+quit = false;
+CLS();
+
+stateSet(TitleScreenState);

+ 691 - 0
gmsrc/scripts/BomberRun.gm

@@ -0,0 +1,691 @@
+
+//-----------------------------------------------------------------------------
+// BomberRun by George Allan
+//-----------------------------------------------------------------------------
+
+global score = 0;
+global height = 23;
+global gframe = 0;
+global detec = table();
+global killStars = 0;
+global playerx = 0;
+global playery = 0;
+global fireCnt = 0;
+global fireMax = 3;
+global clearExplo = 0;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global sync = function() {
+  time = TICK();
+  wait = (1./30.) - time;
+  if(wait > 0) {
+    sleep(wait);
+  }
+  TICK();
+  global gframe = gframe + 1;
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global star = function() {
+
+     global detec;
+     global killStars;
+
+     frame = 0;
+     while (1) {
+
+           x = randint(1,79);
+           y = randint(2,height);
+           
+           // On top of a building ?
+           if (y < detec[x]-1) {
+
+             // Print Star for a while
+             t = randint(50,100);
+             for (i=0;i<t;i=i+1) {
+                 // Choose a twinkle color
+                 if (randint(0,2) == 0) {
+                   CATTRIB(CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.B_BLUE);
+                 }
+                 else {
+                   CATTRIB(CA.F_RED|CA.F_GREEN | CA.B_BLUE);
+                 }
+
+                 // Pop !
+                 if (i>t-2) {
+                   XYTEXT(x,y,",");
+                 }
+                 else {
+                   XYTEXT(x,y,".");
+                 }
+
+                 // Sync to the master frame
+                 while (frame == gframe) {
+                      yield();
+                      if (killStars) {
+                        exit();
+                      }
+                 }
+                 frame = gframe;
+             }
+  
+             // Clear Star
+             CATTRIB(CA.F_BLUE | CA.B_BLUE);
+             XYTEXT(x,y," ");
+           }
+           yield();
+           if (killStars) {
+             exit();
+           }
+     }
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global particle = function(x, y) {
+
+    global detec;
+    global clearExplo;
+
+    pcoltab = table(CA.F_RED|CA.F_INTENSITY,  CA.F_RED|CA.F_GREEN|CA.F_INTENSITY,  CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY);
+    pfrtab = table(".", "+", "*");
+
+    xmom = randfloat(-1.0,1.0);
+//    ymom = randfloat(-0.2,-2.0);
+    ymom = randfloat(-2.0,-0.2);
+
+    xpos = x.Float();
+    ypos = y.Float();
+
+    frame = -1;
+
+    time = randint(10,40);
+    from = time;
+    
+    frint = randint(0,3);
+
+    while (1) {
+
+      if (y < detec[x]) {
+         CATTRIB(CA.F_BLUE | CA.B_BLUE);
+         XYTEXT(x,y," ");
+      }
+      xpos = xpos + xmom;
+      ypos = ypos + ymom;
+      ymom = ymom + 0.15f;
+      x = xpos.Int();
+      y = ypos.Int();
+
+      // On Screen
+      if ( y >= height || y < 0 || x < 0 || x > 79) {
+         exit();
+      }
+
+      // Out of Time ?
+      time = time - 1;
+      if (time < 0 || clearExplo) {
+            exit();
+      }
+
+      if (y < detec[x]) {
+        fr = (time*3) / from;
+        CATTRIB(pcoltab[fr] | CA.B_BLUE);
+        XYTEXT(x,y,pfrtab[frint]);
+      }
+
+      // Sync to the master frame
+      while (frame == gframe) {
+           yield();
+      }
+      frame = gframe;
+    }
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global explode = function(x, y) {
+
+    global particle;
+
+    for (i=0;i<10;i=i+1) {
+        thread(particle, x, y);
+    }
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global bullet = function() {
+
+    global detec;
+    global playerx;
+    global playery;
+    global score;
+    global gframe;
+    global fireCnt;
+    global score;
+    global explode;
+
+    firex = playerx;
+    firey = playery+1;
+    fireHTG = 4;
+    frame = gframe;
+    
+    score = score - 10;
+
+    while (1) {
+
+      // Detection
+      if (firey > height-1 || fireHTG <= 0) {
+        fireCnt = fireCnt - 1;
+        exit();
+      }
+      else {
+        // If okay - display
+        CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
+        XYTEXT(firex,firey,"\31");
+      }
+
+      // Sync to the master frame
+      while (frame == gframe) {
+           yield();
+      }
+      frame = gframe;
+
+      // Clear Bullet
+      CATTRIB(CA.F_BLUE | CA.B_BLUE);
+      XYTEXT(firex,firey," ");
+
+      firey = firey + 1;
+      first = 0;
+
+      // Detection
+      if (detec[firex] < firey) {
+         score = score + 10;
+         detec[firex] = firey;
+         fireHTG = fireHTG - 1;
+         // Boom !
+         explode(firex,firey);
+      }
+    }
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+game = function() {
+
+  global detec;
+  global killStars;
+  global star;
+  global bullet;
+  global playerx;
+  global playery;
+  global fireCnt;
+  global fireMax;
+  global score;
+  global explode;
+
+  killStars = 0;
+  allgone = 0;
+
+  for (i=0;i<30;i=i+1) {
+    thread(star);
+  }
+
+  CATTRIB(CA.F_BLUE | CA.B_BLUE);
+  CLS();
+
+  coltab = table(CA.B_GREEN|CA.B_RED,  CA.B_BLUE|CA.B_RED,  CA.B_BLUE|CA.B_GREEN,  CA.B_GREEN);
+  coltabfore = table(CA.F_GREEN|CA.F_RED,  CA.F_BLUE|CA.F_RED,  CA.F_BLUE|CA.F_GREEN,  CA.F_GREEN);
+  btypes = table("\254", "\58", "\124");
+  ttypes = table("\191", "\194", "\218");
+
+  // Clear Collision
+  for (i=0;i<=150;i=i+1) {
+      detec[i] = height;
+  }
+
+  // Buildings
+  for (x=0;x<80;x=x+1) {
+
+    // Calc Height
+    rand = 10;
+    if (x < 30) {
+       rand = x / 3;
+    }
+    if (x > 50) {
+      rand = (80-x) / 3;
+    }
+    if (x < 10) {
+      rand = 0;
+    }
+    if (x > 70) {
+      rand = 0;
+    }
+    rand = randint(0,rand);
+    if (rand > 1) {
+
+      // Detection
+      detec[x] = height-rand;
+
+      // Random Building Color
+      col = randint(0,4);
+
+      // Random Top
+      if (randint(0,10) < 2) {
+        CATTRIB(coltabfore[col] | CA.B_BLUE);
+        ttype = randint(0,3);
+        XYTEXT(x,height-rand-1,ttypes[ttype]);
+      }
+
+      // The Main Building
+      btype = randint(0,3);
+      CATTRIB(coltab[col] | CA.F_BLACK);
+      for (y=height-rand;y<height;y=y+1) {
+        if (randint(0,5) < 2) {
+           CATTRIB(coltab[col] | CA.F_RED | CA.F_GREEN | CA.F_INTENSITY);
+           XYTEXT(x,y,btypes[btype]);
+           CATTRIB(coltab[col] | CA.F_BLACK);
+        }
+        else {
+          XYTEXT(x,y,btypes[btype]);
+        }
+      }
+    }
+  }
+
+  CATTRIB(CA.F_GREEN | CA.B_BLACK);
+  for (x=0;x<80;x=x+1) {
+    XYTEXT(x,height,"\178");
+  }
+
+  playerx = 2;
+  playery = 3;
+  anim = 0;
+  playermove = 0;
+  fireOkay = 0;
+  landingtimer = 0;
+
+  while(!ISPRESSED('Q')) {
+
+      // Flattened already ?
+      allgone = 1;
+      for (i=0;i<80;i=i+1) {
+          if (detec[i] != height) {
+              allgone = 0;
+          }
+      }
+
+      // Add Loads of stars
+      if (ISPRESSED('S')) {
+          for (i=0;i<30;i=i+1) {
+            thread(star);
+          }
+      }
+
+      // Score
+      CATTRIB(CA.F_BLUE | CA.F_GREEN | CA.F_INTENSITY | CA.B_BLUE);
+      XY(35,1);
+      print("Score ", score);
+
+      // Show Memeory Usage
+      if (ISPRESSED('M')) {
+        XY(1,2);
+        print("Memory ", sysGetMemoryUsage());
+      }
+
+      if (ISPRESSED(' ')) {
+            if (fireCnt < fireMax && (fireOkay || fireMax == 50)) {
+                 thread(bullet);
+                 fireCnt = fireCnt + 1;
+            }
+            fireOkay = 0;
+      }
+      else {
+           fireOkay = 1;
+      }
+
+      // Clear Player
+      CATTRIB(CA.F_BLUE | CA.B_BLUE);
+      XYTEXT(playerx-2,playery,"    ");
+
+        // Auto Pilot landing
+      if (allgone && playery < height-1) {
+         landingtimer = landingtimer + 1;
+         if (landingtimer > 10) {
+           landingtimer = 0;
+           playery = playery + 1;
+         }
+      }
+
+      // Move and reposition if reach the side of the screen
+      playerx = playerx+1;
+      if (playerx > 79) {
+        playerx = 2;
+        playery = playery + 1;
+      }
+      
+      // Not too low
+      if (playery > height-1) {
+         playery = height-1;
+      }      
+
+      // Print at new position
+      CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
+      XYTEXT(playerx-2,playery,"\200");
+      XYTEXT(playerx-1,playery,"\205");
+      XYTEXT(playerx,playery,"\254");
+
+      // Landed !!!
+      if (playerx == 60 && playery == height-1) {
+         for (i=0;i<60;i=i+1) {
+             sync();
+         }
+         // Completion bonus - based on fireMax
+         if (score != 50) {
+            score = score + (6-fireMax) * 1000;
+         }
+         niceone();
+         return;
+      }
+
+      // Hit a building (ignores arieals)
+      if (detec[playerx+1] <= playery) {
+         sync();
+
+         y0 = playery;
+         y1 = playery;
+         y2 = playery;
+         x = playerx;
+         
+         // Dramatic Pause !
+         for (i=0;i<20;i=i+1) {
+             XYTEXT(x-2,y0,"\200");
+             XYTEXT(x-1,y1,"\205");
+             XYTEXT(x-0,y2,"\254");
+             sync();
+         }
+
+         done = 0;
+         while (!done) {
+               done = 1;
+
+               // Clear
+               CATTRIB(CA.F_BLUE | CA.B_BLUE);
+               XYTEXT(x-2,y0," ");
+               XYTEXT(x-1,y1," ");
+               XYTEXT(x-0,y2," ");
+
+               // Move
+               if (y0 < detec[x-2]-1) {
+                  y0 = y0 + 1;
+                  done = 0;
+               }
+               if (y1 < detec[x-1]-1) {
+                  y1 = y1 + 1;
+                  done = 0;
+               }
+               if (y2 < detec[x-0]-1) {
+                  y2 = y2 + 1;
+                  done = 0;
+               }
+
+               // Print
+               CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY | CA.B_BLUE);
+               XYTEXT(x-2,y0,"\200");
+               XYTEXT(x-1,y1,"\205");
+               XYTEXT(x-0,y2,"\254");
+               sync();
+          }
+          
+          for (i=0;i<60;i=i+1) {
+              XYTEXT(x-2,y0,"\200");
+              XYTEXT(x-1,y1,"\205");
+              XYTEXT(x-0,y2,"\254");
+              sync();
+          }
+          gameover();
+          return;
+      }
+      else {
+        if (anim == 1) {
+          XYTEXT(playerx+1,playery,"\217");
+          anim = 0;
+        }
+        else {
+          XYTEXT(playerx+1,playery,"\191");
+          anim = 1;
+        }
+      }
+      
+      // Next Frame
+      sync();
+  }
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+titlescreen = function() {
+
+  global killStars = 1;
+  global fireMax = 0;
+  global score = 0;
+  global detec;
+  global clearExplo = 0;
+  
+  for (i=0;i<81;i=i+1) {
+     detec[i] = height;
+  }
+
+  CATTRIB(0);
+  CLS();
+
+  CATTRIB(CA.F_RED);
+  CURSOR(0,0);
+
+  maxtab = 12;
+  tabindex = 0;
+  tctab = table(  CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY,
+                  CA.F_BLUE,
+                  CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_BLUE | CA.F_INTENSITY,
+                  CA.F_BLUE | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_BLUE|CA.F_GREEN,
+                  CA.F_BLUE,
+                  CA.F_BLUE | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_GREEN | CA.F_INTENSITY  );
+
+  maxtab2 = 8;
+  tabindex2 = 0;
+  tctab2 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_GREEN | CA.F_INTENSITY,
+                  CA.F_RED | CA.F_INTENSITY,
+                  CA.F_RED,
+                  CA.F_RED | CA.F_INTENSITY,
+                  CA.F_RED|CA.F_GREEN | CA.F_INTENSITY  );
+
+
+  // Flush Keyboard !?
+  ISPRESSED(' ');
+  ISPRESSED(' ');
+  ISPRESSED(' ');
+
+  while(!fireMax) {
+
+    // Start in which mode
+    if (ISPRESSED('1')) { fireMax = 1; }
+    if (ISPRESSED('2')) { fireMax = 2; }
+    if (ISPRESSED('3')) { fireMax = 3; }
+    if (ISPRESSED('4')) { fireMax = 4; }
+    if (ISPRESSED('5')) { fireMax = 5; }
+    if (ISPRESSED(' ')) { fireMax = 3; }
+    if (ISPRESSED('C')) { fireMax = 50; }
+    if (ISPRESSED(27))  { threadKillAll(true); } // Kill all threads when ESC is pressed
+
+    // Title Color
+    tabindex = tabindex + 1;
+    if (tabindex >= maxtab) {
+      tabindex = 0;
+    }
+    CATTRIB(tctab[tabindex]);
+
+    CLS();
+    XYTEXT(4,2,`   ________                 ______             ________      `);
+    XYTEXT(4,3,`   ___  __ )____________ ______  /________________  __ \___  ________ `);
+    XYTEXT(4,4,`   __  __  |  __ \_  __ ``__ \_  __ \  _ \_  ___/_  /_/ /  / / /_  __ \ `);
+    XYTEXT(4,5,`   _  /_/ // /_/ /  / / / / /  /_/ /  __/  /   _  _, _// /_/ /_  / / / `);
+    XYTEXT(4,6,`   /_____/ \____//_/ /_/ /_//_.___/\___//_/    /_/ |_| \__,_/ /_/ /_/ `);
+
+    // Text Color
+    tabindex2 = tabindex2 + 1;
+    if (tabindex2 >= maxtab2) {
+      tabindex2 = 0;
+    }
+    CATTRIB(tctab2[tabindex2]);
+
+    XYTEXT(12,8, "            Written in GameMonkey by Happy ");
+
+    XYTEXT(12,11, "     Flatten the city by dropping bombs (spacebar)");
+    XYTEXT(12,12, "        Destory all buildings to land saftley");
+    XYTEXT(12,13, "   Score 10 points for each builing piece destoryed");
+    XYTEXT(12,14, "        Loose 10 points for each bomb dropped");
+    XYTEXT(12,15, "     Landing bonus is based on difficulty setting");
+
+    XYTEXT(12,18, "        Press 'Space' to play at standard level");
+    XYTEXT(12,19, "    Press 1-5 to choose difficulty (number of bombs)");
+    XYTEXT(12,20, "Press 'C' to play the cheat version (hold the spacebar!)");
+    XYTEXT(12,21, "           Press 'Q' to to quit during play");    
+    XYTEXT(12,22, "             Press ESC to exit game now");    
+
+    x = randint(2,78);
+    y = randint(2,height-2);
+    explode(x,y);
+
+    sync();
+  }
+  
+  clearExplo = 1;
+     sync();
+     sync();
+     clearExplo = 0;
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global niceone = function() {
+
+  global killStars = 0;
+  global detec;
+  global score;
+  global clearExplo = 0;
+  global fireMax;
+
+  for (i=0;i<500;i=i+1) {
+    thread(star);
+  }
+
+  for (i=0;i<200;i=i+1) {
+
+    CATTRIB(CA.F_RED|CA.F_GREEN|CA.F_BLUE|CA.F_INTENSITY|CA.B_BLUE);
+    CLS();
+
+    XYTEXT(1,7,`   .__   __. __   ______ _______    ______  .__   __. _______   __  __  __   `);
+    XYTEXT(1,8,`   |  \ |  ||  | /      |   ____|  /  __  \ |  \ |  ||   ____| |  ||  ||  | `);
+    XYTEXT(1,9,`   |   \|  ||  ||  ,----'  |__    |  |  |  ||   \|  ||  |__    |  ||  ||  | `);
+    XYTEXT(1,10,`   |  . ``  ||  ||  |    |   __|   |  |  |  ||  . ``  ||   __|   |  ||  ||  | `);
+    XYTEXT(1,11,`   |  |\   ||  ||  ``----.  |____  |  ``--'  ||  |\   ||  |____  |__||__||__| `);
+    XYTEXT(1,12,`   |__| \__||__| \______|_______|  \______/ |__| \__||_______| (__)(__)(__) `);
+    XY(1,15);
+    if (score < 0) {
+         print("                    You score is to crap too mention !");
+    }
+    else if (score < 1500) {
+        print("                       Your score is a average", score);
+    }
+    else if (score < 3000) {
+        print("                     Your score is a respectable", score);
+    }
+    else {
+        print("                      Your score is an awesome", score);
+    }
+    if (fireMax == 50) {
+         XY(1,17);
+         print("                ---  Now try again without cheating ;)  ---");
+    }
+
+    x = randint(2,78);
+    y = randint(2,height-2);
+    explode(x,y);
+
+    sync();
+  }
+
+
+};
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+global gameover = function() {
+
+  global killStars = 1;
+  global detec;
+  global score;
+  global clearExplo = 1;
+ 
+  CATTRIB(CA.F_RED|CA.F_INTENSITY);
+  for (i=height+15;i>-15;i=i-1) {
+       CLS();
+       y = i;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`              _______     ___     .___  ___. _______ `); }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`             /  _____|   /   \    |   \/   ||   ____| `);   }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`            |  |  __    /  ^  \   |  \  /  ||  |__ `);    }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`            |  | |_ |  /  /_\  \  |  |\/|  ||   __| `);    }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`            |  |__| | /  _____  \ |  |  |  ||  |____ `);   }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`             \______|/__/    _\__\|__|__|__||_______| `);  }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`            /  __  \ \   \  /   /|   ____|   _  \ `);   }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`           |  |  |  | \   \/   / |  |__  |  |_)  | `);   }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`           |  |  |  |  \      /  |   __| |      / `);  }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`           |  ``--'  |   \    /   |  |____|  |\  \----. `);  }
+       y = y + 1;
+       if (y >= 0 && y <= height) {       XYTEXT(8,y,`            \______/     \__/    |_______| _| ``._____| `);  }
+       y = y + 1;
+       sync();
+   }
+};
+
+//-----------------------------------------------------------------------------
+// and finaly - the main loop !
+//-----------------------------------------------------------------------------
+
+  while (1) {
+    titlescreen();
+    game();
+  }
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+

+ 33 - 0
gmsrc/scripts/ScriptDescriptions.txt

@@ -0,0 +1,33 @@
+Descriptions of some of the example scripts
+
+-----------------------------------------------------------
+
+Snake.gm
+Bomber.gm
+BomberRun.gm
+MineSweeper.gm
+
+Were written in a few hours by programmers testing an early 
+version of GameMonkey.  They were given the script 
+reference, a copy of gme.exe and a syntax highlighter file.
+
+-----------------------------------------------------------
+
+gmDoc.gm (and related files)
+
+Is a utility to scan through .cpp files, find comments 
+relating to GM script bindings and generate a .html file 
+documenting those functions.
+
+-----------------------------------------------------------
+
+benchmark.gm
+
+Is a set of benchmarks used by Doug Bagley at 
+http://www.bagley.org/~doug/shootout/
+to compare the performance of several languages.
+( Note that simply executing these scripts with 
+different languages does not provide a true indication 
+of comparative performance. )
+
+-----------------------------------------------------------

+ 230 - 0
gmsrc/scripts/benchmarks.gm

@@ -0,0 +1,230 @@
+// Set of benchmarks
+
+sysSetDesiredMemoryUsageHard(16 * 1024, 1);
+sysSetDesiredMemoryUsageSoft(sysGetDesiredMemoryUsageHard());
+
+//
+//
+// ACKERMAN
+//
+//
+
+print("*** ACKERMAN ***");
+
+global Ack = function(M, N)
+{
+  if (M == 0)
+  {
+    N=N+1;
+    return(N);
+  }
+	
+  if (N == 0)
+  {
+    M=M-1;
+    return(Ack(M, 1));
+  }
+  N=N-1;
+  return (Ack(M-1, Ack(M, N)));
+};
+
+NUM = 8;
+TICK(); //GD Used to be clock() what was that matt?
+print(Ack(3,NUM));
+print("time = ", TICK());
+
+//
+//
+// FIB
+//
+//
+
+print("*** FIB ***");
+
+global fib = function(n)
+{
+    if (n < 2) { return(1); }
+    return fib(n-2) + fib(n-1);
+};
+
+N = 32;
+TICK();
+print(fib(N));
+print("time = ", TICK());
+
+//
+//
+// MATRIX
+//
+//
+
+print("*** MATRIX ***");
+
+local n = 300;
+local size = 30;
+
+mkmatrix = function(rows, cols)
+{
+  count = 1;
+  mx = table();
+
+  for(i=0; i < rows; i=i+1)
+  {
+    row = table();
+    for(j = 0; j < cols; j=j+1)
+    {
+      row[j] = count;
+      count=count+1;
+    }
+    mx[i] = row;
+  }
+  return mx;
+};
+
+mmult = function(rows, cols, m1, m2)
+{
+  m3 = table();
+
+  for(i = 0; i < rows; i=i+1)
+  {
+    m3[i] = table();
+    m1_i = m1[i];
+    for(j = 0; j < cols; j=j+1)
+    {
+      rowj = 0;
+      for(k = 0; k < cols; k=k+1)
+      {
+        rowj = rowj + m1_i[k] * m2[k][j];
+      }
+      m3[i][j] = rowj;
+    }
+  }
+
+  return m3;
+};
+
+TICK();
+m1 = mkmatrix(size, size);
+m2 = mkmatrix(size, size);
+
+for(i = 0; i < n; i=i+1)
+{
+  mm = mmult(size, size, m1, m2);
+}
+
+t = TICK();
+
+print(mm[0][0], mm[2][3], mm[3][2], mm[4][4]);
+print("time = ", t);
+
+//
+//
+// HASH
+//
+//
+
+print("*** HASH ***");
+
+local n = 80000;
+TICK();
+
+X=table();
+for(i=1; i <= n; i=i+1)
+{
+  //print(format("%x", i), i);
+  X[format("%x", i)] = i;
+}
+
+c = 0;
+
+for(i=n; i>=1; i=i-1)
+{
+  if(X[i+""])
+  {
+    c=c+1;
+  }
+}
+
+print(c);
+print("time = ", TICK());
+
+//
+//
+// HEAPSORT
+//
+//
+
+print("*** HEAPSORT ***");
+
+global IM = 139968.;
+global IA =   3877.;
+global IC =  29573.;
+global LAST = 42.;
+
+gen_random = function(max)
+{
+    global LAST = (LAST * IA + IC) % IM;
+    return ((max * LAST) / IM);
+};
+
+heapsort = function(n, ra)
+{
+  l = n/2 + 1;
+  ir = n;
+
+  for(;;)
+  {
+    if(l > 1)
+    {
+      l=l-1;
+      rra = ra[l];
+    }
+    else
+    {
+      rra = ra[ir];
+      ra[ir] = ra[1];
+      ir=ir-1;
+      if(ir == 1)
+      {
+        ra[1] = rra;
+        return;
+      }
+    }
+
+    i = l;
+    j = l * 2;
+    while(j <= ir)
+    {
+      if(j < ir and ra[j] < ra[j+1])
+      {
+        j=j+1;
+      }
+      if(rra < ra[j])
+      {
+        ra[i] = ra[j];
+        i = j;
+        j = j + i;
+      }
+      else
+      {
+        j = ir + 1;
+      }
+    }
+
+    ra[i] = rra;
+  }
+};
+
+TICK();
+
+local ary = table();
+local N = 80000;
+
+for(i = 0; i < N; i=i+1)
+{
+  ary[i] = gen_random(1.0);
+}
+
+heapsort(N, ary);
+print(ary[N-1]);
+print("time = ", TICK());

+ 9 - 0
gmsrc/scripts/gmDoc/gmdoc.cmd

@@ -0,0 +1,9 @@
+@echo off
+
+rem Update GM Script function documentation
+
+..\..\bin\gme.exe gmdoc.gm gmdoc.txt gmdoc.html
+
+copy gmdoc.html ..\..\doc
+
+rem Done.

+ 333 - 0
gmsrc/scripts/gmDoc/gmdoc.gm

@@ -0,0 +1,333 @@
+//debug();
+
+//
+// This program generates html documentation for script functions.
+// It merely converts comments with a certain syntax into documentation.
+// Example:
+//
+//  /*gm
+//    \lib Library Name
+//  */
+//
+//  /*gm
+//    \function Function name
+//    \brief Brief description of function
+//    \param Parameter description, you should describe type information
+//    \return Return value description
+//  */
+//
+
+
+//
+// \function CreateGMDocumenter
+//
+global CreateGMDocumenter = function()
+{
+  documenter = table
+  (
+    // \function ExtractGmCommentStrings
+    // \param fp is source file handle
+    // \param commentHandler is a function taking 2 params, comment string and context
+    // \param context is passed to comment handler
+    ExtractCommentStrings = function(fp, commentHandler, context)
+    {
+      openGmComment = "/*gm";
+      openGmCommentLen = openGmComment.Length();
+      closeGmComment = "*/";
+      closeGmCommentLen = closeGmComment.Length();
+
+      line = fp.ReadLine();
+      while(line)
+      {
+        pos = line.Find(openGmComment);
+        while(pos >= 0)
+        {
+          pos = pos + openGmCommentLen;
+          line = line.Right(line.Length() - pos);
+          comment = "";
+
+          // eat up lines untill end of comment
+          pos = line.Find(closeGmComment);
+          while(pos < 0)
+          {
+            comment = comment + line;
+            line = fp.ReadLine();
+
+            if(line == null)
+            {
+              pos = 0;
+            }
+            else
+            {
+              pos = line.Find(closeGmComment);
+            }
+          }
+
+          if(line)
+          {
+            comment = comment + line.Left(pos);
+            line = line.Right(line.Length() - (pos + closeGmCommentLen));
+            pos = line.Find(openGmComment);
+          }
+          else
+          {
+            pos = -1;
+          }
+
+          if(comment.Length() > 0)
+          {
+            // process comment string
+            comment = comment.ReplaceCharsInSet(' ', "\r\n\v");
+            commentHandler(comment, context);
+          }
+        }
+        line = fp.ReadLine();
+      }
+    },
+
+    // \function CommentHandler
+    // \param comment is an incoming comment string
+    // \param context is a table with a m_sections table where each m_section
+    //        is a function taking the section string and the context
+    //        context also has a BeginComment() call
+    CommentHandler = function(comment, context)
+    {
+      if(comment and comment.Length())
+      {
+        commentSet = table();
+
+        foreach(section and sectionHandler in context.m_sections)
+        {
+          search = comment;
+          sectionStart = search.Find(section);
+          sectionLength = section.Length();
+          offset = 0;
+
+          while(sectionStart >= 0)
+          {
+            commentSet[offset + sectionStart] = section;
+            offset = offset + sectionLength;
+            search = search.Right(search.Length() - sectionLength);
+            sectionStart = search.Find(section);
+          }
+        }
+
+        // commentSet is now a table containing the start positions of each comment bit.
+        length = comment.Length();
+        local j;
+
+        for(i = 0; i < length; i = i + 1)
+        {
+          if(commentSet[i])
+          {
+            // find the next comment...
+            for(j = i + 1; j < length; j = j + 1)
+            {
+              if(commentSet[j] or j == (length - 1))
+              {
+                section = commentSet[i];
+                sectionLength = section.Length();
+                first = i + sectionLength;
+                count = (j - i) - sectionLength;
+                subComment = comment.Mid(first, count);
+                context.m_sections[section](subComment, context);
+                break;
+              }
+            }
+          }
+        }
+      }
+    },
+
+    m_files = table(),
+
+    // \function AddFile will add a file to be documented
+    AddFile = function(filename)
+    {
+      .m_files[tableCount(.m_files)] = filename;
+    },
+
+    // \function CreateDocumentation will create documentation for all added files
+    // \param path is the output path for the resulting .html docco
+    CreateDocumentation = function(filename)
+    {
+      context = table();
+      context.m_sections = table();
+      context.m_htmlOut = system.File();
+      context.m_xmlOut = system.File();
+      context.m_lib = "";
+
+      context.m_sections[`\lib`] = function(comment, context)
+      {
+        comment = comment.TrimLeft().TrimRight();
+        // trim parenthesis
+
+        context.Write("<BR><HR>\n");
+        context.Heading(1, comment);
+        context.Write("<HR><BR>\n");
+        context.m_lib = comment;
+      };
+      context.m_sections[`\function`] = function(comment, context)
+      {
+        comment = comment.TrimLeft().TrimRight();
+        // trim parenthesis
+
+        context.Write("<HR>\n");
+        context.Write(format(`<a name="%s::%s">`,context.m_lib,comment));
+        context.Heading(3, comment);
+        context.Write(`</a>`);
+        context.XMLWrite(format(`<function name="%s::%s"/>`, context.m_lib, comment));
+      };
+      context.m_sections[`\brief`] = function(comment, context)
+      {
+        context.Write(format("<B><EM>Brief:</B></EM>%s<BR>", comment));
+      };
+      context.m_sections[`\param`] = function(comment, context)
+      {
+        context.Write(format("<B><EM>Param:</B></EM>%s<BR>", comment));
+      };
+      context.m_sections[`\return`] = function(comment, context)
+      {
+        context.Write(format("<B><EM>Return:</B></EM>%s<BR>", comment));
+      };
+      context.m_sections[`\sa`] = function(comment, context)
+      {
+        context.Write(format("<B><EM>See Also:</B></EM>%s<BR>", comment));
+      };
+      context.Heading = function(number, string)
+      {
+        .m_htmlOut.WriteString(format("<H%d>%s</H%d>\n", number, string, number));
+      };
+      context.m_sections[`\this`] = function(comment, context)
+      {
+        context.Write(format("<B><EM>This:</B></EM>%s<BR>", comment));
+      };
+      context.Paragraph = function(string)
+      {
+        .m_htmlOut.WriteString(format("<P>%s</P>\n", string));
+      };
+      context.Write = function(string)
+      {
+        .m_htmlOut.WriteString(string);
+      };
+      context.XMLWrite = function(string)
+      {
+        if(.m_xmlOut)
+        {
+          .m_xmlOut.WriteString(string);
+        }
+      };
+
+
+      xmlFilename = filename.SetExtension("xml");
+      if(!context.m_xmlOut.Open(xmlFilename, 0))
+      {
+        print("** ERROR: Failed to open XML output file '",xmlFilename,"' ! **");
+        context.m_xmlOut = null;
+      }
+
+      context.XMLWrite(`<?xml version="1.0" encoding="utf-8"?>`);
+      context.XMLWrite("<functions>");
+
+      //
+      if(context.m_htmlOut.Open(filename, 0))
+      {
+        // write html head
+        context.Write("<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY>");
+        foreach(file in .m_files)
+        {
+          fp = system.File();
+          if(fp.Open(file))
+          {
+            print(`Documenting`, file, `...`);
+
+            .ExtractCommentStrings(fp,.CommentHandler,context);
+
+            fp.Close();
+          }
+        }
+
+        context.XMLWrite("</functions>");
+        if(context.m_xmlOut)
+
+{
+        context.m_xmlOut.Close();
+}
+
+        context.Write("</BODY></HTML>");
+        context.m_htmlOut.Close();
+      }
+    }
+  );
+
+  return documenter;
+};
+
+/*
+ *
+ *  Entry Point
+ *
+*/
+
+print("Starting GMDoc...");
+
+inFile  = arg[0]; // directory/file listings of 'to be documented' files
+outFile = arg[1]; // ouput help files
+
+documenter = CreateGMDocumenter();
+
+dirsFile = system.File();
+
+if(!dirsFile.Open(inFile, 1))
+{
+  print("** Failed to open input file! **");
+}
+else
+{
+  nextLine = dirsFile.ReadLine();
+
+  while(nextLine)
+  {
+    nextLine = nextLine.TrimRight(); // carriage return syndrome
+
+    path = nextLine;
+    filename = nextLine.GetFilename();
+    pos = filename.Find(".cpp",filename.Length() - 5);
+
+    if(pos >= 0) // doc file
+    {
+      if(system.FileExists(path))
+      {
+        documenter.AddFile(path);
+      }
+    }
+    else // doc directory
+    {
+      handle = system.FileFindFirst(path ^ `\*.*`);
+      while(handle)
+      {
+        extension = handle.filename.GetExtension().Lower();
+        print(extension);
+        if(extension == `cpp` or extension == `gm`)
+        {
+          if(system.FileExists(path ^ handle.filename))
+          {
+            documenter.AddFile(path ^ handle.filename);
+          }
+        }
+        handle = system.FileFindNext(handle);
+      }
+    }
+
+    nextLine = dirsFile.ReadLine(); // read next line
+  }
+}
+
+dirsFile.Close();
+
+documenter.CreateDocumentation(outFile);
+
+print(`Done.`);
+
+
+

+ 2 - 0
gmsrc/scripts/gmDoc/gmdoc.txt

@@ -0,0 +1,2 @@
+..\..\src\binds
+..\..\src\gm

+ 608 - 0
gmsrc/scripts/minesweeper.gm

@@ -0,0 +1,608 @@
+//
+// MineSweeper By Andriy Doroshchuk
+//
+
+global cursorOverClosed = CA.F_RED | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
+global cursorOverOpened = CA.F_RED | CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
+global frameColor  = CA.F_RED | CA.F_GREEN | CA.F_BLUE | CA.B_BLACK | CA.TRAILING_BYTE;
+global closedColor = CA.F_BLUE | CA.B_BLACK | CA.TRAILING_BYTE;
+global openedColor = CA.F_BLUE | CA.B_BLUE | CA.TRAILING_BYTE;
+global numberColor = CA.F_BLUE | CA.F_GREEN| CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
+global markColor   = CA.F_BLUE | CA.F_RED  | CA.F_INTENSITY | CA.B_BLUE | CA.TRAILING_BYTE;
+global mineColor   = CA.F_GREEN| CA.F_RED  | CA.B_BLUE | CA.TRAILING_BYTE;
+global helpColor   = CA.F_GREEN| CA.F_RED  | CA.F_BLUE |CA.B_BLUE | CA.TRAILING_BYTE;
+global winColor    = CA.F_GREEN| CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
+global looseColor  = CA.F_RED  | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE;
+
+// wait till key will be released
+global WaitForKey = function(key)
+{
+  for (;ISPRESSED(key);){}
+};
+
+// print debug message
+global DebugText = function(text)
+{
+  CATTRIB(frameColor);
+  XYTEXT(0, 0, text);
+};
+
+
+game = table(
+  x         = 15,
+  y         = 0,
+  lx        = 30,
+  ly        = 15,
+  mineCurr  = 0,
+  mineMax   = 10,
+  currLevel = 2,
+  cX        = 0,
+  cY        = 0,
+
+  field     = table(),
+
+  // index in the field
+  Index = function(x, y)
+  {
+    return (y*.lx+x);
+  },
+
+  // draw field frame
+  DrawFrame = function()
+  {
+    i;
+
+    // set color
+    CATTRIB(frameColor);
+
+    // borders
+    for (i=.x+1; i<.x+.lx+1; i=i+1)
+    {
+      XYTEXT(i, .y,       "-");
+      XYTEXT(i, .y+.ly+1, "-");
+    }
+    for (i=.y+1; i<.y+.ly+1; i=i+1)
+    {
+      XYTEXT(.x,       i, "|");
+      XYTEXT(.x+.lx+1, i, "|");
+    }
+
+    // corners
+    XYTEXT(.x,       .y,       "\1");
+    XYTEXT(.x+.lx+1, .y,       "\1");
+    XYTEXT(.x,       .y+.ly+1, "\1");
+    XYTEXT(.x+.lx+1, .y+.ly+1, "\1");
+
+    // count
+    XYTEXT(.x+3, .y, "[" + .mineCurr.String() + ":" + .mineMax.String() + "]");
+  },
+
+  // field cell drawing
+  DrawCell = function(x, y, cursor)
+  {
+    index = .Index(x, y);
+    // debug cell = .field[index].String();
+    cell = " ";
+
+    // closed cell
+    if (.field[index] < 10)
+    {
+      if (cursor)
+      {
+        CATTRIB(cursorOverClosed);
+        cell = "W";
+      }
+      else
+      {
+        CATTRIB(closedColor);
+      }
+    }
+    // opened cell
+    else if (.field[index] < 20)
+    {
+      if (.field[index] == 10)
+      {
+        if (cursor)
+        {
+          CATTRIB(cursorOverOpened);
+          cell = "W";
+        }
+        else
+        {
+          CATTRIB(openedColor);
+        }
+      }
+      else if (.field[index] < 19)
+      {
+        if (cursor)
+        {
+          CATTRIB(cursorOverOpened);
+        }
+        else
+        {
+          CATTRIB(numberColor);
+        }
+        val = .field[index] % 10;
+        cell = val.String();
+      }
+      else
+      {
+        CATTRIB(mineColor);
+        cell = "@";
+      }
+    }
+    // intended mine
+    else
+    {
+      if (cursor)
+      {
+        CATTRIB(cursorOverOpened);
+      }
+      else
+      {
+        CATTRIB(markColor);
+      }
+      cell = "?";
+    }
+
+    XYTEXT(.x+x+1, .y+y+1, cell);
+  },
+
+  // field cell drawing
+  DrawCurrCell = function(cursor)
+  {
+    .DrawCell(.cX, .cY, cursor);
+  },
+
+  // field drawing
+  DrawField = function()
+  {
+    i; j;
+
+    .DrawFrame();
+    for (i=0; i<.lx; i=i+1)
+    {
+      for (j=0; j<.ly; j=j+1)
+      {
+        .DrawCell(i, j, 0);
+      }
+    }
+  },
+
+  // help string
+  HelpString = function(msg, line, mode)
+  {
+    if (mode == 1)
+    {
+      CATTRIB(CA.F_BLUE | CA.F_GREEN | CA.F_INTENSITY | CA.B_BLACK | CA.TRAILING_BYTE);
+    }
+    else if (mode == 2)
+    {
+      CATTRIB(CA.F_RED | CA.B_BLACK | CA.TRAILING_BYTE);
+    }
+    else
+    {
+      CATTRIB(CA.F_GREEN | CA.B_BLACK | CA.TRAILING_BYTE);
+    }
+
+
+    XYTEXT(0, 25-line, "                                                                               ");
+    XYTEXT((80-msg.Length())/2, 25-line, msg);
+  },
+
+  // Initialise field
+  InitField = function(level)
+  {
+    i; j;
+    t1;t2;
+    count; position; index;
+
+    // clear screen
+    CATTRIB(frameColor);
+    CLS();
+
+    // set current level
+    .currLevel = level;
+
+    // field extents
+    if (level == 1)
+    {
+      .lx      = 10;
+      .ly      = 10;
+      .mineMax = 8;
+    }
+    else if (level == 2)
+    {
+      .lx      = 18;
+      .ly      = 15;
+      .mineMax = 35;
+    }
+    else
+    {
+      .lx      = 30;
+      .ly      = 15;
+      .mineMax = 90;
+    }
+    .x = (80-.lx)/2;
+    .mineCurr = 0;
+
+    // init field contents
+    max = .lx*.ly;
+    for (i=0; i<max; i=i+1)
+    {
+     .field[i] = 0;
+    }
+
+    // put mines on the field
+    for (count=1; count<=.mineMax; count=count+1)
+    {
+      position = randint(0, .lx*.ly+1-count);
+      for (i=0; i<.lx; i=i+1)
+      {
+        for (j=0; j<.ly; j=j+1)
+        {
+          index = .Index(i, j);
+          if (.field[index] == 0)
+          {
+            if (position == 0)
+            {
+              .field[index] = 9;
+              i = .lx;
+              j = .ly;
+            }
+            else
+            {
+              position = position - 1;
+            }
+          }
+        }
+      }
+    }
+
+    // calculate mines environment
+    for (i=0; i<.lx; i=i+1)
+    {
+      for (j=0; j<.ly; j=j+1)
+      {
+        if (.field[.Index(i, j)] == 9)
+        {
+          for (t1=i-1; t1<i+2; t1=t1+1)
+          {
+            for (t2=j-1; t2<j+2; t2=t2+1)
+            {
+              if ((t1!=i || t2!=j)
+              &&  (t1>=0) && (t1<.lx)
+              &&  (t2>=0) && (t2<.ly))
+              {
+                index = .Index(t1, t2);
+                if (.field[index] < 9)
+                {
+                  .field[index] = .field[index] + 1;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // draw field frame
+    .DrawFrame();
+
+    // set current cell
+    .cX=.lx/2;
+    .cY=.ly/2;
+    .DrawField();
+    .DrawCell(.cX, .cY, 1);
+
+    .HelpString("", 4, 0);
+    .HelpString("F2 - beginner. F3 - intermediate. F4 - expert", 2, 0);
+    .HelpString("Enter - open. Space - mark as a mine. Backspace - open around.", 1, 0);
+  },
+
+  // restart current game
+  RestartField = function()
+  {
+    .InitField(.currLevel);
+  },
+
+  // restart game after finishing
+  Restart = function(win)
+  {
+    .HelpString("", 2, 0);
+    .HelpString("", 1, 0);
+
+    if (win)
+    {
+      .HelpString("Congratulations, you won! Press F1 to continue.", 4, 1);
+      for (; !ISPRESSED(112);){}
+    }
+    else
+    {
+      .HelpString("Sorry, you are dead! Press F1 to continue.", 4, 2);
+      .DrawField();
+    }
+    for (; !ISPRESSED(112);){}
+
+    .RestartField();
+  },
+
+  // field opening when open an empty cell
+  CheckCell = function(x, y)
+  {
+    i; j;
+    index = .Index(x, y);
+
+    if (.field[index] == 0)
+    {
+      .field[index] = 10;
+
+      // check surrounding
+      for (i=x-1; i<x+2; i=i+1)
+      {
+        for (j=y-1; j<y+2; j=j+1)
+        {
+          if  ((i!=x || j!=y)
+          &&   (i>=0) && (i<.lx)
+          &&   (j>=0) && (j<.ly))
+          {
+            .CheckCell(i, j);
+          }
+        }
+      }
+    }
+    else
+    {
+      if (.field[index] < 9)
+      {
+        .field[index] = .field[index] + 10;
+      }
+    }
+
+    .DrawCell(x, y, 0);
+  },
+
+  // check win condition
+  CheckWin = function()
+  {
+    i; j; index;
+    count = 0;
+    for (i=0; i<.lx; i=i+1)
+    {
+      for (j=0; j<.ly; j=j+1)
+      {
+        index = .Index(i, j);
+        if (.field[index] < 10)
+        {
+          count = 0;
+          i = .lx;
+          j = .ly;
+        }
+        else if (.field[index] >= 20)
+        {
+          count = count + 1;
+        }
+      }
+    }
+
+    if (count == .mineMax)
+    {
+      .DrawCell(.cX, .cY, 0);
+      .Restart(1);
+    }
+    else
+    {
+      .DrawCell(.cX, .cY, 1);
+    }
+  },
+
+  // open field cell
+  OpenCell = function(x, y, check)
+  {
+    i; j;
+    index = .Index(x, y);
+    if (!check)
+    {
+      if (.field[index] >= 20)
+      {
+        return;
+      }
+    }
+
+    // check the field
+    if (check && (.field[index] % 10) == 9)
+    {
+      for (i=0; i<.lx; i=i+1)
+      {
+        for (j=0; j<.ly; j=j+1)
+        {
+          index = .Index(i, j);
+          .field[index] = (.field[index] % 10) + 10;
+        }
+      }
+
+      .Restart(0);
+    }
+    else
+    {
+      .CheckCell(x, y);
+      .CheckWin();
+    }
+  },
+
+  // open cells around open mine
+  HelperCell = function()
+  {
+    i; j;
+    index = .Index(.cX, .cY);
+
+    // perform the action only if the cell is already open
+    if (.field[index]>=10 && .field[index]<20)
+    {
+      // calculate mines around this cell
+      mines = 0;
+      for (i=.cX-1; i<.cX+2; i=i+1)
+      {
+        for (j=.cY-1; j<.cY+2; j=j+1)
+        {
+          if  ((i!=.cX || j!=.cY)
+          &&   (i>=0) && (i<.lx)
+          &&   (j>=0) && (j<.ly))
+          {
+            index = .Index(i, j);
+            if (.field[index] >= 20)
+            {
+              mines = mines + 1;
+            }
+          }
+        }
+      }
+
+      // check that we can open
+      index = .Index(.cX, .cY);
+      if (mines == (.field[index] % 10))
+      {
+        for (i=.cX-1; i<.cX+2; i=i+1)
+        {
+          for (j=.cY-1; j<.cY+2; j=j+1)
+          {
+            if  ((i!=.cX || j!=.cY)
+            &&   (i>=0) && (i<.lx)
+            &&   (j>=0) && (j<.ly))
+            {
+              .OpenCell(i, j, 0);
+            }
+          }
+        }
+      }
+    }
+  },
+
+  // move cursor
+  Move = function(dir)
+  {
+    .DrawCurrCell(0);
+    if (dir == 1)
+    {
+      if (.cY > 0)
+      {
+        .cY = .cY - 1;
+      }
+    }
+    else if (dir == 3)
+    {
+      if (.cY<.ly-1)
+      {
+        .cY = .cY + 1;
+      }
+    }
+    else if (dir == 0)
+    {
+      if (.cX > 0)
+      {
+        .cX = .cX - 1;
+      }
+    }
+    else if (dir == 2)
+    {
+      if (.cX<.lx-1)
+      {
+        .cX = .cX + 1;
+      }
+    }
+    WaitForKey(37+dir);
+    .DrawCurrCell(1);
+  },
+
+  // open current cell
+  Open = function()
+  {
+    WaitForKey(13);
+    .OpenCell(.cX, .cY, 1);
+  },
+
+  // mark current cell
+  Mark = function()
+  {
+    WaitForKey(32);
+    index = .Index(.cX, .cY);
+    if (.field[index] < 10)
+    {
+      .field[index] = (.field[index] % 10) + 20;
+      .mineCurr = .mineCurr + 1;
+      .DrawFrame();
+      .CheckWin();
+    }
+    else if (.field[index] >= 20)
+    {
+      .field[index] = .field[index] % 10;
+      .mineCurr = .mineCurr -1;
+      .DrawFrame();
+      .DrawCurrCell(1);
+    }
+  },
+
+  // init the game
+  Run = function()
+  {
+    CURSOR(0, 0);
+    .RestartField();
+
+    for (;!ISPRESSED(27);)
+    {
+      if (ISPRESSED(37))
+      {
+        .Move(0);
+      }
+      else if (ISPRESSED(38))
+      {
+        .Move(1);
+      }
+      else if (ISPRESSED(39))
+      {
+        .Move(2);
+      }
+      else if (ISPRESSED(40))
+      {
+        .Move(3);
+      }
+      else if (ISPRESSED(32))
+      {
+        .Mark();
+      }
+      else if (ISPRESSED(13))
+      {
+        .Open();
+      }
+      else if (ISPRESSED(8))
+      {
+        .HelperCell();
+        WaitForKey(8);
+      }
+      else if (ISPRESSED(113))
+      {
+        WaitForKey(113);
+        .InitField(1);
+      }
+      else if (ISPRESSED(114))
+      {
+        WaitForKey(114);
+        .InitField(2);
+      }
+      else if (ISPRESSED(115))
+      {
+        WaitForKey(115);
+        .InitField(3);
+      }
+      else
+      {
+        DebugText("          ");
+      }
+    }
+  }
+
+);
+
+game.Run();
+
+

+ 696 - 0
gmsrc/scripts/snake.gm

@@ -0,0 +1,696 @@
+
+/*
+
+  SNAKE
+
+  by matty riek.
+
+*/
+
+global showMemoryUsage = false;
+
+CURSOR(0,0);
+
+global g_sx = 80;
+global g_sy = 24;
+global g_border =
+
+"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
+"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
+"\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205";
+
+global LocFromXY = function(a_x, a_y) { return a_y * g_sx + a_x; };
+global XFromLoc = function(a_loc) { return a_loc % g_sx; };
+global YFromLoc = function(a_loc) { return a_loc / g_sx; };
+global background =  CA.B_BLUE;
+
+global fade0 = table(  CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY | background,
+                CA.F_BLUE | background,
+                CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_BLUE | CA.F_INTENSITY | background,
+                CA.F_BLUE | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_BLUE|CA.F_GREEN | background,
+                CA.F_BLUE | background,
+                CA.F_BLUE | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background  );
+
+global fade1 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
+                CA.F_RED | CA.F_INTENSITY | background,
+                CA.F_RED | background,
+                CA.F_RED | CA.F_INTENSITY | background,
+                CA.F_RED|CA.F_GREEN | CA.F_INTENSITY  | background );
+
+global fadeRed = table( CA.F_RED|CA.F_INTENSITY | background , CA.F_RED | background);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// CreateGemType
+//
+
+global CreateGemType = function(a_char, a_time, a_score, a_chance, a_length, a_cycle, a_pickup, a_mover)
+{
+  gem = table(m_char = a_char, m_time = a_time * 1000, m_score = a_score, m_chance = a_chance, m_length = a_length, m_cycle = a_cycle, m_mover = a_mover);
+
+  gem.Pickup = function()
+  {
+  };
+
+  if(a_pickup)
+  {
+    gem.Pickup = a_pickup;
+  }
+
+  gem.i = 0;
+  gem.Draw = function(x, y)
+  {
+    .i = .i + 1;
+    if(.i >= tableCount(.m_cycle))
+    {
+      .i = 0;
+    }
+    CATTRIB(.m_cycle[.i]);
+    XYTEXT(x, y, .m_char);
+    CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
+  };
+  return gem;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DrawScreen
+//
+global DrawScreen = function()
+{
+  //
+  CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
+
+  // clear screen
+  CLS();
+
+  // draw border
+  XYTEXT(1, 0, g_border);
+  XYTEXT(1, g_sy - 1, g_border);
+
+  ey = g_sy - 1;
+  for(i = 1; i < ey; i = i + 1)
+  {
+    XYTEXT(0, i, "\186");
+    XYTEXT(g_sx - 1, i, "\186");
+  }
+  XYTEXT(0, 0, "\201");
+  XYTEXT(g_sx - 1, 0, "\187");
+  XYTEXT(0, ey, "\200");
+  XYTEXT(g_sx - 1, ey, "\188");
+
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// EmitGems
+//
+EmitGems = function(a_game)
+{
+  // add a gem
+  for(;;)
+  {
+    // choose a random sleep time
+    sleep( randfloat(
+          a_game.m_levels[a_game.m_level][1] / a_game.m_gameSpeed,
+          a_game.m_levels[a_game.m_level][2] / a_game.m_gameSpeed)
+          );
+
+    // pick a random location
+    x = randint(1, g_sx - 2);
+    y = randint(1, g_sy - 2);
+    loc = LocFromXY(x, y);
+
+    // test if the location is on a existing gem or snake
+    if(a_game.m_gems[loc])
+    {
+      continue;
+    }
+    onsnake = false;
+    foreach(snake in a_game.m_snakes)
+    {
+      if(snake.IsAt(loc))
+      {
+        onsnake = true;
+        break;
+      }
+    }
+    if(onsnake) { continue; }
+
+    // choose a gem
+    found = false;
+    while(!found)
+    {
+      foreach(gem in a_game.m_gemTypes)
+      {
+        if(randint(0, 100) < gem.m_chance)
+        {
+          found = true;
+          break;
+        }
+      }
+    }
+
+    // add the gem
+    gemInstance = table(m_gem = gem, m_expire = sysTime() + (gem.m_time / a_game.m_gameSpeed));
+    a_game.m_gems[loc] = gemInstance;
+    if(gem.m_mover)
+    {
+      gemInstance.m_dirX = randint(-1, 2);
+      gemInstance.m_dirY = randint(-1, 2);
+    }
+
+    // draw the gem
+    gem.Draw(x, y);
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// ReclaimGems
+//
+ReclaimGems = function(a_game)
+{
+  // add a gem
+  for(;;)
+  {
+    time = sysTime();
+
+    foreach(key and gem in a_game.m_gems)
+    {
+      if(time > gem.m_expire)
+      {
+        // remove from screen
+        XYTEXT(XFromLoc(key), YFromLoc(key), " ");
+        a_game.m_gems[key] = null;
+        b = true;
+        break;
+      }
+    }
+
+    yield();
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DrawGems
+//
+DrawGems = function(a_game)
+{
+  for(;;)
+  {
+    foreach(key and gem in a_game.m_gems)
+    {
+      x = XFromLoc(key);
+      y = YFromLoc(key);
+      gem.m_gem.Draw(x, y);
+    }
+    sleep(1 / 30.);
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Move mover gems
+//
+MoveMovers = function(a_game)
+{
+  for(;;)
+  {
+    movers = table();
+    foreach(key and gem in a_game.m_gems)
+    {
+      if(gem.m_gem.m_mover)
+      {
+        x = XFromLoc(key);
+        y = YFromLoc(key);
+        
+        XYTEXT(x, y, " ");
+
+        x = x + gem.m_dirX;
+        y = y + gem.m_dirY;
+
+        if(x <= 1 or x >= g_sx - 2) { gem.m_dirX = -gem.m_dirX;  }
+        if(y <= 1 or y >= g_sy - 2) { gem.m_dirY = -gem.m_dirY;  }
+        gem.m_newLoc = LocFromXY(x, y);
+        movers[key] = gem;
+      }
+    }
+    
+    foreach(key and gem in movers)
+    {
+      a_game.m_gems[key] = null;
+      a_game.m_gems[gem.m_newLoc] = gem;
+    }
+    sleep(1 / 4.);
+  }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Snake
+//
+Snake = function(a_id, a_name, a_score, a_scoreX, a_scoreY, a_headChar, a_tailChar, a_startX, a_startY, a_startDir, a_keys)
+{
+  snake = table(
+    m_id = a_id,
+    m_grow = 2,
+    m_credits = 3,
+    m_startX = a_startX,
+    m_startY = a_startY,
+    m_startDir = a_startDir,
+    m_name = a_name,
+    m_score = a_score,
+    m_scoreX = a_scoreX,
+    m_scoreY = a_scoreY,
+    m_body = array(50),
+    m_length = 0,
+    m_dir = a_startDir, // 0 right, 1 up, 2 left, 3 down
+    m_head = table(
+      m_headChar = a_headChar,
+      m_tailChar = a_tailChar,
+      m_x = a_startX,
+      m_y = a_startY
+    ),
+    m_keys = a_keys
+  );
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // Die
+  //
+  snake.Die = function() // return true if snake has no credits
+  {
+    // flash a death thinggy
+
+    i = 10;
+    while(i)
+    {
+      if(i & 1) {
+        XYTEXT(.m_scoreX, .m_scoreY, format(" ***  FATALITY ***  ", .m_name));
+      }
+      else {
+        XYTEXT(.m_scoreX, .m_scoreY, format("                    ", .m_name));
+      }
+      sleep(0.2f);
+      i = i - 1;
+    }
+
+    .UpdateScore();
+
+    // clear the snake
+    for(i = 0; i < .m_length; i = i + 1)
+    {
+      loc = .m_body[i];
+      x = XFromLoc(loc);
+      y = YFromLoc(loc);
+      XYTEXT(x, y, " ");
+    }
+    XYTEXT(.m_head.m_x, .m_head.m_y, " ");
+
+    // update the credits
+    .m_credits = .m_credits - 1;
+    if(.m_credits)
+    {
+      .m_head.m_x = .m_startX;
+      .m_head.m_y = .m_startY;
+      .m_dir = .m_startDir;
+      .m_grow = 2;
+      .m_length = 0;
+      return false;
+    }
+    return true;
+  };
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // UpdateScore
+  //
+  snake.UpdateScore = function()
+  {
+    XYTEXT(.m_scoreX, .m_scoreY, format(" %d UP %s SCORE %d  ", .m_credits, .m_name, .m_score));
+  };
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // Draw
+  //
+  snake.Draw = function(a_oldX, a_oldY)
+  {
+    // clear the tail.
+    if(.m_length > 0 and .grow == null)
+    {
+      loc = .m_body[.m_length - 1];
+      y = YFromLoc(loc);
+      x = XFromLoc(loc);
+      XYTEXT(x, y, " ");
+    }
+
+    // grow the snake
+    if(.grow)
+    {
+      .m_length = .m_length + 1;
+      if(.m_length >= .m_body.Size())
+      {
+        .m_body.Resize(.m_body.Size() * 2);
+      }
+      .grow = false;
+    }
+
+    // move the body along.
+    for(i = .m_length - 1; i > 0; i = i - 1)
+    {
+      .m_body[i] = .m_body[i - 1];
+    }
+
+    // put the new tail piece in
+    if(.m_length)
+    {
+      .m_body[0] = LocFromXY(a_oldX, a_oldY);
+      XYTEXT(a_oldX, a_oldY, .m_head.m_tailChar);
+    }
+    else
+    {
+      XYTEXT(a_oldX, a_oldY, " ");
+    }
+
+    // draw the new head
+    XYTEXT(.m_head.m_x, .m_head.m_y, .m_head.m_headChar);
+  };
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // IsAt
+  //
+  snake.IsAt = function(a_loc)
+  {
+    for(i = 0; i < .m_length; i = i + 1)
+    {
+      if(a_loc == .m_body[i])
+      {
+        return true;
+      }
+    }
+    if(a_loc == LocFromXY(.m_head.m_x, .m_head.m_y))
+    {
+      return true;
+    }
+    return false;
+  };
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // Update
+  //
+  snake.Update = function(a_game)
+  {
+    member UpdateScore;
+
+    UpdateScore();
+    for(;;)
+    {
+      wait = 1.0f / (a_game.m_snakeMoveRate * a_game.m_gameSpeed);
+      sleep(wait);
+
+      x = .m_head.m_x;
+      y = .m_head.m_y;
+
+      if(.m_dir == 0) { x = x + 1; }
+      else if(.m_dir == 1) { y = y - 1; }
+      else if(.m_dir == 2) { x = x - 1; }
+      else if(.m_dir == 3) { y = y + 1; }
+
+      // did we collect a gem?
+      loc = LocFromXY(x, y);
+      gem = a_game.m_gems[loc];
+      if(gem)
+      {
+        // update score
+        .m_score = .m_score + gem.m_gem.m_score;
+        .m_grow = gem.m_gem.m_length;
+        pickup = gem.m_gem.Pickup;
+        this:pickup();
+        UpdateScore();
+
+        // remove the gem
+        a_game.m_gems[loc] = null;
+      }
+
+      // did we run into a wall??
+      dead = false;
+      if(x <= 0 or x >= g_sx - 1 or y <= 0 or y >= g_sy - 1)
+      {
+        dead = true;
+      }
+      
+      // did we run into ourselves?
+      if(!dead and .IsAt(loc))
+      {
+        dead = true;
+      }
+
+      // update position
+      oldX = .m_head.m_x;
+      oldY = .m_head.m_y;
+      .m_head.m_x = x;
+      .m_head.m_y = y;
+
+      // did we run into another snake
+      if(!dead)
+      {
+        foreach(snake in a_game.m_snakes)
+        {
+          if(snake != this and snake.IsAt(loc))
+          {
+            dead = true;
+            break;
+          }
+        }
+      }
+
+      // did we die?
+      if(dead)
+      {
+        .m_head.m_x = oldX;
+        .m_head.m_y = oldY;
+        die = .Die();
+        .UpdateScore();
+        if(die)
+        {
+          a_game.m_snakes[.m_id] = null;
+          threadKill(.kht);
+          exit();
+        }
+      }
+      .Draw(oldX, oldY);
+      
+      if(.m_grow)
+      {
+        .grow = true;
+        .m_grow = .m_grow - 1;
+      }
+    }
+  };
+  
+  ///////////////////////////////////////////////////////////////////////////////////////////////////
+  //
+  // KeyHandler
+  //
+  snake.KeyHandler = function(a_game)
+  {
+    wait = 1.0f / a_game.m_keyUpdateRate;
+    for(;;)
+    {
+      for(i = 0; i < 4; i = i + 1)
+      {
+        if(ISPRESSED(.m_keys[i]))
+        {
+          .m_dir = i;
+          break;
+        }
+      }
+      sleep(wait);
+    }
+  };
+
+  return snake;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Game
+//
+g_game = table(
+
+  // goodies
+  m_gems = table(),
+  
+  m_levels = table(
+  
+  // level is speed, lower spawn time, upper spawn time, next level score
+    table(1.0f, 10, 20, 500),
+    table(2.0f, 8, 18, 1500),
+    table(2.5f, 5, 16, 2700),
+    table(3.0f, 4, 13, 3800),
+    table(3.5f, 3, 10, 5000)
+  ),
+
+  m_gemTypes = table(
+
+    CreateGemType("\4", 22, 50, 40, 3, fade0), // standard 1
+    CreateGemType("\5", 17, 100, 30, 4, fade0), // standard 2
+    CreateGemType("\6", 15, 200, 20, 5, fade1), // standard 3
+    CreateGemType("\3", 30, 20, 15, 0, fadeRed, function() { .m_credits = .m_credits + 1; }, true),  // 1up
+    CreateGemType("\15", 45, 0, 15, 50, fade1) // bomb
+    ),
+
+  // snakes
+  m_snakes = table(
+    Snake(0, "lefty", 0, 8, 23, "\1", "\176", 1, 12, 0, table('D', 'W', 'A', 'S')),
+    Snake(1, "righty", 0, 50, 23, "\2", "\177", 78, 12, 2, table('L', 'I', 'J', 'K'))
+    ),
+
+  // game
+  m_level = 0,
+  m_over = false,
+  m_totalScore = 0,
+
+  // timing
+  m_speedup = 2.0f, // overall speed multiplier
+  m_gameSpeed = 1.0f, // current game speed
+  m_keyUpdateRate = 30.0f, // times per second
+  m_snakeMoveRate = 4.0f // times per second
+);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Main
+//
+ScoreMonitor = function()
+{
+  .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
+
+  for(;;)
+  {
+    // update scores and levels.
+    .m_totalScore = 0;
+    foreach(snake in .m_snakes)
+    {
+      .m_totalScore = .m_totalScore + snake.m_score;
+    }
+
+    // are they at the next level
+    if(.m_totalScore >= .m_levels[.m_level][3])
+    {
+      // level up.... have we finished
+      .m_level = .m_level + 1;
+      if(.m_level >= tableCount(.m_levels))
+      {
+        // you win....
+        XYTEXT(0,0,"YOUWIN");
+        sleep(5);
+        .gameover = true;
+      }
+      else
+      {
+        // next level
+        .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
+      }
+    }
+
+    XYTEXT(8, 0, format("LEVEL %d, SCORE %d, NEXT LEVEL TARGET %d", .m_level, .m_totalScore, .m_levels[.m_level][3]));
+    if(showMemoryUsage)
+    {
+      XYTEXT(8, 1, format("mem %d Desired H: %d S: %d F: %d I: %d W: %d",
+                           sysGetMemoryUsage(), 
+                           sysGetDesiredMemoryUsageHard(), 
+                           sysGetDesiredMemoryUsageSoft(),
+                           sysGetStatsGCNumFullCollects(),
+                           sysGetStatsGCNumIncCollects(),
+                           sysGetStatsGCNumWarnings() ));
+    }
+    sleep(0.1);
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Title Screen
+//
+
+// todo
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Main
+//
+
+DrawScreen();
+
+// start the snakes
+foreach(snake in g_game.m_snakes)
+{
+  snake.kht = snake:thread(snake.KeyHandler, g_game);
+  snake.ut = snake:thread(snake.Update, g_game);
+}
+
+// start the gems
+egt = thread(EmitGems, g_game);
+rgt = thread(ReclaimGems, g_game);
+dgt = thread(DrawGems, g_game);
+mmt = thread(MoveMovers, g_game);
+smt = g_game:thread(ScoreMonitor);
+
+for(;;)
+{
+  // test for escape key
+  if(g_game.gameover or ISPRESSED(27))
+  {
+    // kill threads
+    threadKill(egt);
+    threadKill(rgt);
+    threadKill(dgt);
+    threadKill(smt);
+    threadKill(mmt);
+
+    foreach(snake in g_game.m_snakes)
+    {
+      threadKill(snake.kht);
+      threadKill(snake.ut);
+    }
+    exit();
+  }
+  yield();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 410 - 0
gmsrc/src/binds/gmArrayLib.cpp

@@ -0,0 +1,410 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmArrayLib.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+#include "gmHelpers.h"
+
+//
+//
+// Implementation of array binding
+//
+//
+
+#if GM_ARRAY_LIB
+
+// Statics and globals
+gmType GM_ARRAY = GM_NULL;
+gmVariable gmUserArray::m_null;
+
+
+bool gmUserArray::Construct(gmMachine * a_machine, int a_size)
+{
+  m_null.Nullify();
+  m_array = NULL;
+  m_size = 0;
+  return Resize(a_machine, a_size);
+}
+
+
+void gmUserArray::Destruct(gmMachine * a_machine)
+{
+  if(m_array)
+  {
+    a_machine->Sys_Free(m_array);
+    m_array = NULL;
+  }
+  m_size = 0;
+}
+
+
+bool gmUserArray::Resize(gmMachine * a_machine, int a_size)
+{
+  if(a_size < 0) a_size = 0;
+  int copysize = (a_size > m_size) ? m_size : a_size;
+  gmVariable * array = (gmVariable *) a_machine->Sys_Alloc(sizeof(gmVariable) * a_size);
+  // copy contents.
+  if(m_array)
+  {
+    memcpy(array, m_array, sizeof(gmVariable) * copysize);
+    if(a_size > copysize)
+    {
+      memset(array + copysize, 0, sizeof(gmVariable) * (a_size - copysize));
+    }
+    a_machine->Sys_Free(m_array);
+  }
+  else
+  {
+    memset(array, 0, sizeof(gmVariable) * a_size);
+  }
+  m_array = array;
+  m_size = a_size;
+  return true;
+}
+
+#if GM_USE_INCGC
+bool gmUserArray::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
+{
+  int i;
+  for(i = 0; i < m_size; ++i)
+  {
+    if(m_array[i].IsReference())
+    {
+      gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
+      a_gc->GetNextObject(object);
+      ++a_workDone;
+    }
+  }
+  
+  ++a_workDone;
+  return true;
+}
+#else //GM_USE_INCGC
+void gmUserArray::Mark(gmMachine * a_machine, gmuint32 a_mark)
+{
+  int i;
+  for(i = 0; i < m_size; ++i)
+  {
+    if(m_array[i].IsReference())
+    {
+      gmObject * object = GM_MOBJECT(a_machine, m_array[i].m_value.m_ref);
+      if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
+    }
+  }
+}
+#endif //GM_USE_INCGC
+
+
+bool gmUserArray::Shift(int a_shift)
+{
+  if(a_shift < 0) // shift left
+  {
+    a_shift = -a_shift;
+    if(a_shift >= m_size)
+    {
+      memset(m_array, 0, m_size);
+    }
+    else
+    {
+      int size = m_size - a_shift;
+      memmove(m_array, m_array + a_shift, sizeof(gmVariable) * size);
+      memset(m_array + size, 0, sizeof(gmVariable) * a_shift);
+    }
+  }
+  else if(a_shift > 0) // shift right
+  {
+    if(a_shift >= m_size)
+    {
+      memset(m_array, 0, m_size);
+    }
+    else
+    {
+      int size = m_size - a_shift;
+      memmove(m_array + a_shift, m_array, sizeof(gmVariable) * size);
+      memset(m_array, 0, sizeof(gmVariable) * a_shift);
+    }
+  }
+  return true;
+}
+
+
+int gmUserArray::Move(int a_dest, int a_src, int a_size)
+{
+  int start = a_src;
+  int dest = a_dest;
+  int size = a_size;
+
+  if(start < 0)
+  {
+    size += start;
+    dest -= start;
+    start = 0;
+  }
+
+  if(dest < 0)
+  {
+    size += dest;
+    start -= dest;
+    dest = 0;
+  }
+
+  if(size <= 0) return 0;
+  if(start >= m_size) return 0;
+  if(dest >= m_size) return 0;
+  if((dest + size) < 0) return 0;
+
+  if(start + size > m_size)
+  {
+    size = m_size - start;
+  }
+  if(dest + size > m_size)
+  {
+    size = m_size - dest;
+  }
+  if(size <= 0) return 0;
+
+  GM_ASSERT(dest >= 0);
+  GM_ASSERT(start >= 0);
+  GM_ASSERT(start + size <= m_size);
+  GM_ASSERT(dest + size <= m_size);
+
+  memmove(m_array + dest, m_array + start, sizeof(gmVariable) * size);
+
+  return size;
+}
+
+gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size)
+{
+  gmUserArray * newArray = (gmUserArray *) a_machine->Sys_Alloc(sizeof(gmUserArray));
+  newArray->Construct(a_machine, a_size);
+  return newArray;
+}
+
+
+// functions
+
+static int GM_CDECL gmfArray(gmThread * a_thread) // size
+{
+  GM_INT_PARAM(size, 0, 0);
+  gmUserArray * array = (gmUserArray *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmUserArray));
+  array->Construct(a_thread->GetMachine(), size);
+  a_thread->PushNewUser(array, GM_ARRAY);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfArraySize(gmThread * a_thread) // return size
+{
+  gmUserObject * arrayObject = a_thread->ThisUserObject();
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  if(arrayObject->m_user)
+  {
+    gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+    a_thread->PushInt(array->Size());
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfArrayResize(gmThread * a_thread) // size
+{
+  GM_INT_PARAM(size, 0, 0);
+  gmUserObject * arrayObject = a_thread->ThisUserObject();
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  if(arrayObject->m_user)
+  {
+    gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+    array->Resize(a_thread->GetMachine(), size);
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfArrayShift(gmThread * a_thread) // shift
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(shift, 0);
+
+  gmUserObject * arrayObject = a_thread->ThisUserObject();
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  if(arrayObject->m_user)
+  {
+    gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+    array->Shift(shift);
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfArrayMove(gmThread * a_thread) // dst, src, size
+{
+  GM_CHECK_NUM_PARAMS(3);
+  GM_CHECK_INT_PARAM(dst, 0);
+  GM_CHECK_INT_PARAM(src, 1);
+  GM_CHECK_INT_PARAM(size, 2);
+
+  gmUserObject * arrayObject = a_thread->ThisUserObject();
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  if(arrayObject->m_user)
+  {
+    gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+    array->Move(dst, src, size);
+  }
+  return GM_OK;
+}
+
+static void GM_CDECL gmArrayGetInd(gmThread * a_thread, gmVariable * a_operands)
+{
+  gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+  if(a_operands[1].m_type == GM_INT)
+  {
+    int index = a_operands[1].m_value.m_int;
+    *a_operands = array->GetAt(index);
+    return;
+  }
+  a_operands->Nullify();
+}
+
+static void GM_CDECL gmArraySetInd(gmThread * a_thread, gmVariable * a_operands)
+{
+  gmUserObject * arrayObject = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
+  GM_ASSERT(arrayObject->m_userType == GM_ARRAY);
+  gmUserArray * array = (gmUserArray *) arrayObject->m_user;
+  if(a_operands[1].m_type == GM_INT)
+  {
+    int index = a_operands[1].m_value.m_int;
+
+#if GM_USE_INCGC
+    //Apply write barrier
+    gmVariable oldVar = array->GetAt(index);
+    if(oldVar.IsReference())
+    {
+      a_thread->GetMachine()->GetGC()->WriteBarrier((gmObject*)oldVar.m_value.m_ref);
+    }
+#endif //GM_USE_INCGC
+
+    array->SetAt(index, a_operands[2]);
+  }
+}
+
+#if GM_USE_INCGC
+static void GM_CDECL gmGCDestructArrayUserType(gmMachine * a_machine, gmUserObject* a_object)
+{
+  if(a_object->m_user) 
+  {
+    gmUserArray * array = (gmUserArray *) a_object->m_user;
+    array->Destruct(a_machine);
+    a_machine->Sys_Free(array);
+  }
+  a_object->m_user = NULL;
+}
+
+static bool GM_CDECL gmGCTraceArrayUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
+{
+  if(a_object->m_user) 
+  {
+    gmUserArray * array = (gmUserArray *) a_object->m_user;
+    return array->Trace(a_machine, a_gc, a_workLeftToGo, a_workDone);
+  }
+  return true;
+}
+
+#else //GM_USE_INCGC
+static void GM_CDECL gmMarkArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  if(a_object->m_user) 
+  {
+    gmUserArray * array = (gmUserArray *) a_object->m_user;
+    array->Mark(a_machine, a_mark);
+  }
+}
+
+static void GM_CDECL gmGCArrayUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  if(a_object->m_user) 
+  {
+    gmUserArray * array = (gmUserArray *) a_object->m_user;
+    array->Destruct(a_machine);
+    a_machine->Sys_Free(array);
+  }
+  a_object->m_user = NULL;
+}
+#endif //GM_USE_INCGC
+
+// libs
+
+static gmFunctionEntry s_arrayLib[] = 
+{ 
+  /*gm
+    \lib gm
+  */
+  /*gm
+    \function array
+    \brief array will create a fixed size array object
+    \param int size optional (0)
+    \return array
+  */
+  {"array", gmfArray},
+};
+
+static gmFunctionEntry s_arrayTypeLib[] = 
+{ 
+  /*gm
+    \lib array
+  */
+  /*gm
+    \function Size
+    \brief Size will return the current size of the fixed array
+    \return int array size
+  */
+  {"Size", gmfArraySize},
+  /*gm
+    \function Resize
+    \brief Resize will resize the array to a new size
+    \param int size optional (0)
+    \return null
+  */
+  {"Resize", gmfArrayResize},
+  /*gm
+    \function Shift
+    \brief Shift will shift slide the array elements by a delta, nulls are shifted in
+    \param int delta
+    \return null
+  */
+  {"Shift", gmfArrayShift},
+  /*gm
+    \function Move
+    \brief Move will perform a non destructive move on the array
+    \param int dst
+    \param int src
+    \param int size
+    \return null
+  */
+  {"Move", gmfArrayMove},
+};
+
+void gmBindArrayLib(gmMachine * a_machine)
+{
+  gmUserArray::m_null.Nullify(); //Init static null
+
+  a_machine->RegisterLibrary(s_arrayLib, sizeof(s_arrayLib) / sizeof(s_arrayLib[0]));
+  GM_ARRAY = a_machine->CreateUserType("array");
+  a_machine->RegisterTypeLibrary(GM_ARRAY, s_arrayTypeLib, sizeof(s_arrayTypeLib) / sizeof(s_arrayTypeLib[0]));
+#if GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(GM_ARRAY, gmGCTraceArrayUserType, gmGCDestructArrayUserType);
+#else //GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(GM_ARRAY, gmMarkArrayUserType, gmGCArrayUserType);
+#endif //GM_USE_INCGC
+  a_machine->RegisterTypeOperator(GM_ARRAY, O_GETIND, NULL, gmArrayGetInd);
+  a_machine->RegisterTypeOperator(GM_ARRAY, O_SETIND, NULL, gmArraySetInd);
+}
+
+#endif // GM_ARRAY_LIB

+ 95 - 0
gmsrc/src/binds/gmArrayLib.h

@@ -0,0 +1,95 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMARRAYLIB_H_
+#define _GMARRAYLIB_H_
+
+#include "gmConfig.h"
+#include "gmVariable.h"
+
+// Fwd decls
+class gmMachine;
+
+#define GM_ARRAY_LIB 1
+#define GM_ARRAY_LIB_GROW_BY    16
+
+#if GM_ARRAY_LIB
+
+extern gmType GM_ARRAY;
+
+void gmBindArrayLib(gmMachine * a_machine);
+
+/*!
+  \class gmUserArray
+*/
+class gmUserArray
+{
+public:
+
+  /// \brief Construct()
+  bool Construct(gmMachine * a_machine, int a_size);
+
+  /// \brief Destruct()
+  void Destruct(gmMachine * a_machine);
+#if GM_USE_INCGC
+  bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
+#else //GM_USE_INCGC
+  /// \brief Mark()
+  void Mark(gmMachine * a_machine, gmuint32 a_mark);
+#endif //GM_USE_INCGC
+
+  /// \brief GetAt()
+  GM_FORCEINLINE const gmVariable &GetAt(int a_index)
+  {
+    if(a_index >= 0 && a_index < m_size)
+    {
+      return m_array[a_index];
+    }
+    return m_null;
+  }
+
+  /// \brief SetAt()
+  GM_FORCEINLINE bool SetAt(int a_index, const gmVariable &a_variable)
+  {
+    if(a_index >= 0 && a_index < m_size)
+    {
+      m_array[a_index] = a_variable;
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Size()
+  GM_FORCEINLINE int Size() const { return m_size; }
+
+  /// \brief Resize()
+  bool Resize(gmMachine * a_machine, int a_size);
+
+  /// \brief Shift()
+  bool Shift(int a_shift);
+
+  /// \brief Move()
+  int Move(int a_dest, int a_src, int a_size);
+
+  // data
+  gmVariable * m_array;
+  int m_size;
+
+  static gmVariable m_null;
+};
+
+
+/// \brief Create a GM_ARRAY.  This much be put into a user object.
+gmUserArray* gmUserArray_Create(gmMachine* a_machine, int a_size = 0);
+
+#endif // GM_ARRAY_LIB
+
+#endif // _GMARRAYLIB_H_

+ 13 - 0
gmsrc/src/binds/gmCall.cpp

@@ -0,0 +1,13 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmCall.h"

+ 429 - 0
gmsrc/src/binds/gmCall.h

@@ -0,0 +1,429 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCALL_H_
+#define _GMCALL_H_
+
+#include "gmConfig.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+
+#undef GetObject //Fix for Win32 where GetObject is #defined
+
+/// \class gmCall
+/// \brief A helper class to call script functions from C
+/// Warning: Do not store any of the reference type return variables (eg. GM_SRING).  
+/// As the object may be garbage collected.  Instead, copy immediately as necessary.
+class gmCall
+{
+public:
+
+  /// \brief Constructor
+  gmCall()
+  {
+#ifdef GM_DEBUG_BUILD
+    m_locked = false;
+#endif //GM_DEBUG_BUILD
+  }
+
+  /// \brief Begin the call of a global function
+  /// \param a_machine Virtual machine instance 
+  /// \param a_funcName Name of function
+  /// \param a_this The 'this' used by the function.
+  /// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
+  /// \return true on sucess, false if function was not found.
+  GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, const char * a_funcName, 
+                                          const gmVariable& a_this = gmVariable::s_null, 
+                                          bool a_delayExecuteFlag = false)
+  {
+    GM_ASSERT(a_machine);
+
+    gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
+    
+    return BeginGlobalFunction(a_machine, funcNameStringObj, a_this, a_delayExecuteFlag);
+  }
+
+  /// \brief Begin the call of a global function
+  /// \param a_machine Virtual machine instance 
+  /// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
+  /// \param a_this The 'this' used by the function.
+  /// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
+  /// \return true on sucess, false if function was not found.
+  GM_FORCEINLINE bool BeginGlobalFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj, 
+                                          const gmVariable& a_this = gmVariable::s_null, 
+                                          bool a_delayExecuteFlag = false)
+  {
+    GM_ASSERT(a_machine);
+    GM_ASSERT(a_funcNameStringObj);
+    
+    gmVariable lookUpVar;
+    gmVariable foundFunc;
+
+    lookUpVar.SetString(a_funcNameStringObj);
+    foundFunc = a_machine->GetGlobals()->Get(lookUpVar);
+
+    if( GM_FUNCTION == foundFunc.m_type )         // Check found variable is a function
+    {
+      gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
+  
+      return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
+    }
+    return false;
+  }
+
+  /// \brief Begin the call of a object function
+  /// \param a_machine Virtual machine instance 
+  /// \param a_funcName Name of function.
+  /// \param a_tableObj The table on the object to look up the function.
+  /// \param a_this The 'this' used by the function.
+  /// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
+  /// \return true on sucess, false if function was not found.
+  GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, const char * a_funcName, 
+                                  gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null, 
+                                  bool a_delayExecuteFlag = false)
+  {
+    GM_ASSERT(a_machine);
+    GM_ASSERT(a_funcName);
+
+    gmStringObject * funcNameStringObj = a_machine->AllocPermanantStringObject(a_funcName); // Slow
+    
+    return BeginTableFunction(a_machine, funcNameStringObj, a_tableObj, a_this, a_delayExecuteFlag);
+  }
+
+  /// \brief Begin the call of a object function
+  /// \param a_machine Virtual machine instance 
+  /// \param a_funcNameStringObj A string object that was found or created earlier, much faster than creating from c string.
+  /// \param a_tableObj The table on the object to look up the function.
+  /// \param a_this The 'this' used by the function.
+  /// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
+  /// \return true on sucess, false if function was not found.
+  GM_FORCEINLINE bool BeginTableFunction(gmMachine * a_machine, gmStringObject * a_funcNameStringObj, 
+                                  gmTableObject * a_tableObj, const gmVariable& a_this = gmVariable::s_null, 
+                                  bool a_delayExecuteFlag = false)
+  {
+    GM_ASSERT(a_machine);   
+    GM_ASSERT(a_tableObj);
+    GM_ASSERT(a_funcNameStringObj);
+
+    gmVariable lookUpVar;
+    gmVariable foundFunc;
+
+    lookUpVar.SetString(a_funcNameStringObj);
+    foundFunc = a_tableObj->Get(lookUpVar);
+
+    if( GM_FUNCTION == foundFunc.m_type )         // Check found variable is a function
+    {
+      gmFunctionObject * functionObj = (gmFunctionObject *)foundFunc.m_value.m_ref; //Func Obj from variable
+      return BeginFunction(a_machine, functionObj, a_this, a_delayExecuteFlag);
+    }
+    return false;
+  }
+
+  /// \brief Begin the call of a object function
+  /// \param a_funcObj A function object that was found or created earlier.
+  /// \param a_tableObj The table on the object to look up the function.
+  /// \param a_this The 'this' used by the function.
+  /// \param a_delayExecuteFlag Set true if you want function thread to not execute now.
+  /// \return true on sucess, false if function was not found.
+  GM_FORCEINLINE bool BeginFunction(gmMachine * a_machine, gmFunctionObject * a_funcObj, 
+                                    const gmVariable &a_thisVar = gmVariable::s_null, 
+                                    bool a_delayExecuteFlag = false)
+  {
+    GM_ASSERT(a_machine);   
+    GM_ASSERT(a_funcObj);
+
+#ifdef GM_DEBUG_BUILD
+    // YOU CANNOT NEST gmCall::Begin
+    GM_ASSERT(m_locked == false);
+    m_locked = true;
+#endif //GM_DEBUG_BUILD
+
+    Reset(a_machine);
+
+    if( GM_FUNCTION == a_funcObj->GetType() )         // Check found variable is a function
+    {
+      m_thread = m_machine->CreateThread();     // Create thread for func to run on      
+      m_thread->Push(a_thisVar);                // this
+      m_thread->PushFunction(a_funcObj);        // function
+      m_delayExecuteFlag = a_delayExecuteFlag;
+      return true;
+    }
+
+#ifdef GM_DEBUG_BUILD
+    GM_ASSERT(m_locked == true);
+    m_locked = false;
+#endif //GM_DEBUG_BUILD
+
+    return false;
+  }
+ 
+  /// \brief Add a parameter variable
+  GM_FORCEINLINE void AddParam(const gmVariable& a_var)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->Push(a_var);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is null
+  GM_FORCEINLINE void AddParamNull()
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->PushNull();
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a integer
+  GM_FORCEINLINE void AddParamInt(const int a_value)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->PushInt(a_value);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a float
+  GM_FORCEINLINE void AddParamFloat(const float a_value)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->PushFloat(a_value);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a string
+  GM_FORCEINLINE void AddParamString(const char * a_value, int a_len = -1)
+  {
+    GM_ASSERT(m_machine);    
+    GM_ASSERT(m_thread);
+
+    m_thread->PushNewString(a_value, a_len);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a string (faster version since c string does not need lookup)
+  GM_FORCEINLINE void AddParamString(gmStringObject * a_value)
+  {
+    GM_ASSERT(m_machine);    
+    GM_ASSERT(m_thread);
+
+    m_thread->PushString(a_value);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a user object.  Creates a new user object.
+  /// \param a_value Pointer to user object data
+  /// \param a_userType Type of user object beyond GM_USER
+  GM_FORCEINLINE void AddParamUser(void * a_value, int a_userType)
+  {
+    GM_ASSERT(m_machine);    
+    GM_ASSERT(m_thread);
+
+    m_thread->PushNewUser(a_value, a_userType);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a user object.
+  /// \param a_userObj Pushes an existing user object without creating a new one.
+  GM_FORCEINLINE void AddParamUser(gmUserObject * a_userObj)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->PushUser(a_userObj);
+    ++m_paramCount;
+  }
+
+  /// \brief Add a parameter that is a table object.
+  /// \param a_tableObj Pushes an existing table object without creating a new one.
+  GM_FORCEINLINE void AddParamTable(gmTableObject * a_tableObj)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+
+    m_thread->PushTable(a_tableObj);
+    ++m_paramCount;
+  }
+
+  /// \brief Make the call.  If a return value was expected, it will be set in here.
+  /// \param a_threadId Optional 
+  GM_FORCEINLINE void End(int * a_threadId = NULL)
+  {
+    GM_ASSERT(m_machine);
+    GM_ASSERT(m_thread);
+    
+#ifdef GM_DEBUG_BUILD
+    // CAN ONLY CALL ::End() after a successful ::Begin
+    GM_ASSERT(m_locked == true);
+    m_locked = false;
+#endif //GM_DEBUG_BUILD
+        
+    int state = m_thread->PushStackFrame(m_paramCount);
+    if(state != gmThread::KILLED) // Can be killed immedialy if it was a C function
+    {
+      if(m_delayExecuteFlag)
+      {
+        state = m_thread->GetState();
+      }
+      else
+      {
+        state = m_thread->Sys_Execute(&m_returnVar);
+      }
+    }
+    else
+    {
+      // Was a C function call, grab return var off top of stack
+      m_returnVar = *(m_thread->GetTop() - 1);
+      m_machine->Sys_SwitchState(m_thread, gmThread::KILLED);
+    }
+
+    // If we requested a thread Id
+    if(a_threadId)
+    {
+      if(state != gmThread::KILLED)
+      {
+        *a_threadId = m_thread->GetId();
+      }
+      else
+      {
+        *a_threadId = GM_INVALID_THREAD;
+      }
+    }
+
+    if(state == gmThread::KILLED)
+    {
+      m_returnFlag = true; // Function always returns something, null if not explicit.
+      m_thread = NULL; // Thread has exited, no need to remember it.
+    }
+  }
+
+  /// \brief Accesss thread created for function call.
+  GM_FORCEINLINE gmThread * GetThread() { return m_thread; }
+
+  /// \brief Returns reference to 'return' variable.  Never fails, but variable may be a 'null' varaible if none was returned.
+  const gmVariable& GetReturnedVariable() { return m_returnVar; }
+  
+  /// \brief Returns true if function exited and returned a variable.
+  bool DidReturnVariable() { return m_returnFlag; }
+
+  /// \brief Did function return a null?
+  /// \return true if function returned null. 
+  bool GetReturnedNull()
+  {
+    if(m_returnFlag && (m_returnVar.m_type == GM_NULL))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Get returned int
+  /// \return true if function returned an int. 
+  bool GetReturnedInt(int& a_value)
+  {
+    if(m_returnFlag && (m_returnVar.m_type == GM_INT))
+    {
+      a_value = m_returnVar.m_value.m_int;
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Get returned float
+  /// \return true if function returned an float. 
+  bool GetReturnedFloat(float& a_value)
+  {
+    if(m_returnFlag && (m_returnVar.m_type == GM_FLOAT))
+    {
+      a_value = m_returnVar.m_value.m_float;
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Get returned string
+  /// \return true if function returned an string. 
+  bool GetReturnedString(const char *& a_value)
+  {
+    if(m_returnFlag && (m_returnVar.m_type == GM_STRING))
+    {
+      a_value = ((gmStringObject *)m_machine->GetObject(m_returnVar.m_value.m_ref))->GetString();
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Get returned user
+  /// \return true if function returned an user. 
+  bool GetReturnedUser(void *& a_value, int a_userType)
+  {
+    if(m_returnFlag && (m_returnVar.m_type == a_userType))
+    {
+      a_value = (void *)m_returnVar.m_value.m_ref;
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Get returned user or null
+  /// \return true if function returned an user or null. 
+  bool GetReturnedUserOrNull(void *& a_value, int a_userType)
+  {
+    if(m_returnFlag)
+    {
+      if(m_returnVar.m_type == a_userType)
+      {
+        a_value = (void *)m_returnVar.m_value.m_ref;
+        return true;
+      }
+      else if(m_returnVar.m_type == GM_NULL)
+      {
+        a_value = (void *)NULL;
+        return true;
+      }
+    }
+    return false;
+  }
+
+protected:
+
+  gmMachine * m_machine;
+  gmThread * m_thread;
+  gmVariable m_returnVar;
+  int m_paramCount;
+  bool m_returnFlag;
+  bool m_delayExecuteFlag;
+#ifdef GM_DEBUG_BUILD
+  bool m_locked;
+#endif //GM_DEBUG_BUILD
+
+  /// \brief Used internally to clear call information.
+  GM_FORCEINLINE void Reset(gmMachine * a_machine)
+  {
+    GM_ASSERT(a_machine);
+    m_machine = a_machine;
+    m_thread = NULL;
+    m_returnVar.Nullify();
+    m_returnFlag = false;
+    m_paramCount = 0;
+    m_delayExecuteFlag = false;
+  };
+ 
+};
+
+
+#endif // _GMCALL_H_

+ 212 - 0
gmsrc/src/binds/gmGCRoot.cpp

@@ -0,0 +1,212 @@
+//
+// gmGCRoot.cpp
+//
+
+#include "gmGCRoot.h"
+
+
+// Init statics and constants
+gmGCRootManager* gmGCRootManager::s_staticInstance;
+
+
+gmGCRootManager::gmGCRootManager()
+{
+  m_machineHolderSet.Init(0, 4);
+}
+
+
+gmGCRootManager::~gmGCRootManager()
+{
+  DestroyAllMachines();
+}
+
+ 
+void gmGCRootManager::Init()
+{
+  GM_ASSERT( !s_staticInstance );
+  
+  if( !s_staticInstance )
+  {
+    s_staticInstance = new gmGCRootManager;
+  }
+}
+
+
+void gmGCRootManager::Destroy()
+{
+  if( s_staticInstance )
+  {
+    delete s_staticInstance;
+    s_staticInstance = NULL;
+  }
+}
+
+
+gmgcrHolder* gmGCRootManager::FindOrAdd(gmObject* a_object, gmMachine* a_machine)
+{
+  GM_ASSERT(a_object);
+  GM_ASSERT(a_machine);
+  
+  if( !a_object || !a_machine )
+  {
+    return NULL;
+  }
+  
+  MachineHolders* machineSet = FindOrAddMachine(a_machine);
+  
+  gmgcrHolder* holder = NULL;
+  if( !machineSet->m_mapPtrToHolder.GetAt(a_object, holder) )
+  {
+    // Add new
+    holder = m_memHolder.Alloc();
+    holder->Init(a_object, a_machine);
+    machineSet->m_mapPtrToHolder.SetAt(a_object, holder);
+    
+    a_machine->AddCPPOwnedGMObject(a_object); // Add to GC roots
+  }
+  return holder;
+}
+
+
+void gmGCRootManager::RemoveObject(gmObject* a_object, gmMachine* a_machine)
+{
+  if( a_object && a_machine )
+  {
+    a_machine->RemoveCPPOwnedGMObject(a_object); // Remove from GC roots
+
+    gmGCRootManager::MachineHolders* machineSet = FindOrAddMachine(a_machine);
+
+    machineSet->m_mapPtrToHolder.RemoveAt(a_object);
+  }
+}
+
+
+void gmGCRootManager::FreeHolder(const gmgcrHolder* a_holder)
+{
+  if( a_holder )
+  {
+    m_memHolder.Free( const_cast<gmgcrHolder*>(a_holder) );
+  }
+}
+
+
+void gmGCRootManager::DestroyMachine(gmMachine* a_machine)
+{
+  int foundIndex = -1;
+
+  // Find existing set
+  for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
+  {
+    if( m_machineHolderSet[mIndex].m_machine == a_machine )
+    {
+      foundIndex = mIndex;
+      break;
+    }
+  }
+  
+  if( foundIndex >= 0 )
+  {
+    MachineHolders* destroySet = &m_machineHolderSet[foundIndex];
+    
+    // Cleanup all internal pointers
+    gmgcrMap<gmObject*, gmgcrHolder*>::Iterator it;
+    
+    // Iterate carefully as we are deleting while iterating
+    destroySet->m_mapPtrToHolder.GetFirst(it);
+    while( !destroySet->m_mapPtrToHolder.IsNull(it) )
+    {
+      gmgcrHolder* toDelete = NULL;
+      if( destroySet->m_mapPtrToHolder.GetValue(it, toDelete) )
+      {
+        // NOTE: Delete this found value after incrementing iterator
+      }
+      destroySet->m_mapPtrToHolder.GetNext(it);
+      if( toDelete )
+      {
+        toDelete->Destroy();
+      }
+    }    
+
+    // Remove machine set
+    m_machineHolderSet.RemoveAt(foundIndex);
+  }
+}
+
+
+gmGCRootManager::MachineHolders* gmGCRootManager::FindOrAddMachine(gmMachine* a_machine)
+{
+  // NOTE: Expect very small number of machines.  If this is not so, accelerate search.
+  
+  GM_ASSERT( a_machine );
+
+  // Find existing set
+  for(int mIndex=0; mIndex < m_machineHolderSet.GetSize(); ++mIndex)
+  {
+    if( m_machineHolderSet[mIndex].m_machine == a_machine )
+    {
+      return &m_machineHolderSet[mIndex];
+    }
+  }
+  
+  // Create new set
+  int newIndex = m_machineHolderSet.AddEmpty();
+  MachineHolders* newSet = &m_machineHolderSet[newIndex];
+  newSet->m_machine = a_machine;
+  return newSet;
+}
+
+
+void gmGCRootManager::DestroyAllMachines()
+{
+  while( m_machineHolderSet.GetSize() )
+  {
+    DestroyMachine( m_machineHolderSet[m_machineHolderSet.GetLastIndex()].m_machine );
+  }
+}
+
+
+#if 0
+// Test the gmGCRoot system
+void TestGMGCRoot()
+{ 
+  gmGCRootManager::Init(); // Init system
+  {
+    gmMachine machine1;
+
+    gmGCRoot<gmStringObject> ptr1;
+    
+    gmStringObject* stringObj1 = machine1.AllocStringObject("hello");
+    
+    ptr1.Set(stringObj1, &machine1); // Initialize via func
+    
+    gmStringObject* nat1 = ptr1; // Assign pointer
+    
+    gmGCRoot<gmStringObject> ptr2;
+    ptr2 = ptr1;
+    
+    ptr1 = NULL; // Assign to null
+    ptr2 = NULL;
+    //ptr2 = nat1; // ERROR
+
+    gmStringObject* stringObj2 = machine1.AllocStringObject("apple");
+    gmGCRoot<gmStringObject> ptr3(stringObj2, &machine1); // Initialize via constructor
+    gmGCRoot<gmStringObject> ptr4(stringObj2, &machine1); // Duplicate without copy
+
+    gmGCRoot<gmStringObject> ptr5;
+    {
+      gmMachine machine2;
+
+      gmStringObject* stringObj3 = machine2.AllocStringObject("bannana");
+      ptr5.Set(stringObj3, &machine2);
+     
+      gmGCRootManager::Get()->DestroyMachine(&machine2); // Null associated pointers
+    }
+    if( ptr5 )
+    {
+      int i=1; // Won't get here
+    }
+    
+  }
+  gmGCRootManager::Destroy();
+}
+#endif

+ 399 - 0
gmsrc/src/binds/gmGCRoot.h

@@ -0,0 +1,399 @@
+#ifndef GMGCROOT_H
+#define GMGCROOT_H
+
+//
+// gmGCRoot.h
+//
+
+#include "gmThread.h"
+#include "gmGCRootUtil.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// gmGCRoot 
+//
+// Templated smart pointer style wrapper for gmObject*
+// Automatically adds and removed CPP owned gmObject* from gmMachine roots
+//
+// It works by storing the gmObject* inside a shared reference counted holder 
+// object. When a gmObject* is first wrapped, a holder object is created to 
+// store it. Subsequent initializations will share the same holder object.  It 
+// is much more efficient to assign the pointer from one gmGCRoot to another 
+// than create a new one from the raw gmObject*.
+//
+//
+// To use, you must initialize and destroy the system with:
+//   gmGCRootManager::Init();
+//   gmGCRootManager::Destroy();
+// When you destruct a gmMachine that was used by the system, call this first:
+//   gmGCRootManager::Get()->DestroyMachine(myMachine);
+// Initialize a pointer by either:
+//   gmGCRoot<gmStringObject> ptr1(myObject, myMachine);
+//   gmGCRoot<gmStringObject> ptr2;  ptr2.Set(myObject, myMachine);
+// Copy and test pointers as you would a C style pointer eg:
+//   if( ptr1 ) { int type = ptr1->GetType(); }
+//   ptr2 = ptr1;
+//   ptr1 = NULL;
+//
+//
+// Example usage:
+//   gmGCRootManager::Init();                            // Call once first eg. App initialization
+//   gmGCRoot<gmStringObject> ptr1;                      // A null pointer
+//   gmGCRoot<gmStringObject> ptr2(myString, myMachine); // A pointer
+//   gmStringObject* nat1 = ptr2;                        // Cast to native
+//   gmGCRoot<gmStringObject> ptr3 = ptr2;               // Assign from other (Fast copy of shared pointer)
+//   ptr2 = NULL;                                        // Assign to null
+//   ptr2 = nat1;                                        // COMPILE ERROR! can't assign without gmMachine
+//   gmGCRootManager::Get()->DestroyMachine(myMachine);  // Call before destructing a gmMachine
+//   gmGCRootManager::Destroy();                         // Call once before App exit
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Fwd decls
+class gmGCRootManager;
+class gmgcrHolder;
+
+
+/// Bass class for objects reference counted types
+class gmgcrRefCount
+{
+public:
+
+  /// Construct, starts reference count at 0 (Must be wrapped in smart ptr to increment and later release)
+  gmgcrRefCount()                                 { m_referenceCount = 0; }
+  /// Destructor
+  virtual ~gmgcrRefCount()                        { }
+
+  /// Get reference count
+  int GetReferenceCount() const                   { return m_referenceCount; }
+
+  /// Increment reference count
+  void AddReference() const                       { ++m_referenceCount; }
+
+  /// Decrement reference count
+  int ReleaseReference() const
+  { 
+    GM_ASSERT( m_referenceCount > 0 );
+    if( --m_referenceCount == 0 )
+    {
+      DeleteThis();
+      return true;
+    }
+    return false;
+  }
+
+  /// Delete this object.  May override if this is not desired behavior.
+  /// NOTE: This function is 'const' so override with const also!
+  virtual void DeleteThis() const                 { delete this; }
+  
+private:
+
+  mutable int m_referenceCount;                   ///< Reference counter
+  
+};
+
+
+/// Manager for gm gc roots
+class gmGCRootManager
+{
+public:
+
+  /// Access singleton
+  static gmGCRootManager* Get()                   { return s_staticInstance; }
+  
+  /// Initialize gm gcroot manager
+  static void Init();
+  
+  /// Destroy gm gcroot manager
+  static void Destroy();
+  
+  ~gmGCRootManager();
+
+  // Find or Add shard holder for gmObject
+  gmgcrHolder* FindOrAdd(gmObject* a_object, gmMachine* a_machine);
+  
+  // Free holder memory only
+  void FreeHolder(const gmgcrHolder* a_holder);
+  
+  // Dissassociate holder contents
+  void RemoveObject(gmObject* a_object, gmMachine* a_machine);
+  
+  /// Call before destroying a gmMachine so that any associated objects can be disassociated and have pointers nullified.
+  void DestroyMachine(gmMachine* a_machine);
+  
+protected:
+
+  // Store gmObjects per machine
+  class MachineHolders
+  {
+  public:
+    gmMachine* m_machine;
+    //gmgcrMap m_mapPtrToHolder;
+    gmgcrMap<gmObject*, gmgcrHolder*> m_mapPtrToHolder;
+  };
+
+  static gmGCRootManager* s_staticInstance;         // Singleton
+  
+  gmgcrArray< MachineHolders > m_machineHolderSet;  // Holders per machine
+  gmgcrMemFixed m_memHolder;                        // Holder memory
+  
+  // Non public constructor to prevent multiple instances
+  gmGCRootManager();
+  
+  // Find or add machine
+  MachineHolders* FindOrAddMachine(gmMachine* a_machine);
+
+  // Destroy all machine associations
+  void DestroyAllMachines();
+  
+};
+
+
+/// Holder that is always valid and may contain the real pointer
+class gmgcrHolder : public gmgcrRefCount
+{
+public:
+
+  /// Default contsructor for simple allocation, Init() should be called immediately.
+  gmgcrHolder()
+  {
+    m_object = NULL;
+    m_machine = NULL;
+  }
+  
+  /// Initialize with real object
+  void Init(gmObject* a_object,
+            gmMachine* a_machine)
+  {
+    m_object = a_object;
+    m_machine = a_machine;
+  }
+
+  /// Destroy object pointer (May occur before this holder dies)
+  void Destroy() const
+  {
+    if( m_object )
+    {
+      GM_ASSERT( gmGCRootManager::Get() );
+      gmGCRootManager::Get()->RemoveObject(m_object, m_machine);
+    }
+    
+    m_object = NULL;
+    m_machine = NULL;
+  }
+
+  /// Get the real pointer
+  void* GetPtr()                              { return m_object; }
+
+private:
+
+  /// Called when reference count reaches zero
+  void DeleteThis() const
+  {
+    Destroy();
+    
+    GM_ASSERT( gmGCRootManager::Get() );
+    
+    gmGCRootManager::Get()->FreeHolder(this);
+  }
+
+  mutable gmObject* m_object;                     ///< The object
+  mutable gmMachine* m_machine;                   ///< The owning machine
+  
+};
+
+
+/// This is the smart pointer to a gmObject
+/// It adds the gmObject to the gmMachine GC roots so that the object is not collected while held
+/// Initialize via constructor or Set(). eg. gmGCRoot<gmStringObject> ptr(myString, myMachine);
+/// Fast cast to native type and fast copy to compatible pointer.
+/// Slower FIRST construct and FINAL destruct as shared holder object performs map insert/removal.
+/// If gmMachine is destructed, contents will be nullified. (As long as manager was called correctly.)
+template< typename TYPE >
+class gmGCRoot
+{
+public:
+
+  /// Empty Constructor.  Equals NULL.
+  gmGCRoot()                                      { m_ptrToHolder = NULL; }
+  
+  // NOTE: Currently not very safe since holder and controller are not typed.
+  /// Construct from holder
+  explicit gmGCRoot(gmgcrHolder* a_holder)
+  {
+    m_ptrToHolder = a_holder;
+    if( m_ptrToHolder )
+    {
+      m_ptrToHolder->AddReference();
+    }
+  }
+
+  /// Construct from pointers
+  gmGCRoot(TYPE* a_object, gmMachine* a_machine)
+  {
+    m_ptrToHolder = NULL;
+    Set(a_object, a_machine);
+  }
+
+  /// Set pointer
+  void Set(TYPE* a_object, gmMachine* a_machine)
+  {
+    if( m_ptrToHolder )
+    {
+      m_ptrToHolder->ReleaseReference();
+    }
+
+    if( !a_object ) // Handle null assignment
+    {
+      m_ptrToHolder = NULL;
+      return;
+    }
+   
+    GM_ASSERT( gmGCRootManager::Get() );
+   
+    m_ptrToHolder = gmGCRootManager::Get()->FindOrAdd( (gmObject*)a_object, (gmMachine*) a_machine );
+    m_ptrToHolder->AddReference();
+  }
+
+  /// Copy Constructor
+  gmGCRoot(const gmGCRoot<TYPE>& a_ref)
+  {
+    m_ptrToHolder = a_ref.m_ptrToHolder;
+    if (m_ptrToHolder != NULL)
+    {
+      m_ptrToHolder->AddReference();
+    }
+  }
+  
+  /// Destructor
+  ~gmGCRoot()
+  {
+    if( m_ptrToHolder )
+    {
+      m_ptrToHolder->ReleaseReference();
+    }
+  }
+  
+  /// Access the referenced type
+  operator TYPE* ()                               { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  operator const TYPE* () const                   { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  
+  /// Access the object as pointer
+  TYPE* operator -> ()                            { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  const TYPE* operator -> () const                { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  
+  /// Access the object as reference
+  TYPE& operator * ()                             { return *((TYPE*)m_ptrToHolder->GetPtr()); }
+  const TYPE& operator * () const                 { return *((TYPE*)m_ptrToHolder->GetPtr()); }
+
+  /// Access object with standard function instead of operator.  Useful when compiler finds conversion ambigous.
+  TYPE* Resolve()                                 { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  const TYPE* Resolve() const                     { return (TYPE*)m_ptrToHolder->GetPtr(); }
+  
+  /// Assign from other reference
+  gmGCRoot<TYPE>& operator = (const gmGCRoot<TYPE>& a_ref)
+  {
+    if( m_ptrToHolder != a_ref.GetHolder() )
+    {
+      if( m_ptrToHolder != NULL )
+      {
+        m_ptrToHolder->ReleaseReference();
+      }
+      m_ptrToHolder = a_ref.GetHolder();
+      if(m_ptrToHolder)
+      {
+        m_ptrToHolder->AddReference();
+      }
+    }
+    return *this;
+  }
+
+  /// Assign to NULL.  Make shorter code path for assignment with zero (null in C++)
+  gmGCRoot<TYPE>& operator = (int a_null)
+  {
+    GM_ASSERT(a_null == 0);
+    if( m_ptrToHolder != NULL )
+    {
+      m_ptrToHolder->ReleaseReference();
+    }
+    m_ptrToHolder = NULL;
+    return *this;
+  }
+  
+  /// Equality test
+  int operator == (const gmGCRoot<TYPE>& a_ref) const
+  {
+    if( m_ptrToHolder == a_ref.GetHolder() )
+    {
+      return true;
+    }
+    // Both holders cannot be NULL at this point, but one maybe.
+    if( (a_ref.GetHolder() == NULL) && (m_ptrToHolder->GetPtr() == NULL) )
+    {
+        return true;
+    }
+    if( m_ptrToHolder == NULL )
+    {
+      if( a_ref.GetHolder()->GetPtr() == NULL )
+      {
+        return true;
+      }
+    }
+    else
+    {
+      if( a_ref.GetHolder() != NULL )
+      {
+        if( a_ref.GetHolder()->GetPtr()  == m_ptrToHolder->GetPtr() )
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /// Non equality test
+  int operator != (const gmGCRoot<TYPE>& a_ref) const { return !operator==(a_ref); }
+  
+  /// Less than comparison for use in Containers
+  int operator < (const gmGCRoot<TYPE>& a_ref) const
+  {
+    if( a_ref.GetHolder() == NULL )
+    {
+      return false;
+    }
+    if( m_ptrToHolder == NULL )
+    {
+      return true;
+    }
+    return ( m_ptrToHolder->GetPtr() < a_ref.GetHolder()->GetPtr() );
+  }
+  
+  /// Greater than comparison for use in Containers
+  int operator > (const gmGCRoot<TYPE>& a_ref) const
+  {
+    if( m_ptrToHolder == NULL )
+    {
+      return false;
+    }
+    if( a_ref.GetHolder() == NULL )
+    {
+      return true;
+    }
+    return ( m_ptrToHolder->GetPtr() > a_ref.GetHolder()->GetPtr() );
+  }
+
+  /// Access the holder.  Necessary since this is a templated class.
+  gmgcrHolder* GetHolder() const                  { return m_ptrToHolder; }
+
+private:
+
+  mutable gmgcrHolder* m_ptrToHolder;             ///< Pointer to holder of the real pointer
+  
+};
+
+
+
+
+#endif //GMGCROOT_H

+ 29 - 0
gmsrc/src/binds/gmGCRootUtil.cpp

@@ -0,0 +1,29 @@
+//
+// gmGCRootUtil.cpp
+//
+
+#include "gmGCRoot.h"
+#include "gmGCRootUtil.h"
+
+
+gmgcrMemFixed::gmgcrMemFixed()
+   : gmMemFixed( sizeof(gmgcrHolder) )
+{
+ 
+}
+  
+gmgcrHolder* gmgcrMemFixed::Alloc()
+{
+  gmgcrHolder* ptr = (gmgcrHolder*)gmMemFixed::Alloc();
+  gmConstructElement<gmgcrHolder>(ptr);
+  return ptr;
+}
+
+void gmgcrMemFixed::Free(gmgcrHolder* a_ptr)
+{
+  if( a_ptr )
+  {
+    gmDestructElement(a_ptr);
+    gmMemFixed::Free(a_ptr);
+  }
+}

+ 171 - 0
gmsrc/src/binds/gmGCRootUtil.h

@@ -0,0 +1,171 @@
+#ifndef GMGCROOTUTIL_H
+#define GMGCROOTUTIL_H
+
+//
+// gmGCRootUtil.h
+//
+
+#include "gmThread.h"
+
+//
+// This file has containers and utilities for implementing gmGCRoot
+// You may replace these implementations with your own containers with relative ease.
+//
+
+// Fwd decls
+class gmGCRootManager;
+class gmgcrHolder;
+
+
+// STL version
+#include <map>
+#include <vector>
+
+template<typename KEY, typename VALUE>
+class gmgcrMap 
+{
+  typename std::map<KEY, VALUE> m_map;
+
+public:
+  
+  // Iterator, derive or contain native container iterator
+  class Iterator 
+  {
+  public:
+    typename std::map<KEY, VALUE>::iterator m_it;
+  };
+  
+  // Get Value at unique Key, return true if found
+  int GetAt(KEY a_key, VALUE& a_value)
+  {
+    typename std::map<KEY, VALUE>::iterator it = m_map.find(a_key);
+    if( it != m_map.end() )
+    {
+      a_value = (*it).second;
+      return true;
+    }
+    return false;
+  };
+
+  // Set Value and unique Key
+  void SetAt(KEY a_key, VALUE& a_value)
+  {
+    m_map[a_key] = a_value;
+  }
+  
+  // Remove pair at unique Key
+  void RemoveAt(KEY a_key)
+  {
+    m_map.erase(a_key);
+  }
+  
+  // Iteration Get first pair
+  void GetFirst(Iterator& a_it)
+  {
+    a_it.m_it = m_map.begin();
+  }
+  
+  // Iteration Get next pair
+  void GetNext(Iterator& a_it)
+  {
+    if( a_it.m_it != m_map.end() )
+    {
+      ++a_it.m_it;
+    }
+  }
+  
+  // Iteration Is iterator valid.  Return false if not.
+  int IsNull(Iterator& a_it)
+  {
+    if( a_it.m_it == m_map.end() )
+    {
+      return true;
+    }
+    return false;
+  }
+  
+  // Iteration Get Value from iterator, return false if failed
+  int GetValue(Iterator& a_it, VALUE& a_value)
+  {
+    if( a_it.m_it != m_map.end() )
+    {
+      a_value = (*a_it.m_it).second;
+      return true;
+    }
+    return false;
+  }
+
+};
+
+
+// Array, derive or contain array style container
+template<typename TYPE>
+class gmgcrArray
+{
+  typename std::vector<TYPE> m_array;
+
+public:
+
+  // Set initial array size and any grow / presize for efficiency
+  void Init(int a_initialSize, int a_growSize)
+  {
+    SetSize(a_initialSize);
+  }
+  
+  // Set array size, and grow size
+  void SetSize(int a_size)
+  {
+    m_array.resize(a_size);
+  }
+
+  // Return number of elements in array
+  int GetSize()
+  {
+    return m_array.size();
+  }
+
+  // Return last index eg. GetSize()-1
+  int GetLastIndex()
+  {
+    return GetSize() - 1;
+  }
+
+  // Remove element at index
+  void RemoveAt(int a_index)
+  {
+    m_array.erase(m_array.begin() + a_index);
+  }
+
+  // Access element at index (by reference)
+  TYPE& operator[](int a_index)
+  {
+    return m_array[a_index];
+  }
+
+  // Add (default) element to array, returning index of new element
+  int AddEmpty()
+  {
+    int oldCount = GetSize(); // old count becomes new index
+    SetSize(oldCount+1);
+    return oldCount;
+  }
+
+};
+
+
+// Allocator, derive or contain frame style allocator
+class gmgcrMemFixed : public gmMemFixed 
+{
+public:
+  
+  gmgcrMemFixed();
+  
+  // Allocate and construct
+  gmgcrHolder* Alloc();
+  
+  // Destruct and free
+  void Free(gmgcrHolder* a_ptr);
+  
+};
+
+#endif  //GMGCROOTUTIL_H

+ 60 - 0
gmsrc/src/binds/gmHelpers.cpp

@@ -0,0 +1,60 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmHelpers.h"
+
+
+/*!
+  \brief gmRandomInt() returns a random int b/n two values
+  \return number is >= min and < max (exclusive of max)
+*/
+int gmRandomInt(int a_min, int a_max)
+{
+  if(a_min > a_max)
+  {
+    int temp = a_max;
+    a_max = a_min;
+    a_min = temp;
+  }
+  else if(a_min == a_max)
+  {
+    return a_min; // hmmm, not good.
+  }
+ 
+  int randVal = rand();
+  int val = (randVal % (a_max - a_min)) + a_min;
+ 
+  return val;
+}
+ 
+
+/*!
+  \brief gmRandomInt() returns a random int b/n two values
+  \return number is >= min and < max (exclusive of max)
+*/
+float gmRandomFloat(float a_min, float a_max)
+{
+  if(a_min > a_max)
+  {
+    float temp = a_max;
+    a_max = a_min;
+    a_min = temp;
+  }
+  else if(a_min == a_max)
+  {
+    return a_min; // hmmm, not good.
+  }
+  
+  int randVal = rand() % RAND_MAX;
+  float frandVal = (float)randVal / (float)RAND_MAX;
+  return a_min + (frandVal * (a_max - a_min));
+}

+ 179 - 0
gmsrc/src/binds/gmHelpers.h

@@ -0,0 +1,179 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMHELPERS_H_
+#define _GMHELPERS_H_
+
+#include "gmConfig.h"
+#include "gmThread.h"
+#include <math.h>
+
+//
+// Helpers
+//
+
+#define GM_PI_VALUE 3.1415927f
+
+// Clamp value between to range
+template <class TYPE>
+GM_FORCEINLINE TYPE gmClamp(const TYPE a_min, const TYPE a_value, const TYPE a_max)
+{
+  if(a_value < a_min)
+  {
+    return a_min;
+  }
+  else if(a_value > a_max)
+  {
+    return a_max;
+  }
+  return a_value;
+}
+
+template <class TYPE>
+GM_FORCEINLINE TYPE gmMin3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
+{
+  if(a_y < a_x)
+  {
+    if(a_z <  a_y)
+    {
+      return a_z;
+    }
+    else
+    {
+      return a_y;
+    }
+  }
+  else
+  { 
+    if (a_z < a_x)
+    {
+      return a_z;
+    }
+    else
+    {
+      return a_x;
+    }
+  }
+}
+
+template <class TYPE>
+GM_FORCEINLINE TYPE gmMax3(const TYPE a_x, const TYPE a_y, const TYPE a_z)
+{
+  if(a_y > a_x)
+  {
+    if(a_z >  a_y)
+    {
+      return a_z;
+    }
+    else
+    {
+      return a_y;
+    }
+  }
+  else
+  { 
+    if (a_z > a_x)
+    {
+      return a_z;
+    }
+    else
+    {
+      return a_x;
+    }
+  }
+}
+
+GM_FORCEINLINE float gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex)
+{
+  if(a_thread->ParamType(a_paramIndex) == GM_INT)
+  {
+    return (float)a_thread->Param(a_paramIndex).m_value.m_int;
+  }
+  else
+  {
+    return a_thread->Param(a_paramIndex).m_value.m_float;
+  }
+}
+
+
+GM_FORCEINLINE bool gmGetFloatOrIntParamAsFloat(gmThread * a_thread, int a_paramIndex, float& a_retValue)
+{
+  if(a_thread->ParamType(a_paramIndex) == GM_INT)
+  {
+    a_retValue = (float)a_thread->Param(a_paramIndex).m_value.m_int;
+    return true;
+  }
+  else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
+  {
+    a_retValue = a_thread->Param(a_paramIndex).m_value.m_float;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+GM_FORCEINLINE int gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex)
+{
+  if(a_thread->ParamType(a_paramIndex) == GM_INT)
+  {
+    return a_thread->Param(a_paramIndex).m_value.m_int;
+  }
+  else
+  {
+    return (int)a_thread->Param(a_paramIndex).m_value.m_float;
+  }
+}
+
+GM_FORCEINLINE bool gmGetFloatOrIntParamAsInt(gmThread * a_thread, int a_paramIndex, int& a_retValue)
+{
+  if(a_thread->ParamType(a_paramIndex) == GM_INT)
+  {
+    a_retValue = a_thread->Param(a_paramIndex).m_value.m_int;
+    return true;
+  }
+  else if(a_thread->ParamType(a_paramIndex) == GM_FLOAT)
+  {
+    a_retValue = (int)a_thread->Param(a_paramIndex).m_value.m_float;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+/*!
+  \brief gmRandomInt() returns a random int b/n two values
+         Beware of overflow since ints are only 32bit on Intel
+  \return number is >= min and < max (exclusive of max)
+*/
+int gmRandomInt(int a_min, int a_max);
+
+/*!
+  \brief gmRandomInt() returns a random int b/n two values
+         Note this is a low precision random value since it is generated from an int.
+  \return number is >= min and < max (exclusive of max)
+*/
+float gmRandomFloat(float a_min, float a_max);
+
+/*!
+  \brief Returns the sine and cosine of values
+         Note should make platform specific version
+*/
+GM_FORCEINLINE void gmSinCos(const float a_angle, float& a_sin, float& a_cos)
+{
+  a_sin = (float)sinf(a_angle);
+  a_cos = (float)cosf(a_angle);
+}
+
+#endif // _GMHELPERS_H_

+ 854 - 0
gmsrc/src/binds/gmMathLib.cpp

@@ -0,0 +1,854 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmMathLib.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+#include "gmHelpers.h"
+#include "gmUtil.h"
+#include <math.h>
+
+
+//
+// Conversion
+//
+
+int GM_CDECL gmfToString(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+
+  if(GM_INT == var->m_type)
+  {
+    char numberAsStringBuffer[64];
+    sprintf(numberAsStringBuffer, "%d", var->m_value.m_int); // this won't be > 64 chars
+    a_thread->PushNewString(numberAsStringBuffer);
+  }
+  else if (GM_FLOAT == var->m_type)
+  {
+    char numberAsStringBuffer[64];
+
+    sprintf(numberAsStringBuffer, "%f", var->m_value.m_float); // this won't be > 64 chars
+
+    a_thread->PushNewString(numberAsStringBuffer);
+  }
+  else if (GM_STRING == var->m_type)
+  {
+    a_thread->PushString( (gmStringObject *) GM_OBJECT(var->m_value.m_ref) );
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+
+  
+  return GM_OK;
+}
+
+
+
+int GM_CDECL gmfToFloat(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+
+  if(GM_INT == var->m_type)
+  {
+    a_thread->PushFloat((float)var->m_value.m_int);
+  }
+  else if (GM_FLOAT == var->m_type)
+  {
+    a_thread->PushFloat(var->m_value.m_float);
+  }
+  else if (GM_STRING == var->m_type)
+  {
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char * cstr = * strObj;
+
+    a_thread->PushFloat( (float)atof(cstr) );
+  }
+  else
+  {
+    //a_thread->PushFloat( 0.0f );
+    return GM_EXCEPTION;
+  }
+  
+  return GM_OK;
+}
+
+
+
+int GM_CDECL gmfToInt(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+
+  if(GM_INT == var->m_type)
+  {
+    a_thread->PushInt(var->m_value.m_int);
+  }
+  else if (GM_FLOAT == var->m_type)
+  {
+    a_thread->PushInt( (int)var->m_value.m_float);
+  }
+  else if (GM_STRING == var->m_type)
+  {
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char * cstr = * strObj;
+
+    a_thread->PushInt( atoi(cstr) );
+  }
+  else
+  {
+    //a_thread->PushInt( 0 );
+    return GM_EXCEPTION;
+  }
+  
+  return GM_OK;
+}
+
+
+
+//
+// Math
+//
+
+
+
+// int randint(int a_min, int a_max);
+// returned number is >= a_min and < a_max (exclusive of max)
+static int GM_CDECL gmfRandInt(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_INT_PARAM(min, 0);
+  GM_CHECK_INT_PARAM(max, 1);
+  
+  a_thread->PushInt( gmRandomInt(min, max) );
+
+  return GM_OK;
+}
+
+
+
+// float randfloat(float a_min, float a_max);
+// returned number is >= a_min and < a_max (exclusive of max)
+static int GM_CDECL gmfRandFloat(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_FLOAT_PARAM(min, 0);
+  GM_CHECK_FLOAT_PARAM(max, 1);
+
+  a_thread->PushFloat( gmRandomFloat(min, max) );
+
+  return GM_OK;
+}
+
+
+
+// void randseed(int a_seed);
+static int GM_CDECL gmfRandSeed(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(seed, 0);
+  
+  srand(seed);
+  
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfAbs(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_INT)
+  {
+    int intValue = a_thread->Param(0).m_value.m_int;
+    a_thread->PushInt(abs(intValue));
+
+    return GM_OK;
+  }
+  else if(a_thread->ParamType(0) == GM_FLOAT)
+  {
+    float floatValue = a_thread->Param(0).m_value.m_float;
+    a_thread->PushFloat((float)fabsf(floatValue));
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+
+static int GM_CDECL gmfSqrt(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_INT)
+  {
+    int intValue = a_thread->Param(0).m_value.m_int;
+    a_thread->PushInt((int)sqrtf((float)intValue));
+
+    return GM_OK;
+  }
+  else if(a_thread->ParamType(0) == GM_FLOAT)
+  {
+    float floatValue = a_thread->Param(0).m_value.m_float;
+    a_thread->PushFloat(sqrtf(floatValue));
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+
+static int GM_CDECL gmfPower(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+
+  int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+  if(minType < GM_INT)
+  {
+    return GM_EXCEPTION;
+  }
+  int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+
+  if(maxType == GM_INT)
+  {
+    int valX = a_thread->Param(0).m_value.m_int;
+    int valY = a_thread->Param(1).m_value.m_int;
+    a_thread->PushInt((int)pow((float)valX, (float)valY));
+
+    return GM_OK;
+  }
+  else if(maxType == GM_FLOAT)
+  {
+    float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
+    float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
+    a_thread->PushFloat((float)pow(valX, valY));
+
+    return GM_OK;
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+}
+
+
+
+static int GM_CDECL gmfFloor(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
+  {
+    int intValue = a_thread->Param(0).m_value.m_int;
+    a_thread->PushInt(intValue);
+
+    return GM_OK;
+  }
+  else if(a_thread->ParamType(0) == GM_FLOAT)
+  {
+    float floatValue = a_thread->Param(0).m_value.m_float;
+    a_thread->PushFloat(floorf(floatValue));
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+
+static int GM_CDECL gmfCeil(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
+  {
+    int intValue = a_thread->Param(0).m_value.m_int;
+    a_thread->PushInt(intValue);
+
+    return GM_OK;
+  }
+  else if(a_thread->ParamType(0) == GM_FLOAT)
+  {
+    float floatValue = a_thread->Param(0).m_value.m_float;
+    a_thread->PushFloat(ceilf(floatValue));
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+
+static int GM_CDECL gmfRound(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_INT) //Do nothing if Int
+  {
+    int intValue = a_thread->Param(0).m_value.m_int;
+    a_thread->PushInt(intValue);
+
+    return GM_OK;
+  }
+  else if(a_thread->ParamType(0) == GM_FLOAT)
+  {
+    float floatValue = a_thread->Param(0).m_value.m_float;
+    a_thread->PushFloat(floorf(floatValue + 0.5f));
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+
+static int GM_CDECL gmfDegToRad(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat( floatValue * (GM_PI_VALUE / 180.0f) );
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfRadToDeg(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat( floatValue * (180.0f / GM_PI_VALUE) );
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfSin(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(sinf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfASin(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(asinf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfCos(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(cosf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfACos(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(acosf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfTan(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(tanf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfATan(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  float floatValue;
+
+  if(a_thread->ParamType(0) == GM_INT) { floatValue = (float) a_thread->Param(0).m_value.m_int; }
+  else if(a_thread->ParamType(0) == GM_FLOAT) { floatValue = a_thread->Param(0).m_value.m_float; }
+  else { return GM_EXCEPTION; }
+
+  a_thread->PushFloat(atanf(floatValue));
+
+  return GM_OK;
+}
+
+
+
+//returns the arctangent of y/x
+static int GM_CDECL gmfATan2(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+
+  float floatValueY;
+  float floatValueX;
+
+  if(a_thread->ParamType(0) == GM_INT) {floatValueY = (float) a_thread->Param(0).m_value.m_int;}
+  else if(a_thread->ParamType(0) == GM_FLOAT) {floatValueY = a_thread->Param(0).m_value.m_float;}
+  else {return GM_EXCEPTION;}
+
+  if(a_thread->ParamType(1) == GM_INT) {floatValueX = (float) a_thread->Param(1).m_value.m_int;}
+  else if(a_thread->ParamType(1) == GM_FLOAT) {floatValueX = a_thread->Param(1).m_value.m_float;}
+  else {return GM_EXCEPTION;}
+
+  a_thread->PushFloat(atan2f(floatValueY, floatValueX));
+
+  return GM_OK;
+}
+
+
+
+static int GM_CDECL gmfLog(gmThread * a_thread)
+{
+  int numParams = GM_THREAD_ARG->GetNumParams();
+
+  if(numParams == 1) //Natural log
+  {
+    if(a_thread->ParamType(0) == GM_INT) 
+    {
+      float floatValue = (float) a_thread->Param(0).m_value.m_int;
+      a_thread->PushInt( (int) log(floatValue) );
+      return GM_OK;
+    }
+    else if(a_thread->ParamType(0) == GM_FLOAT) 
+    {
+      float floatValue = (float) a_thread->Param(0).m_value.m_float;
+
+      a_thread->PushFloat( logf(floatValue) );
+      return GM_OK;
+    }
+    else {return GM_EXCEPTION;}
+  }
+  else if(numParams == 2) //Log to base params: base, value
+  {
+    int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+    if(minType < GM_INT)
+    {
+      return GM_EXCEPTION;
+    }
+    int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+
+    if(maxType == GM_INT)
+    {
+      int base = a_thread->Param(0).m_value.m_int;
+      int value = a_thread->Param(1).m_value.m_int;
+      
+      a_thread->PushInt( (int)( log10f((float)value) / log10f((float)base) ) );
+
+      return GM_OK;
+    }
+    else if(maxType == GM_FLOAT)
+    {
+      float base = gmGetFloatOrIntParamAsFloat(a_thread, 0);
+      float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
+      
+      a_thread->PushFloat( (float)( log10(value) / log10(base) ) );
+
+      return GM_OK;
+    }
+    else
+    {
+      return GM_EXCEPTION;
+    }
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+}
+
+
+
+static int GM_CDECL gmfMin(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+
+  int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+  if(minType < GM_INT)
+  {
+    return GM_EXCEPTION;
+  }
+
+  int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+
+  if(maxType == GM_INT)
+  {
+    int valX = a_thread->Param(0).m_value.m_int;
+    int valY = a_thread->Param(1).m_value.m_int;
+    a_thread->PushInt( gmMin(valX, valY) );
+
+    return GM_OK;
+  }
+  else if(maxType == GM_FLOAT)
+  {
+    float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
+    float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
+    a_thread->PushFloat( gmMin(valX, valY) );
+
+    return GM_OK;
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+}
+
+
+
+static int GM_CDECL gmfMax(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+
+  int minType = gmMin<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+  if(minType < GM_INT)
+  {
+    return GM_EXCEPTION;
+  }
+
+  int maxType = gmMax<int>(a_thread->ParamType(0), a_thread->ParamType(1));
+
+  if(maxType == GM_INT)
+  {
+    int valX = a_thread->Param(0).m_value.m_int;
+    int valY = a_thread->Param(1).m_value.m_int;
+    a_thread->PushInt( gmMax(valX, valY) );
+
+    return GM_OK;
+  }
+  else if(maxType == GM_FLOAT)
+  {
+    float valX = gmGetFloatOrIntParamAsFloat(a_thread, 0);
+    float valY = gmGetFloatOrIntParamAsFloat(a_thread, 1);
+    a_thread->PushFloat( gmMax(valX, valY) );
+
+    return GM_OK;
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+}
+
+
+
+static int GM_CDECL gmfClamp(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(3);
+
+  //params: min, value, max
+
+  int minType = gmMin3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
+  if(minType < GM_INT)
+  {
+    return GM_EXCEPTION;
+  }
+
+  int maxType = gmMax3(a_thread->ParamType(0), a_thread->ParamType(1), a_thread->ParamType(2));
+
+  if(maxType == GM_INT)
+  {
+    int limitMin = a_thread->Param(0).m_value.m_int;
+    int value = a_thread->Param(1).m_value.m_int;
+    int limitMax = a_thread->Param(2).m_value.m_int;
+    
+    a_thread->PushInt( gmClamp(limitMin, value, limitMax) );
+
+    return GM_OK;
+  }
+  else if(maxType == GM_FLOAT)
+  {
+    float limitMin = gmGetFloatOrIntParamAsFloat(a_thread, 0);
+    float value = gmGetFloatOrIntParamAsFloat(a_thread, 1);
+    float limitMax = gmGetFloatOrIntParamAsFloat(a_thread, 2);
+    
+    a_thread->PushFloat( gmClamp(limitMin, value, limitMax) );
+
+    return GM_OK;
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+}
+
+
+//
+// Libs and bindings
+//
+
+
+static gmFunctionEntry s_mathLib[] = 
+{ 
+  /*gm
+    \lib math
+  */
+  /*gm
+    \function abs
+    \brief abs will return the absolute value of the passed int \ float
+    \param int\float
+    \return int\float abs(param)
+  */
+  {"abs", gmfAbs},
+  /*gm
+    \function sqrt
+    \brief sqrt will return the square root of the passed int \ float
+    \param int\float
+    \return int\float sqrt(param)
+  */
+  {"sqrt", gmfSqrt},
+  /*gm
+    \function sqrt
+    \brief sqrt will return the square root of the passed int \ float
+    \param int\float A
+    \param int\float B
+    \return int\float A to the power of B
+  */
+  {"power", gmfPower},
+  /*gm
+    \function floor
+    \brief floor
+    \param float A
+    \return float floor(A)
+  */
+  {"floor", gmfFloor},
+  /*gm
+    \function ceil
+    \brief ceil
+    \param float A
+    \return float ceil(A)
+  */
+  {"ceil", gmfCeil},
+  /*gm
+    \function round
+    \brief round
+    \param float A
+    \return float round(A)
+  */
+  {"round", gmfRound},
+  /*gm
+    \function degtorad
+    \brief degtorad will convert degrees to radians
+    \param float\int deg
+    \return float
+  */
+  {"degtorad", gmfDegToRad},
+  /*gm
+    \function radtodeg
+    \brief radtodeg will convert radians to degrees
+    \param float\int rad
+    \return float
+  */
+  {"radtodeg", gmfRadToDeg},
+  /*gm
+    \function cos
+    \brief cos will return the radian cosine
+    \param float
+    \return float
+  */
+  {"cos", gmfCos},
+  /*gm
+    \function sin
+    \brief sin will return the radian sine
+    \param float
+    \return float
+  */
+  {"sin", gmfSin},
+  /*gm
+    \function tan
+    \brief tan will return the radian tan (sin/cos)
+    \param float
+    \return float
+  */
+  {"tan", gmfTan},
+  /*gm
+    \function acos
+    \brief acos will return the radian arc cosine
+    \param float
+    \return float
+  */
+  {"acos", gmfACos},
+  /*gm
+    \function asin
+    \brief asin will return the radian arc sine
+    \param float
+    \return float
+  */
+  {"asin", gmfASin},
+  /*gm
+    \function atan
+    \brief atan will return the radian arc tangent
+    \param float
+    \return float
+  */
+  {"atan", gmfATan},
+  /*gm
+    \function atan
+    \brief atan will return the radian arc tangent of x / y
+    \param float x
+    \param float y
+    \return float
+  */
+  {"atan2", gmfATan2},
+  /*gm
+    \function log
+    \brief log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base
+    \param float natural \ base
+    \param float value (optional)
+    \return float
+  */
+  {"log", gmfLog},
+  /*gm
+    \function min
+    \brief min will return the min of the 2 passed values
+    \param float\int A
+    \param float\int B
+    \return float \ int min(A, B)
+  */
+  {"min", gmfMin},
+  /*gm
+    \function max
+    \brief max will return the max of the 2 passed values
+    \param float\int A
+    \param float\int B
+    \return float \ int max(A, B)
+  */
+  {"max", gmfMax},
+  /*gm
+    \function clamp
+    \brief clamp will return the clamed value. clamp(min, val, max)
+    \param float\int MIN
+    \param float\int VALUE
+    \param float\int MAX
+    \return float\int value clamped to min, max
+  */
+  {"clamp", gmfClamp},
+  /*gm
+    \function randint
+    \brief randint will return a random int from lower inclusive to upper.
+    \param int lower inclusive
+    \param int upper
+    \return int 
+  */
+  {"randint", gmfRandInt},
+  /*gm
+    \function randfloat
+    \brief randfloat will return a random float from lower inclusive to upper.
+    \param float lower inclusive
+    \param float upper
+    \return float 
+  */
+  {"randfloat", gmfRandFloat},
+  /*gm
+    \function randseed
+    \brief randseed will seed the random number generator
+    \param int seed
+  */
+  {"randseed", gmfRandSeed},
+};
+
+
+
+static gmFunctionEntry s_intLib[] = 
+{ 
+  {"String", gmfToString},
+  {"Float", gmfToFloat},
+  {"Int", gmfToInt},
+};
+
+
+
+static gmFunctionEntry s_floatLib[] = 
+{ 
+  {"String", gmfToString},
+  {"Float", gmfToFloat},
+  {"Int", gmfToInt},
+};
+
+
+void gmBindMathLib(gmMachine * a_machine)
+{
+  a_machine->RegisterLibrary(s_mathLib, sizeof(s_mathLib) / sizeof(s_mathLib[0]));
+  a_machine->RegisterTypeLibrary(GM_INT, s_intLib, sizeof(s_intLib) / sizeof(s_intLib[0]));
+  a_machine->RegisterTypeLibrary(GM_FLOAT, s_floatLib, sizeof(s_floatLib) / sizeof(s_floatLib[0]));
+}
+

+ 21 - 0
gmsrc/src/binds/gmMathLib.h

@@ -0,0 +1,21 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMMATHLIB_H_
+#define _GMMATHLIB_H_
+
+#include "gmConfig.h"
+
+class gmMachine;
+
+void gmBindMathLib(gmMachine * a_machine);
+
+#endif // _GMMATHLIB_H_

+ 1170 - 0
gmsrc/src/binds/gmStringLib.cpp

@@ -0,0 +1,1170 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmStringLib.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+#include "gmHelpers.h"
+
+
+//
+// String
+//
+
+
+#define GM_WHITE_SPACE " \t\v\r\n"
+
+
+static int GM_CDECL gmfStringLeft(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(count, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+  count = gmClamp(0, count, length);
+
+  char * buffer = (char *) alloca(count + 1);
+  memcpy(buffer, str, count);
+  buffer[count] = 0;
+
+  a_thread->PushNewString(buffer);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringRight(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(count, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+  
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+  count = gmClamp(0, count, length);
+
+  char * buffer = (char *) alloca(count + 1);
+  memcpy(buffer, str + length - count, count);
+  buffer[count] = 0;
+
+  a_thread->PushNewString(buffer);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringRightAt(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(index, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+  
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+  index = gmClamp(0, index, length);
+  int count = (length - index);
+  char * buffer = (char *) alloca(count + 1);
+  memcpy(buffer, str + index, count);
+  buffer[count] = 0;
+
+  a_thread->PushNewString(buffer, count);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringMid(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+
+  int first = 0, count = 0;
+
+  if(!gmGetFloatOrIntParamAsInt(a_thread, 0, first)) {return GM_EXCEPTION;}
+  if(!gmGetFloatOrIntParamAsInt(a_thread, 1, count)) {return GM_EXCEPTION;}
+
+  const gmVariable * var = a_thread->GetThis();
+  
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+
+  //Check bounds
+  if (first < 0)
+  {
+    first = 0;
+  }
+  if (count < 0)
+  {
+    count = 0;
+  }
+  if (first + count > length)
+  {
+    count = length - first;
+  }
+  if (first > length)
+  {
+    count = 0;
+  }
+
+  char * buffer = (char *) alloca(count + 1);
+  memcpy(buffer, str + first, count);
+  buffer[count] = 0;
+
+  a_thread->PushNewString(buffer);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringLength(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+
+  a_thread->PushInt(strObj->GetLength());
+  
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringIsEmpty(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+
+  a_thread->PushInt( (strObj->GetLength() == 0) );
+  
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringCompare(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const gmVariable * var = a_thread->GetThis();
+  
+    GM_ASSERT(var->m_type == GM_STRING);
+
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char* thisStr = (const char *) *strObj;
+    const char* otherStr = a_thread->ParamString(0);
+
+    a_thread->PushInt(strcmp(thisStr, otherStr));
+  
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+static int GM_CDECL gmfStringCompareNoCase(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const gmVariable * var = a_thread->GetThis();
+  
+    GM_ASSERT(var->m_type == GM_STRING);
+
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char* thisStr = (const char *) *strObj;
+    const char* otherStr = a_thread->ParamString(0);
+
+    a_thread->PushInt(_gmstricmp(thisStr, otherStr));
+  
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+static int GM_CDECL gmfStringLower(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+  char * buffer = (char *) alloca(length + 1);
+  memcpy(buffer, str, length + 1);
+
+  strlwr(buffer);
+
+  a_thread->PushNewString(buffer, length);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringUpper(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  
+  int length = strObj->GetLength();
+  char * buffer = (char *) alloca(length + 1);
+  memcpy(buffer, str, length + 1);
+  
+  strupr(buffer);
+
+  a_thread->PushNewString(buffer, length);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfStringSpanIncluding(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const gmVariable * var = a_thread->GetThis();
+  
+    GM_ASSERT(var->m_type == GM_STRING);
+
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char * thisStr = (const char *) *strObj;
+    const char * otherStr = a_thread->ParamString(0);
+    
+    int offset = strspn(thisStr, otherStr);
+    char * buffer = (char *) alloca(offset + 1);
+    memcpy(buffer, thisStr, offset);
+    buffer[offset] = 0;
+
+    a_thread->PushNewString(buffer, offset);
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+static int GM_CDECL gmfStringSpanExcluding(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const gmVariable * var = a_thread->GetThis();
+  
+    GM_ASSERT(var->m_type == GM_STRING);
+
+    gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+    const char * thisStr = (const char *) *strObj;
+    const char * otherStr = a_thread->ParamString(0);
+    
+    int offset = strcspn(thisStr, otherStr);
+    char * buffer = (char *) alloca(offset + 1);
+    memcpy(buffer, thisStr, offset);
+    buffer[offset] = 0;
+
+    a_thread->PushNewString(buffer, offset);
+
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+// string.AppendPath(a_appendString, a_endWithSlash);
+// Append this string with another string, fixing for slashes
+// a_endWithSlash is optional, default is false, removing trailing slashes.
+static int GM_CDECL gmfStringAppendPath(gmThread * a_thread)
+{
+  //Need at least 1 parameter
+  if(a_thread->GetNumParams() < 1)
+  {
+    return GM_EXCEPTION;
+  }
+
+  //Optional trailing slash flag
+  int PutTrailingSlash = a_thread->ParamInt(1, false);
+
+  if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const gmVariable * varA = a_thread->GetThis();
+  
+    GM_ASSERT(varA->m_type == GM_STRING);
+
+    gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(varA->m_value.m_ref);
+    gmStringObject * strObjB = a_thread->ParamStringObject(0);
+    const char* cStrA = strObjA->GetString();
+    const char* cStrB = strObjB->GetString();
+    int lenA = strObjA->GetLength();
+    int lenB = strObjB->GetLength();
+
+    int curLength = lenA;
+    int appLength = lenB;
+
+    //Alloc buffer on stack is fine, path strings cannot be long
+    char * buffer = (char *) alloca(curLength + appLength + 2);
+
+    if(lenA > 0)
+    {
+      memcpy(buffer, cStrA, lenA);
+
+
+      //Make sure first part has a slash
+      if(buffer[curLength-1] != '\\' && buffer[curLength-1] != '/')
+      {
+        buffer[curLength] = '\\';
+        curLength += 1;
+      }
+    }
+
+    if(lenB > 0)
+    {
+      //Remove slash from start of append string
+      const char* startOfAppend = cStrB;
+      if((startOfAppend[0] == '\\') || (startOfAppend[0] == '/'))
+      {
+        startOfAppend += 1;
+        appLength -= 1;
+      }
+
+      //Append the string
+      memcpy(&buffer[curLength], startOfAppend, appLength);
+    }
+
+    int newLength = curLength + appLength;
+  
+    if(PutTrailingSlash && (newLength > 0)) //Only add slash if string is not empty
+    {
+      //Make sure path ends with slash
+      if(buffer[newLength-1] != '\\' && buffer[newLength-1] != '/')
+      {
+        buffer[newLength] = '\\';
+        newLength += 1;
+      }
+    }
+    else
+    {
+      //Make sure path does not end with slash
+      if(buffer[newLength-1] == '\\' || buffer[newLength-1] == '/')
+      {
+        newLength -= 1;
+      }
+    }
+
+    //Make sure it is terminated
+    buffer[newLength] = 0;
+
+    a_thread->PushNewString(buffer, newLength);
+  
+    return GM_OK;
+  }
+
+  return GM_EXCEPTION;
+}
+
+
+// string.RemoveInvalidChars(a_replaceChar, a_invalidSet)
+// eg. "File Name#1.tga".RemoveInvalidChars("_","# ") returns "File_Name_1.tga"
+// Note: Parameters are optional.
+static int GM_CDECL gmfStringReplaceCharsInSet(gmThread * a_thread)
+{
+  GM_INT_PARAM(repCharInt, 0, '_');
+  GM_STRING_PARAM(invalidCharSet, 1, " \\/:-+");
+
+  char repChar = (char)repCharInt; //Convert full int to char
+  const gmVariable * varA = a_thread->GetThis();
+  
+  GM_ASSERT(varA->m_type == GM_STRING);
+
+  gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(varA->m_value.m_ref);
+  const char* cStrA = strObjA->GetString();
+  int lenA = strObjA->GetLength();
+
+  //Alloc buffer on stack is fine, path strings cannot be long
+  char * buffer = (char *) alloca(lenA + 1);
+  memcpy(buffer, cStrA, lenA + 1);
+
+  int validPos;
+
+  //Check that replacement char is NOT in invalid set, otherwise endless loop...
+  if(strchr(invalidCharSet, repChar))
+  {
+    return GM_EXCEPTION;
+  }
+
+  for(;;)
+  {
+    validPos = strcspn(buffer, invalidCharSet);
+    if(validPos != lenA)
+    {
+      buffer[validPos] = repChar;
+    }
+    else
+    {
+      break;
+    }
+  }
+
+  a_thread->PushNewString(buffer, lenA);
+      
+  return GM_OK;
+}
+
+
+// string.AppendPath(a_appendString, a_endWithSlash);
+// Append this string with another string, fixing for slashes
+// a_endWithSlash is optional, default is false, removing trailing slashes.
+static void GM_CDECL gmStringOpAppendPath(gmThread * a_thread, gmVariable * a_operands)
+{
+  // Both types must be strings
+  if(a_operands[0].m_type != GM_STRING ||
+     a_operands[1].m_type != GM_STRING)
+  {
+    a_operands[0].m_type = GM_NULL;
+    a_operands[0].m_value.m_ref = 0;
+    return;
+  }
+
+  gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(a_operands[0].m_value.m_ref);
+  gmStringObject * strObjB = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
+  const char* cStrA = strObjA->GetString();
+  const char* cStrB = strObjB->GetString();
+  int lenA = strObjA->GetLength();
+  int lenB = strObjB->GetLength();
+
+  int curLength = lenA;
+  int appLength = lenB;
+
+  //Alloc buffer on stack is fine, path strings cannot be long
+  char * buffer = (char *) alloca(curLength + appLength + 2);
+
+  if(lenA <= 0)
+  {
+    a_operands[0] = a_operands[1];
+  }
+
+  if(lenB <= 0)
+  {
+    return;
+  }
+
+  memcpy(buffer, cStrA, lenA);
+
+  //Make sure first part has a slash
+  if(buffer[curLength-1] != '\\' && buffer[curLength-1] != '/')
+  {
+    buffer[curLength] = '\\';
+    curLength += 1;
+  }
+
+  //Remove slash from start of append string
+  const char* startOfAppend = cStrB;
+  if((startOfAppend[0] == '\\') || (startOfAppend[0] == '/'))
+  {
+    startOfAppend += 1;
+    appLength -= 1;
+  }
+
+  //Append the string
+  memcpy(&buffer[curLength], startOfAppend, appLength);
+
+  int newLength = curLength + appLength;
+
+  //Make sure it is terminated
+  buffer[newLength] = 0;
+
+  a_operands[0].m_type = GM_STRING;
+  a_operands[0].m_value.m_ref = a_thread->GetMachine()->AllocStringObject(buffer, newLength)->GetRef();
+}
+
+
+// int string.Find(char/string a_charOrStringToFind, int a_startOffset == 0);
+// Find a character or character string in a string.
+// Returns character offset or -1 if not found.
+static int GM_CDECL gmStringFind(gmThread * a_thread)
+{
+  int numParams = GM_THREAD_ARG->GetNumParams();
+  int startOffset = 0;
+  char* retCharPtr = NULL;
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char* thisStr = (const char *) *strObj;
+  
+  if(numParams == 2)
+  {
+    if(a_thread->ParamType(1) == GM_INT)
+    {
+      startOffset = a_thread->ParamInt(1); //Optional start offset param
+    }
+    else
+    {
+      return GM_EXCEPTION;
+    }
+  } 
+  else if(numParams < 1 || numParams > 2)
+  {
+    return GM_EXCEPTION;
+  }
+
+  //Check if this string is empty, or start offset out of range
+  if( (strObj->GetLength() == 0) || 
+      (startOffset > strObj->GetLength()) ||
+      (startOffset < 0) 
+    )
+  {
+    a_thread->PushInt( -1 ); //return Not Found
+    return GM_OK;
+  }
+
+  if(a_thread->ParamType(0) == GM_INT)
+  {
+    const char otherChar = (char)a_thread->ParamInt(0);
+    
+    //Find character
+    retCharPtr = (char*)strchr(thisStr + startOffset, otherChar);
+  }
+  else if(a_thread->ParamType(0) == GM_STRING)
+  {
+    const char* otherStr = a_thread->ParamString(0);
+
+    //Find string
+    retCharPtr = (char*)strstr(thisStr + startOffset, otherStr);
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+
+  // return -1 for not found, distance from beginning otherwise
+  int retOffset = (retCharPtr == NULL) ? -1 : (int)(retCharPtr - thisStr);
+  a_thread->PushInt(retOffset);
+
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringReverse(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+
+  int len = strlen(str);
+  if(len > 0)
+  {
+    char * buffer = (char *) alloca(len + 1); 
+    memcpy(buffer, str, len + 1); //Copy old string
+
+    while(len--)
+    {
+      buffer[len] = *(str++);
+    }
+
+    a_thread->PushNewString(buffer);
+  }
+  return GM_OK;
+}
+
+
+// int string.ReverseFind(char/string a_charOrStringToFind);
+// Find the last instance of a specific character in a string.
+// Returns character offset or -1 if not found.
+static int GM_CDECL gmStringReverseFind(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  const char* retCharPtr = NULL;
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * thisStrObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char* thisStr = (const char *) *thisStrObj;
+  
+  if(a_thread->ParamType(0) == GM_INT)
+  {
+    const char otherChar = (char)a_thread->ParamInt(0);
+    
+    //Find character
+    retCharPtr = strrchr(thisStr, otherChar);
+  }
+  else if(a_thread->ParamType(0) == GM_STRING)
+  {
+    gmStringObject * otherStrObj = a_thread->ParamStringObject(0);
+    const char* otherStr = a_thread->ParamString(0);
+
+    //Find string
+    const char* lastFoundPtr = NULL;
+    const char* newTestPtr = NULL;
+    const char* curTestPtr = thisStr;
+    const char* endThisStr = thisStr + thisStrObj->GetLength();
+    int searchStrLength = otherStrObj->GetLength();
+
+    //Search through string for last occurence
+    //Not very efficient, but very rarely used.
+    for(;;)
+    {
+      newTestPtr = strstr(curTestPtr, otherStr);
+      if(!newTestPtr)
+      {
+        break;
+      }
+      lastFoundPtr = newTestPtr;
+      curTestPtr = newTestPtr + searchStrLength;
+      if(curTestPtr > endThisStr)
+      {
+        break;
+      }
+    };
+
+    retCharPtr = lastFoundPtr;
+  }
+  else
+  {
+    return GM_EXCEPTION;
+  }
+
+  // return -1 for not found, distance from beginning otherwise
+  int retOffset = (retCharPtr == NULL) ? -1 : (int)(retCharPtr - thisStr);
+  a_thread->PushInt(retOffset);
+
+  return GM_OK;
+}
+
+
+// int string[int index]
+// Note: Because strings are constant and in a table, it is impossible to implement SetInd.
+static void GM_CDECL gmStringOpGetInd(gmThread * a_thread, gmVariable * a_operands)
+{
+  if( a_operands[0].m_type != GM_STRING ||
+      a_operands[1].m_type != GM_INT )
+  {
+    a_operands->Nullify();
+    return;
+  }
+
+  gmStringObject * strObjA = (gmStringObject *) GM_OBJECT(a_operands[0].m_value.m_ref);
+  const char* cStrA = strObjA->GetString();
+  int index = a_operands[1].m_value.m_int;
+  
+  if( index < 0 || index > strObjA->GetLength()-1 )
+  {
+    a_operands->Nullify();
+  }
+  else
+  {
+    a_operands->SetInt( (int)cStrA[index] );
+  }
+}
+
+// int string.GetAt(int a_index);
+// Returns character at offset or null if index out of range.
+static int GM_CDECL gmStringGetAt(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(index, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+
+  if(index < 0 || index >= strObj->GetLength())
+  {
+    a_thread->PushNull(); //Return null if index out of range
+    return GM_OK;
+  }
+
+  a_thread->PushInt(str[index]);
+  return GM_OK;
+}
+
+
+// string string.SetAt(int a_index, int a_char);
+// Returns string with modified character at offset, or original string if index out of range.
+static int GM_CDECL gmStringSetAt(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(index, 0);
+  GM_CHECK_INT_PARAM(newChar, 1);
+
+  const gmVariable * var = a_thread->GetThis();
+
+  GM_ASSERT(var->m_type == GM_STRING);
+
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+
+  if(index < 0 || index >= strLength)
+  {
+    a_thread->PushString(strObj); //Return original string if index out of range
+    return GM_OK;
+  }
+
+  char * buffer = (char *) alloca(strLength + 1); 
+  memcpy(buffer, str, strLength + 1); //Copy old string
+  buffer[index] = (char)newChar; //Set character in string
+
+  a_thread->PushNewString(buffer, strLength);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringTrimLeft(gmThread * a_thread)
+{
+  GM_STRING_PARAM(trim, 0, GM_WHITE_SPACE);
+
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  if(strlen(str) > 0)
+  {
+    while(*str && strchr(trim, *str))
+      ++str;
+    a_thread->PushNewString(str);
+  }
+  else
+    a_thread->PushString(strObj);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringTrimRight(gmThread * a_thread)
+{
+  GM_STRING_PARAM(trim, 0, GM_WHITE_SPACE);
+
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+  if(strLength > 0)
+  {
+    char * buffer = (char *) alloca(strLength + 1);
+    memcpy(buffer, str, strLength + 1); //Copy old string
+
+    // Find beginning of trailing matches by starting at end
+    char *lpsz = buffer + strLength;
+    while (--lpsz >= buffer && strchr(trim, *lpsz) != NULL) {}
+    ++lpsz;
+    *lpsz = '\0';
+
+    a_thread->PushNewString(buffer);
+  }
+  else
+  {
+    a_thread->PushString(strObj);
+  }
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringGetFilenameNoExt(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+
+  char * buffer = (char *) alloca(strLength + 1);
+  memcpy(buffer, str, strLength + 1); //Copy old string
+
+  char *lpsz = buffer + strLength;
+  while (--lpsz >= buffer && *lpsz != '\\' && *lpsz != '/') {}
+
+  buffer = ++lpsz;
+  strLength = strlen(buffer);
+  lpsz = buffer + strLength;
+  while (--lpsz >= buffer && *lpsz != '.') {}
+  if(*lpsz == '.') *lpsz = 0;
+
+  a_thread->PushNewString(buffer);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringGetFilename(gmThread * a_thread)
+{
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+
+  const char *lpsz = str + strLength;
+  while (--lpsz >= str && *lpsz != '\\' && *lpsz != '/') {}
+  ++lpsz;
+
+  a_thread->PushNewString(lpsz);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringGetExtension(gmThread * a_thread)
+{
+  GM_INT_PARAM(keepDot, 0, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+
+  const char *lpsz = str + strLength;
+  while (--lpsz >= str && *lpsz != '.') {}
+
+  if(*lpsz == '.')
+  {
+    if(!keepDot)
+    {
+      ++lpsz;
+    }
+    a_thread->PushNewString(lpsz);
+  }
+  else
+  {
+    a_thread->PushNewString("");
+  }
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringSetExtension(gmThread * a_thread)
+{
+  GM_STRING_PARAM(newExt, 0, "");
+
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+  int extLength = strlen(newExt);
+
+  if (extLength && newExt[0] == '.')
+  {
+    ++newExt;
+    extLength = strlen(newExt);
+  }
+
+  char *buffer = (char *) alloca(strLength + 1 + extLength);
+  memcpy(buffer, str, strLength + 1);
+
+  char *lpsz = buffer + strLength;
+  while (--lpsz >= buffer && *lpsz != '.') {}
+
+  if(*lpsz == '.')
+  {
+    *lpsz = '\0';
+    if (extLength)
+      sprintf(buffer, "%s.%s", buffer, newExt);
+
+  }
+  else if (extLength)
+  {
+    sprintf(buffer, "%s.%s", buffer, newExt);
+  }
+
+  a_thread->PushNewString(buffer);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmStringGetPath(gmThread * a_thread)
+{
+  GM_INT_PARAM(keepSlash, 0, 0);
+
+  const gmVariable * var = a_thread->GetThis();
+  GM_ASSERT(var->m_type == GM_STRING);
+  gmStringObject * strObj = (gmStringObject *) GM_OBJECT(var->m_value.m_ref);
+  const char * str = (const char *) *strObj;
+  int strLength = strObj->GetLength();
+
+  char * buffer = (char *) alloca(strLength + 1); 
+  memcpy(buffer, str, strLength + 1); //Copy old string
+
+  char *lpsz = buffer + strLength;
+  while (--lpsz >= buffer && *lpsz != '\\' && *lpsz != '/') {}
+
+  if(*lpsz == '\\' || *lpsz == '/')
+  {
+    if(keepSlash)
+      lpsz[1] = 0;
+    else
+      lpsz[0] = 0;
+    a_thread->PushNewString(buffer);
+  }
+  else
+  {
+    a_thread->PushNewString("");
+  }
+  return GM_OK;
+}
+
+extern int GM_CDECL gmfToInt(gmThread * a_thread);
+extern int GM_CDECL gmfToFloat(gmThread * a_thread);
+extern int GM_CDECL gmfToString(gmThread * a_thread);
+
+static gmFunctionEntry s_stringLib[] = 
+{ 
+  /*gm
+    \lib string
+    \brief string operations often store a copy of the string on the stack, so keep string sizes reasonable
+  */
+  /*gm
+    \function IsEmpty
+    \brief IsEmpty will test to see if the string is 0 length
+    \return non-zero if the string is empty
+  */
+  {"IsEmpty", gmfStringIsEmpty},
+  /*gm
+    \function Length
+    \brief Length will return the length of the string not including the null terminating character
+    \return int length
+  */
+  {"Length", gmfStringLength},
+  /*gm
+    \function Left
+    \brief Left will return the left count charaters of the string
+    \param int count
+    \return string
+  */
+  {"Left", gmfStringLeft},
+  /*gm
+    \function Right
+    \brief Right will return the right count charaters of the string
+    \param int count
+    \return string
+  */
+  {"Right", gmfStringRight},
+  /*gm
+    \function RightAt
+    \brief RightAt will return the charaters right of and including the given index
+    \param int index
+    \return string
+  */
+  {"RightAt", gmfStringRightAt},
+  /*gm
+    \function Mid
+    \brief Mid will return count characters from the start index
+    \param int startIndex
+    \param int count
+    \return string
+  */
+  {"Mid", gmfStringMid},
+  /*gm
+    \function Compare
+    \brief Compare will perform a string compare
+    \param string to compare
+    \return -1 if the this < compare string, 0 if the strings are equal, 1 otherwise
+  */
+  {"Compare", gmfStringCompare},
+  /*gm
+    \function CompareNoCase
+    \brief CompareNoCase will perform a string compare (case insensitive)
+    \param string to compare
+    \return -1 if the this < compare string, 0 if the strings are equal, 1 otherwise
+  */
+  {"CompareNoCase", gmfStringCompareNoCase},
+  /*gm
+    \function Int
+    \brief Int will return the int value of the string
+    \return int value
+  */
+  {"Int", gmfToInt},
+  /*gm
+    \function Float
+    \brief Float will return the float value of the string
+    \return float value
+  */
+  {"Float", gmfToFloat},
+  /*gm
+    \function String
+    \return string
+  */
+  {"String", gmfToString},
+  /*gm
+    \function Upper
+    \brief Upper will return the string as uppercase
+    \return string
+  */
+  {"Upper", gmfStringUpper},
+  /*gm
+    \function Lower
+    \brief Lower will return the string as lowercase
+    \return string
+  */
+  {"Lower", gmfStringLower},
+  /*gm
+    \function SpanIncluding
+    \brief SpanIncluding will return this string while characters are within the passed string
+    \param string charset
+    \return string
+  */
+  {"SpanIncluding", gmfStringSpanIncluding},
+  /*gm
+    \function SpanExcluding
+    \brief SpanExcluding will return this string while characters are not within the passed string
+    \param string charset
+    \return string
+  */
+  {"SpanExcluding", gmfStringSpanExcluding},
+  /*gm
+    \function AppendPath
+    \brief AppendPath will append a path make sure one '\' is maintained
+    \param string path to append
+    \return string
+  */
+  {"AppendPath", gmfStringAppendPath},
+  /*gm
+    \function ReplaceCharsInSet
+    \brief ReplaceCharsInSet will replace all chars in this that are within the charset with the given int char
+    \param int char to replace with
+    \param string charset of chars to replace
+    \return string
+  */
+  {"ReplaceCharsInSet", gmfStringReplaceCharsInSet},
+  /*gm
+    \function Find
+    \brief Find will find the first occurance of the passed string within this string
+    \param string search string
+    \param int start index optional (0)
+    \return int index of first occurance, or -1 if the string was not found
+  */
+  {"Find", gmStringFind},
+  /*gm
+    \function Reverse
+    \brief Reverse characters of a string
+    \return string
+  */
+  {"Reverse", gmStringReverse},
+  /*gm
+    \function ReverseFind
+    \brief ReverseFind will find the first occurance of the passed string within this string starting from the right
+    \param string search string
+    \return int index of first occurance, or -1 if the string was not found
+  */
+  {"ReverseFind", gmStringReverseFind},
+  /*gm
+    \function GetAt
+    \brief GetAt will return the char at the given index
+    \param int index
+    \return int char, or null if index was out of range
+  */
+  {"GetAt", gmStringGetAt},
+  /*gm
+    \function SetAt
+    \brief SetAt will return the string with the character set at the given position
+    \param int index
+    \param int char
+    \return string
+  */
+  {"SetAt", gmStringSetAt},
+  /*gm
+    \function TrimLeft
+    \brief TrimLeft will return the string with the chars from the passed char set trimmed from the left
+    \param string charset optional (" \r\n\v\t")
+    \return string
+  */
+  {"TrimLeft", gmStringTrimLeft},
+  /*gm
+    \function TrimRight
+    \brief TrimRight will return the string with the chars from the passed char set trimmed from the right
+    \param string charset optional (" \r\n\v\t")
+    \return string
+  */
+  {"TrimRight", gmStringTrimRight},
+
+  /*gm
+    \function GetFilenameNoExt
+    \brief GetFilenameNoExt will return the filename part of a path string
+    \return string
+  */
+  {"GetFilenameNoExt", gmStringGetFilenameNoExt},
+  /*gm
+    \function GetFilename
+    \brief GetFilename will return the filename part of a path string incl. extension
+    \return string
+  */
+  {"GetFilename", gmStringGetFilename},
+  /*gm
+    \function GetExtension
+    \brief GetExtension will return the file extension
+    \param int inclDot optional (0) 1 will include '.', 0 won't
+    \return string
+  */
+  {"GetExtension", gmStringGetExtension},
+  /*gm
+    \function SetExtension
+    \brief SetExtension returns a string with the extension change to the given one.
+    \param string ext optional (null) the new extension, with or without the dot. null to remove extension.
+    \return string
+  */
+  {"SetExtension", gmStringSetExtension},
+  /*gm
+    \function GetPath
+    \brief GetPath will return the file path from a path string
+    \param int inclSlash optional (0) will include a '\' on the end of the path
+    \return string
+  */
+  {"GetPath", gmStringGetPath},
+/*
+  {"Insert", }, //int Insert(int index, char/string)
+  {"Delete", }, //int Delete(int index, int count=1)
+  {"Replace", }, //int Replace(char/string,char/string)
+  {"Scan", },
+*/
+};
+
+void gmBindStringLib(gmMachine * a_machine)
+{
+  a_machine->RegisterTypeOperator(GM_STRING, O_BIT_XOR, NULL, gmStringOpAppendPath);
+  a_machine->RegisterTypeOperator(GM_STRING, O_GETIND, NULL, gmStringOpGetInd);
+  a_machine->RegisterTypeLibrary(GM_STRING, s_stringLib, sizeof(s_stringLib) / sizeof(s_stringLib[0]));
+}
+

+ 21 - 0
gmsrc/src/binds/gmStringLib.h

@@ -0,0 +1,21 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMSTRINGLIB_H_
+#define _GMSTRINGLIB_H_
+
+#include "gmConfig.h"
+
+class gmMachine;
+
+void gmBindStringLib(gmMachine * a_machine);
+
+#endif // _GMSTRINGLIB_H_

+ 986 - 0
gmsrc/src/binds/gmSystemLib.cpp

@@ -0,0 +1,986 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmSystemLib.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+#include "gmHelpers.h"
+
+#if GM_SYSTEM_LIB
+
+#include <ctype.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#undef GetObject
+
+//
+//
+// system functions
+//
+//
+
+extern void gmConcat(gmMachine * a_machine, char * &a_dst, int &a_len, int &a_size, const char * a_src, int a_growBy = 32);
+
+
+static int GM_CDECL gmfSystem(gmThread * a_thread)
+{
+  const int bufferSize = 256;
+  int len = 0, size = 0, i, ret = -1;
+  char * str = NULL, buffer[bufferSize];
+
+  // build the string
+  for(i = 0; i < a_thread->GetNumParams(); ++i)
+  {
+    gmConcat(a_thread->GetMachine(), str, len, size, a_thread->Param(i).AsString(a_thread->GetMachine(), buffer, bufferSize), 64);
+
+    if(str)
+    {
+      GM_ASSERT(len < size);
+      str[len++] = ' ';
+      str[len] = '\0';
+    }
+  }
+
+  // print the string
+  if(str)
+  {
+    ret = system(str);
+    a_thread->GetMachine()->Sys_Free(str);
+  }
+
+  a_thread->PushInt(ret);
+
+  return GM_OK;
+}
+
+static int GM_CDECL gmfDoFile(gmThread * a_thread) // filename, now (1), return thread id, null on error, exception on compile error.
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+  GM_INT_PARAM(now, 1, 1);
+  gmVariable paramThis = a_thread->Param(2, gmVariable::s_null); // 3rd param is 'this'
+
+  int id = GM_INVALID_THREAD;
+  if(filename)
+  {
+    char * string = NULL;
+
+    FILE * fp = fopen(filename, "rb");
+    if(fp)
+    {
+      fseek(fp, 0, SEEK_END);
+      int size = ftell(fp);
+      rewind(fp);
+      string = new char[size + 1];
+      fread(string, 1, size, fp);
+      string[size] = 0;
+      fclose(fp);
+    }
+    else
+    {
+      GM_EXCEPTION_MSG("failed to open file '%s'", filename);
+      return GM_EXCEPTION;
+    }
+    if(string == NULL) return GM_OK;
+
+    int errors = a_thread->GetMachine()->ExecuteString(string, &id, (now) ? true : false, filename, &paramThis);
+    delete[] string;
+    if(errors)
+    {
+      return GM_EXCEPTION;
+    }
+    else
+    {
+      a_thread->PushInt(id);
+    }
+  }
+  return GM_OK;
+}
+
+//
+//
+// Implementation of ansi file binding
+//
+//
+
+static gmType s_gmFileType = GM_NULL;
+
+static int GM_CDECL gmfFile(gmThread * a_thread)
+{
+  a_thread->PushNewUser(NULL, s_gmFileType);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileExists(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+
+  FILE * fp = fopen(filename, "rb");
+  if(fp)
+  {
+    a_thread->PushInt(1);
+    fclose(fp);
+    return GM_OK;
+  }
+  a_thread->PushInt(0);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileOpen(gmThread * a_thread) // path, readonly(true), return 1 on success.
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+  GM_INT_PARAM(readonly, 1, 1);
+
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
+  fileObject->m_user = (void *) fopen(filename, (readonly) ? "rb" : "wb");
+  if(fileObject->m_user) a_thread->PushInt(1);
+  else a_thread->PushInt(0);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileOpenText(gmThread * a_thread) // path, readonly(true), return 1 on success.
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+  GM_INT_PARAM(readonly, 1, 1);
+
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
+  fileObject->m_user = (void *) fopen(filename, (readonly) ? "r" : "w");
+  if(fileObject->m_user) a_thread->PushInt(1);
+  else a_thread->PushInt(0);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileClose(gmThread * a_thread)
+{
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user) fclose((FILE *) fileObject->m_user);
+  fileObject->m_user = NULL;
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileIsOpen(gmThread * a_thread) // return 1 if open, else 0
+{
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  a_thread->PushInt((fileObject->m_user) ? 1 : 0);
+  return GM_OK;
+}
+
+
+static void GM_CDECL gmFileOpGetDot(gmThread * a_thread, gmVariable * a_operands)
+{
+  gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
+  if(user && user->m_user)
+  {
+    gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
+
+    GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
+
+    if(strcmp(member->GetString(), "SEEK_CUR") == 0)
+      a_operands->SetInt(SEEK_CUR);
+    else if(strcmp(member->GetString(), "SEEK_END") == 0)
+      a_operands->SetInt(SEEK_END);
+    else if(strcmp(member->GetString(), "SEEK_SET") == 0)
+      a_operands->SetInt(SEEK_SET);
+    else
+    {
+      a_operands->Nullify();
+      return;
+    }
+    return;
+  }
+  a_operands->Nullify();
+}
+
+
+static int GM_CDECL gmfFileSeek(gmThread * a_thread) // return false on error
+{
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_INT_PARAM(offset, 0);
+  GM_CHECK_INT_PARAM(origin, 1);
+
+  if(    origin != SEEK_CUR 
+      && origin != SEEK_END 
+      && origin != SEEK_SET )
+  {
+    return GM_EXCEPTION;
+  }
+
+  int result = fseek((FILE*)fileObject->m_user, offset, origin);
+  if(result != 0)
+  {
+    a_thread->PushInt(false);
+  }
+  a_thread->PushInt(true);
+
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileTell(gmThread * a_thread) // return -1 on error, else file pos.
+{
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  long pos = -1;
+  if(fileObject->m_user) pos = ftell((FILE *) fileObject->m_user);
+  a_thread->PushInt(pos);
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileReadLine(gmThread * a_thread) // flag keep \n (0), return string, or null on eof
+{
+  GM_INT_PARAM(keepLF, 0, 0);
+  const int len = GM_SYSTEM_LIB_MAX_LINE;
+  char buffer[len];
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user)
+  {
+    char * str = fgets(buffer, len, (FILE *) fileObject->m_user);
+    if(str)
+    {
+      int slen = strlen(str);
+      if(!keepLF)
+      {
+        if(!feof((FILE *) fileObject->m_user))
+          str[--slen] = '\0';
+      }
+      a_thread->PushNewString(str, slen);
+    }
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileReadChar(gmThread * a_thread) // return int, return NULL on eof, or on error
+{
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user)
+  {
+    int c = fgetc((FILE*) fileObject->m_user);
+    if(c != EOF) a_thread->PushInt(c);
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileWriteChar(gmThread * a_thread) // int, return char written, or NULL on error
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(c, 0);
+
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user)
+  {
+    int r = fputc(c, (FILE *) fileObject->m_user);
+    if(r != EOF) a_thread->PushInt(r);
+  }
+  return GM_OK;
+}
+
+static int GM_CDECL gmfFileWriteString(gmThread * a_thread) // string, return 1 on success, or NULL on error
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(s, 0);
+
+  gmUserObject * fileObject = a_thread->ThisUserObject();
+  GM_ASSERT(fileObject->m_userType == s_gmFileType);
+  if(fileObject->m_user)
+  {
+    if(fputs(s, (FILE *) fileObject->m_user) != EOF) a_thread->PushInt(1);
+  }
+  return GM_OK;
+}
+
+#if GM_USE_INCGC
+static void GM_CDECL gmGCDestructFileUserType(gmMachine * a_machine, gmUserObject* a_object)
+{
+  if(a_object->m_user) fclose((FILE *) a_object->m_user);
+  a_object->m_user = NULL;
+}
+#else //GM_USE_INCGC
+static void GM_CDECL gmGCFileUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  if(a_object->m_user) fclose((FILE *) a_object->m_user);
+  a_object->m_user = NULL;
+}
+#endif //GM_USE_INCGC
+
+//
+//
+// File Find user type
+//
+//
+
+static gmType s_gmFileFindType = GM_NULL;
+
+struct gmFileFindUser
+{
+  WIN32_FIND_DATA m_findData;
+  HANDLE m_iterator;
+};
+
+//
+//
+// File Info user type
+//
+//
+
+static gmType s_gmFileInfoType = GM_NULL;
+
+struct gmFileInfoUser
+{
+  time_t m_accessedTime; // last access time
+  time_t m_creationTime; // creation time
+  time_t m_modifiedTime; // last modify time
+  unsigned int m_size; // size
+};
+
+//
+//
+// System lib
+//
+//
+
+static int GM_CDECL gmfFindFirstFile(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+
+  gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileFindUser));
+  fileFind->m_iterator = FindFirstFile(filename, &fileFind->m_findData);
+
+  if(fileFind->m_iterator == INVALID_HANDLE_VALUE)
+  {
+    a_thread->GetMachine()->Sys_Free(fileFind);
+    return GM_OK;
+  }
+
+  a_thread->PushNewUser(fileFind, s_gmFileFindType);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfFindNextFile(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+
+  if(a_thread->ParamType(0) == s_gmFileFindType)
+  {
+    gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ParamUser(0);
+    if(fileFind && fileFind->m_iterator != INVALID_HANDLE_VALUE)
+    {
+      if(FindNextFile(fileFind->m_iterator, &fileFind->m_findData))
+      {
+        a_thread->PushUser(a_thread->ParamUserObject(0));
+      }
+    }
+  }
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfFileInfo(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(filename, 0);
+  
+  struct _stat buf;
+  int fh, result;
+
+  if((fh = _open(filename, _O_RDONLY)) ==  -1) return GM_OK; // return null
+  result = _fstat(fh, &buf); // Get data associated with "fh"
+  if(result == 0) //function obtained data correctly (0 == success, -1 == fail)
+  {
+    // create and push a gmFileInfoUser object
+    gmFileInfoUser * fileInfo = (gmFileInfoUser *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmFileInfoUser));
+    fileInfo->m_creationTime = buf.st_ctime;
+    fileInfo->m_accessedTime = buf.st_atime;
+    fileInfo->m_modifiedTime = buf.st_mtime;
+    fileInfo->m_size = buf.st_size;
+    a_thread->PushNewUser(fileInfo, s_gmFileInfoType);
+  }
+  _close( fh );
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfCreateFolder(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(path, 0);
+  BOOL result = CreateDirectory(path, NULL);
+  if(result)
+  {
+    a_thread->PushInt(1);
+  }
+  else
+  {
+    WIN32_FIND_DATA findData;
+    HANDLE handle = FindFirstFile(path, &findData);
+    if(handle == INVALID_HANDLE_VALUE)
+    {
+      a_thread->PushInt(0);
+    }
+    else
+    {
+      if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+      {
+        a_thread->PushInt(2);
+      }
+      else
+      {
+        a_thread->PushInt(0);
+      }
+      FindClose(handle);
+    }
+  }
+  return GM_OK;
+}
+
+
+bool RecurseDeletePath(const char * a_path)
+{
+  WIN32_FIND_DATA findData;
+
+  char path[MAX_PATH] = "";
+  strcpy(path,a_path);
+
+  // remove trailing '\' char
+  int last = strlen(path) - 1;
+  if(path[last] == '\\')
+  {
+    path[last] = '\0';
+  }
+
+  // is path valid
+  HANDLE h = FindFirstFile(path,&findData);
+
+  // path not could not be found OR path is a file, not a folder
+  if((h == INVALID_HANDLE_VALUE) || (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))
+  {
+    return false;
+  }
+
+  FindClose(h);
+  h = NULL;
+
+  // push current working directory
+  char currDir[MAX_PATH + 1] = "";
+  GetCurrentDirectory(MAX_PATH,currDir);
+  SetCurrentDirectory(path);
+
+  // iterate over contents of folder
+  h = FindFirstFile("*",&findData);
+
+  if(h != INVALID_HANDLE_VALUE)
+  {
+    for(;;)
+    {
+      if(strcmp(findData.cFileName,".") != 0 && strcmp(findData.cFileName,"..") != 0)
+      {
+        if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+          RecurseDeletePath(findData.cFileName);
+        }
+        else
+        {
+          DWORD attrs = GetFileAttributes(findData.cFileName);
+          if(attrs & FILE_ATTRIBUTE_READONLY)
+          {
+            SetFileAttributes(findData.cFileName,attrs ^ FILE_ATTRIBUTE_READONLY);
+          }
+          if(DeleteFile(findData.cFileName) != TRUE)
+          {
+            DWORD res = GetLastError();
+            printf("\nDeleteFile() returned '%d'..\n",(int)res);
+          }
+        }
+      }
+
+      if(!FindNextFile(h,&findData)) break;
+    }
+  }
+
+  // pop current working directory
+  SetCurrentDirectory(currDir);
+
+  FindClose(h);
+  h = NULL;
+
+  // remove this directory
+  DWORD attrs = GetFileAttributes(path);
+  if(attrs & FILE_ATTRIBUTE_READONLY)
+  {
+    SetFileAttributes(path,attrs ^ FILE_ATTRIBUTE_READONLY);
+  }
+  return RemoveDirectory(path) != 0;
+}
+
+
+static int GM_CDECL gmfDeleteFolder(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(path, 0);
+  GM_INT_PARAM(removeSubFolders, 1, 0);
+
+  if(removeSubFolders)
+  {
+    a_thread->PushInt(RecurseDeletePath(path) ? 1 : 0);
+  }
+  else
+  {
+    a_thread->PushInt(RemoveDirectory(path) ? 1 : 0);
+  }
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfTime(gmThread * a_thread)
+{
+  time_t t;
+  time(&t);
+  GM_ASSERT(sizeof(time_t) == sizeof(gmptr));
+  a_thread->PushInt((gmptr) t);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfFormatTime(gmThread * a_thread)
+{
+  GM_INT_PARAM(t, 0, -1);
+  GM_STRING_PARAM(format, 1, "%A %d %B %Y, %I:%M:%S %p");
+  char buffer[256];
+
+  if(t == -1)
+  {
+    time_t lt;
+    time(&lt);
+    t = (int) lt;
+  }
+  struct tm * ct = localtime((time_t *) &t);
+  strftime(buffer, 256, format, ct);
+  a_thread->PushNewString(buffer);
+  return GM_OK;
+}
+
+
+static int GM_CDECL gmfFileFindGetAttribute(gmThread * a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(attribute, 0);
+
+  GM_ASSERT(a_thread->GetThis()->m_type == s_gmFileFindType);
+  gmFileFindUser * fileFind = (gmFileFindUser *) a_thread->ThisUser();
+
+  DWORD attr = 0;
+  if(attribute == 'r')
+  {
+    attr = FILE_ATTRIBUTE_READONLY;
+  }
+  else if(attribute == 'a')
+  {
+    attr = FILE_ATTRIBUTE_ARCHIVE;
+  }
+  else if(attribute == 's')
+  {
+    attr = FILE_ATTRIBUTE_SYSTEM;
+  }
+  else if(attribute == 'h')
+  {
+    attr = FILE_ATTRIBUTE_HIDDEN;
+  }
+  else if(attribute == 'd')
+  {
+    attr = FILE_ATTRIBUTE_DIRECTORY;
+  }
+  else if(attribute == 'c')
+  {
+    attr = FILE_ATTRIBUTE_COMPRESSED;
+  }
+
+  a_thread->PushInt((fileFind->m_findData.dwFileAttributes & attr) ? 1 : 0);
+  return GM_OK;
+}
+
+
+static void GM_CDECL gmFileFindOpGetDot(gmThread * a_thread, gmVariable * a_operands)
+{
+  gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
+  if(user && user->m_user)
+  {
+    gmFileFindUser * fileFind = (gmFileFindUser *) user->m_user;
+    gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
+
+    if(strcmp(member->GetString(), "filename") == 0)
+    {
+      a_operands->SetString(a_thread->GetMachine()->AllocStringObject(fileFind->m_findData.cFileName));
+      return;
+    }
+    else if(strcmp(member->GetString(), "size") == 0)
+    {
+      a_operands->SetInt(fileFind->m_findData.nFileSizeLow);
+      return;
+    }
+  }
+  a_operands->Nullify();
+}
+
+#if GM_USE_INCGC
+static void GM_CDECL gmGCDestructFileFindUserType(gmMachine * a_machine, gmUserObject* a_object)
+{
+  if(a_object->m_user)
+  {
+    gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
+    if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
+    {
+      FindClose(fileFind->m_iterator);
+    }
+    a_machine->Sys_Free(a_object->m_user);
+  }
+  a_object->m_user = NULL;
+}
+#else //GM_USE_INCGC
+static void GM_CDECL gmGCFileFindUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  if(a_object->m_user)
+  {
+    gmFileFindUser * fileFind = (gmFileFindUser *) a_object->m_user;
+    if(fileFind->m_iterator != INVALID_HANDLE_VALUE)
+    {
+      FindClose(fileFind->m_iterator);
+    }
+    a_machine->Sys_Free(a_object->m_user);
+  }
+  a_object->m_user = NULL;
+}
+#endif //GM_USE_INCGC
+
+
+static void GM_CDECL gmFileInfoOpGetDot(gmThread * a_thread, gmVariable * a_operands)
+{
+  gmUserObject * user = (gmUserObject *) GM_OBJECT(a_operands->m_value.m_ref);
+  if(user && user->m_user)
+  {
+    gmFileInfoUser * fileInfo = (gmFileInfoUser *) user->m_user;
+    gmStringObject * member = (gmStringObject *) GM_OBJECT(a_operands[1].m_value.m_ref);
+
+    GM_ASSERT(sizeof(gmptr) == sizeof(time_t));
+
+    if(strcmp(member->GetString(), "creationTime") == 0)
+      a_operands->SetInt((gmptr) fileInfo->m_creationTime);
+    else if(strcmp(member->GetString(), "accessedTime") == 0)
+      a_operands->SetInt((gmptr) fileInfo->m_accessedTime);
+    else if(strcmp(member->GetString(), "modifiedTime") == 0)
+      a_operands->SetInt((gmptr) fileInfo->m_modifiedTime);
+    else if(strcmp(member->GetString(), "size") == 0)
+      a_operands->SetInt((gmptr) fileInfo->m_size);
+    else
+    {
+      a_operands->Nullify();
+      return;
+    }
+    return;
+  }
+  a_operands->Nullify();
+}
+
+
+#if GM_USE_INCGC
+static void GM_CDECL gmGCDestructFileInfoUserType(gmMachine * a_machine, gmUserObject* a_object)
+{
+  if(a_object->m_user)
+  {
+    a_machine->Sys_Free(a_object->m_user);
+  }
+  a_object->m_user = NULL;
+}
+#else //GM_USE_INCGC
+static void GM_CDECL gmGCFileInfoUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  if(a_object->m_user)
+  {
+    a_machine->Sys_Free(a_object->m_user);
+  }
+  a_object->m_user = NULL;
+}
+#endif //GM_USE_INCGC
+
+//
+//
+// binding
+//
+//
+
+
+static gmFunctionEntry s_systemLib[] = 
+{ 
+  /*gm
+    \lib system
+    \brief system functions are bound in a "system" table.
+  */
+  /*gm
+    \function Exec
+    \brief Exec will execute a system command
+    \param string params will be concatinated together with a single space to form the final system command string
+    \return integer value returned from system exec call, -1 on error
+  */
+  {"Exec", gmfSystem},
+  /*gm
+    \function DoFile
+    \brief DoFile will execute the gm script in the named file
+    \param string filename
+    \param int optional (1) as 1 will execute string before returning, 0 will execute later.
+    \param ref optional (null) set 'this'
+    \return thread id of new thread created to execute file
+  */
+  {"DoFile", gmfDoFile},
+  /*gm
+    \function File
+    \brief File will create a file object
+    \return file object
+  */
+  {"File", gmfFile},
+  /*gm
+    \function FileExists
+    \brief FileExists will test to see if a file exists
+    \param string filename 
+    \return 1 if the file exists, otherwise 0
+  */
+  {"FileExists", gmfFileExists},
+  /*gm
+    \function FileFindFirst
+    \brief FileFindFirst will start a file search will test to see if a file exists
+    \param string filesearch (may contain wildcards, eg, `c:\temp\*.txt`)
+    \return fileFind object.  fileFind object has .filename and .size member
+    \sa fileFind
+  */
+  {"FileFindFirst", gmfFindFirstFile},
+  /*gm
+    \function FileFindNext
+    \brief FileFindNext will get the next file matching the file find
+    \param fileFind object returned by FileFindFirst call or FileFindNext call
+    \return fileFind object
+    \sa fileFind
+  */
+  {"FileFindNext", gmfFindNextFile},
+
+  /*gm
+    \function FileInfo
+    \brief FileInfo will return a file info object that has readonly members for .creationDate
+    \param string path
+    \return fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size
+  */
+  {"FileInfo", gmfFileInfo},
+
+  /*gm
+    \function CreateFolder
+    \brief CreateFolder will create a file path if it does not already exist
+    \param string path
+    \return int 0 on failure, 1 on successful create, 2 if folder already exists
+  */
+  {"CreateFolder", gmfCreateFolder},
+  /*gm
+    \function DeleteFolder
+    \brief DeleteFolder will remove a file path
+    \param string path
+    \param int remove subfiles and folders optional (0)
+    \return int 1 on success, 0 con failure
+  */
+  {"DeleteFolder", gmfDeleteFolder},
+
+  /*gm
+    \function Time
+    \brief Time will return a unix style time_t as an int
+    \return the current time
+  */
+  {"Time", gmfTime},
+  /*gm
+    \function FormatTime
+    \brief FormatTime will take a int (time_t) value and format according to the passed format string.
+    \param int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time
+    \param string format ("%A %d %B %Y, %I:%M:%S %p") is the format string to use.<BR>
+            %a : Abbreviated weekday name<BR>
+            %A : Full weekday name<BR>
+            %b : Abbreviated month name<BR>
+            %B : Full month name<BR>
+            %c : Date and time representation appropriate for locale<BR>
+            %d : Day of month as decimal number (01 – 31)<BR>
+            %H : Hour in 24-hour format (00 – 23)<BR>
+            %I : Hour in 12-hour format (01 – 12)<BR>
+            %j : Day of year as decimal number (001 – 366)<BR>
+            %m : Month as decimal number (01 – 12)<BR>
+            %M : Minute as decimal number (00 – 59)<BR>
+            %p : Current locale’s A.M./P.M. indicator for 12-hour clock<BR>
+            %S : Second as decimal number (00 – 59)<BR>
+            %U : Week of year as decimal number, with Sunday as first day of week (00 – 53)<BR>
+            %w : Weekday as decimal number (0 – 6; Sunday is 0)<BR>
+            %W : Week of year as decimal number, with Monday as first day of week (00 – 53)<BR>
+            %x : Date representation for current locale<BR>
+            %X : Time representation for current locale<BR>
+            %y : Year without century, as decimal number (00 – 99)<BR>
+            %Y : Year with century, as decimal number<BR>
+            %z, %Z : Time-zone name or abbreviation; no characters if time zone is unknown<BR>
+            %% : Percent sign<BR>
+    \return the time as a string.
+  */
+  {"FormatTime", gmfFormatTime},
+};
+
+static gmFunctionEntry s_fileFindLib[] = 
+{
+  /*gm
+    \lib fileFind
+    \brief fileFind object has a "filename" and "size" member
+  */
+  /*gm
+    \function GetAttribute
+    \brief GetAttribute will test a file attribute.
+    \param int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory
+    \return 1 if the attribute is set, 0 otherwise
+  */
+  {"GetAttribute", gmfFileFindGetAttribute},
+};
+
+
+static gmFunctionEntry s_fileLib[] = 
+{ 
+  /*gm
+    \lib file
+  */
+  /*gm
+    \function Open
+    \brief Open will open a file in binary mode
+    \param string filename
+    \param int readonly optional (1)
+    \return 1 if the open was successful, 0 otherwise 
+  */
+  {"Open", gmfFileOpen},
+  /*gm
+    \function OpenText
+    \brief OpenText will open a file in text mode
+    \param string filename
+    \param int readonly optional (1)
+    \return 1 if the open was successful, 0 otherwise 
+  */
+  {"OpenText", gmfFileOpenText},
+  /*gm
+    \function Close
+    \brief Close will close a file
+  */
+  {"Close", gmfFileClose},
+  /*gm
+    \function IsOpen
+    \brief IsOpen will test to see if a file is open
+    \return 1 if the file is open, 0 otherwise
+  */
+  {"IsOpen", gmfFileIsOpen},
+  /*gm
+    \function Seek
+    \brief Move to position within file
+    \param int offset positional offset relative to origin
+    \param int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins.
+    \return int 1 if operation succeeded, 0 if failed.
+  */
+  {"Seek", gmfFileSeek},
+  /*gm
+    \function Tell
+    \brief Tell will return the current cursor position of the file
+    \return int the current cursor position, -1 on error
+  */
+  {"Tell", gmfFileTell},
+  /*gm
+    \function ReadLine
+    \brief ReadLine will read a line of text from the file.
+    \param int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed
+    \return string, or null on eof
+  */
+  {"ReadLine", gmfFileReadLine},
+  /*gm
+    \function ReadChar
+    \brief ReadChar will read a char from the file
+    \return int char or null on eof
+  */
+  {"ReadChar", gmfFileReadChar},
+  /*gm
+    \function WriteString
+    \brief WriteString will write a string to the file
+    \param string to write to file
+    \return 1 on success, null on error
+  */
+  {"WriteString", gmfFileWriteString},
+  /*gm
+    \function WriteChar
+    \brief WriteChar will write a char to the file
+    \param int char to write to file
+    \return 1 on success, null on error
+  */
+  {"WriteChar", gmfFileWriteChar},
+};
+
+
+void gmBindSystemLib(gmMachine * a_machine)
+{
+  // system
+  a_machine->RegisterLibrary(s_systemLib, sizeof(s_systemLib) / sizeof(s_systemLib[0]), "system");
+
+  // file
+  s_gmFileType = a_machine->CreateUserType("file");
+#if GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCDestructFileUserType);
+#else //GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileType, NULL, gmGCFileUserType);
+#endif //GM_USE_INCGC
+  a_machine->RegisterTypeLibrary(s_gmFileType, s_fileLib, sizeof(s_fileLib) / sizeof(s_fileLib[0]));
+  a_machine->RegisterTypeOperator(s_gmFileType, O_GETDOT, NULL, gmFileOpGetDot);
+
+  // fileFind
+  s_gmFileFindType = a_machine->CreateUserType("fileFind");
+  a_machine->RegisterTypeLibrary(s_gmFileFindType, s_fileFindLib, sizeof(s_fileFindLib) / sizeof(s_fileFindLib[0]));
+#if GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCDestructFileFindUserType);
+#else //GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileFindType, NULL, gmGCFileFindUserType);
+#endif //GM_USE_INCGC
+  a_machine->RegisterTypeOperator(s_gmFileFindType, O_GETDOT, NULL, gmFileFindOpGetDot);
+
+  // fileInfo
+  s_gmFileInfoType = a_machine->CreateUserType("fileInfo");
+#if GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCDestructFileInfoUserType);
+#else //GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(s_gmFileInfoType, NULL, gmGCFileInfoUserType);
+#endif //GM_USE_INCGC
+  a_machine->RegisterTypeOperator(s_gmFileInfoType, O_GETDOT, NULL, gmFileInfoOpGetDot);
+}
+
+#endif // GM_SYSTEM_LIB

+ 28 - 0
gmsrc/src/binds/gmSystemLib.h

@@ -0,0 +1,28 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMSYSTEMLIB_H_
+#define _GMSYSTEMLIB_H_
+
+#include "gmConfig.h"
+
+class gmMachine;
+
+#define GM_SYSTEM_LIB 1
+#define GM_SYSTEM_LIB_MAX_LINE    1024      // maximum file line length
+
+#if GM_SYSTEM_LIB
+
+void gmBindSystemLib(gmMachine * a_machine);
+
+#endif // GM_SYSTEM_LIB
+
+#endif // _GMSYSTEMLIB_H_

+ 1056 - 0
gmsrc/src/binds/gmVector3Lib.cpp

@@ -0,0 +1,1056 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmVector3Lib.h"
+#include "gmThread.h"
+#include "gmMachine.h"
+#include "gmHelpers.h"
+#include <math.h>
+
+
+//
+// Vector3
+//
+
+/// \brief Helper Vector3 class that resembles one used in a game.
+/// Users will not use this class but cast to their own Vector3 implementation.
+struct gmVector3
+{
+  static int DominantAxis(const gmVector3& a_vec)
+  {
+    float absX, absY, absZ;
+
+    absX = fabsf(a_vec.m_x);
+    absY = fabsf(a_vec.m_y);
+    absZ = fabsf(a_vec.m_z);
+  
+    if(absY > absX)
+    {
+      if(absZ > absY) 
+        { return 2;} // Dominant Z
+      else 
+        { return 1;} // Dominant Y
+    }
+    else if (absZ > absX)
+      {return 2;} // Dominant Z
+    else
+      {return 0;} // Dominant X
+  }
+  static float Dot(const gmVector3& a_vec1, const gmVector3& a_vec2)
+  {
+    return (a_vec1.m_x * a_vec2.m_x + a_vec1.m_y * a_vec2.m_y + a_vec1.m_z * a_vec2.m_z);
+  }
+  static void Cross(const gmVector3& a_vec1, const gmVector3& a_vec2, gmVector3& a_result)
+  {
+    GM_ASSERT( (&a_result != &a_vec1) && (&a_result != &a_vec2) );
+
+    a_result.m_x = (a_vec1.m_y * a_vec2.m_z) - (a_vec1.m_z * a_vec2.m_y);
+    a_result.m_y = (a_vec1.m_z * a_vec2.m_x) - (a_vec1.m_x * a_vec2.m_z);
+    a_result.m_z = (a_vec1.m_x * a_vec2.m_y) - (a_vec1.m_y * a_vec2.m_x);
+  }
+  static float Length(const gmVector3& a_vec)
+  {
+    return (float)sqrt(LengthSquared(a_vec));
+  }
+  static float LengthSquared(const gmVector3& a_vec)
+  {
+    return Dot(a_vec, a_vec);
+  }
+  static void Normalize(const gmVector3& a_vec, gmVector3& a_result)
+  {
+    float len2 = LengthSquared(a_vec);
+    if(len2 != 0.0f)
+    {
+      float ooLen = 1.0f / (float)sqrt(len2);
+      MulScalar(a_vec, ooLen, a_result);
+    }
+    else
+    {
+      a_result.m_x = 0.0f;
+      a_result.m_y = 0.0f;
+      a_result.m_z = 0.0f;
+    }
+  }
+  static void Add(const gmVector3& a_vec1, const gmVector3& a_vec2, gmVector3& a_result)
+  {
+    a_result.m_x = a_vec1.m_x + a_vec2.m_x;
+    a_result.m_y = a_vec1.m_y + a_vec2.m_y;
+    a_result.m_z = a_vec1.m_z + a_vec2.m_z;
+  }
+  static void Sub(const gmVector3& a_vec1, const gmVector3& a_vec2, gmVector3& a_result)
+  {
+    a_result.m_x = a_vec1.m_x - a_vec2.m_x;
+    a_result.m_y = a_vec1.m_y - a_vec2.m_y;
+    a_result.m_z = a_vec1.m_z - a_vec2.m_z;
+  }
+  static void MulVector3(const gmVector3& a_vec1, const gmVector3& a_vec2, gmVector3& a_result)
+  {
+    a_result.m_x = a_vec1.m_x * a_vec2.m_x;
+    a_result.m_y = a_vec1.m_y * a_vec2.m_y;
+    a_result.m_z = a_vec1.m_z * a_vec2.m_z;
+  }
+  static void MulScalar(const gmVector3& a_vec, const float& a_scale, gmVector3& a_result)
+  {
+    a_result.m_x = a_vec.m_x * a_scale;
+    a_result.m_y = a_vec.m_y * a_scale;
+    a_result.m_z = a_vec.m_z * a_scale;
+  }
+  static void LerpPoints(const gmVector3& a_vecFrom, const gmVector3& a_vecTo, const float a_frac, gmVector3& a_result)  
+  {
+    a_result.m_x = a_vecFrom.m_x + a_frac * (a_vecTo.m_x - a_vecFrom.m_x);
+    a_result.m_y = a_vecFrom.m_y + a_frac * (a_vecTo.m_y - a_vecFrom.m_y);
+    a_result.m_z = a_vecFrom.m_z + a_frac * (a_vecTo.m_z - a_vecFrom.m_z);
+  }
+
+  // Set to Vector rotated by AxisAngle rotation 
+  // Rotate a vector by a axis (unit vector) and angle (radians)
+  // Only useful if you want to do this once off, otherwise, create a matrix and rotate multiple vectors more efficiently
+  static void RotateAxisAngle(const gmVector3& a_point, const gmVector3& a_axis, const float a_angle, gmVector3& a_result)
+  {
+    //cos(t) V + (1 - cos(t)) (A dot V) A + sin(t) (A cross V).
+
+    float sinAng, cosAng;
+    gmVector3 temp1, temp2;
+    gmSinCos(a_angle, sinAng, cosAng);
+
+    MulScalar(a_point, cosAng, temp1);
+    MulScalar(a_axis, (1 - cosAng) * Dot(a_axis, a_point), temp2);
+    Cross(a_axis, a_point, a_result);
+    MulScalar(a_result, sinAng, a_result);
+    Add(temp1, a_result, a_result);
+    Add(temp2, a_result, a_result);
+  }
+
+  static void RotateAboutX(const gmVector3& a_vec, float a_angle, gmVector3& a_result)
+  {
+    float sinAng, cosAng;
+
+    gmSinCos(a_angle, sinAng, cosAng);
+
+    a_result.m_y = a_vec.m_y * cosAng - a_vec.m_z * sinAng;
+    a_result.m_z = a_vec.m_y * sinAng + a_vec.m_z * cosAng;
+    a_result.m_x = a_vec.m_x;
+  }
+
+  static void RotateAboutY(const gmVector3& a_vec, float a_angle, gmVector3& a_result)
+  {
+    float sinAng, cosAng;
+
+    gmSinCos(a_angle, sinAng, cosAng);
+
+    a_result.m_z = a_vec.m_z * cosAng - a_vec.m_x * sinAng;
+    a_result.m_x = a_vec.m_z * sinAng + a_vec.m_x * cosAng;
+    a_result.m_y = a_vec.m_y;
+  }
+
+  static void RotateAboutZ(const gmVector3& a_vec, float a_angle, gmVector3& a_result)
+  {
+    float sinAng, cosAng;
+
+    gmSinCos(a_angle, sinAng, cosAng);
+
+    a_result.m_x = a_vec.m_x * cosAng - a_vec.m_y * sinAng;
+    a_result.m_y = a_vec.m_x * sinAng + a_vec.m_y * cosAng;
+    a_result.m_z = a_vec.m_z;
+  }
+
+  // Spherical linear interpolation between two vectors
+  // Using quaternion style, find vector along smallest great circle between vectors
+  // [sin((1-t)*A)/sin(A)]*P + [sin(t*A)/sin(A)]*Q
+  static void SlerpVectors(const gmVector3& a_vecFrom, const gmVector3& a_vecTo, const float a_frac, gmVector3& a_result)
+  {
+    float sinA;
+    float ang;
+    float ooSinA;
+    gmVector3 partSrc, partDst;
+    float cosAng;
+
+    cosAng = Dot(a_vecFrom, a_vecTo);
+    if(fabsf(cosAng) >= 0.999f) //if From is very similar to To
+    {
+      a_result = a_vecFrom;
+      return;
+    }
+
+    ang = acosf(cosAng);
+    sinA = sinf(ang);
+    ooSinA = 1.0f / sinA;
+
+    MulScalar(a_vecFrom, sinf((1 - a_frac) * ang) * ooSinA, partSrc);
+    MulScalar(a_vecTo, sinf(a_frac * ang) * ooSinA, partDst);
+
+    Add(partSrc, partDst, a_result);
+  }
+
+  static void Project(const gmVector3& a_dir, const gmVector3& a_point, const float a_time, gmVector3& a_result)
+  {
+    a_result.m_x = a_point.m_x + a_dir.m_x * a_time;
+    a_result.m_y = a_point.m_y + a_dir.m_y * a_time;
+    a_result.m_z = a_point.m_z + a_dir.m_z * a_time;
+  }
+
+  union
+  {
+    float m_v[3];
+    struct
+    {
+      float m_x;
+      float m_y;
+      float m_z;
+    };
+  };
+};
+
+
+/// \brief The Vector3 bindings
+/// Just a set of useful functions, operators, etc. for Vector3
+struct gmVector3Obj
+{
+  static int GM_CDECL DominantAxis(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    a_thread->PushInt( gmVector3::DominantAxis(*thisVec) );
+    return GM_OK;
+  }
+
+  static int GM_CDECL Dot(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+    a_thread->PushFloat( gmVector3::Dot(*thisVec, *otherVec) );
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL Cross(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+    gmVector3* newVec = Alloc(a_thread->GetMachine(), false);
+
+    gmVector3::Cross(*thisVec, *otherVec, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL RotateAxisAngle(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(2);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+        
+    float angle = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 1, angle))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    
+    gmVector3::RotateAxisAngle(*thisVec, *otherVec, angle, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL RotateX(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+        
+    float angle = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 0, angle))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::RotateAboutX(*thisVec, angle, *newVec);
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL RotateY(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+        
+    float angle = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 0, angle))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::RotateAboutY(*thisVec, angle, *newVec);
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL RotateZ(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+        
+    float angle = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 0, angle))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::RotateAboutZ(*thisVec, angle, *newVec);
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL Length(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+    a_thread->PushFloat( gmVector3::Length(*thisVec) );
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL LengthSquared(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+    a_thread->PushFloat( gmVector3::LengthSquared(*thisVec) );
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL Normalize(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::Normalize(*thisVec, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL Clone(gmThread * a_thread)
+  {
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    *newVec = *thisVec;
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+    
+    return GM_OK;
+  }
+
+  static int GM_CDECL Set(gmThread * a_thread)
+  {
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+        
+    if(a_thread->Param(0).m_type == GM_VECTOR3)
+    {
+      GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+      *thisVec = *otherVec;
+    }
+    else
+    {
+      GM_CHECK_NUM_PARAMS(3);
+      if(!gmGetFloatOrIntParamAsFloat(a_thread, 0, thisVec->m_x))
+      {
+        return GM_EXCEPTION;
+      }
+      if(!gmGetFloatOrIntParamAsFloat(a_thread, 1, thisVec->m_y))
+      {
+        return GM_EXCEPTION;
+      }
+      if(!gmGetFloatOrIntParamAsFloat(a_thread, 2, thisVec->m_z))
+      {
+        return GM_EXCEPTION;
+      }
+    }
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL LerpToPoint(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(2);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    float frac = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 1, frac))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::LerpPoints(*thisVec, *otherVec, frac, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL SlerpToVector(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(2);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    float frac = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 1, frac))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::SlerpVectors(*thisVec, *otherVec, frac, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static int GM_CDECL ProjectFrom(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(2);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, otherVec, 0);
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    float time = 0;
+    if(!gmGetFloatOrIntParamAsFloat(a_thread, 1, time))
+    {
+      return GM_EXCEPTION;
+    }
+
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmVector3::Project(*thisVec, *otherVec, time, *newVec);
+    
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+
+    return GM_OK;
+  }
+
+  static void GM_CDECL OpAdd(gmThread * a_thread, gmVariable * a_operands)
+  {
+    // Check types
+    if(a_operands[0].m_type != GM_VECTOR3 || a_operands[1].m_type != GM_VECTOR3)
+    {
+      a_operands[0].Nullify();
+      return;
+    }
+
+    // Get operands
+    gmVector3* vecObjA = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    gmVector3* vecObjB = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[1].m_value.m_ref))->m_user;
+
+    // Create new
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+    // Perform operation
+    gmVector3::Add(*vecObjA, *vecObjB, *newVec);
+
+    // Return result
+    a_operands[0].SetUser(newUserObj);
+  }
+
+  // a.SetAdd(b,c)  Demonstrate relative efficiency compared to operator version
+  static int GM_CDECL SetAdd(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(2);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, a_vec1, 0);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, a_vec2, 1);
+
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    // Perform operation
+    gmVector3::Add(*a_vec1, *a_vec2, *thisVec);
+    
+    return GM_OK;
+  }
+
+  // a.Add(b)  Demonstrate relative efficiency compared to operator version
+  static int GM_CDECL Add(gmThread * a_thread)
+  {
+    GM_CHECK_NUM_PARAMS(1);
+    GM_CHECK_USER_PARAM(gmVector3*, GM_VECTOR3, a_vec1, 0);
+
+    gmVector3* thisVec = (gmVector3*)a_thread->ThisUser_NoChecks();
+
+    // Perform operation
+    gmVector3::Add(*a_vec1, *thisVec, *thisVec);
+    
+    return GM_OK;
+  }
+
+  static void GM_CDECL OpSub(gmThread * a_thread, gmVariable * a_operands)
+  {
+    // Check types
+    if(a_operands[0].m_type != GM_VECTOR3 || a_operands[1].m_type != GM_VECTOR3)
+    {
+      a_operands[0].Nullify();
+      return;
+    }
+
+    // Get operands
+    gmVector3* vecObjA = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    gmVector3* vecObjB = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[1].m_value.m_ref))->m_user;
+
+    // Create new
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+    // Perform operation
+    gmVector3::Sub(*vecObjA, *vecObjB, *newVec);
+
+    // Return result
+    a_operands[0].SetUser(newUserObj);
+  }
+
+  static void GM_CDECL OpMul(gmThread * a_thread, gmVariable * a_operands)
+  {
+    // Check types
+    if(a_operands[0].m_type == GM_VECTOR3 && a_operands[1].m_type == GM_VECTOR3)
+    {
+      // Get operands
+      gmVector3* vecObjA = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+      gmVector3* vecObjB = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[1].m_value.m_ref))->m_user;
+
+      // Create new
+      gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+      gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+      // Perform operation
+      gmVector3::MulVector3(*vecObjA, *vecObjB, *newVec);
+
+      // Return result
+      a_operands[0].SetUser(newUserObj);
+    }
+    else if(a_operands[0].m_type == GM_VECTOR3 && (a_operands[1].m_type != GM_VECTOR3))
+    {
+      // Get operands
+      gmVector3* vecObjA = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+      float scaleB = 0.0f;
+      
+      if(a_operands[1].m_type == GM_FLOAT)
+      {
+        scaleB = a_operands[1].m_value.m_float;
+      }
+      else if(a_operands[1].m_type == GM_INT)
+      {
+        scaleB = (float)a_operands[1].m_value.m_int;
+      }
+       
+      // Create new
+      gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+      gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+      // Perform operation
+      gmVector3::MulScalar(*vecObjA, scaleB, *newVec);
+
+      // Return result
+      a_operands[0].SetUser(newUserObj);
+    }
+    else if((a_operands[0].m_type != GM_VECTOR3) && a_operands[1].m_type == GM_VECTOR3)
+    {
+      // Get operands
+      float scaleA = 0.0f;
+      gmVector3* vecObjB = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[1].m_value.m_ref))->m_user;
+
+      if(a_operands[0].m_type == GM_FLOAT)
+      {
+        scaleA = a_operands[0].m_value.m_float;
+      }
+      else if(a_operands[0].m_type == GM_INT)
+      {
+        scaleA = (float)a_operands[0].m_value.m_int;
+      }
+
+      // Create new
+      gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+      gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+      // Perform operation
+      gmVector3::MulScalar(*vecObjB, scaleA, *newVec);
+
+      // Return result
+      a_operands[0].SetUser(newUserObj);
+    }
+    else
+    {
+      a_operands[0].Nullify();
+      return;
+    }
+  }
+
+  static void GM_CDECL OpNeg(gmThread * a_thread, gmVariable * a_operands)
+  {
+    // Check types
+    if(a_operands[0].m_type != GM_VECTOR3)
+    {
+      a_operands[0].Nullify();
+      return;
+    }
+
+    // Get operands
+    gmVector3* vecObjA = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+
+    // Create new
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),false);
+    gmUserObject* newUserObj = a_thread->GetMachine()->AllocUserObject(newVec, GM_VECTOR3);
+    
+    // Perform operation
+    newVec->m_x = -vecObjA->m_x;
+    newVec->m_y = -vecObjA->m_y;
+    newVec->m_z = -vecObjA->m_z;
+
+    // Return result
+    a_operands[0].SetUser(newUserObj);
+  }
+
+  static void GM_CDECL OpGetDot(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+
+    GM_ASSERT(a_operands[1].m_type == GM_STRING);
+    gmStringObject* stringObj = (gmStringObject*)GM_OBJECT(a_operands[1].m_value.m_ref);
+    const char* cstr = stringObj->GetString();
+    if(stringObj->GetLength() != 1)
+    {
+      a_operands[0].Nullify();
+      return;
+    }
+
+    if(cstr[0] == 'x')
+    {
+      a_operands[0].SetFloat(thisVec->m_x);
+    }
+    else if(cstr[0] == 'y')
+    {
+      a_operands[0].SetFloat(thisVec->m_y);
+    }
+    else if(cstr[0] == 'z')
+    {
+      a_operands[0].SetFloat(thisVec->m_z);
+    }
+    else
+    {
+      a_operands[0].Nullify();
+    }
+  }
+
+  static void GM_CDECL OpSetDot(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+
+    GM_ASSERT(a_operands[2].m_type == GM_STRING);
+    gmStringObject* stringObj = (gmStringObject*)GM_OBJECT(a_operands[2].m_value.m_ref);
+    const char* cstr = stringObj->GetString();
+    if(stringObj->GetLength() != 1)
+    {
+      return;
+    }
+
+    float newFloat = 0.0f;
+    if(a_operands[1].m_type == GM_FLOAT)
+    {
+      newFloat = a_operands[1].m_value.m_float;
+    }
+    else if(a_operands[1].m_type == GM_INT)
+    {
+      newFloat = (float)a_operands[1].m_value.m_int;
+    }
+
+    if(cstr[0] == 'x')
+    {
+      thisVec->m_x = newFloat;
+    }
+    else if(cstr[0] == 'y')
+    {
+      thisVec->m_y = newFloat;
+    }
+    else if(cstr[0] == 'z')
+    {
+      thisVec->m_z = newFloat;
+    }
+  }
+  
+  static void GM_CDECL OpGetInd(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    if(a_operands[1].m_type == GM_INT)
+    {
+      int index = a_operands[1].m_value.m_int;
+      a_operands[0].SetFloat(thisVec->m_v[index]);
+      return;
+    }
+    a_operands[0].Nullify();
+  }
+
+  static void GM_CDECL OpSetInd(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    if(a_operands[1].m_type == GM_INT)
+    {
+      int index = a_operands[1].m_value.m_int;
+      if(index < 0 || index >= 3)
+      {
+        return;
+      }
+      
+      if(a_operands[2].m_type == GM_FLOAT)
+      {
+        thisVec->m_v[index] = a_operands[2].m_value.m_float;
+      }
+      else if(a_operands[2].m_type == GM_INT)
+      {
+        thisVec->m_v[index] = (float)a_operands[2].m_value.m_int;
+      }
+      else
+      {
+        thisVec->m_v[index] = 0.0f;
+      }
+    }
+  }
+
+#if GM_BOOL_OP
+  static void GM_CDECL OpBool(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    if (thisVec->m_x != 0 || thisVec->m_y != 0 && thisVec->m_z != 0)
+    {
+      a_operands[0] = gmVariable(1);
+    }
+    else
+    {
+      a_operands[0] = gmVariable(0);
+    }
+  }
+
+
+  static void GM_CDECL OpNot(gmThread * a_thread, gmVariable * a_operands)
+  {
+    GM_ASSERT(a_operands[0].m_type == GM_VECTOR3);
+    gmVector3* thisVec = (gmVector3*) ((gmUserObject*)GM_OBJECT(a_operands[0].m_value.m_ref))->m_user;
+    if (thisVec->m_x != 0 || thisVec->m_y != 0 && thisVec->m_z != 0)
+    {
+      a_operands[0] = gmVariable(0);
+    }
+    else
+    {
+      a_operands[0] = gmVariable(1);
+    }
+  }
+#endif // GM_BOOL_OP
+  
+  static int GM_CDECL Vector3(gmThread * a_thread)
+  {
+    int numParams = a_thread->GetNumParams();
+    gmVector3* newVec = Alloc(a_thread->GetMachine(),true);
+    if(numParams > 0)
+    {
+      gmGetFloatOrIntParamAsFloat(a_thread, 0, newVec->m_x);
+    }
+    if(numParams > 1)
+    {
+      gmGetFloatOrIntParamAsFloat(a_thread, 1, newVec->m_y);
+    }
+    if(numParams > 2)
+    {
+      gmGetFloatOrIntParamAsFloat(a_thread, 2, newVec->m_z);
+    }
+    a_thread->PushNewUser(newVec, GM_VECTOR3);
+    return GM_OK;
+  }
+
+#if GM_USE_INCGC
+  static void GM_CDECL GCDestruct(gmMachine * a_machine, gmUserObject* a_object)
+  {
+    GM_ASSERT(a_object->m_userType == GM_VECTOR3);
+    gmVector3* object = (gmVector3*)a_object->m_user;
+    Free(a_machine, object);
+  }
+
+#else //GM_USE_INCGC
+
+  // Garbage collect 'Garbage Collect' function
+  static void GM_CDECL Collect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+  {
+    GM_ASSERT(a_object->m_userType == GM_VECTOR3);
+    gmVector3* object = (gmVector3*)a_object->m_user;
+    Free(a_machine, object);
+  }
+#endif //GM_USE_INCGC
+  
+  static void GM_CDECL AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen)
+  {
+    gmVector3* vec = (gmVector3*)a_object->m_user;
+    //Note '#' always display decimal place, 'g' display exponent if > precision or 4
+    _gmsnprintf(a_buffer, a_bufferLen, "(%#.8g, %#.8g, %#.8g)", vec->m_x, vec->m_y, vec->m_z);
+  }
+
+  // Allocate memory for one object
+  static gmVector3* Alloc(gmMachine* a_machine, bool a_clearToZero)
+  {
+    a_machine->AdjustKnownMemoryUsed(sizeof(gmVector3));
+    gmVector3* newObj = (gmVector3*)s_mem.Alloc(); //Allocate our type
+    if(a_clearToZero) //Optionally clear our members
+    {
+      newObj->m_x = 0.0f;
+      newObj->m_y = 0.0f;
+      newObj->m_z = 0.0f;
+    }
+    return newObj;
+  }
+
+  // Free memory for one object
+  static void Free(gmMachine* a_machine, gmVector3* a_obj)
+  {
+    a_machine->AdjustKnownMemoryUsed(-(int)sizeof(gmVector3));
+    s_mem.Free(a_obj);
+  }
+
+  // Only call when gmMachine is shutdown
+  static void FreeAllMemory()
+  {
+    s_mem.ResetAndFreeMemory();
+  }
+
+  // Static members
+  static gmMemFixed s_mem;                        ///< Memory for vector3s
+};
+
+// Static and Global instances
+gmMemFixed gmVector3Obj::s_mem(sizeof(gmVector3), 64);
+gmType GM_VECTOR3 = GM_NULL;
+
+/// \brief Push a Vector3. (Eg. Use to return result).
+void gmVector3_Push(gmThread* a_thread, const float* a_vec)
+{
+  gmVector3* newVec = gmVector3Obj::Alloc(a_thread->GetMachine(), false);
+  *newVec = *(gmVector3*)a_vec;
+  a_thread->PushNewUser(newVec, GM_VECTOR3);
+}
+
+/// \brief Create a Vector3 user object and fill it (Eg. use, to set as table member).
+gmUserObject* gmVector3_Create(gmMachine* a_machine, const float* a_vec)
+{
+  gmVector3* newVec = gmVector3Obj::Alloc(a_machine, false);
+  *newVec = *(gmVector3*)a_vec;
+  return a_machine->AllocUserObject(newVec, GM_VECTOR3);
+}
+
+// libs
+
+static gmFunctionEntry s_vector3Lib[] =
+{
+  /*gm
+    \lib Vector3
+  */
+  /*gm
+    \function Vector3
+    \brief Create a Vector3 object
+    \param float x or [0] optional (0)
+    \param float y or [1] optional (0)
+    \param float z or [2] optional (0)
+  */
+  {"Vector3", gmVector3Obj::Vector3},
+};
+
+static gmFunctionEntry s_vector3TypeLib[] = 
+{ 
+  /*gm
+    \lib Vector3
+    \brief Vector3 math class
+  */
+  /*gm
+    \function DominantAxis
+    \brief Find the index of the largest vector component.
+    \this Vector to evaluate.
+    \return int Index of largest component.
+  */
+  {"DominantAxis", gmVector3Obj::DominantAxis},
+  /*gm
+    \function Dot
+    \brief Calculate the Dot (or Inner) Product of two vectors.
+    \this Vector3 First vector.
+    \param Vector3 Second vector.
+    \return float result.
+  */
+  {"Dot", gmVector3Obj::Dot},
+  /*gm
+    \function Length
+    \brief Length will return the length of the vector.
+    \return float Dot product result that is cosine of the angle between the two vectors.
+  */
+  {"Length", gmVector3Obj::Length},
+  /*gm
+    \function Cross
+    \brief Calculate the Cross (or Outer) Product of two vectors.
+    \this Vector3 First vector.
+    \param Vector3 Second vector.
+    \return Vector3 Cross product resultant vector that is perpendicular to the two input vectors and length sine of the angle between them.
+  */
+  {"Cross", gmVector3Obj::Cross},
+  /*gm
+    \function Normalize
+    \brief Return a unit length copy of this vector.
+    \this Vector to be copied.
+    \return Vector3 Unit length copy of this vector.
+  */
+  {"Normalize", gmVector3Obj::Normalize},
+  /*gm
+    \function LengthSquared
+    \brief Return the squared length of the vector.
+    \return float Squared length of the vector.
+  */
+  {"LengthSquared", gmVector3Obj::LengthSquared},
+  /*gm
+    \function ProjectFrom
+    \brief Project a direction from a point.
+    \this Vector3 Direction.
+    \param Vector3 Start point;
+    \param float Distance or time.
+    \return Vector3 Projected result.
+  */
+  {"ProjectFrom", gmVector3Obj::ProjectFrom},
+  /*gm
+    \function Clone
+    \brief Return a copy of this vector.
+    \return A copy of this vector.
+  */
+  {"Clone", gmVector3Obj::Clone},
+  /*gm
+    \function Set
+    \brief Set vector from other vector or 3 components.
+  */
+  {"Set", gmVector3Obj::Set},
+  /*gm
+    \function LerpToPoint
+    \brief Linear interpolate between two 'point' vectors.
+    \this Vector3 From vector.
+    \param Vector3 To vector.
+    \param float Fraction or time between 0 and 1.
+    \return Vector3 Resulting inbetween vector.
+  */
+  {"LerpToPoint", gmVector3Obj::LerpToPoint},
+  /*gm
+    \function SlerpToVector
+    \brief Spherical linear interpolate between two vectors.
+    \this Vector3 From vector.
+    \param Vector3 To vector.
+    \param float Fraction or time between 0 and 1.
+    \return Vector3 Resulting inbetween vector.
+  */
+  {"SlerpToVector", gmVector3Obj::SlerpToVector},
+  /*gm
+    \function RotateAxisAngle
+    \brief Rotate around Axis by Angle.
+    \this Vector3 Vector to rotate.
+    \param Vector3 Unit length axis of rotation.
+    \param float Angle amount to rotate.
+    \return Vector3 Resulting rotated vector.
+  */
+  {"RotateAxisAngle", gmVector3Obj::RotateAxisAngle},
+  /*gm
+    \function RotateX
+    \brief Rotate around X Axis by Angle.
+    \this Vector3 Vector to rotate.
+    \param float Angle amount to rotate.
+    \return Vector3 Resulting rotated vector.
+  */
+  {"RotateX", gmVector3Obj::RotateX},
+  /*gm
+    \function RotateX
+    \brief Rotate around Y Axis by Angle.
+    \this Vector3 Vector to rotate.
+    \param float Angle amount to rotate.
+    \return Vector3 Resulting rotated vector.
+  */
+  {"RotateY", gmVector3Obj::RotateY},
+  /*gm
+    \function RotateZ
+    \brief Rotate around Z Axis by Angle.
+    \this Vector3 Vector to rotate.
+    \param float Angle amount to rotate.
+    \return Vector3 Resulting rotated vector.
+  */
+  {"RotateZ", gmVector3Obj::RotateZ},
+
+  /*gm
+    \function SetAdd
+    \brief Add two vectors, store result in this. Demonstrate relative efficiency compared to operator version.
+    \this Vector3 Result vector.
+    \param Vector3 First vector.
+    \param Vector3 Second vector.
+  */
+  {"SetAdd", gmVector3Obj::SetAdd},
+
+  /*gm
+    \function Add
+    \brief Add vector to this. Demonstrate relative efficiency compared to operator version.
+    \this Vector3 Result vector.
+    \param Vector3 vector to add.
+  */
+  {"Add", gmVector3Obj::Add},
+};
+
+
+/// \brief Bind Vector3 library
+void gmBindVector3Lib(gmMachine * a_machine)
+{
+  // Lib
+  a_machine->RegisterLibrary(s_vector3Lib, sizeof(s_vector3Lib) / sizeof(s_vector3Lib[0]));
+    
+  // Register new user type
+  GM_VECTOR3 = a_machine->CreateUserType("Vector3");
+
+  // Operators
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_ADD, NULL, gmVector3Obj::OpAdd);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_SUB, NULL, gmVector3Obj::OpSub);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_MUL, NULL, gmVector3Obj::OpMul);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_NEG, NULL, gmVector3Obj::OpNeg);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_GETDOT, NULL, gmVector3Obj::OpGetDot);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_SETDOT, NULL, gmVector3Obj::OpSetDot);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_GETIND, NULL, gmVector3Obj::OpGetInd);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_SETIND, NULL, gmVector3Obj::OpSetInd);
+#if GM_BOOL_OP
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_BOOL, NULL, gmVector3Obj::OpBool);
+  a_machine->RegisterTypeOperator(GM_VECTOR3, O_NOT, NULL, gmVector3Obj::OpNot);
+#endif // GM_BOOL_OP
+
+  // Type Lib
+  a_machine->RegisterTypeLibrary(GM_VECTOR3, s_vector3TypeLib, sizeof(s_vector3TypeLib) / sizeof(s_vector3TypeLib[0]));
+
+  // Register garbage collection for type
+#if GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(GM_VECTOR3, NULL, gmVector3Obj::GCDestruct, gmVector3Obj::AsString); 
+#else //GM_USE_INCGC
+  a_machine->RegisterUserCallbacks(GM_VECTOR3, NULL, gmVector3Obj::Collect, gmVector3Obj::AsString); 
+#endif //GM_USE_INCGC
+}
+

+ 37 - 0
gmsrc/src/binds/gmVector3Lib.h

@@ -0,0 +1,37 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMVECTOR3LIB_H_
+#define _GMVECTOR3LIB_H_
+
+#include "gmConfig.h"
+#include "gmVariable.h"
+
+class gmMachine;
+class gmThread;
+class gmUserObject;
+
+// Bind the Vector3 Library.
+void gmBindVector3Lib(gmMachine * a_machine);
+
+// Push a Vector3 onto the stack
+void gmVector3_Push(gmThread* a_thread, const float* a_vec);
+
+// Create a Vector3 user object and fill it
+gmUserObject* gmVector3_Create(gmMachine* a_machine, const float* a_vec);
+
+// The Vector3 type Id.
+extern gmType GM_VECTOR3;
+
+// Example of getting Vector3 from parameter
+// GM_CHECK_USER_PARAM(float*, GM_VECTOR3, vec1, 0);
+
+#endif // _GMVECTOR3LIB_H_

+ 473 - 0
gmsrc/src/examples/GameObject/App.cpp

@@ -0,0 +1,473 @@
+#include <windows.h>  
+#include <mmsystem.h> // multimedia timer (may need winmm.lib)
+#include "App.h"
+#include "ScriptSys.h"
+#include "GameObj.h"
+#include "ScriptObj.h"
+#include "gmCall.h"
+#include "InputKBWin32.h"
+
+App* App::s_instancePtr = NULL;
+
+
+App::App()
+{
+  s_instancePtr = this;
+}
+
+
+
+App::~App()
+{
+  s_instancePtr = NULL;
+}
+
+
+bool App::Init()
+{
+  InputKBWin32::Get().Init();
+
+  // Init console
+  InitConsole();
+
+  // Init script system for game objects
+  ScriptSys::Init();
+
+  // Register app bindings
+  RegisterScriptBindings();
+
+  // Init timer
+  m_deltaTime = 0;
+  m_lastTime = timeGetTime();
+
+  // Compile and run script
+  ScriptSys::Get()->ExecuteFile("TestGameObj.gm");
+
+//TEST REMOVE
+//  ClearScreen();
+//  SetColor(COLOR_YELLOW, COLOR_RED);
+//  SetCursor(10,10);
+//  Print("Hello");
+//  PrintAt(10,10,"Hello");
+
+
+  return true;
+}
+
+
+void App::Destroy()
+{
+  ScriptSys::Destroy();
+}
+
+
+bool App::Update()
+{
+  int curTime = timeGetTime();
+  m_deltaTime = curTime - m_lastTime;
+  m_lastTime = curTime;
+
+  // Update input
+  InputKBWin32::Get().Update();
+      
+  // Execute some script
+  ScriptSys::Get()->Execute(m_deltaTime);
+
+  if(InputKBWin32::Get().IsKeyPressed('A'))
+  {
+    TestA();
+  }
+  else if(InputKBWin32::Get().IsKeyPressed('B'))
+  {
+    TestB();
+  }
+  else if(InputKBWin32::Get().IsKeyPressed('C'))
+  {
+    TestC();
+  }
+
+  if(InputKBWin32::Get().IsKeyPressed(VK_ESCAPE))
+  {
+    return false;
+  }
+
+  return true;
+}
+
+
+void App::InitConsole()
+{
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+  m_console = GetStdHandle(STD_OUTPUT_HANDLE);
+  
+  GetConsoleScreenBufferInfo(m_console, &csbi);
+
+  DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+
+  m_screenSizeX = csbi.dwSize.X;
+  m_screenSizeY = csbi.dwSize.Y;
+}
+
+
+void App::TestA()
+{
+  // Test create a game object and set member in script
+  GameObj* newObj = new GameObj;
+
+  printf("new GameObj = %x\n", newObj);
+  printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
+
+  newObj->GetScriptObj()->SetMemberString("m_name", "MangoBoy");
+  newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
+
+  delete newObj;
+
+  gmCall call;
+  if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "RunGlobalObject"))
+  {
+    call.End();
+  }
+}
+
+
+void App::TestB()
+{
+  // Test call script function that keeps running for a bit
+  gmCall call;
+  if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "ThreadYieldTest"))
+  {
+    call.End();
+  }
+}
+
+
+void App::TestC()
+{
+  // Quick test of collect garbage and cpp owned objects
+
+  GameObj* newObj = new GameObj;
+
+  printf("new GameObj = %x\n", newObj);
+  printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
+
+  newObj->GetScriptObj()->SetMemberString("m_name", "PotatoHead");
+  //newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
+
+  ScriptSys::Get()->GetMachine()->CollectGarbage(true);
+
+  delete newObj;
+}
+
+
+void App::SetCursor(int a_x, int a_y)
+{
+  ClipScreenCoordsi(a_x, a_y);
+  
+  COORD point;
+
+  point.X = (short) a_x; 
+  point.Y = (short) a_y;
+  
+  SetConsoleCursorPosition(m_console, point);
+}
+
+
+void App::ClearScreen()
+{
+  COORD coordScreen = { 0, 0 };
+  DWORD cCharsWritten;
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+  DWORD dwConSize;
+  GetConsoleScreenBufferInfo(m_console, &csbi);
+  dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+  FillConsoleOutputCharacter(m_console, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
+  GetConsoleScreenBufferInfo(m_console, &csbi);
+  FillConsoleOutputAttribute(m_console, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
+  SetConsoleCursorPosition(m_console, coordScreen);
+}
+
+
+void App::Print(const char* a_string)
+{
+  printf("%s", a_string);
+}
+
+
+void App::PrintAt(int a_x, int a_y, const char* a_string)
+{
+  SetCursor(a_x, a_y);
+  Print(a_string);
+}
+
+
+int App::GetAttribFromColIndex(int a_colorIndex, bool a_isForeground)
+{
+  // WARNING These struct must match the color enums
+  static int foreColors[COLOR_MAX]=
+  {
+    0, //COLOR_BLACK
+    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_WHITE,
+    FOREGROUND_RED, //COLOR_RED
+    FOREGROUND_GREEN, //COLOR_GREEN,
+    FOREGROUND_BLUE, //COLOR_BLUE,
+    FOREGROUND_RED | FOREGROUND_BLUE, //COLOR_MAGENTA,
+    FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_CYAN,
+    FOREGROUND_RED | FOREGROUND_GREEN, //COLOR_YELLOW,
+  };
+  
+  static int backColors[COLOR_MAX]=
+  {
+    0, //COLOR_BLACK
+    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_WHITE,
+    BACKGROUND_RED, //COLOR_RED
+    BACKGROUND_GREEN, //COLOR_GREEN,
+    BACKGROUND_BLUE, //COLOR_BLUE,
+    BACKGROUND_RED | BACKGROUND_BLUE, //COLOR_MAGENTA,
+    BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_CYAN,
+    BACKGROUND_RED | BACKGROUND_GREEN, //COLOR_YELLOW,
+  };
+
+
+  if( (a_colorIndex < COLOR_MIN) && (a_colorIndex >= COLOR_MAX) )
+  {
+    if(a_isForeground)
+    {
+      return foreColors[COLOR_WHITE];
+    }
+    else
+    {
+      return backColors[COLOR_WHITE];
+    }
+  }
+
+  if(a_isForeground)
+  {
+    return foreColors[a_colorIndex];
+  }
+  else
+  {
+    return backColors[a_colorIndex];
+  }
+}
+
+
+void App::SetColor(int a_foreColorIndex, int a_backColorIndex)
+{
+  int foreCol = GetAttribFromColIndex(a_foreColorIndex, true);
+  int backCol = GetAttribFromColIndex(a_backColorIndex, false);
+
+  int param = foreCol | backCol;
+
+  SetConsoleTextAttribute(m_console, (short) param);  
+}
+
+
+bool App::ClipScreenCoordsf(float& a_posX, float& a_posY)
+{
+  bool wasClipped = false;
+
+  if(a_posX < 0.0f)
+  {
+    a_posX = 0.0f;
+    wasClipped = true;
+  }
+  else if(a_posX >= (float)App::Get()->GetScreenSizeX())
+  {
+    a_posX = (float)(App::Get()->GetScreenSizeX() - 1);
+    wasClipped = true;
+  }
+
+  if(a_posY < 0.0f)
+  {
+    a_posY = 0.0f;
+    wasClipped = true;
+  }
+  else if(a_posY >= (float)App::Get()->GetScreenSizeY())
+  {
+    a_posY = (float)(App::Get()->GetScreenSizeY() - 1);
+    wasClipped = true;
+  }
+
+  return wasClipped;
+}
+
+
+bool App::ClipScreenCoordsi(int& a_posX, int& a_posY)
+{
+  bool wasClipped = false;
+
+  if(a_posX < 0)
+  {
+    a_posX = 0;
+    wasClipped = true;
+  }
+  else if(a_posX >= App::Get()->GetScreenSizeX())
+  {
+    a_posX = App::Get()->GetScreenSizeX() - 1;
+    wasClipped = true;
+  }
+
+  if(a_posY < 0)
+  {
+    a_posY = 0;
+    wasClipped = true;
+  }
+  else if(a_posY >= App::Get()->GetScreenSizeY())
+  {
+    a_posY = App::Get()->GetScreenSizeY() - 1;
+    wasClipped = true;
+  }
+
+  return wasClipped;
+}
+
+
+//////////////////////////////////////////////////
+// Script bindings
+//////////////////////////////////////////////////
+
+
+int GM_CDECL App::Console_Print(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_STRING_PARAM(a_string, 0);
+
+  App::Get()->Print(a_string);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL App::Console_SetCursor(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_INT_PARAM(a_curX, 0);
+  GM_CHECK_INT_PARAM(a_curY, 1);
+
+  App::Get()->ClipScreenCoordsi(a_curX, a_curY);
+  App::Get()->SetCursor(a_curX, a_curY);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL App::Console_SetColor(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_INT_PARAM(a_foreCol, 0);
+  GM_CHECK_INT_PARAM(a_backCol, 1);
+
+  App::Get()->SetColor(a_foreCol, a_backCol);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL App::Input_KeyPressed(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(a_vKey, 0);
+
+  if(InputKBWin32::Get().IsKeyPressed(a_vKey))
+  {
+    a_thread->PushInt(1);
+  }
+  else
+  {
+    a_thread->PushInt(0);
+  }
+
+  return GM_OK;
+}
+
+
+int GM_CDECL App::Input_KeyDown(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(a_vKey, 0);
+
+  if(InputKBWin32::Get().IsKeyDown(a_vKey))
+  {
+    a_thread->PushInt(1);
+  }
+  else
+  {
+    a_thread->PushInt(0);
+  }
+
+  return GM_OK;
+}
+
+
+void App::RegisterScriptBindings()
+{
+  static gmFunctionEntry ConsoleLib[] = 
+  { 
+    /*gm
+      \lib Console
+      \brief Console Library
+    */
+    /*gm
+      \function Print
+      \brief Print string at current cursor position
+      \param string a_string
+    */
+    {"Print", Console_Print},
+    /*gm
+      \function SetCursor
+      \brief Set cursor position
+      \param int a_curX Cursor x position
+      \param int a_curY Cursor y position
+    */
+    {"SetCursor", Console_SetCursor},
+
+    /*gm
+      \function SetColor
+      \brief Set text color
+      \param int a_foreCol Foreground color
+      \param int a_backCol Background color
+    */
+    {"SetColor", Console_SetColor},
+  };
+
+
+  static gmFunctionEntry InputLib[] = 
+  { 
+    /*gm
+      \lib Console
+      \brief Console Library
+    */
+    /*gm
+      \function KeyPressed
+      \brief Was this key pressed
+      \param int a_vKey windows virtual key code (Most match ascii uppercase)
+      \return true if key was pressed this frame
+    */
+    {"KeyPressed", Input_KeyPressed},
+
+    /*gm
+      \function KeyDown
+      \brief Is this key down
+      \param int a_vKey windows virtual key code (Most match ascii uppercase)
+      \return true if is down
+    */
+    {"KeyDown", Input_KeyDown},
+
+  };
+
+  gmMachine* machine = ScriptSys::Get()->GetMachine();
+
+  machine->RegisterLibrary(ConsoleLib, sizeof(ConsoleLib) / sizeof(ConsoleLib[0]), "Console");
+  machine->RegisterLibrary(InputLib, sizeof(InputLib) / sizeof(InputLib[0]), "Input");
+
+  // Make some global constants
+  machine->GetGlobals()->Set(machine, "COLOR_BLACK", gmVariable(GM_INT, COLOR_BLACK));
+  machine->GetGlobals()->Set(machine, "COLOR_WHITE", gmVariable(GM_INT, COLOR_WHITE));
+  machine->GetGlobals()->Set(machine, "COLOR_RED", gmVariable(GM_INT, COLOR_RED));
+  machine->GetGlobals()->Set(machine, "COLOR_GREEN", gmVariable(GM_INT, COLOR_GREEN));
+  machine->GetGlobals()->Set(machine, "COLOR_BLUE", gmVariable(GM_INT, COLOR_BLUE));
+  machine->GetGlobals()->Set(machine, "COLOR_MAGENTA", gmVariable(GM_INT, COLOR_MAGENTA));
+  machine->GetGlobals()->Set(machine, "COLOR_CYAN", gmVariable(GM_INT, COLOR_CYAN));
+  machine->GetGlobals()->Set(machine, "COLOR_YELLOW", gmVariable(GM_INT, COLOR_YELLOW));
+}

+ 72 - 0
gmsrc/src/examples/GameObject/App.h

@@ -0,0 +1,72 @@
+//
+// Example application
+//
+#include <Windows.h>
+#include "gmThread.h"
+
+class App
+{
+public:
+   
+  enum
+  {
+    COLOR_MIN = 0,
+    
+    COLOR_BLACK = COLOR_MIN,
+    COLOR_WHITE,
+    COLOR_RED,
+    COLOR_GREEN,
+    COLOR_BLUE,
+    COLOR_MAGENTA,
+    COLOR_CYAN,
+    COLOR_YELLOW,
+    
+    
+    COLOR_MAX
+  };
+
+  static App* Get()                               { return s_instancePtr; }
+
+  App();
+  ~App();
+  bool Init();
+  void Destroy();
+  bool Update();
+
+  void SetCursor(int a_x, int a_y);
+  void ClearScreen();
+  void Print(const char* a_string);
+  void PrintAt(int a_x, int a_y, const char* a_string);
+  void SetColor(int a_foreColorIndex, int a_backColorIndex);
+  int GetScreenSizeX()                            { return m_screenSizeX; }
+  int GetScreenSizeY()                            { return m_screenSizeY; }
+
+  bool ClipScreenCoordsf(float& a_posX, float& a_posY);
+  bool ClipScreenCoordsi(int& a_posX, int& a_posY);
+
+protected:
+
+  int GetAttribFromColIndex(int a_colorIndex, bool a_isForeground);
+  void InitConsole();
+  void RegisterScriptBindings();
+
+  static int GM_CDECL Console_Print(gmThread* a_thread);
+  static int GM_CDECL Console_SetCursor(gmThread* a_thread);
+  static int GM_CDECL Console_SetColor(gmThread* a_thread);
+
+  static int GM_CDECL Input_KeyPressed(gmThread* a_thread);
+  static int GM_CDECL Input_KeyDown(gmThread* a_thread);
+
+  void TestA();
+  void TestB();
+  void TestC();
+
+  int m_deltaTime;
+  int m_lastTime;
+
+  int m_screenSizeX;
+  int m_screenSizeY;
+  HANDLE m_console;
+
+  static App* s_instancePtr;                      ///< Ptr to instance of this class when created
+};

+ 317 - 0
gmsrc/src/examples/GameObject/GameObj.cpp

@@ -0,0 +1,317 @@
+//
+// GameObj.cpp
+//
+
+#include <math.h>
+#include "GameObj.h"
+#include "App.h"
+#include "ScriptObj.h"
+#include "ScriptSys.h"
+
+GameObj::GameObj()
+{
+  m_scriptObj = NULL;
+  m_posX = -1;
+  m_posY = -1;
+  m_destX = m_posX;
+  m_destY = m_posY;
+  m_speed = 1.0f;
+  m_colorIndex = App::COLOR_WHITE;
+
+  m_scriptObj = new ScriptObj(this);
+}
+
+
+GameObj::~GameObj()
+{
+  if(m_scriptObj)
+  {
+    delete m_scriptObj;
+  }
+}
+
+
+float GameObj::GetPosX() const
+{
+  return m_posX;
+}
+
+
+float GameObj::GetPosY() const
+{
+  return m_posY;
+}
+
+
+void GameObj::SetPos(const float a_x, const float a_y)
+{
+  m_posX = a_x;
+  m_posY = a_y;
+
+  App::Get()->ClipScreenCoordsf(m_posX, m_posY);
+
+  m_destX = m_posX;
+  m_destY = m_posY;
+}
+
+
+void GameObj::MoveTo(const float a_x, const float a_y)
+{
+  m_destX = a_x;
+  m_destY = a_y;
+
+  App::Get()->ClipScreenCoordsf(m_destX, m_destY);
+}
+
+
+void GameObj::SetSpeed(const float a_speed)
+{
+  m_speed = a_speed;
+}
+
+
+float GameObj::GetSpeed()
+{
+  return m_speed;
+}
+
+
+void GameObj::SetColor(int a_colorIndex)
+{
+  if(a_colorIndex >= App::COLOR_MIN && a_colorIndex < App::COLOR_MAX)
+  {
+    m_colorIndex = a_colorIndex;
+  }
+}
+
+
+void GameObj::Update(float a_deltaTime)
+{
+  // NOTE: A real game would probably not update each object each frame
+  //       but instead only update an object in a particular way when required.
+  //       This example will do all updating in one place, and do so each frame.
+
+  // Update movement
+  {
+    float dx = m_destX - m_posX;
+    float dy = m_destY - m_posY;
+
+    float len2 = dx*dx + dy*dy;
+
+    if(len2 > 0.0f)
+    {
+      float moveThisFrame = a_deltaTime * m_speed;
+      float distToGo = sqrtf(len2);
+
+      // We can reach dest this frame
+      if(moveThisFrame > distToGo)
+      {
+        m_posX = m_destX;
+        m_posY = m_destY;
+      }
+      else
+      {
+        m_posX += dx * moveThisFrame;
+        m_posY += dy * moveThisFrame;
+      }
+    }
+  }
+}
+
+
+void GameObj::Render()
+{
+  
+}
+
+
+//////////////////////////////////////////////////
+// Script 
+//////////////////////////////////////////////////
+
+
+int GM_CDECL GameObj::GameObj_MoveTo(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_FLOAT_PARAM(destX, 0);
+  GM_CHECK_FLOAT_PARAM(destY, 1);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  thisPtr->MoveTo(destX, destY);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_SetPos(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(2);
+  GM_CHECK_FLOAT_PARAM(posX, 0);
+  GM_CHECK_FLOAT_PARAM(posY, 1);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  thisPtr->SetPos(posX, posY);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_GetPosX(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  a_thread->PushFloat(thisPtr->GetPosX());
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_GetPosY(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  a_thread->PushFloat(thisPtr->GetPosY());
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_IsValid(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    a_thread->PushInt(false);
+  }
+  else
+  {
+    a_thread->PushInt(true);
+  }
+
+  return GM_OK;
+}
+
+
+
+int GM_CDECL GameObj::GameObj_SetSpeed(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_FLOAT_PARAM(speed, 0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  thisPtr->SetSpeed(speed);
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_GetSpeed(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  a_thread->PushFloat(thisPtr->GetSpeed());
+
+  return GM_OK;
+}
+
+
+int GM_CDECL GameObj::GameObj_SetColor(gmThread* a_thread)
+{
+  GM_CHECK_NUM_PARAMS(1);
+  GM_CHECK_INT_PARAM(colorIndex, 0);
+  GameObj* thisPtr = GetThisGameObj(a_thread);
+  if(!thisPtr)
+  {
+    return GM_EXCEPTION;
+  }
+
+  thisPtr->SetColor(colorIndex);
+
+  return GM_OK;
+}
+
+
+void GameObj::RegisterScriptBindings()
+{
+  static gmFunctionEntry gameObjTypeLib[] = 
+  { 
+    /*gm
+      \lib GameObj
+      \brief Game Object class
+    */
+    /*gm
+      \function SetPos
+      \brief Set position
+      \param float a_posX New position X component
+      \param float a_posY New position Y component
+    */
+    {"SetPos", GameObj_SetPos},
+    /*gm
+      \function GetPosX
+      \brief Get position X
+      \return float Get position X component.
+    */
+    {"GetPosX", GameObj_GetPosX},
+    /*gm
+      \function GetPosY
+      \brief Get position Y
+      \return float Get position Y component.
+    */
+    {"GetPosY", GameObj_GetPosY},
+    /*gm
+      \function IsValid
+      \brief Is this a valid object, or has it been deleted or such
+      \return int true if valid
+    */
+    {"IsValid", GameObj_IsValid},
+    /*gm
+      \function SetSpeed
+      \brief Set speed
+      \param a_speed New speed
+    */
+    {"SetSpeed", GameObj_SetSpeed},
+    /*gm
+      \function GetSpeed
+      \brief Get speed
+      \return float Current speed
+    */
+    {"GetSpeed", GameObj_GetSpeed},
+    /*gm
+      \function SetColor
+      \brief Set color
+      \param a_color New color
+    */
+    {"SetColor", GameObj_SetColor},
+
+  };
+
+  ScriptSys::Get()->GetMachine()->RegisterTypeLibrary(ScriptObj::GMTYPE_GAMEOBJ, gameObjTypeLib, sizeof(gameObjTypeLib) / sizeof(gameObjTypeLib[0]));
+}

+ 61 - 0
gmsrc/src/examples/GameObject/GameObj.h

@@ -0,0 +1,61 @@
+#ifndef GAMEOBJ_H
+#define GAMEOBJ_H
+
+#include "gmThread.h"
+
+//
+// GameObj.h
+//
+// Example game object that uses the script interface component
+//
+
+// Fwd decls
+class ScriptObj;
+
+
+
+class GameObj
+{
+public:
+
+  GameObj();
+  virtual ~GameObj();
+
+  ScriptObj* GetScriptObj()               { return m_scriptObj; }
+
+  float GetPosX() const;
+  float GetPosY() const;
+  void SetPos(const float a_x, const float a_y);
+  void MoveTo(const float a_x, const float a_y);
+  void SetSpeed(const float a_speed);
+  float GetSpeed();
+  void SetColor(int a_colorIndex);
+
+  void Update(float a_deltaTime);
+  void Render();
+
+  static void RegisterScriptBindings();
+
+private:
+
+  static int GM_CDECL GameObj_SetPos(gmThread* a_thread);
+  static int GM_CDECL GameObj_GetPosX(gmThread* a_thread);
+  static int GM_CDECL GameObj_GetPosY(gmThread* a_thread);
+  static int GM_CDECL GameObj_IsValid(gmThread* a_thread);
+  static int GM_CDECL GameObj_MoveTo(gmThread* a_thread);
+  static int GM_CDECL GameObj_SetSpeed(gmThread* a_thread);
+  static int GM_CDECL GameObj_GetSpeed(gmThread* a_thread);
+  static int GM_CDECL GameObj_SetColor(gmThread* a_thread);
+
+  ScriptObj* m_scriptObj;
+  float m_posX;
+  float m_posY;
+  float m_destX;
+  float m_destY;
+  float m_speed;
+  int m_colorIndex;
+
+};
+
+
+#endif //GAMEOBJ_H

+ 470 - 0
gmsrc/src/examples/GameObject/GameObject.dsp

@@ -0,0 +1,470 @@
+# Microsoft Developer Studio Project File - Name="GameObject" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=GameObject - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "GameObject.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "GameObject.mak" CFG="GameObject - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "GameObject - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "GameObject - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "Perforce Project"
+# PROP Scc_LocalPath "..\.."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "GameObject - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "GameObject - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /I "..\..\binds" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "GameObject - Win32 Release"
+# Name "GameObject - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\App.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\App.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\GameObj.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\GameObj.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\InputKBWin32.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\InputKBWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClient.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\NetClient.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScriptObj.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScriptObj.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScriptSys.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScriptSys.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdStuff.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdStuff.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TestGameObj.gm
+# End Source File
+# End Group
+# Begin Group "gm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\gm\gmArraySimple.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmArraySimple.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCodeGen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCodeGen.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGen.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGenHooks.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGenHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCrc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCrc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmDebug.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmFunctionObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmFunctionObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmHash.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIncGC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIncGC.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIterator.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLibHooks.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLibHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmListDouble.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmListDouble.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLog.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachine.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachine.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachineLib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachineLib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMem.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMem.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemChain.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemChain.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixed.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixedSet.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixedSet.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmOperators.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmOperators.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmParser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmParser.cpp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmScanner.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmScanner.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStreamBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStreamBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStringObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStringObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmTableObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmTableObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmThread.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmThread.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUserObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUserObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmVariable.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmVariable.h
+# End Source File
+# End Group
+# Begin Group "gmBinds"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\binds\gmCall.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmCall.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmHelpers.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmHelpers.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmMathLib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmMathLib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmStringLib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmStringLib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmSystemLib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\binds\gmSystemLib.h
+# End Source File
+# End Group
+# Begin Group "gmConfig"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\platform\win32msvc\gmConfig_p.h
+# End Source File
+# End Group
+# End Target
+# End Project

+ 33 - 0
gmsrc/src/examples/GameObject/GameObject.dsw

@@ -0,0 +1,33 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "GameObject"=.\GameObject.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    Perforce Project
+    ..\..
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

+ 20 - 0
gmsrc/src/examples/GameObject/GameObject.sln

@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameObject", "GameObject.vcproj", "{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}.Debug|Win32.ActiveCfg = Debug|Win32
+		{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}.Debug|Win32.Build.0 = Debug|Win32
+		{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}.Release|Win32.ActiveCfg = Release|Win32
+		{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 1383 - 0
gmsrc/src/examples/GameObject/GameObject.vcproj

@@ -0,0 +1,1383 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="GameObject"
+	ProjectGUID="{1C3881F9-DC70-4FBD-91B9-9CD0197F7CBF}"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\Release"
+			IntermediateDirectory=".\Release"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Release/GameObject.tlb"
+				HeaderFileName=""
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="..\..\gm,..\..\platform\win32msvc,..\..\binds"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="true"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				PrecompiledHeaderFile=".\Release/GameObject.pch"
+				AssemblerListingLocation=".\Release/"
+				ObjectFile=".\Release/"
+				ProgramDataBaseFileName=".\Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="3081"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib Ws2_32.lib"
+				OutputFile=".\Release/GameObject.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="true"
+				ProgramDatabaseFile=".\Release/GameObject.pdb"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+				SuppressStartupBanner="true"
+				OutputFile=".\Release/GameObject.bsc"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\Debug"
+			IntermediateDirectory=".\Debug"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Debug/GameObject.tlb"
+				HeaderFileName=""
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\..\gm,..\..\platform\win32msvc,..\..\binds"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\Debug/GameObject.pch"
+				AssemblerListingLocation=".\Debug/"
+				ObjectFile=".\Debug/"
+				ProgramDataBaseFileName=".\Debug/"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="3081"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib Ws2_32.lib"
+				OutputFile=".\Debug/GameObject.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="true"
+				GenerateDebugInformation="true"
+				ProgramDatabaseFile=".\Debug/GameObject.pdb"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+				SuppressStartupBanner="true"
+				OutputFile=".\Debug/GameObject.bsc"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
+			>
+			<File
+				RelativePath="App.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="App.h"
+				>
+			</File>
+			<File
+				RelativePath="GameObj.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="GameObj.h"
+				>
+			</File>
+			<File
+				RelativePath="InputKBWin32.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="InputKBWin32.h"
+				>
+			</File>
+			<File
+				RelativePath="main.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="NetClient.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="NetClient.h"
+				>
+			</File>
+			<File
+				RelativePath="ReadMe.txt"
+				>
+			</File>
+			<File
+				RelativePath="ScriptObj.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="ScriptObj.h"
+				>
+			</File>
+			<File
+				RelativePath="ScriptSys.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="ScriptSys.h"
+				>
+			</File>
+			<File
+				RelativePath="StdStuff.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="StdStuff.h"
+				>
+			</File>
+			<File
+				RelativePath="TestGameObj.gm"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="gm"
+			>
+			<File
+				RelativePath="..\..\gm\gmArraySimple.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmArraySimple.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCode.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCode.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCodeGen.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCodeGen.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGen.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGen.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGenHooks.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGenHooks.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeTree.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeTree.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmConfig.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCrc.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCrc.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmDebug.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmDebug.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmFunctionObject.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmFunctionObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmHash.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmHash.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIncGC.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIncGC.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIterator.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLibHooks.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLibHooks.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmListDouble.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmListDouble.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLog.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLog.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachine.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachine.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachineLib.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachineLib.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMem.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMem.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemChain.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemChain.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixed.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixed.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixedSet.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixedSet.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmOperators.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmOperators.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmParser.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmParser.cpp.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmScanner.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmScanner.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStream.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStream.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStreamBuffer.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStreamBuffer.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStringObject.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStringObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmTableObject.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmTableObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmThread.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmThread.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUserObject.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUserObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUtil.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUtil.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmVariable.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmVariable.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="gmBinds"
+			>
+			<File
+				RelativePath="..\..\binds\gmCall.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmCall.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmHelpers.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmHelpers.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmMathLib.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmMathLib.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmStringLib.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmStringLib.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmSystemLib.cpp"
+				>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\binds\gmSystemLib.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="gmConfig"
+			>
+			<File
+				RelativePath="..\..\platform\win32msvc\gmConfig_p.h"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 102 - 0
gmsrc/src/examples/GameObject/InputKBWin32.cpp

@@ -0,0 +1,102 @@
+//
+// InputKBWin32.cpp
+//
+
+#include <windows.h>
+#include "InputKBWin32.h"
+
+// Init statics and constants
+InputKBWin32 InputKBWin32::s_staticInstance;
+
+
+
+InputKBWin32::InputKBWin32()
+{
+  m_keyDownBufferIndex = 0;
+
+  Init();
+}
+
+
+
+InputKBWin32::~InputKBWin32()
+{
+}
+
+
+void InputKBWin32::Init()
+{
+  for(int kIndex=0; kIndex<MAX_KEYS; ++kIndex)
+  {
+    m_keyDownBuffer[0][kIndex] = 0;
+    m_keyDownBuffer[1][kIndex] = 0;
+    m_keyStatus[kIndex] = KEY_STATUS_UP;
+  }
+}
+
+
+void InputKBWin32::Update()
+{
+//#define HAS_WINDOW_UPDATE // Define this if we have a window and a message pump
+
+  int lastBuffIndex = !m_keyDownBufferIndex;
+  int curBuffIndex = m_keyDownBufferIndex;
+  
+  m_keyDownBufferIndex = lastBuffIndex; // Flip buffers
+
+  char* bufferCurrent = &m_keyDownBuffer[curBuffIndex][0];
+  char* bufferLast = &m_keyDownBuffer[lastBuffIndex][0];
+
+  // Get the button states from Win32
+#ifdef HAS_WINDOW_UPDATE
+  // We have a window and message pump
+  BYTE win32KeyBuffer[256];
+  GetKeyboardState(win32KeyBuffer);
+#else // HAS_WINDOW_UPDATE
+  short win32KeyBuffer[256];
+  for(int vkIndex=0; vkIndex < 256; ++vkIndex)
+  {
+    win32KeyBuffer[vkIndex] = GetAsyncKeyState(vkIndex);
+  }
+#endif //HAS_WINDOW_UPDATE
+
+  // Find state changes
+  for(int kIndex=0; kIndex < MAX_KEYS; ++kIndex)
+  {
+    int status;
+  
+    // Convert win32 keystate to true / false
+#ifdef HAS_WINDOW_UPDATE
+    if(win32KeyBuffer[kIndex] & (1<<7))
+#else // HAS_WINDOW_UPDATE
+    if(win32KeyBuffer[kIndex] & (1<<15))
+#endif // HAS_WINDOW_UPDATE
+    {
+      bufferCurrent[kIndex] = true;
+    }
+    else
+    {
+      bufferCurrent[kIndex] = false;
+    }
+
+    status = 0;
+    if(bufferCurrent[kIndex])
+    {
+      status |= KEY_STATUS_DOWN;
+      if(!bufferLast[kIndex])
+      {
+        status |= KEY_STATUS_PRESSED;
+      }
+    }
+    else
+    {
+      status |= KEY_STATUS_UP;
+      if(bufferLast[kIndex])
+      {
+        status |= KEY_STATUS_RELEASED;
+      }
+    }
+  
+    m_keyStatus[kIndex] = status;
+  }
+}

+ 90 - 0
gmsrc/src/examples/GameObject/InputKBWin32.h

@@ -0,0 +1,90 @@
+#ifndef INPUTKBWIN32_H
+#define INPUTKBWIN32_H
+
+//
+// InputKBWin32.h
+//
+
+#include "gmThread.h" // For some basic types
+
+/// A simple keyboard input class for Win32.
+class InputKBWin32
+{
+public:
+
+  enum
+  {
+    MAX_KEYS = 256,                               ///< Max keys on keyboard (for buffer size etc.)
+  };
+
+  enum
+  {
+    KEY_STATUS_UNKNOWN =  0,                      ///< Invalid status
+    KEY_STATUS_UP =       (1<<0),                 ///< Button is up
+    KEY_STATUS_RELEASED = (1<<1),                 ///< Button released this frame
+    KEY_STATUS_DOWN =     (1<<2),                 ///< Button is down
+    KEY_STATUS_PRESSED =  (1<<3),                 ///< Button pressed this frame
+  };
+
+  /// Access single instance of this class
+  static InputKBWin32& Get()                      { return s_staticInstance; }
+
+  /// Destructor
+  virtual ~InputKBWin32();
+
+  /// Initialize.  Call before use.
+  void Init();
+
+  /// Call each frame.
+  void Update();
+
+  /// What is the status of a key.  Returns one of the KEY_STATUS_* enums.
+  inline int GetKeyStatus(int a_keyIndex)
+  {
+    GM_ASSERT((a_keyIndex >=0) && (a_keyIndex < MAX_KEYS));
+    return m_keyStatus[a_keyIndex];
+  }
+
+  /// Was key pressed this frame? (Non-zero if true)
+  inline int IsKeyPressed(int a_keyIndex)
+  {
+    GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
+    return (m_keyStatus[a_keyIndex] & KEY_STATUS_PRESSED);
+  }
+
+  /// Is key down this frame? (Non-zero if true)
+  inline int IsKeyDown(int a_keyIndex)
+  {
+    GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
+    return (m_keyStatus[a_keyIndex] & KEY_STATUS_DOWN);
+  }
+
+  /// Was key released this frame? (Non-zero if true)
+  inline int IsKeyRelesed(int a_keyIndex)
+  {
+    GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
+    return (m_keyStatus[a_keyIndex] & KEY_STATUS_RELEASED);
+  }
+
+  /// Is key up this frame? (Non-zero if true)
+  inline int IsKeyUp(int a_keyIndex)
+  {
+    GM_ASSERT((a_keyIndex >= 0) && (a_keyIndex < MAX_KEYS));
+    return (m_keyStatus[a_keyIndex] & KEY_STATUS_UP);
+  }
+
+
+private:
+
+  /// Constructor, non-public to prevent multiple instances
+  InputKBWin32();
+
+  char m_keyDownBuffer[2][MAX_KEYS];              ///< Store current and last frame snapshot
+  int m_keyDownBufferIndex;                       ///< Index to swap buffers for current and last frame
+  int m_keyStatus[MAX_KEYS];                      ///< Status of keys for this frame, persists until updated.
+
+  static InputKBWin32 s_staticInstance;           ///< Single instance of this class
+};
+
+
+#endif //INPUTKBWIN32_H

+ 356 - 0
gmsrc/src/examples/GameObject/NetClient.cpp

@@ -0,0 +1,356 @@
+//  See Copyright Notice in gmMachine.h
+
+#include "NetClient.h"
+#include <windows.h>
+#include <process.h> // Requires Multi threaded library for _beginthread and _endthread
+#include <stddef.h>
+#include <stdlib.h>
+#include <conio.h>
+#include <winsock.h> // Requires Ws2_32.lib
+#include <math.h>
+
+#undef SendMessage // stupid windows
+
+// These two are for MSVS 2005 security consciousness until safe std lib funcs are available
+#pragma warning(disable : 4996) // Deprecated functions
+#define _CRT_SECURE_NO_DEPRECATE // Allow old unsecure standard library functions, Disable some 'warning C4996 - function was deprecated'
+
+
+struct nPacket
+{
+  int id; // id == 0x4fe27d9a
+  int len;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// QUEUE
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+struct nQueueNode
+{
+  char * m_buffer;
+  int m_len;
+  nQueueNode * m_next;
+};
+
+class nQueue
+{
+public:
+  nQueue()
+  {
+    m_queue = NULL;
+    m_lastDeQueue = NULL;
+    m_mutex = CreateMutex(NULL, FALSE, NULL);
+  }
+
+  ~nQueue()
+  {
+    WaitForSingleObject(m_mutex, INFINITE);
+
+    int a;
+    while(DeQueue(a));
+    DeQueue(a);
+
+    ReleaseMutex(m_mutex);
+
+    // destroy mutex... todo
+  }
+
+  bool EnQueue(const char * a_buffer, int a_len)
+  {
+    WaitForSingleObject(m_mutex, INFINITE);
+
+    nQueueNode * node = new nQueueNode;
+    node->m_len = a_len;
+    node->m_buffer = new char[a_len];
+    memcpy(node->m_buffer, a_buffer, a_len);
+    node->m_next = NULL;
+
+    // add to end of list
+    nQueueNode ** n = &m_queue;
+    while(*n) n = &(*n)->m_next;
+    *n = node;
+
+    ReleaseMutex(m_mutex);
+
+    return true;
+  }
+
+  const char * DeQueue(int &a_len)
+  {
+    const char * ret = NULL;
+    
+    WaitForSingleObject(m_mutex, INFINITE);
+
+    if(m_lastDeQueue)
+    {
+      delete[] m_lastDeQueue->m_buffer;
+      delete m_lastDeQueue;
+      m_lastDeQueue = NULL;
+    }
+
+    if(m_queue)
+    {
+      m_lastDeQueue = m_queue;
+      m_queue = m_queue->m_next;
+      a_len = m_lastDeQueue->m_len;
+      ret = m_lastDeQueue->m_buffer;
+    }
+
+    ReleaseMutex(m_mutex);
+
+    return ret;
+  }
+
+  bool IsEmpty() { return (m_queue == NULL); }
+
+private:
+
+  HANDLE m_mutex;
+
+  nQueueNode * m_lastDeQueue;
+  nQueueNode * m_queue;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// CLIENT
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+struct nClientData
+{
+  SOCKET client;
+  nQueue messages;
+  CRITICAL_SECTION criticalSection;
+  bool threadAlive; 
+};
+
+
+
+nClient::nClient()
+{
+  nClientData * cd = new nClientData;
+  cd->threadAlive = false;
+  cd->client = INVALID_SOCKET;
+  InitializeCriticalSection(&cd->criticalSection);
+  m_data = cd;
+}
+
+
+
+nClient::~nClient()
+{
+  Close();
+  nClientData * cd = (nClientData *) m_data;
+  DeleteCriticalSection(&cd->criticalSection);
+  delete cd;
+}
+
+
+
+void nClient::Close()
+{
+  nClientData * cd = (nClientData *) m_data;
+  EnterCriticalSection(&cd->criticalSection);
+
+  if(cd->client != INVALID_SOCKET)
+  {
+    closesocket(cd->client);
+    cd->client = INVALID_SOCKET;
+  }
+
+  LeaveCriticalSection(&cd->criticalSection);
+
+  // wait for the thread to die.
+  while(cd->threadAlive) 
+  {
+    _sleep(0);
+  }
+
+  WSACleanup();
+}
+
+
+
+bool nClient::IsConnected()
+{
+  bool result = false;
+  nClientData * cd = (nClientData *) m_data;
+  EnterCriticalSection(&cd->criticalSection);
+  if(cd->client != INVALID_SOCKET)
+  {
+    result = true;
+  }
+  LeaveCriticalSection(&cd->criticalSection);
+  return result;
+}
+
+
+
+bool nClient::SendMessage(const char * a_buffer, int a_len)
+{
+  bool res = false;
+  nClientData * cd = (nClientData *) m_data;
+  nPacket packet;
+  packet.id = 0x4fe27d9a;
+  packet.len = a_len;
+
+  EnterCriticalSection(&cd->criticalSection);
+  if(cd->client != INVALID_SOCKET)
+  {
+    send(cd->client, (const char *) &packet, sizeof(nPacket), 0);
+    send(cd->client, (const char *) a_buffer, a_len, 0);
+    res = true;
+  }
+  LeaveCriticalSection(&cd->criticalSection);
+  return res;
+}
+
+
+
+const char * nClient::PumpMessage(int &a_len)
+{
+  nClientData * cd = (nClientData *) m_data;
+  const char * buffer = cd->messages.DeQueue(a_len);
+  return buffer;
+}
+
+
+
+void nClientThread(void * param)
+{
+  nClientData * cd = (nClientData *) param;
+  EnterCriticalSection(&cd->criticalSection);
+  cd->threadAlive = true;
+  SOCKET client = cd->client;
+  LeaveCriticalSection(&cd->criticalSection);
+
+  char * dbuffer = NULL;
+  char buffer[4096];
+  char * sbp;
+  int state = 0; // 0 searching for packet, 1 getting message
+  int need = sizeof(nPacket);
+
+  // packet header
+  nPacket packet;
+  char * dbp = (char *) &packet;
+
+  // packet data
+  int dbufferSize = 0, n;
+
+  // read loop
+  for(;;)
+  {
+    // read
+    n = recv(client, buffer, 4096, 0);
+    if(n == SOCKET_ERROR || n == 0) break;
+    sbp = buffer;
+
+    // consume
+    while(n > 0)
+    {
+      int have = (n > need) ? need : n;
+      need -= have;
+      n -= have;
+      memcpy(dbp, sbp, have);
+      sbp += have;
+      dbp += have;
+
+      // can we change state?
+      if(need == 0)
+      {
+        if(state == 0)
+        {
+          if(packet.id != 0x4fe27d9a) goto terror;
+          state = 1;
+          need = packet.len;
+
+          // allocate the dbuffer
+          if(need > dbufferSize)
+          {
+            if(dbuffer) { delete[] dbuffer; }
+            dbufferSize = need + 512;
+            dbuffer = new char[dbufferSize];
+          }
+          dbp = dbuffer;
+        }
+        else if(state == 1)
+        {
+          cd->messages.EnQueue(dbuffer, packet.len);
+          dbp = (char *) &packet;
+          need = sizeof(nPacket);
+          state = 0;
+        }
+      }
+    }
+  }
+
+terror:
+
+  if(dbuffer) { delete[] dbuffer; }
+
+  EnterCriticalSection(&cd->criticalSection);
+  cd->threadAlive = false;
+  LeaveCriticalSection(&cd->criticalSection);
+
+  _endthread();
+}
+
+
+
+bool nClient::Connect(const char * a_server, short a_port)
+{
+	WSADATA wsaData;
+	struct hostent *hp;
+	unsigned int addr;
+	struct sockaddr_in server;
+
+	int wsaret=WSAStartup(0x101,&wsaData);
+	if(wsaret)	
+		return false;
+
+	SOCKET conn;
+
+	conn = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
+	if(conn==INVALID_SOCKET)
+		return false;
+
+	addr=inet_addr(a_server);
+	hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
+
+  if(hp==NULL)
+	{
+		closesocket(conn);
+		return false;
+	}
+
+  server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
+	server.sin_family=AF_INET;
+	server.sin_port=htons((u_short) a_port);
+
+	if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
+	{
+		closesocket(conn);
+		return false;	
+	}
+
+  nClientData * cd = (nClientData *) m_data;
+  EnterCriticalSection(&cd->criticalSection);
+  cd->client = conn;
+  LeaveCriticalSection(&cd->criticalSection);
+
+  _beginthread(nClientThread, 0, cd);
+  _sleep(0);
+
+  return true;
+}
+

+ 30 - 0
gmsrc/src/examples/GameObject/NetClient.h

@@ -0,0 +1,30 @@
+#ifndef _NETCLIENT_H_
+#define _NETCLIENT_H_
+
+//  See Copyright Notice in gmMachine.h
+
+#undef SendMessage // windows clash
+
+//
+// nClient
+//
+class nClient
+{
+public:
+  nClient();
+  ~nClient();
+
+  bool Connect(const char * a_server, short a_port);
+  void Close();
+  bool IsConnected(); 
+
+  bool SendMessage(const char * a_buffer, int a_len);
+  const char * PumpMessage(int &a_len);
+
+private:
+
+  void * m_data;
+};
+
+
+#endif //_NETCLIENT_H_

+ 10 - 0
gmsrc/src/examples/GameObject/ReadMe.txt

@@ -0,0 +1,10 @@
+This example is simply one way to implement 'Game Objects'.  The example is a work in 
+progress, and is included because some of the code may provide useful to look at.
+
+
+TODO
+o make 'triggers' and 'units'
+o make IsUnitInSquare function to clip movement
+o use states for units to be 'player' 'following' 'mental'
+o make triggers that change unit color and triggers that create mental state if not player
+o highlight player by color and allow player to cycle through units to control

+ 326 - 0
gmsrc/src/examples/GameObject/ScriptObj.cpp

@@ -0,0 +1,326 @@
+//
+// ScriptObj.cpp
+//
+
+#include "gmCall.h"
+#include "ScriptObj.h"
+#include "ScriptSys.h"
+#include "GameObj.h"
+
+
+// Init statics and constants
+gmType ScriptObj::GMTYPE_GAMEOBJ = -1;
+
+
+ScriptObj::ScriptObj(GameObj* a_gameObj)
+{
+  GM_ASSERT(ScriptSys::Get());
+
+  m_userObject = NULL; // A user object will be created when it is first used, and shared by all script variables.
+  m_gameObj = a_gameObj;
+  m_tableObject = ScriptSys::Get()->GetMachine()->AllocTableObject();
+
+  ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_tableObject);
+}
+
+
+ScriptObj::~ScriptObj()
+{
+  // Stop related threads
+  KillThreads();
+
+  if(m_userObject)
+  {
+    // Nullify script link to C object
+    m_userObject->m_user = NULL;
+  }
+
+  // Destruct the gmObjects
+#if GM_USE_INCGC
+  // Do nothing, it will be collected later, just nullify all reference to it
+#else
+  if(m_userObject)
+  {
+    m_userObject->Destruct(ScriptSys::Get()->GetMachine());
+  }
+  m_tableObject->Destruct(ScriptSys::Get()->GetMachine());
+#endif
+
+  // Remove object from the list of all objects
+  ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_tableObject);
+  if( m_userObject )
+  {
+    ScriptSys::Get()->GetMachine()->RemoveCPPOwnedGMObject(m_userObject);
+  }
+}
+
+
+gmUserObject* ScriptObj::GetUserObject()
+{
+  if(!m_userObject)
+  {
+    m_userObject = ScriptSys::Get()->GetMachine()->AllocUserObject(this, GMTYPE_GAMEOBJ);
+    ScriptSys::Get()->GetMachine()->AddCPPOwnedGMObject(m_userObject);
+  }
+
+  return m_userObject;
+}
+
+
+void ScriptObj::KillThreads()
+{
+  for(unsigned int tIndex=0; tIndex<m_threads.GetSize(); ++tIndex)
+  {
+    ScriptSys::Get()->RemoveThreadIdButDontTouchGameObj(m_threads[tIndex]);
+    ScriptSys::Get()->GetMachine()->KillThread(m_threads[tIndex]);
+  }
+  m_threads.Reset();
+}
+
+
+void ScriptObj::ExecuteStringOnThis(const char* a_string)
+{
+  gmMachine* machine = ScriptSys::Get()->GetMachine();
+  gmVariable thisVar;
+  thisVar.SetUser(GetUserObject());
+  int threadId = GM_INVALID_THREAD;
+
+  int errors = machine->ExecuteString(a_string, &threadId, true, NULL, &thisVar);
+  if(errors)
+  {
+    bool first = true;
+    const char * message;
+    while((message = machine->GetLog().GetEntry(first)))
+    {
+      ScriptSys::Get()->LogError("%s\n", message);
+    }
+    machine->GetLog().Reset();
+  }
+  else
+  {
+    ScriptSys::Get()->AssociateThreadIdWithGameObj(threadId, *GetGameObj());
+  }
+}
+
+
+bool ScriptObj::ExecuteGlobalFunctionOnThis(const char* a_functionName)
+{
+  gmVariable thisVar;
+  thisVar.SetUser(GetUserObject());
+
+  gmCall call;
+  if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), a_functionName, thisVar, false))
+  {
+    call.End();
+    return true;
+  }
+
+  return false;
+}
+
+
+void ScriptObj::SetMemberInt(const char* a_memberName, int a_int)
+{
+  ScriptSys::Get()->SetTableInt(a_memberName, a_int, m_tableObject);
+}
+
+
+void ScriptObj::SetMemberFloat(const char* a_memberName, float a_float)
+{
+  ScriptSys::Get()->SetTableFloat(a_memberName, a_float, m_tableObject);
+}
+
+
+void ScriptObj::SetMemberString(const char* a_memberName, const char* a_string, int a_strLength)
+{
+  ScriptSys::Get()->SetTableString(a_memberName, a_string, a_strLength, m_tableObject);
+}
+
+
+void ScriptObj::SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj)
+{
+  ScriptSys::Get()->SetTableGameObj(a_memberName, a_gameObj, m_tableObject);
+}
+
+
+gmTableObject* ScriptObj::SetMemberTable(const char* a_memberName)
+{
+  return ScriptSys::Get()->SetTableTable(a_memberName, m_tableObject);
+}
+
+
+bool ScriptObj::GetMemberInt(const char* a_memberName, int& a_int)
+{
+  return ScriptSys::Get()->GetTableInt(a_memberName, a_int, m_tableObject);
+}
+
+
+bool ScriptObj::GetMemberFloat(const char* a_memberName, float& a_float)
+{
+  return ScriptSys::Get()->GetTableFloat(a_memberName, a_float, m_tableObject);
+}
+
+
+bool ScriptObj::GetMemberString(const char* a_memberName, String& a_string)
+{
+  return ScriptSys::Get()->GetTableString(a_memberName, a_string, m_tableObject);
+}
+
+
+bool ScriptObj::GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj)
+{
+  return ScriptSys::Get()->GetTableGameObj(a_memberName, a_gameObj, m_tableObject);
+}
+
+
+bool ScriptObj::GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable)
+{
+  return ScriptSys::Get()->GetTableTable(a_memberName, a_retTable, m_tableObject);
+}
+
+
+void GM_CDECL ScriptObj::GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen)
+{
+  char mixBuffer[128];
+
+  ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
+  GameObj* gameObjPtr = NULL;
+
+  if(scriptObj)
+  {
+    gameObjPtr = scriptObj->GetGameObj();
+  }
+  
+  sprintf(mixBuffer,"CPtr: %x", gameObjPtr);
+
+  int mixLength = strlen(mixBuffer);
+  int useLength = gmMin(mixLength, a_bufferLen-1);
+  GM_ASSERT(useLength > 0);
+  strncpy(a_buffer, mixBuffer, useLength);
+  a_buffer[useLength] = 0;
+}
+
+
+#if GM_USE_INCGC
+
+bool GM_CDECL ScriptObj::GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone)
+{
+  GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
+  ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
+
+  if(scriptObj)
+  {
+    a_gc->GetNextObject(scriptObj->GetTableObject());
+  }
+  a_workDone +=2;
+  return true;
+}
+
+
+void GM_CDECL ScriptObj::GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object)
+{
+  GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
+  ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
+
+  if(scriptObj)
+  {
+    scriptObj->m_userObject = NULL;
+  }
+}
+
+#else //GM_USE_INCGC
+
+void GM_CDECL ScriptObj::GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
+  ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
+
+  if(scriptObj)
+  {
+    if(scriptObj->GetTableObject()->NeedsMark(a_mark))
+    {
+      scriptObj->GetTableObject()->Mark(a_machine, a_mark);
+    }
+  }
+}
+
+
+void GM_CDECL ScriptObj::GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
+{
+  GM_ASSERT(a_object->m_userType == GMTYPE_GAMEOBJ);
+  ScriptObj* scriptObj = (ScriptObj*)a_object->m_user;
+
+  if(scriptObj)
+  {
+    scriptObj->m_userObject = NULL;
+  }
+}
+
+#endif //GM_USE_INCGC
+
+
+// NOTE: If you wanted to enable other dot operator behavior
+// here is the place to do it, in the GetDot and SetDot operators.
+// This example merely uses the gmTable embedded in the GameObj
+// to allow script functions and data to be members of this object type.
+// GameObj also registers 'type' functions that are accessed via the
+// dot operator.
+
+
+void GM_CDECL ScriptObj::GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands)
+{
+  //O_GETDOT = 0,       // object, "member"          (tos is a_operands + 2)
+  GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
+
+  gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
+  ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
+
+  if(!scriptObj)
+  {
+    a_operands[0].Nullify();
+
+    return;
+  }
+
+  a_operands[0] = scriptObj->GetTableObject()->Get(a_operands[1]);
+}
+
+
+void GM_CDECL ScriptObj::GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands)
+{
+  //O_SETDOT,           // object, value, "member"   (tos is a_operands + 3)
+  GM_ASSERT(a_operands[0].m_type == GMTYPE_GAMEOBJ);
+
+  gmUserObject* userObj = (gmUserObject*) GM_OBJECT(a_operands[0].m_value.m_ref);
+  ScriptObj* scriptObj = (ScriptObj*)userObj->m_user;
+
+  if(scriptObj)
+  {
+    scriptObj->GetTableObject()->Set(a_thread->GetMachine(), a_operands[2], a_operands[1]);
+  }
+}
+
+
+void ScriptObj::RegisterScriptBindings()
+{
+  gmMachine* machine = ScriptSys::Get()->GetMachine();
+  
+  GM_ASSERT(machine);
+
+  // Register new user type
+  GMTYPE_GAMEOBJ = machine->CreateUserType("GameObj");
+  // Register garbage collection for our new type
+#if GM_USE_INCGC
+  machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCTrace, GameObjCallback_GCDestruct, GameObjCallback_AsString); 
+#else //GM_USE_INCGC
+  machine->RegisterUserCallbacks(GMTYPE_GAMEOBJ, GameObjCallback_GCMark, GameObjCallback_GCCollect, GameObjCallback_AsString); 
+#endif //GM_USE_INCGC
+  // Bind Get dot operator for our type
+  machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_GETDOT, NULL, GameObj_GetDot); 
+  // Bind Set dot operator for our type
+  machine->RegisterTypeOperator(GMTYPE_GAMEOBJ, O_SETDOT, NULL, GameObj_SetDot); 
+  // Bind functions
+//  machine->RegisterLibrary(regFuncList, sizeof(regFuncList) / sizeof(regFuncList[0])); 
+  // Bind type functions
+//  machine->RegisterTypeLibrary(GM_GOB, regTypeFuncList, sizeof(regTypeFuncList) / sizeof(regTypeFuncList[0]));
+}

+ 165 - 0
gmsrc/src/examples/GameObject/ScriptObj.h

@@ -0,0 +1,165 @@
+#ifndef SCRIPTOBJ_H
+#define SCRIPTOBJ_H
+
+//
+// ScriptObj.h
+//
+// Example script interface component for game object
+//
+
+#include "gmThread.h"
+#include "StdStuff.h"
+
+// Fwd decls
+class GameObj;
+
+
+// NOTE: In this implementation, the 'gmUserObject' only exists when the cpp object is used or needed by script.
+//       This implementation also shares that single user object amongst all referencing variables in script.
+//       Because of this, the cpp code does not need to handle the user object as if it were owned by cpp.
+//       The cpp object does however always contain a gmTableObject, and this is owned by cpp as it may not 
+//       exist (be referenced) within the script.  For this reason, the gmTableObject must be handled as a 
+//       cpp owned object to allow correct GC handling.
+//
+//       An alternate method, would be to always have a gmUserObject and let cpp code own this.  This 
+//       user object would be the root of its own child objects like the gmTableObject.  This method may
+//       be simpler.
+//
+
+// Script interface for game objects
+class ScriptObj
+{
+public:
+
+  static gmType GMTYPE_GAMEOBJ;                   ///< The user type of a game object
+
+  static void RegisterScriptBindings();           ///< Register game object script bindings
+
+  ScriptObj(GameObj* a_gameObj);
+  virtual ~ScriptObj();
+
+  GameObj* GetGameObj()                           { return m_gameObj; }
+
+  gmTableObject* GetTableObject()                 { return m_tableObject; }
+  gmUserObject* GetUserObject();
+
+  void AddThreadId(int a_threadId)                
+  {
+    m_threads.InsertLast(a_threadId);
+  } 
+
+  void RemoveThreadId(int a_threadId)
+  {
+    for(unsigned int tIndex=0; tIndex < m_threads.GetSize(); ++tIndex)
+    {
+      if(m_threads[tIndex] == a_threadId)
+      {
+        m_threads.RemoveSwapLast(tIndex);
+      }
+    }
+  }
+
+  /// Kill all threads running on this object
+  void KillThreads();
+
+  void ExecuteStringOnThis(const char* a_string);
+
+  bool ExecuteGlobalFunctionOnThis(const char* a_functionName);
+  
+  void SetMemberInt(const char* a_memberName, int a_int);
+  void SetMemberFloat(const char* a_memberName, float a_float);
+  void SetMemberString(const char* a_memberName, const char* a_string, int a_strLength = -1);
+  void SetMemberGameObj(const char* a_memberName, GameObj* a_gameObj);
+  gmTableObject* SetMemberTable(const char* a_memberName);
+
+  bool GetMemberInt(const char* a_memberName, int& a_int);
+  bool GetMemberFloat(const char* a_memberName, float& a_float);
+  bool GetMemberString(const char* a_memberName, String& a_string);
+  bool GetMemberGameObj(const char* a_memberName, GameObj*& a_gameObj);
+  bool GetMemberTable(const char* a_memberName, gmTableObject*& a_retTable);
+
+protected:
+
+  static void GM_CDECL GameObjCallback_AsString(gmUserObject * a_object, char* a_buffer, int a_bufferLen);
+#if GM_USE_INCGC
+  static bool GM_CDECL GameObjCallback_GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workRemaining, int& a_workDone);
+  static void GM_CDECL GameObjCallback_GCDestruct(gmMachine * a_machine, gmUserObject * a_object);
+#else //GM_USE_INCGC
+  static void GM_CDECL GameObjCallback_GCMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
+  static void GM_CDECL GameObjCallback_GCCollect(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark);
+#endif //GM_USE_INCGC
+  static void GM_CDECL GameObj_GetDot(gmThread * a_thread, gmVariable * a_operands);
+  static void GM_CDECL GameObj_SetDot(gmThread * a_thread, gmVariable * a_operands);
+
+  GameObj* m_gameObj;                             ///< The game object owner of this interface
+  gmUserObject* m_userObject;                     ///< The script object
+  gmTableObject* m_tableObject;                   ///< Table functionality for script object members
+  gmArraySimple<int> m_threads;                   ///< Threads associated with this game object
+
+};
+
+/*
+/// \brief Get 'this' as GameObj of TYPE
+/// Eg. Soldier* obj = GetThisGameObj<Soldier>(a_thread);
+template<class TYPE>
+TYPE* GetThisGameObj(gmThread* a_thread)
+{
+  GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
+
+  ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
+
+  CHECK(scriptObj); //Check for null GameObj ptr
+
+  // You can check for valid derived type here
+
+  return static_cast<TYPE*>(scriptObj->GetGameObj());
+}
+
+
+/// \brief Get param as GameObj of TYPE
+/// Eg. Soldier* obj = GetGameObjParam<Soldier>(a_thread, 0);
+template<class TYPE>
+TYPE* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
+{
+  ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
+
+  CHECK(scriptObj); //Check for null GameObj ptr
+
+  // You can check for valid derived type here
+
+  return static_cast<TYPE*>(scriptObj->GetGameObj());
+}
+*/
+
+
+/// \brief Get 'this' as GameObj of TYPE
+inline GameObj* GetThisGameObj(gmThread* a_thread)
+{
+  GM_ASSERT(a_thread->GetThis()->m_type == ScriptObj::GMTYPE_GAMEOBJ); //Paranoid check for type function
+
+  ScriptObj* scriptObj = (ScriptObj*)a_thread->ThisUser();
+
+  if(!scriptObj)
+  {
+    return NULL;
+  }
+
+  return scriptObj->GetGameObj();
+}
+
+
+/// \brief Get param as GameObj of TYPE
+inline GameObj* GetGameObjParam(gmThread* a_thread, int a_paramIndex)
+{
+  ScriptObj* scriptObj = (ScriptObj*)a_thread->ParamUserCheckType(a_paramIndex, ScriptObj::GMTYPE_GAMEOBJ);
+
+  if(!scriptObj)
+  {
+    return NULL;
+  }
+
+  return scriptObj->GetGameObj();
+}
+
+
+#endif //SCRIPTOBJ_H

+ 432 - 0
gmsrc/src/examples/GameObject/ScriptSys.cpp

@@ -0,0 +1,432 @@
+//
+// ScriptSys.cpp
+//
+
+#include "gmCall.h"
+#include "ScriptSys.h"
+#include "ScriptObj.h"
+#include "GameObj.h"
+
+
+// Init statics and constants
+ScriptSys* ScriptSys::s_instance = NULL;
+const int ScriptSys::DEBUGGER_DEFAULT_PORT = 49001;
+const char* ScriptSys::DEBUGGER_DEFAULT_IP = "127.0.0.1"; // localhost
+
+
+void ScriptSys::Init()
+{
+  GM_ASSERT( !s_instance ); // Just have one instance for this example
+  
+  s_instance = new ScriptSys;
+
+  // Register Game Object type and bindings
+  ScriptObj::RegisterScriptBindings();
+  GameObj::RegisterScriptBindings();
+}
+
+
+void ScriptSys::Destroy()
+{
+  delete s_instance;
+  s_instance = NULL;
+}
+
+
+void ScriptSys::DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len)
+{
+  nClient * client = (nClient *) a_session->m_user;
+  client->SendMessage((const char *) a_command, a_len);
+}
+
+
+const void* ScriptSys::DebuggerPumpMessage(gmDebugSession * a_session, int &a_len)
+{
+  nClient * client = (nClient *) a_session->m_user;
+  return client->PumpMessage(a_len);
+}
+
+
+ScriptSys::ScriptSys()
+{
+  m_machine = new gmMachine;
+
+  //Set machine callbacks
+  gmMachine::s_machineCallback = ScriptSysCallback_Machine;
+  gmMachine::s_printCallback = ScriptSysCallback_Print;
+
+  // Init debugger
+  gmBindDebugLib(m_machine); // Register debugging library
+
+  m_debuggerIP = DEBUGGER_DEFAULT_IP;
+  m_debuggerPort = DEBUGGER_DEFAULT_PORT;
+  m_debugSession.m_sendMessage = DebuggerSendMessage;
+  m_debugSession.m_pumpMessage = DebuggerPumpMessage;
+  m_debugSession.m_user = &m_debugClient;
+
+  if(m_debugClient.Connect(m_debuggerIP, ((short) m_debuggerPort)))
+  {
+    m_debugSession.Open(m_machine);
+    fprintf(stderr, "Debug session opened"GM_NL);
+  }
+  m_machine->SetDebugMode(true);
+}
+
+
+ScriptSys::~ScriptSys()
+{
+  // End debugger session if any
+  m_debugSession.Close();
+  m_debugClient.Close();
+
+  // For debugging
+  _gmDumpLeaks();
+
+  delete m_machine;
+}
+
+
+void __cdecl ScriptSys::LogError(const char *a_str, ...)
+{
+  // WARNING This is not safe for longer strings, should use non-ansi vsprintnf, string type, or similar.
+  const int MAX_CHARS = 512;
+  char buffer[MAX_CHARS];
+
+  va_list args;
+  va_start(args, a_str);
+  
+  vsprintf(buffer, a_str, args);
+  
+  va_end(args);
+
+  fprintf(stderr, "ERROR: %s", a_str);
+}
+
+
+GameObj* ScriptSys::GetGameObjFromThreadId(int a_threadId)
+{
+  ScriptObj* scriptObj;
+
+  if(m_mapThreadGameObjs.GetAt(a_threadId, scriptObj))
+  {
+    return scriptObj->GetGameObj();
+  }
+  return NULL;
+}
+
+
+void ScriptSys::AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj)
+{
+  ScriptObj* scriptObj = a_gameObj.GetScriptObj();
+  
+  scriptObj->AddThreadId(a_threadId);
+  m_mapThreadGameObjs.SetAt(a_threadId, scriptObj);
+}
+
+
+void ScriptSys::DisassociateThreadIdWithGameObj(int a_threadId)
+{
+  ScriptObj* scriptObj;
+
+  if(m_mapThreadGameObjs.RemoveAt(a_threadId, scriptObj))
+  {
+    scriptObj->RemoveThreadId(a_threadId);
+  }
+}
+
+
+void ScriptSys::RemoveThreadIdButDontTouchGameObj(int a_threadId)
+{
+  m_mapThreadGameObjs.RemoveAt(a_threadId);
+}
+
+
+void ScriptSys::SetTableNull(const char* a_memberName, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable newVar;
+  newVar.Nullify();
+  table->Set(m_machine, a_memberName, newVar);
+}
+
+
+void ScriptSys::SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable newVar;
+  newVar.SetInt(a_int);
+  table->Set(m_machine, a_memberName, newVar);
+}
+
+
+void ScriptSys::SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable newVar;
+  newVar.SetFloat(a_float);
+  table->Set(m_machine, a_memberName, newVar);
+}
+
+
+void ScriptSys::SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable newVar;
+  newVar.SetString(m_machine->AllocStringObject(a_string, a_strLength));
+  table->Set(m_machine, a_memberName, newVar);
+}
+
+void ScriptSys::SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  GM_ASSERT(a_gameObj);
+  gmTableObject* table = a_table;
+  gmVariable newVar;
+  newVar.SetUser(a_gameObj->GetScriptObj()->GetUserObject());
+  table->Set(m_machine, a_memberName, newVar);
+}
+
+
+gmTableObject* ScriptSys::SetTableTable(const char* a_memberName, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmMachine* machine = m_machine;
+  gmVariable newVar;
+  gmTableObject* newTable = machine->AllocTableObject();
+  newVar.SetTable(newTable);
+  table->Set(machine, a_memberName, newVar);
+  return newTable; //Return the table so we can potentially put things in it
+}
+
+
+bool ScriptSys::GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable stringName;
+  gmVariable retVar;
+
+  stringName.SetString(m_machine->AllocStringObject(a_memberName));
+  retVar = table->Get(stringName);
+  if(retVar.m_type == GM_INT)
+  {
+    a_int = retVar.m_value.m_int;
+    return true;
+  }
+  return false;
+}
+
+
+bool ScriptSys::GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable stringName;
+  gmVariable retVar;
+
+  stringName.SetString(m_machine->AllocStringObject(a_memberName));
+  retVar = table->Get(stringName);
+  if(retVar.m_type == GM_FLOAT)
+  {
+    a_float = retVar.m_value.m_float;
+    return true;
+  }
+  return false;
+}
+
+
+bool ScriptSys::GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable stringName;
+  gmVariable retVar;
+
+  stringName.SetString(m_machine->AllocStringObject(a_memberName));
+  retVar = table->Get(stringName);
+  if(retVar.m_type == GM_STRING)
+  {
+    gmStringObject* stringObj = (gmStringObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
+    a_string = stringObj->GetString();
+    return true;
+  }
+  return false;
+}
+
+
+bool ScriptSys::GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable stringName;
+  gmVariable retVar;
+
+  stringName.SetString(m_machine->AllocStringObject(a_memberName));
+  retVar = table->Get(stringName);
+  if(retVar.m_type == ScriptObj::GMTYPE_GAMEOBJ)
+  {
+    gmUserObject* userObj = (gmUserObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
+    a_gameObj = ((ScriptObj*)userObj->m_user)->GetGameObj();
+    return true;
+  }
+  return false;
+}
+
+
+bool ScriptSys::GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table)
+{
+  GM_ASSERT(a_table);
+  gmTableObject* table = a_table;
+  gmVariable stringName;
+  gmVariable retVar;
+
+  stringName.SetString(m_machine->AllocStringObject(a_memberName));
+  retVar = table->Get(stringName);
+  if(retVar.m_type == GM_TABLE)
+  {
+    a_retTable = (gmTableObject*)GM_MOBJECT(m_machine, retVar.m_value.m_ref);
+    return true;
+  }
+  return false;
+}
+
+
+void GM_CDECL ScriptSys::ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string)
+{
+  printf("%s\n", a_string);
+}
+
+
+bool GM_CDECL ScriptSys::ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context)
+{
+  switch(a_command)
+  {
+    case MC_THREAD_EXCEPTION:
+    {
+      ScriptSys::Get()->LogAnyMachineErrorMessages();
+      break;
+    }
+    case MC_COLLECT_GARBAGE:
+    {
+/* // Old code
+#if GM_USE_INCGC
+      gmGarbageCollector* gc = a_machine->GetGC();
+
+      for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
+      {
+        ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
+        gc->GetNextObject(scriptObj->GetTableObject());
+      }
+#else //GM_USE_INCGC
+      gmuint32 mark = *(gmuint32*)a_context;
+
+      for(unsigned int objIndex = 0; objIndex<ScriptSys::Get()->m_allScriptObjs.Count(); ++objIndex)
+      {
+        ScriptObj* scriptObj = ScriptSys::Get()->m_allScriptObjs[objIndex];
+
+        if(scriptObj->GetTableObject()->NeedsMark(mark))
+        {
+          scriptObj->GetTableObject()->Mark(a_machine, mark);
+        }
+      }
+#endif //GM_USE_INCGC
+*/
+      break;
+    }
+    case MC_THREAD_CREATE: // Called when a thread is created.  a_context is the thread.
+    {
+      break;
+    }
+    case MC_THREAD_DESTROY: // Called when a thread is destroyed.  a_context is the thread that is about to die
+    {
+      gmThread* thread = (gmThread*)a_context;
+      ScriptSys::Get()->DisassociateThreadIdWithGameObj(thread->GetId());
+      break;
+    }
+  }
+  return false;
+}
+
+
+
+bool ScriptSys::ExecuteFile(const char* a_fileName)
+{
+  FILE* scriptFile = NULL;
+  char* fileString = NULL;
+  int fileSize = 0;
+
+  GM_ASSERT(m_machine);
+
+  if( !(scriptFile = fopen(a_fileName, "rb")) )
+  {
+    return false;
+  }
+
+  fseek(scriptFile, 0, SEEK_END);
+  fileSize = ftell(scriptFile);
+  fseek(scriptFile, 0, SEEK_SET);
+  fileString = new char [fileSize+1];
+  fread(fileString, fileSize, 1, scriptFile);
+  fileString[fileSize] = 0; // Terminating null
+  fclose(scriptFile);
+
+  int threadId = GM_INVALID_THREAD;
+  int errors = m_machine->ExecuteString(fileString, &threadId, true, a_fileName);
+  if(errors)
+  {
+    LogAnyMachineErrorMessages();
+  }
+
+  delete [] fileString;
+
+  return true;
+}
+
+
+bool ScriptSys::ExecuteString(const char* a_string)
+{
+  GM_ASSERT(m_machine);
+
+  int threadId = GM_INVALID_THREAD;
+  int errors = m_machine->ExecuteString(a_string, &threadId, true);
+  if (errors)
+  {
+    LogAnyMachineErrorMessages();
+  }
+
+  return true;
+}
+
+
+int ScriptSys::Execute(unsigned int a_deltaTimeMS)
+{
+  int numThreads = m_machine->Execute(a_deltaTimeMS);
+  
+  if(m_debugClient.IsConnected())
+  {
+    m_debugSession.Update();
+  }
+  else
+  {
+    m_debugSession.Close();
+  }
+
+  return numThreads;
+}
+
+
+void ScriptSys::LogAnyMachineErrorMessages()
+{
+  bool first = true;
+  const char * message;
+  while((message = m_machine->GetLog().GetEntry(first)))
+  {
+    LogError("%s"GM_NL, message);
+  }
+  m_machine->GetLog().Reset();
+}

+ 107 - 0
gmsrc/src/examples/GameObject/ScriptSys.h

@@ -0,0 +1,107 @@
+#ifndef SCRIPTSYS_H
+#define SCRIPTSYS_H
+
+//
+// ScriptSys.h
+//
+// Example script system to support scriptable game objects
+//
+
+#include "gmThread.h"
+#include "gmDebug.h"
+#include "gmArraySimple.h"
+#include "StdStuff.h"
+#include "NetClient.h"
+
+// Fwd decls
+class GameObj;
+class ScriptObj;
+
+// Script system to support and control the virtual machine
+class ScriptSys
+{
+public:
+
+  /// Access this system from anywhere once it has been initialized for convenience
+  static ScriptSys* Get()                         { return s_instance; }
+
+  ScriptSys();
+  virtual ~ScriptSys();
+
+  /// Get the GM machine
+  gmMachine* GetMachine()                         { return m_machine; }
+
+  /// Set bindings and Init constant strings
+  static void Init();
+  /// Clean out this structure
+  static void Destroy();
+
+
+  /// Log an error message
+  void __cdecl LogError(const char *a_str, ...);
+
+  /// Log any machine error messages that may be waiting
+  void LogAnyMachineErrorMessages();
+
+  /// Get GameObj that was associated with a thread Id.
+  GameObj* GetGameObjFromThreadId(int a_threadId);
+  
+  /// Associate a threadId with a GameObj, logically as a primary thread.
+  void AssociateThreadIdWithGameObj(int a_threadId, GameObj& a_gameObj);
+  /// Disassociate a threadId with a GameObj.
+  void DisassociateThreadIdWithGameObj(int a_threadId);
+  /// Remove the thread Id association, but don't modify the GameObj.
+  /// This can be used internally by GameObj to perform iteration and removal.
+  void RemoveThreadIdButDontTouchGameObj(int a_threadId);
+
+  /// Run a script file
+  bool ExecuteFile(const char* a_fileName);
+  /// Executes a string.
+  bool ExecuteString(const char* a_str);
+
+  /// Update the virtual machine.
+  int Execute(unsigned int a_deltaTimeMS);
+
+  void SetTableNull(const char* a_memberName, gmTableObject* a_table);
+  void SetTableInt(const char* a_memberName, int a_int, gmTableObject* a_table);
+  void SetTableFloat(const char* a_memberName, float a_float, gmTableObject* a_table);
+  void SetTableString(const char* a_memberName, const char* a_string, int a_strLength, gmTableObject* a_table);
+  void SetTableGameObj(const char* a_memberName, GameObj* a_gameObj, gmTableObject* a_table);
+  gmTableObject* SetTableTable(const char* a_memberName, gmTableObject* a_table);
+
+  bool GetTableInt(const char* a_memberName, int& a_int, gmTableObject* a_table);
+  bool GetTableFloat(const char* a_memberName, float& a_float, gmTableObject* a_table);
+  bool GetTableString(const char* a_memberName, String& a_string, gmTableObject* a_table);
+  bool GetTableGameObj(const char* a_memberName, GameObj*& a_gameObj, gmTableObject* a_table);
+  bool GetTableTable(const char* a_memberName, gmTableObject*& a_retTable, gmTableObject* a_table);
+
+protected:
+
+  /// Machine 'print' binding callback
+  static void GM_CDECL ScriptSysCallback_Print(gmMachine* a_machine, const char* a_string);
+  /// Machine general and exception callback
+  static bool GM_CDECL ScriptSysCallback_Machine(gmMachine* a_machine, gmMachineCommand a_command, const void* a_context);
+
+  /// Debugging support Send a message
+  static void DebuggerSendMessage(gmDebugSession * a_session, const void * a_command, int a_len);
+  /// Debugging support Pump a message
+  static const void* DebuggerPumpMessage(gmDebugSession * a_session, int &a_len);
+
+  gmMachine* m_machine;                           ///< GM machine instance
+  Map<int, ScriptObj*> m_mapThreadGameObjs;       ///< Map script threadId to game object
+/* // Old code
+  gmArraySimple<ScriptObj*> m_allScriptObjs;      ///< All the script objects, for garbage collection handling
+*/
+
+  nClient m_debugClient;                          ///< Debugger network client
+  gmDebugSession m_debugSession;                  ///< Debugger session
+  const char* m_debuggerIP;                       ///< Debugger IP
+  int m_debuggerPort;                             ///< Debugger port
+
+  static const int DEBUGGER_DEFAULT_PORT;         ///< Debugger port number
+  static const char* DEBUGGER_DEFAULT_IP;         ///< Debugger port number
+  static ScriptSys* s_instance;                   ///< Static instance for convenience
+};
+
+
+#endif //SCRIPTSYS_H

+ 5 - 0
gmsrc/src/examples/GameObject/StdStuff.cpp

@@ -0,0 +1,5 @@
+//
+// StdStuff.cpp
+//
+
+#include "StdStuff.h"

+ 194 - 0
gmsrc/src/examples/GameObject/StdStuff.h

@@ -0,0 +1,194 @@
+#ifndef STDSTUFF_H
+#define STDSTUFF_H
+
+//
+// StdStuff.h
+//
+// Merely some containers and standard things you would
+// find in MFC, STL, or your favourite library/engine.
+// These were implemented as quickly and minimally as possible
+// rather than introduce more code or external libraries.
+//
+
+#include "gmHash.h"
+
+// Quick n dirty Map using the available hash table
+template<class KEY, class VALUE>
+class Map
+{
+public:
+
+  Map()
+   : m_hashTable(1024) // Just an arbitrary number at present
+  {
+  }
+
+  virtual ~Map()
+  {
+    m_hashTable.RemoveAndDeleteAll();
+  }
+
+  /// \brief Insert a element associated with a key.
+  /// \param a_key Key to identify data.
+  /// \param a_value Data associated with Key.
+  void SetAt(const KEY& a_key, const VALUE& a_value)
+  {
+    HashNode* node = m_hashTable.Find(a_key);
+    if(node)
+    {
+      node->m_value = a_value;
+    }
+    else
+    {
+      node = new HashNode;
+      node->m_key = a_key;
+      node->m_value = a_value;
+      m_hashTable.Insert(node);
+    }
+  };
+
+  /// \brief Find a node in the map.
+  /// \param a_key Key to identiy element.
+  /// \param a_value Found element returned here.
+  /// \return TRUE if found, FALSE if not in map.
+  bool GetAt(const KEY& a_key, VALUE& a_value)
+  {
+    HashNode* node = m_hashTable.Find(a_key);
+    if(node)
+    {
+      a_value = node->m_value;
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Remove a node from the map.
+  /// \param a_key Key to identiy element.
+  /// \param a_removedData Found element returned here.
+  /// \return TRUE if found, FALSE if not in map.
+  bool RemoveAt(const KEY& a_key, VALUE& a_removedData)
+  {
+    HashNode* node = m_hashTable.Find(a_key);
+    if(node)
+    {
+      a_removedData = node->m_value;
+
+      m_hashTable.Remove(node);
+      delete node;
+
+      return true;
+    }
+    return false;
+  }
+
+  /// \brief Remove a node from the map.
+  /// \param a_key Key to identiy element.
+  /// \return TRUE if found, FALSE if not in map.
+  bool RemoveAt(const KEY& a_key)
+  {
+    HashNode* node = m_hashTable.Find(a_key);
+    if(node)
+    {
+      m_hashTable.Remove(node);
+      delete node;
+
+      return true;
+    }
+    return false;
+  }
+
+protected:
+
+  struct HashNode : public gmHashNode<KEY, HashNode, HashNode>
+  {
+    VALUE m_value;
+    KEY m_key;
+
+    virtual const KEY& GetKey() const { return m_key; }
+
+    static inline gmuint Hash(const KEY& a_key)
+    {
+      return (unsigned int)a_key;
+    }
+
+    static inline int Compare(const KEY& a_keyA, const KEY& a_keyB)
+    {
+      if(a_keyA < a_keyB) 
+        { return -1; }
+      if(a_keyA > a_keyB) 
+        { return 1; }
+      return 0;
+    }
+  };
+
+  // Blocking
+  gmHash<KEY, HashNode, HashNode> m_hashTable;
+
+};
+
+
+// The most crap string implementation ever
+class String
+{
+public:
+
+  String()
+  {
+    m_buffer = NULL;
+    SetBuffer("");
+  }
+
+  String(const char* a_newString)
+  {
+    m_buffer = NULL;
+    SetBuffer(a_newString);
+  }
+
+  String(const char* a_newString, const int a_newStringLength)
+  {
+    m_buffer = NULL;
+    SetBuffer(a_newString, a_newStringLength);
+  }
+
+  ~String()
+  {
+    delete [] m_buffer;
+  }
+
+  operator const char* () const
+  {
+    return m_buffer;
+  }
+
+  const char* operator = (const char* a_newString)
+  {
+    SetBuffer(a_newString);
+    return m_buffer;
+  }
+
+  const char* operator = (const String& a_newString)
+  {
+    SetBuffer(a_newString);
+    return m_buffer;
+  }
+
+private:
+
+  void SetBuffer(const char* a_newString, const int a_newStringLength)
+  {
+    delete [] m_buffer;
+    m_buffer = new char [a_newStringLength + 1];
+    memcpy(m_buffer, a_newString, a_newStringLength);
+    m_buffer[a_newStringLength] = 0;
+  }
+
+  void SetBuffer(const char* a_newString)
+  {
+    int newStringLength = strlen(a_newString);
+    SetBuffer(a_newString, newStringLength);
+  }
+
+  char * m_buffer;
+};
+
+#endif //STDSTUFF_H

+ 44 - 0
gmsrc/src/examples/GameObject/TestGameObj.gm

@@ -0,0 +1,44 @@
+// Just a bunch of tests that don't mean anything at present.
+
+global WhatsMyName = function()
+{
+  print("m_name = ", .m_name);
+  print("this = ", this);
+
+  if(.IsValid())
+  {
+    .SetPos(23.0f, 56.0f);
+    print("position(", .GetPosX(), ",", .GetPosY(), ")");
+
+    global g_globalObj = this;
+  }
+};
+
+
+global RunGlobalObject = function ()
+{
+  g_globalObj:WhatsMyName();
+};
+
+
+global ThreadYieldTest = function()
+{
+  count = 0;
+  while(count < 30)
+  {
+    sleep(0.5f);
+    count += 1;
+    print("count=",count);
+    yield();
+  }
+};
+
+
+Console.SetColor(COLOR_RED, COLOR_BLACK);
+Console.Print("Red");
+Console.SetColor(COLOR_GREEN, COLOR_BLACK);
+Console.Print("Green");
+Console.Print("\n");
+Console.SetColor(COLOR_WHITE, COLOR_BLACK);
+
+print("Script compiled and executed. \n");

+ 30 - 0
gmsrc/src/examples/GameObject/main.cpp

@@ -0,0 +1,30 @@
+#include <windows.h>  
+#include "StdStuff.h"
+#include "App.h"
+
+// Entry point for Win32 app
+int main(int argc, char* argv[])
+{
+  String test1("hello");
+  test1 = "world";
+  const char* huh = test1;
+
+  App app;
+
+  if(!app.Init())
+  {
+    fprintf(stderr,"Failed App::Init()");
+    return 1;
+  }
+
+  while(app.Update())
+  {
+  }
+
+  app.Destroy();
+
+  printf("App finished, press ENTER to exit.");
+  getchar(); // Wait for key press
+
+  return 0;
+}

+ 362 - 0
gmsrc/src/examples/Minimal/Minimal.dsp

@@ -0,0 +1,362 @@
+# Microsoft Developer Studio Project File - Name="Minimal" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Minimal - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Minimal.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Minimal.mak" CFG="Minimal - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Minimal - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Minimal - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "Perforce Project"
+# PROP Scc_LocalPath "..\.."
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Minimal - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0xc09 /d "NDEBUG"
+# ADD RSC /l 0xc09 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "Minimal - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\gm" /I "..\..\platform\win32msvc" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0xc09 /d "_DEBUG"
+# ADD RSC /l 0xc09 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Minimal - Win32 Release"
+# Name "Minimal - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\main.cpp
+# End Source File
+# End Group
+# Begin Group "gm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\gm\gmArraySimple.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmArraySimple.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCodeGen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmByteCodeGen.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGen.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGenHooks.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeGenHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeTree.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCodeTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmConfig.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCrc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmCrc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmDebug.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmFunctionObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmFunctionObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmHash.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIncGC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIncGC.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmIterator.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLibHooks.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLibHooks.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmListDouble.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmListDouble.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLog.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmLog.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachine.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachine.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachineLib.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMachineLib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMem.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMem.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemChain.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemChain.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixed.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixedSet.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmMemFixedSet.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmOperators.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmOperators.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmParser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmParser.cpp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmScanner.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmScanner.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStreamBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStreamBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStringObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmStringObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmTableObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmTableObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmThread.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmThread.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUserObject.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUserObject.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmVariable.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\gm\gmVariable.h
+# End Source File
+# End Group
+# Begin Group "win32"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\platform\win32msvc\gmConfig_p.h
+# End Source File
+# End Group
+# End Target
+# End Project

+ 33 - 0
gmsrc/src/examples/Minimal/Minimal.dsw

@@ -0,0 +1,33 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Minimal"=.\Minimal.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+    begin source code control
+    Perforce Project
+    ..\..
+    end source code control
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

+ 20 - 0
gmsrc/src/examples/Minimal/Minimal.sln

@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minimal", "Minimal.vcproj", "{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}.Debug|Win32.ActiveCfg = Debug|Win32
+		{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}.Debug|Win32.Build.0 = Debug|Win32
+		{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}.Release|Win32.ActiveCfg = Release|Win32
+		{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 1059 - 0
gmsrc/src/examples/Minimal/Minimal.vcproj

@@ -0,0 +1,1059 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="Minimal"
+	ProjectGUID="{FDDA6118-C06F-4EBA-B1F6-69D0772F3C72}"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory=".\Debug"
+			IntermediateDirectory=".\Debug"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Debug/Minimal.tlb"
+				HeaderFileName=""
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\..\gm,..\..\platform\win32msvc"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile=".\Debug/Minimal.pch"
+				AssemblerListingLocation=".\Debug/"
+				ObjectFile=".\Debug/"
+				ProgramDataBaseFileName=".\Debug/"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="3081"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib"
+				OutputFile=".\Debug/Minimal.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="true"
+				GenerateDebugInformation="true"
+				ProgramDatabaseFile=".\Debug/Minimal.pdb"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+				SuppressStartupBanner="true"
+				OutputFile=".\Debug/Minimal.bsc"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\Release"
+			IntermediateDirectory=".\Release"
+			ConfigurationType="1"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TypeLibraryName=".\Release/Minimal.tlb"
+				HeaderFileName=""
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="1"
+				AdditionalIncludeDirectories="..\..\gm,..\..\platform\win32msvc"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				StringPooling="true"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				PrecompiledHeaderFile=".\Release/Minimal.pch"
+				AssemblerListingLocation=".\Release/"
+				ObjectFile=".\Release/"
+				ProgramDataBaseFileName=".\Release/"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="3081"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="winmm.lib odbc32.lib odbccp32.lib"
+				OutputFile=".\Release/Minimal.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="true"
+				ProgramDatabaseFile=".\Release/Minimal.pdb"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+				SuppressStartupBanner="true"
+				OutputFile=".\Release/Minimal.bsc"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;h;hpp;hxx;hm;inl"
+			>
+			<File
+				RelativePath="main.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="gm"
+			>
+			<File
+				RelativePath="..\..\gm\gmArraySimple.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmArraySimple.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCode.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCode.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCodeGen.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmByteCodeGen.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGen.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGen.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGenHooks.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeGenHooks.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeTree.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCodeTree.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmConfig.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCrc.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmCrc.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmDebug.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmDebug.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmFunctionObject.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmFunctionObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmHash.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmHash.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIncGC.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIncGC.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmIterator.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLibHooks.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLibHooks.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmListDouble.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmListDouble.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLog.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmLog.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachine.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachine.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachineLib.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMachineLib.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMem.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMem.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemChain.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemChain.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixed.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixed.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixedSet.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmMemFixedSet.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmOperators.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmOperators.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmParser.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmParser.cpp.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmScanner.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmScanner.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStream.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStream.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStreamBuffer.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStreamBuffer.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStringObject.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmStringObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmTableObject.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmTableObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmThread.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmThread.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUserObject.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUserObject.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUtil.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmUtil.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmVariable.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath="..\..\gm\gmVariable.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="win32"
+			>
+			<File
+				RelativePath="..\..\platform\win32msvc\gmConfig_p.h"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

+ 78 - 0
gmsrc/src/examples/Minimal/main.cpp

@@ -0,0 +1,78 @@
+#if 0 // This is trully the smallest app
+
+#include "gmThread.h" // game monkey script
+
+int main(int argc, char* argv[])
+{
+  gmMachine machine;
+  machine.ExecuteString("print(`Hello world`);");
+  getchar(); // Keypress before exit
+  return 0;
+}
+
+#else // This is a tiny app
+
+#include <windows.h>  
+#include <mmsystem.h> // multimedia timer (may need winmm.lib)
+#include "gmThread.h" // game monkey script
+
+int main(int argc, char* argv[])
+{
+  // Create virtual machine
+  gmMachine* machine = new gmMachine;
+
+  // Get a script from stdin.  Some examples:
+  // print("Hello world");
+  // for( i = 0; i < 10; i=i+1 ) { print("i=",i); sleep(1.0); }
+  fprintf(stdout,"Please enter one line of script\n>");
+  const int MAX_SCRIPT_SIZE = 4096;
+  char script[MAX_SCRIPT_SIZE];
+  fgets(script, MAX_SCRIPT_SIZE-1, stdin);
+  
+  // Compile the script, but don't run it for now
+  int errors = machine->ExecuteString(script, NULL, false, NULL);
+  // Dump compile time errors to output
+  if(errors)
+  {
+    bool first = true;
+    const char * message;
+    
+    while((message = machine->GetLog().GetEntry(first))) 
+    {
+      fprintf(stderr, "%s"GM_NL, message);
+    }
+    machine->GetLog().Reset();
+  }
+  else
+  {
+    int deltaTime = 0;
+    int lastTime = timeGetTime();
+    
+    // Keep executing script while threads persist
+    while(machine->Execute(deltaTime))
+    {
+      // Update delta time
+      int curTime = timeGetTime();
+      deltaTime = curTime - lastTime;
+      lastTime = curTime;
+
+      // Dump run time errors to output
+      bool first = true;
+      const char * message;
+      while((message = machine->GetLog().GetEntry(first))) 
+      {
+        fprintf(stderr, "%s"GM_NL, message);
+      }
+      machine->GetLog().Reset();
+    }
+  }
+
+  delete machine; // Finished with VM
+      
+  fprintf(stdout,"Script complete.  Press a key to exit.");
+  getchar(); // Keypress before exit
+  
+  return 0;
+}
+
+#endif // Minimal build type

+ 334 - 0
gmsrc/src/gm/bison.hairy

@@ -0,0 +1,334 @@
+
+extern int timeclock;
+
+
+int yyerror;		/*  Yyerror and yycost are set by guards.	*/
+int yycost;		/*  If yyerror is set to a nonzero value by a	*/
+			/*  guard, the reduction with which the guard	*/
+			/*  is associated is not performed, and the	*/
+			/*  error recovery mechanism is invoked.	*/
+			/*  Yycost indicates the cost of performing	*/
+			/*  the reduction given the attributes of the	*/
+			/*  symbols.					*/
+
+
+/*  YYMAXDEPTH indicates the size of the parser's state and value	*/
+/*  stacks.								*/
+
+#ifndef	YYMAXDEPTH
+#define	YYMAXDEPTH	500
+#endif
+
+/*  YYMAXRULES must be at least as large as the number of rules that	*/
+/*  could be placed in the rule queue.  That number could be determined	*/
+/*  from the grammar and the size of the stack, but, as yet, it is not.	*/
+
+#ifndef	YYMAXRULES
+#define	YYMAXRULES	100
+#endif
+
+#ifndef	YYMAXBACKUP
+#define YYMAXBACKUP	100
+#endif
+
+
+short	yyss[YYMAXDEPTH];	/*  the state stack			*/
+YYSTYPE	yyvs[YYMAXDEPTH];	/*  the semantic value stack		*/
+YYLTYPE yyls[YYMAXDEPTH];	/*  the location stack			*/
+short	yyrq[YYMAXRULES];	/*  the rule queue			*/
+int	yychar;			/*  the lookahead symbol		*/
+
+YYSTYPE	yylval;			/*  the semantic value of the		*/
+				/*  lookahead symbol			*/
+
+YYSTYPE yytval;			/*  the semantic value for the state	*/
+				/*  at the top of the state stack.	*/
+
+YYSTYPE yyval;			/*  the variable used to return		*/
+				/*  semantic values from the action	*/
+				/*  routines				*/
+
+YYLTYPE yylloc;		/*  location data for the lookahead	*/
+				/*  symbol				*/
+
+YYLTYPE yytloc;		/*  location data for the state at the	*/
+				/*  top of the state stack		*/
+
+
+int	yynunlexed;
+short	yyunchar[YYMAXBACKUP];
+YYSTYPE	yyunval[YYMAXBACKUP];
+YYLTYPE yyunloc[YYMAXBACKUP];
+
+short *yygssp;			/*  a pointer to the top of the state	*/
+				/*  stack; only set during error	*/
+				/*  recovery.				*/
+
+YYSTYPE *yygvsp;		/*  a pointer to the top of the value	*/
+				/*  stack; only set during error	*/
+				/*  recovery.				*/
+
+YYLTYPE *yyglsp;		/*  a pointer to the top of the		*/
+				/*  location stack; only set during	*/
+				/*  error recovery.			*/
+
+
+/*  Yyget is an interface between the parser and the lexical analyzer.	*/
+/*  It is costly to provide such an interface, but it avoids requiring	*/
+/*  the lexical analyzer to be able to back up the scan.		*/
+
+yyget()
+{
+  if (yynunlexed > 0)
+    {
+      yynunlexed--;
+      yychar = yyunchar[yynunlexed];
+      yylval = yyunval[yynunlexed];
+      yylloc = yyunloc[yynunlexed];
+    }
+  else if (yychar <= 0)
+    yychar = 0;
+  else
+    {
+      yychar = yylex();
+      if (yychar < 0)
+	yychar = 0;
+      else yychar = YYTRANSLATE(yychar);
+    }
+}
+
+
+
+yyunlex(chr, val, loc)
+int chr;
+YYSTYPE val;
+YYLTYPE loc;
+{
+  yyunchar[yynunlexed] = chr;
+  yyunval[yynunlexed] = val;
+  yyunloc[yynunlexed] = loc;
+  yynunlexed++;
+}
+
+
+
+yyrestore(first, last)
+register short *first;
+register short *last;
+{
+  register short *ssp;
+  register short *rp;
+  register int symbol;
+  register int state;
+  register int tvalsaved;
+
+  ssp = yygssp;
+  yyunlex(yychar, yylval, yylloc);
+
+  tvalsaved = 0;
+  while (first != last)
+    {
+      symbol = yystos[*ssp];
+      if (symbol < YYNTBASE)
+	{
+	  yyunlex(symbol, yytval, yytloc);
+	  tvalsaved = 1;
+	  ssp--;
+	}
+
+      ssp--;
+
+      if (first == yyrq)
+	first = yyrq + YYMAXRULES;
+
+      first--;
+
+      for (rp = yyrhs + yyprhs[*first]; symbol = *rp; rp++)
+	{
+	  if (symbol < YYNTBASE)
+	    state = yytable[yypact[*ssp] + symbol];
+	  else
+	    {
+	      state = yypgoto[symbol - YYNTBASE] + *ssp;
+
+	      if (state >= 0 && state <= YYLAST && yycheck[state] == *ssp)
+		state = yytable[state];
+	      else
+		state = yydefgoto[symbol - YYNTBASE];
+	    }
+
+	  *++ssp = state;
+	}
+    }
+
+  if ( ! tvalsaved && ssp > yyss)
+    {
+      yyunlex(yystos[*ssp], yytval, yytloc);
+      ssp--;
+    }
+
+  yygssp = ssp;
+}
+
+
+
+int
+yyparse()
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register short *yyrq0;
+  register short *yyptr;
+  register YYSTYPE *yyvsp;
+
+  int yylen;
+  YYLTYPE *yylsp;
+  short *yyrq1;
+  short *yyrq2;
+
+  yystate = 0;
+  yyssp = yyss - 1;
+  yyvsp = yyvs - 1;
+  yylsp = yyls - 1;
+  yyrq0 = yyrq;
+  yyrq1 = yyrq0;
+  yyrq2 = yyrq0;
+
+  yychar = yylex();
+  if (yychar < 0)
+    yychar = 0;
+  else yychar = YYTRANSLATE(yychar);
+
+yynewstate:
+
+  if (yyssp >= yyss + YYMAXDEPTH - 1)
+    {
+      yyabort("Parser Stack Overflow");
+      YYABORT;
+    }
+
+  *++yyssp = yystate;
+
+yyresume:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  yyn += yychar;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  yystate = yyn;
+
+  yyptr = yyrq2;
+  while (yyptr != yyrq1)
+    {
+      yyn = *yyptr++;
+      yylen = yyr2[yyn];
+      yyvsp -= yylen;
+      yylsp -= yylen;
+
+      yyguard(yyn, yyvsp, yylsp);
+      if (yyerror)
+	goto yysemerr;
+
+      yyaction(yyn, yyvsp, yylsp);
+      *++yyvsp = yyval;
+
+      yylsp++;
+      if (yylen == 0)
+	{
+	  yylsp->timestamp = timeclock;
+	  yylsp->first_line = yytloc.first_line;
+	  yylsp->first_column = yytloc.first_column;
+	  yylsp->last_line = (yylsp-1)->last_line;
+	  yylsp->last_column = (yylsp-1)->last_column;
+	  yylsp->text = 0;
+	}
+      else
+	{
+	  yylsp->last_line = (yylsp+yylen-1)->last_line;
+	  yylsp->last_column = (yylsp+yylen-1)->last_column;
+	}
+	  
+      if (yyptr == yyrq + YYMAXRULES)
+        yyptr = yyrq;
+    }
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  yyrq2 = yyptr;
+  yyrq1 = yyrq0;
+
+  *++yyvsp = yytval;
+  *++yylsp = yytloc;
+  yytval = yylval;
+  yytloc = yylloc;
+  yyget();
+
+  goto yynewstate;
+
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+yyreduce:
+
+  *yyrq0++ = yyn;
+
+  if (yyrq0 == yyrq + YYMAXRULES)
+    yyrq0 = yyrq;
+
+  if (yyrq0 == yyrq2)
+    {
+      yyabort("Parser Rule Queue Overflow");
+      YYABORT;
+    }
+
+  yyssp -= yyr2[yyn];
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yysemerr:
+  *--yyptr = yyn;
+  yyrq2 = yyptr;
+  yyvsp += yyr2[yyn];
+
+yyerrlab:
+
+  yygssp = yyssp;
+  yygvsp = yyvsp;
+  yyglsp = yylsp;
+  yyrestore(yyrq0, yyrq2);
+  yyrecover();
+  yystate = *yygssp;
+  yyssp = yygssp;
+  yyvsp = yygvsp;
+  yyrq0 = yyrq;
+  yyrq1 = yyrq0;
+  yyrq2 = yyrq0;
+  goto yyresume;
+}
+
+$

+ 688 - 0
gmsrc/src/gm/bison.simple

@@ -0,0 +1,688 @@
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "bison.simple"
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include <malloc.h>
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include <malloc.h>
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc.  */
+#endif /* not GNU C.  */
+#endif /* alloca not defined.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         -2
+#define YYEOF           0
+#define YYACCEPT        return(0)
+#define YYABORT         return(1)
+#define YYERROR         goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL          goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do                                                              \
+  if (yychar == YYEMPTY && yylen == 1)                          \
+    { yychar = (token), yylval = (value);                       \
+      yychar1 = YYTRANSLATE (yychar);                           \
+      YYPOPSTACK;                                               \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    { yyerror ("syntax error: cannot back up"); YYERROR; }      \
+while (0)
+
+#define YYTERROR        1
+#define YYERRCODE       256
+
+#ifndef YYPURE
+#define YYLEX           yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX           yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX           yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX           yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX           yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int     yychar;                 /*  the lookahead symbol                */
+YYSTYPE yylval;                 /*  the semantic value of the           */
+                                /*  lookahead symbol                    */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;                 /*  location data for the lookahead     */
+                                /*  symbol                              */
+#endif
+
+int yynerrs;                    /*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;                    /*  nonzero means print parse trace     */
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks       */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1                /* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(FROM,TO,COUNT)      __builtin_memcpy(TO,FROM,COUNT)
+#else                           /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (from, to, count)
+     char *from;
+     char *to;
+     int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *from, char *to, int count)
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 192 "bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;      /*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;              /*  lookahead token as an internal (translated) token number */
+
+  short yyssa[YYINITDEPTH];     /*  the state stack                     */
+  YYSTYPE yyvsa[YYINITDEPTH];   /*  the semantic value stack            */
+
+  short *yyss = yyssa;          /*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;        /*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];   /*  the location stack                  */
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;                /*  the variable used to return         */
+                                /*  semantic values from the action     */
+                                /*  routines                            */
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;             /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = (short) yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+         the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+         but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+                 &yyss1, size * sizeof (*yyssp),
+                 &yyvs1, size * sizeof (*yyvsp),
+                 &yyls1, size * sizeof (*yylsp),
+                 &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+                 &yyss1, size * sizeof (*yyssp),
+                 &yyvs1, size * sizeof (*yyvsp),
+                 &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+        {
+          yyerror("parser stack overflow");
+          return 2;
+        }
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+        yystacksize = YYMAXDEPTH;
+      yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+      yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+        YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)              /* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;           /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+        {
+          fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+          /* Give the individual parser a way to print the precise meaning
+             of a token, for further debugging info.  */
+#ifdef YYPRINT
+          YYPRINT (stderr, yychar, yylval);
+#endif
+          fprintf (stderr, ")\n");
+        }
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+               yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+        fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+$   /* the action file gets copied in in place of this dollarsign */
+#line 487 "bison.simple"
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+        fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+        {
+          int size = 0;
+          char *msg;
+          int x, count;
+
+          count = 0;
+          /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+          for (x = (yyn < 0 ? -yyn : 0);
+               x < (int)(sizeof(yytname) / sizeof(char *)); x++) //_GD_
+            if (yycheck[x + yyn] == x)
+              size += strlen(yytname[x]) + 15, count++;
+          //_GD_ msg = (char *) malloc(size + 15);
+          msg = GM_NEW( char [size + 15] );
+          if (msg != 0)
+            {
+              strcpy(msg, "parse error");
+
+              if (count < 5)
+                {
+                  count = 0;
+                  for (x = (yyn < 0 ? -yyn : 0);
+                       x < (sizeof(yytname) / sizeof(char *)); x++)
+                    if (yycheck[x + yyn] == x)
+                      {
+                        strcat(msg, count == 0 ? ", expecting `" : " or `");
+                        strcat(msg, yytname[x]);
+                        strcat(msg, "'");
+                        count++;
+                      }
+                }
+              yyerror(msg);
+              //_GD_ free(msg);
+              delete [] msg;
+            }
+          else
+            yyerror ("parse error; also virtual memory exceeded");
+        }
+      else
+#endif /* YYERROR_VERBOSE */
+        yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+        YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+        fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;              /* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+        fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+        goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+}

+ 1512 - 0
gmsrc/src/gm/flex.skl

@@ -0,0 +1,1512 @@
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.85 95/04/24 10:48:47 vern Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_NEVER_INTERACTIVE 1
+
+%-
+#include <stdio.h>
+#include <errno.h>
+%*
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+%+
+class istream;
+%*
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif   /* __STDC__ */
+#endif   /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+%-
+extern FILE *yyin, *yyout;
+%*
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ *    if ( condition_holds )
+ *    yyless( 5 );
+ * else
+ *    do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+   do \
+      { \
+      /* Undo effects of setting up yytext. */ \
+      *yy_cp = yy_hold_char; \
+      yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+      YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+      } \
+   while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+   {
+%-
+   FILE *yy_input_file;
+%+
+   istream* yy_input_file;
+%*
+
+   char *yy_ch_buf;     /* input buffer */
+   char *yy_buf_pos;    /* current position in input buffer */
+
+   /* Size of input buffer in bytes, not including room for EOB
+    * characters.
+    */
+   yy_size_t yy_buf_size;
+
+   /* Number of characters read into yy_ch_buf, not including EOB
+    * characters.
+    */
+   int yy_n_chars;
+
+   /* Whether we "own" the buffer - i.e., we know we created it,
+    * and can realloc() it to grow it, and should free() it to
+    * delete it.
+    */
+   int yy_is_our_buffer;
+
+   /* Whether this is an "interactive" input source; if so, and
+    * if we're using stdio for input, then we want to use getc()
+    * instead of fread(), to make sure we stop fetching input after
+    * each newline.
+    */
+   int yy_is_interactive;
+
+   /* Whether we're considered to be at the beginning of a line.
+    * If so, '^' rules will be active on the next match, otherwise
+    * not.
+    */
+   int yy_at_bol;
+
+   /* Whether to try to fill the input buffer when we reach the
+    * end of it.
+    */
+   int yy_fill_buffer;
+
+   int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+   /* When an EOF's been seen but there's still some text to process
+    * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+    * shouldn't try reading from the input source any more.  We might
+    * still have a bunch of tokens to match, though, because of
+    * possible backing-up.
+    *
+    * When we actually see the EOF, we change the status to "new"
+    * (via yyrestart()), so that the user can continue scanning by
+    * just pointing yyin at a new input file.
+    */
+#define YY_BUFFER_EOF_PENDING 2
+   };
+
+%- Standard (non-C++) definition
+static YY_BUFFER_STATE yy_current_buffer = 0;
+%*
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+%- Standard (non-C++) definition
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;     /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;    /* whether we need to initialize */
+static int yy_start = 0;   /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+%*
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+   { \
+   if ( ! yy_current_buffer ) \
+      yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+   yy_current_buffer->yy_is_interactive = is_interactive; \
+   }
+
+#define yy_set_bol(at_bol) \
+   { \
+   if ( ! yy_current_buffer ) \
+      yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+   yy_current_buffer->yy_at_bol = at_bol; \
+   }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+%% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here
+
+%- Standard (non-C++) definition
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+%*
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+   yytext_ptr = yy_bp; \
+%% code to fiddle yytext and yyleng for yymore() goes here
+   yy_hold_char = *yy_cp; \
+   *yy_cp = '\0'; \
+%% code to copy yytext_ptr to yytext[] goes here, if %array
+   yy_c_buf_p = yy_cp;
+
+%% data tables for the DFA and the user's section 1 definitions go here
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+%-
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+%*
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifndef YY_NO_INPUT
+%- Standard (non-C++) definition
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+%*
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+%- Standard (non-C++) definition
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+%+ C++ definition
+#define ECHO LexerOutput( yytext, yyleng )
+%*
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+%% fread()/read() definition of YY_INPUT goes here unless we're doing C++
+%+ C++ definition
+   if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \
+      YY_FATAL_ERROR( "input in flex scanner failed" );
+%*
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+%-
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+%+
+#define YY_FATAL_ERROR(msg) LexerError( msg )
+%*
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+%- Standard (non-C++) definition
+#define YY_DECL int yylex YY_PROTO(( void ))
+%+ C++ definition
+#define YY_DECL int yyFlexLexer::yylex()
+%*
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+%% YY_RULE_SETUP definition goes here
+
+YY_DECL
+   {
+   register yy_state_type yy_current_state;
+   register char *yy_cp, *yy_bp;
+   register int yy_act;
+
+%% user's declarations go here
+
+   if ( yy_init )
+      {
+      yy_init = 0;
+
+#ifdef YY_USER_INIT
+      YY_USER_INIT;
+#endif
+
+      if ( ! yy_start )
+         yy_start = 1;  /* first start state */
+
+      if ( ! yyin )
+%-
+         yyin = stdin;
+%+
+         yyin = &cin;
+%*
+
+      if ( ! yyout )
+%-
+         yyout = stdout;
+%+
+         yyout = &cout;
+%*
+
+      if ( ! yy_current_buffer )
+         yy_current_buffer =
+            yy_create_buffer( yyin, YY_BUF_SIZE );
+
+      yy_load_buffer_state();
+      }
+
+   while ( 1 )    /* loops until end-of-file is reached */
+      {
+%% yymore()-related code goes here
+      yy_cp = yy_c_buf_p;
+
+      /* Support of yytext. */
+      *yy_cp = yy_hold_char;
+
+      /* yy_bp points to the position in yy_ch_buf of the start of
+       * the current run.
+       */
+      yy_bp = yy_cp;
+
+%% code to set up and find next match goes here
+
+yy_find_action:
+%% code to find the action number goes here
+
+      YY_DO_BEFORE_ACTION;
+
+%% code for yylineno update goes here
+
+do_action:  /* This label is used only to access EOF actions. */
+
+%% debug code goes here
+
+      switch ( yy_act )
+   { /* beginning of action switch */
+%% actions go here
+
+   case YY_END_OF_BUFFER:
+      {
+      /* Amount of text matched not including the EOB char. */
+      int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+      /* Undo the effects of YY_DO_BEFORE_ACTION. */
+      *yy_cp = yy_hold_char;
+
+      if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+         {
+         /* We're scanning a new file or input source.  It's
+          * possible that this happened because the user
+          * just pointed yyin at a new source and called
+          * yylex().  If so, then we have to assure
+          * consistency between yy_current_buffer and our
+          * globals.  Here is the right place to do so, because
+          * this is the first action (other than possibly a
+          * back-up) that will match for the new input source.
+          */
+         yy_n_chars = yy_current_buffer->yy_n_chars;
+         yy_current_buffer->yy_input_file = yyin;
+         yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+         }
+
+      /* Note that here we test for yy_c_buf_p "<=" to the position
+       * of the first EOB in the buffer, since yy_c_buf_p will
+       * already have been incremented past the NUL character
+       * (since all states make transitions on EOB to the
+       * end-of-buffer state).  Contrast this with the test
+       * in input().
+       */
+      if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+         { /* This was really a NUL. */
+         yy_state_type yy_next_state;
+
+         yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+         yy_current_state = yy_get_previous_state();
+
+         /* Okay, we're now positioned to make the NUL
+          * transition.  We couldn't have
+          * yy_get_previous_state() go ahead and do it
+          * for us because it doesn't know how to deal
+          * with the possibility of jamming (and we don't
+          * want to build jamming into it because then it
+          * will run more slowly).
+          */
+
+         yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+         yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+         if ( yy_next_state )
+            {
+            /* Consume the NUL. */
+            yy_cp = ++yy_c_buf_p;
+            yy_current_state = yy_next_state;
+            goto yy_match;
+            }
+
+         else
+            {
+%% code to do back-up for compressed tables and set up yy_cp goes here
+            goto yy_find_action;
+            }
+         }
+
+      else switch ( yy_get_next_buffer() )
+         {
+         case EOB_ACT_END_OF_FILE:
+            {
+            yy_did_buffer_switch_on_eof = 0;
+
+            if ( yywrap() )
+               {
+               /* Note: because we've taken care in
+                * yy_get_next_buffer() to have set up
+                * yytext, we can now set up
+                * yy_c_buf_p so that if some total
+                * hoser (like flex itself) wants to
+                * call the scanner after we return the
+                * YY_NULL, it'll still work - another
+                * YY_NULL will get returned.
+                */
+               yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+               yy_act = YY_STATE_EOF(YY_START);
+               goto do_action;
+               }
+
+            else
+               {
+               if ( ! yy_did_buffer_switch_on_eof )
+                  YY_NEW_FILE;
+               }
+            break;
+            }
+
+         case EOB_ACT_CONTINUE_SCAN:
+            yy_c_buf_p =
+               yytext_ptr + yy_amount_of_matched_text;
+
+            yy_current_state = yy_get_previous_state();
+
+            yy_cp = yy_c_buf_p;
+            yy_bp = yytext_ptr + YY_MORE_ADJ;
+            goto yy_match;
+
+         case EOB_ACT_LAST_MATCH:
+            yy_c_buf_p =
+            &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+            yy_current_state = yy_get_previous_state();
+
+            yy_cp = yy_c_buf_p;
+            yy_bp = yytext_ptr + YY_MORE_ADJ;
+            goto yy_find_action;
+         }
+      break;
+      }
+
+   default:
+      YY_FATAL_ERROR(
+         "fatal flex scanner internal error--no action found" );
+   } /* end of action switch */
+      } /* end of scanning one token */
+   } /* end of yylex */
+
+%+
+yyFlexLexer::yyFlexLexer( istream* arg_yyin, ostream* arg_yyout )
+   {
+   yyin = arg_yyin;
+   yyout = arg_yyout;
+   yy_c_buf_p = 0;
+   yy_init = 1;
+   yy_start = 0;
+   yy_flex_debug = 0;
+   yylineno = 1;  // this will only get updated if %option yylineno
+
+   yy_did_buffer_switch_on_eof = 0;
+
+   yy_looking_for_trail_begin = 0;
+   yy_more_flag = 0;
+   yy_more_len = 0;
+
+   yy_start_stack_ptr = yy_start_stack_depth = 0;
+   yy_start_stack = 0;
+
+   yy_current_buffer = 0;
+
+#ifdef YY_USES_REJECT
+   yy_state_buf = new yy_state_type[YY_BUF_SIZE + 2];
+#else
+   yy_state_buf = 0;
+#endif
+   }
+
+yyFlexLexer::~yyFlexLexer()
+   {
+   delete yy_state_buf;
+   yy_delete_buffer( yy_current_buffer );
+   }
+
+void yyFlexLexer::switch_streams( istream* new_in, ostream* new_out )
+   {
+   if ( new_in )
+      {
+      yy_delete_buffer( yy_current_buffer );
+      yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE ) );
+      }
+
+   if ( new_out )
+      yyout = new_out;
+   }
+
+#ifdef YY_INTERACTIVE
+int yyFlexLexer::LexerInput( char* buf, int /* max_size */ )
+#else
+int yyFlexLexer::LexerInput( char* buf, int max_size )
+#endif
+   {
+   if ( yyin->eof() || yyin->fail() )
+      return 0;
+
+#ifdef YY_INTERACTIVE
+   yyin->get( buf[0] );
+
+   if ( yyin->eof() )
+      return 0;
+
+   if ( yyin->bad() )
+      return -1;
+
+   return 1;
+
+#else
+   (void) yyin->read( buf, max_size );
+
+   if ( yyin->bad() )
+      return -1;
+   else
+      return yyin->gcount();
+#endif
+   }
+
+void yyFlexLexer::LexerOutput( const char* buf, int size )
+   {
+   (void) yyout->write( buf, size );
+   }
+%*
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+%-
+static int yy_get_next_buffer()
+%+
+int yyFlexLexer::yy_get_next_buffer()
+%*
+   {
+   register char *dest = yy_current_buffer->yy_ch_buf;
+   register char *source = yytext_ptr;
+   register int number_to_move, i;
+   int ret_val;
+
+   if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+      YY_FATAL_ERROR(
+      "fatal flex scanner internal error--end of buffer missed" );
+
+   if ( yy_current_buffer->yy_fill_buffer == 0 )
+      { /* Don't try to fill the buffer, so this is an EOF. */
+      if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+         {
+         /* We matched a singled characater, the EOB, so
+          * treat this as a final EOF.
+          */
+         return EOB_ACT_END_OF_FILE;
+         }
+
+      else
+         {
+         /* We matched some text prior to the EOB, first
+          * process it.
+          */
+         return EOB_ACT_LAST_MATCH;
+         }
+      }
+
+   /* Try to read more data. */
+
+   /* First move last chars to start of buffer. */
+   number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+   for ( i = 0; i < number_to_move; ++i )
+      *(dest++) = *(source++);
+
+   if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+      /* don't do the read, it's not guaranteed to return an EOF,
+       * just force an EOF
+       */
+      yy_n_chars = 0;
+
+   else
+      {
+      int num_to_read =
+         yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+      while ( num_to_read <= 0 )
+         { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+         YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+         /* just a shorter name for the current buffer */
+         YY_BUFFER_STATE b = yy_current_buffer;
+
+         int yy_c_buf_p_offset =
+            (int) (yy_c_buf_p - b->yy_ch_buf);
+
+         if ( b->yy_is_our_buffer )
+            {
+            //_GD_
+            int oldSize = b->yy_buf_size;
+
+            int new_size = b->yy_buf_size * 2;
+
+            if ( new_size <= 0 )
+               b->yy_buf_size += b->yy_buf_size / 8;
+            else
+               b->yy_buf_size *= 2;
+
+            /* Include room in for 2 EOB chars. */
+            //_GD_ b->yy_ch_buf = (char *)
+            //_GD_ yy_flex_realloc( (void *) b->yy_ch_buf,
+            //_GD_       b->yy_buf_size + 2 );
+            //_GD_
+            void* newBytes = yy_flex_alloc( b->yy_buf_size + 2 );
+            memcpy(newBytes, b->yy_ch_buf, oldSize));
+            yy_flex_free(b->yy_ch_buf);
+            b->yy_ch_buf = newBytes;
+            }
+         else
+            /* Can't grow it, we don't own it. */
+            b->yy_ch_buf = 0;
+
+         if ( ! b->yy_ch_buf )
+            YY_FATAL_ERROR(
+            "fatal error - scanner input buffer overflow" );
+
+         yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+         num_to_read = yy_current_buffer->yy_buf_size -
+                  number_to_move - 1;
+#endif
+         }
+
+      if ( num_to_read > YY_READ_BUF_SIZE )
+         num_to_read = YY_READ_BUF_SIZE;
+
+      /* Read in more data. */
+      YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+         yy_n_chars, num_to_read );
+      }
+
+   if ( yy_n_chars == 0 )
+      {
+      if ( number_to_move == YY_MORE_ADJ )
+         {
+         ret_val = EOB_ACT_END_OF_FILE;
+         yyrestart( yyin );
+         }
+
+      else
+         {
+         ret_val = EOB_ACT_LAST_MATCH;
+         yy_current_buffer->yy_buffer_status =
+            YY_BUFFER_EOF_PENDING;
+         }
+      }
+
+   else
+      ret_val = EOB_ACT_CONTINUE_SCAN;
+
+   yy_n_chars += number_to_move;
+   yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+   yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+   yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+   return ret_val;
+   }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+%-
+static yy_state_type yy_get_previous_state()
+%+
+yy_state_type yyFlexLexer::yy_get_previous_state()
+%*
+   {
+   register yy_state_type yy_current_state;
+   register char *yy_cp;
+
+%% code to get the start state into yy_current_state goes here
+
+   for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+      {
+%% code to find the next state goes here
+      }
+
+   return yy_current_state;
+   }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+%-
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+%+
+yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state )
+%*
+   {
+   register int yy_is_jam;
+%% code to find the next state, and perhaps do backing up, goes here
+
+   return yy_is_jam ? 0 : yy_current_state;
+   }
+
+
+%-
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+%+
+void yyFlexLexer::yyunput( int c, register char* yy_bp )
+%*
+   {
+   register char *yy_cp = yy_c_buf_p;
+
+   /* undo effects of setting up yytext */
+   *yy_cp = yy_hold_char;
+
+   if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+      { /* need to shift things up to make room */
+      /* +2 for EOB chars. */
+      register int number_to_move = yy_n_chars + 2;
+      register char *dest = &yy_current_buffer->yy_ch_buf[
+               yy_current_buffer->yy_buf_size + 2];
+      register char *source =
+            &yy_current_buffer->yy_ch_buf[number_to_move];
+
+      while ( source > yy_current_buffer->yy_ch_buf )
+         *--dest = *--source;
+
+      yy_cp += (int) (dest - source);
+      yy_bp += (int) (dest - source);
+      yy_n_chars = yy_current_buffer->yy_buf_size;
+
+      if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+         YY_FATAL_ERROR( "flex scanner push-back overflow" );
+      }
+
+   *--yy_cp = (char) c;
+
+%% update yylineno here
+
+   yytext_ptr = yy_bp;
+   yy_hold_char = *yy_cp;
+   yy_c_buf_p = yy_cp;
+   }
+%-
+#endif   /* ifndef YY_NO_UNPUT */
+%*
+
+
+%-
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+%+
+int yyFlexLexer::yyinput()
+%*
+   {
+   int c;
+
+   *yy_c_buf_p = yy_hold_char;
+
+   if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+      {
+      /* yy_c_buf_p now points to the character we want to return.
+       * If this occurs *before* the EOB characters, then it's a
+       * valid NUL; if not, then we've hit the end of the buffer.
+       */
+      if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+         /* This was really a NUL. */
+         *yy_c_buf_p = '\0';
+
+      else
+         { /* need more input */
+         yytext_ptr = yy_c_buf_p;
+         ++yy_c_buf_p;
+
+         switch ( yy_get_next_buffer() )
+            {
+            case EOB_ACT_END_OF_FILE:
+               {
+               if ( yywrap() )
+                  {
+                  yy_c_buf_p =
+                  yytext_ptr + YY_MORE_ADJ;
+                  return EOF;
+                  }
+
+               if ( ! yy_did_buffer_switch_on_eof )
+                  YY_NEW_FILE;
+#ifdef __cplusplus
+               return yyinput();
+#else
+               return input();
+#endif
+               }
+
+            case EOB_ACT_CONTINUE_SCAN:
+               yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+               break;
+
+            case EOB_ACT_LAST_MATCH:
+#ifdef __cplusplus
+               YY_FATAL_ERROR(
+               "unexpected last match in yyinput()" );
+#else
+               YY_FATAL_ERROR(
+               "unexpected last match in input()" );
+#endif
+            }
+         }
+      }
+
+   c = *(unsigned char *) yy_c_buf_p;  /* cast for 8-bit char's */
+   *yy_c_buf_p = '\0';  /* preserve yytext */
+   yy_hold_char = *++yy_c_buf_p;
+
+%% update BOL and yylineno
+
+   return c;
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+%+
+void yyFlexLexer::yyrestart( istream* input_file )
+%*
+   {
+   if ( ! yy_current_buffer )
+      yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+   yy_init_buffer( yy_current_buffer, input_file );
+   yy_load_buffer_state();
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+%+
+void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+%*
+   {
+   if ( yy_current_buffer == new_buffer )
+      return;
+
+   if ( yy_current_buffer )
+      {
+      /* Flush out information for old buffer. */
+      *yy_c_buf_p = yy_hold_char;
+      yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+      yy_current_buffer->yy_n_chars = yy_n_chars;
+      }
+
+   yy_current_buffer = new_buffer;
+   yy_load_buffer_state();
+
+   /* We don't actually know whether we did this switch during
+    * EOF (yywrap()) processing, but the only time this flag
+    * is looked at is after yywrap() is called, so it's safe
+    * to go ahead and always set it.
+    */
+   yy_did_buffer_switch_on_eof = 1;
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+%+
+void yyFlexLexer::yy_load_buffer_state()
+%*
+   {
+   yy_n_chars = yy_current_buffer->yy_n_chars;
+   yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+   yyin = yy_current_buffer->yy_input_file;
+   yy_hold_char = *yy_c_buf_p;
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+%+
+YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( istream* file, int size )
+%*
+   {
+   YY_BUFFER_STATE b;
+
+   b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+   if ( ! b )
+      YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+   b->yy_buf_size = size;
+
+   /* yy_ch_buf has to be 2 characters longer than the size given because
+    * we need to put in 2 end-of-buffer characters.
+    */
+   b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+   if ( ! b->yy_ch_buf )
+      YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+   b->yy_is_our_buffer = 1;
+
+   yy_init_buffer( b, file );
+
+   return b;
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+%+
+void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b )
+%*
+   {
+   if ( ! b )
+      return;
+
+   if ( b == yy_current_buffer )
+      yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+   if ( b->yy_is_our_buffer )
+      yy_flex_free( (void *) b->yy_ch_buf );
+
+   yy_flex_free( (void *) b );
+   }
+
+
+%-
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+%+
+extern "C" int isatty YY_PROTO(( int ));
+void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )
+%*
+
+   {
+   yy_flush_buffer( b );
+
+   b->yy_input_file = file;
+   b->yy_fill_buffer = 1;
+
+%-
+#if YY_ALWAYS_INTERACTIVE
+   b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+   b->yy_is_interactive = 0;
+#else
+   b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+%+
+   b->yy_is_interactive = 0;
+%*
+   }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+%+
+void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b )
+%*
+   {
+   b->yy_n_chars = 0;
+
+   /* We always need two end-of-buffer characters.  The first causes
+    * a transition to the end-of-buffer state.  The second causes
+    * a jam in that state.
+    */
+   b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+   b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+   b->yy_buf_pos = &b->yy_ch_buf[0];
+
+   b->yy_at_bol = 1;
+   b->yy_buffer_status = YY_BUFFER_NEW;
+
+   if ( b == yy_current_buffer )
+      yy_load_buffer_state();
+   }
+%*
+
+
+#ifndef YY_NO_SCAN_BUFFER
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+   {
+   YY_BUFFER_STATE b;
+
+   if ( size < 2 ||
+        base[size-2] != YY_END_OF_BUFFER_CHAR ||
+        base[size-1] != YY_END_OF_BUFFER_CHAR )
+      /* They forgot to leave room for the EOB's. */
+      return 0;
+
+   b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+   if ( ! b )
+      YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+   b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+   b->yy_buf_pos = b->yy_ch_buf = base;
+   b->yy_is_our_buffer = 0;
+   b->yy_input_file = 0;
+   b->yy_n_chars = b->yy_buf_size;
+   b->yy_is_interactive = 0;
+   b->yy_at_bol = 1;
+   b->yy_fill_buffer = 0;
+   b->yy_buffer_status = YY_BUFFER_NEW;
+
+   yy_switch_to_buffer( b );
+
+   return b;
+   }
+%*
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *str )
+#else
+YY_BUFFER_STATE yy_scan_string( str )
+yyconst char *str;
+#endif
+   {
+   int len;
+   for ( len = 0; str[len]; ++len )
+      ;
+
+   return yy_scan_bytes( str, len );
+   }
+%*
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+   {
+   YY_BUFFER_STATE b;
+   char *buf;
+   yy_size_t n;
+   int i;
+
+   /* Get memory for full buffer, including space for trailing EOB's. */
+   n = len + 2;
+   buf = (char *) yy_flex_alloc( n );
+   if ( ! buf )
+      YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+   for ( i = 0; i < len; ++i )
+      buf[i] = bytes[i];
+
+   buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+   b = yy_scan_buffer( buf, n );
+   if ( ! b )
+      YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+   /* It's okay to grow etc. this buffer, and we should throw it
+    * away when we're done.
+    */
+   b->yy_is_our_buffer = 1;
+
+   return b;
+   }
+%*
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+%-
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+%+
+void yyFlexLexer::yy_push_state( int new_state )
+%*
+   {
+   if ( yy_start_stack_ptr >= yy_start_stack_depth )
+      {
+      yy_size_t new_size;
+
+      yy_start_stack_depth += YY_START_STACK_INCR;
+      new_size = yy_start_stack_depth * sizeof( int );
+
+      if ( ! yy_start_stack )
+         yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+      else
+         yy_start_stack = (int *) yy_flex_realloc(
+               (void *) yy_start_stack, new_size );
+
+      if ( ! yy_start_stack )
+         YY_FATAL_ERROR(
+         "out of memory expanding start-condition stack" );
+      }
+
+   yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+   BEGIN(new_state);
+   }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+%-
+static void yy_pop_state()
+%+
+void yyFlexLexer::yy_pop_state()
+%*
+   {
+   if ( --yy_start_stack_ptr < 0 )
+      YY_FATAL_ERROR( "start-condition stack underflow" );
+
+   BEGIN(yy_start_stack[yy_start_stack_ptr]);
+   }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+%-
+static int yy_top_state()
+%+
+int yyFlexLexer::yy_top_state()
+%*
+   {
+   return yy_start_stack[yy_start_stack_ptr - 1];
+   }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+%-
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+   {
+   (void) fprintf( stderr, "%s\n", msg );
+   exit( YY_EXIT_FAILURE );
+   }
+
+%+
+
+void yyFlexLexer::LexerError( yyconst char msg[] )
+   {
+   cerr << msg << '\n';
+   exit( YY_EXIT_FAILURE );
+   }
+%*
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+   do \
+      { \
+      /* Undo effects of setting up yytext. */ \
+      yytext[yyleng] = yy_hold_char; \
+      yy_c_buf_p = yytext + n - YY_MORE_ADJ; \
+      yy_hold_char = *yy_c_buf_p; \
+      *yy_c_buf_p = '\0'; \
+      yyleng = n; \
+      } \
+   while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+   {
+   register int i;
+   for ( i = 0; i < n; ++i )
+      s1[i] = s2[i];
+   }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+   {
+   //_GD_ return (void *) malloc( size );
+   return (void *) GM_NEW( char [size] );
+   }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+   {
+   /* The cast to (char *) in the following accommodates both
+    * implementations that use char* generic pointers, and those
+    * that use void* generic pointers.  It works with the latter
+    * because both ANSI C and C++ allow castless assignment from
+    * any pointer type to void*, and deal with argument conversions
+    * as though doing an assignment.
+    */
+   //_GD_ return (void *) realloc( (char *) ptr, size );
+   }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+   {
+   //_GD_ free( ptr );
+   delete [] (char*) ptr;
+   }
+
+#if YY_MAIN
+int main()
+   {
+   yylex();
+   return 0;
+   }
+#endif

+ 15 - 0
gmsrc/src/gm/gmArraySimple.cpp

@@ -0,0 +1,15 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmArraySimple.h"
+
+

+ 333 - 0
gmsrc/src/gm/gmArraySimple.h

@@ -0,0 +1,333 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMARRAYSIMPLE_H_
+#define _GMARRAYSIMPLE_H_
+
+#include "gmConfig.h"
+#include "gmUtil.h"
+
+#define TMPL template <class T>
+#define QUAL gmArraySimple<T>
+
+/// \class gmArraySimple
+/// \brief templated array class for simple types with power of 2 auto size option.
+///        Elements may be moved around in memory using memcpy() during resize operations.
+TMPL class gmArraySimple
+{  
+public:
+  
+  enum
+  {
+    NULL_INDEX = ~0
+  };
+  
+  gmArraySimple(void);
+  gmArraySimple(const QUAL &a_array);
+  ~gmArraySimple(void);
+  
+  template <class J>
+  inline void InsertLast(const J &a_elem)
+  {
+    if(m_count >= m_size)
+    {
+      Resize(m_count + 1);
+    }
+    m_elem[m_count++] = a_elem;
+  }
+
+  /// \brief SetBlockSize() will set the hysteresis memory grow by in elements. 
+  /// \param a_blockSize as 0 will set automatic power of 2.
+  inline void SetBlockSize(gmuint a_blockSize) { m_blockSize = a_blockSize; }
+
+  inline bool InsertLastIfUnique(const T &a_elem);
+  inline T& InsertLast(void);
+  inline void InsertBefore(gmuint a_index, const T &a_elem);
+  inline void Remove(gmuint a_index);
+  inline void RemoveSwapLast(gmuint a_index);
+  inline void RemoveLast(void);
+  
+  inline T &operator[](gmuint a_index);
+  inline const T &operator[](gmuint a_index) const;
+
+  inline gmuint Count(void) const { return m_count; }
+  inline bool IsEmpty(void) const { return (m_count == 0); }
+
+  inline void Reset(void) { SetCount(0); }
+  inline void ResetAndFreeMemory(void);
+
+  inline void SetCount(gmuint a_count);
+  inline void SetCountAndFreeMemory(gmuint a_count);
+  inline void Touch(gmuint a_element);
+
+  inline T* GetData(void) { return m_elem; }
+  inline const T* GetData(void) const { return m_elem; }
+  inline gmuint GetSize(void) { return m_size; }
+  bool IsValid(const T* a_elem) const;
+  
+  inline QUAL &operator=(const QUAL &a_array);
+  
+  inline bool FindRemove(const T &a_elem);
+  
+  template <class Q>
+  inline gmuint FindIndex(const Q &a_elem) const
+  {
+    // iterate backwards, better chance of finding a_elem, given InsertLast() is 
+    // used commonly which presents possible element coherence
+    if(m_count == 0) return NULL_INDEX;
+    gmuint i = m_count - 1;
+    do
+    {
+      if (m_elem[i] == a_elem)    // used commonly which presents possible element coherence
+        return i;
+    } while(i-- > 0);
+    return NULL_INDEX;
+  }
+  
+private:
+    
+  T *m_elem;
+  gmuint m_count, m_size;
+  gmuint m_blockSize; //!< 0 and will be power of 2 sizing.
+  
+  /// \brief Resize() will resize the array.
+  /// \param a_size is the required size.
+  void Resize(gmuint a_size, bool a_shrinkIfPossible = false);
+};
+
+
+//
+// implementation
+//
+
+
+TMPL
+inline QUAL::gmArraySimple(void)
+{
+  m_elem = NULL;
+  m_count = 0;
+  m_size = 0;
+  m_blockSize = 0; // power of 2 auto
+}
+
+
+TMPL inline QUAL::gmArraySimple(const QUAL &a_array)
+{
+  m_elem = NULL;
+  m_count = 0;
+  m_size = 0;
+  m_blockSize = 0; // power of 2 auto
+  operator=(a_array);
+}
+
+
+TMPL
+inline QUAL::~gmArraySimple(void)
+{
+  if(m_elem)
+  {
+    delete[] (char *) m_elem;
+  }
+}
+
+
+TMPL bool QUAL::InsertLastIfUnique(const T &a_elem)
+{
+  if(FindIndex(a_elem) == NULL_INDEX)
+  {
+    InsertLast(a_elem);
+    return true;
+  }
+  return false;
+}
+
+
+TMPL inline T& QUAL::InsertLast(void)
+{
+  if(m_count >= m_size)
+  {
+    Resize(m_count + 1);
+  }
+  return m_elem[m_count++];
+}
+
+
+TMPL
+inline void QUAL::InsertBefore(gmuint a_index, const T &a_elem)
+{
+  if(a_index >= m_count)
+  {
+    InsertLast(a_elem);
+  }
+  else
+  {
+    if(m_count >= m_size)
+    {
+      Resize(m_count + 1);
+    }
+    memmove(&m_elem[a_index+1], &m_elem[a_index], (m_count - a_index) * sizeof(T));
+    m_elem[a_index] = a_elem;
+    ++m_count;
+  }
+}
+
+
+TMPL
+inline void QUAL::Remove(gmuint a_index)
+{
+  if(a_index >= m_count) return;
+  memmove(&m_elem[a_index], &m_elem[a_index+1], (m_count - (a_index + 1)) * sizeof(T));
+  --m_count;
+}
+
+
+TMPL
+inline void QUAL::RemoveSwapLast(gmuint a_index)
+{
+  if (a_index >= m_count) return;
+  if(--m_count != a_index)
+  {
+    m_elem[a_index] = m_elem[m_count];
+  }
+}
+
+
+TMPL
+inline void QUAL::RemoveLast(void)
+{
+  GM_ASSERT(m_count > 0);
+  --m_count;
+}
+
+
+TMPL
+inline T &QUAL::operator[](gmuint a_index)
+{
+  GM_ASSERT(a_index >= 0 && a_index < m_count);
+  return m_elem[a_index];
+}
+
+
+TMPL
+inline const T &QUAL::operator[](gmuint a_index) const
+{
+  GM_ASSERT(a_index >= 0 && a_index < m_count);
+  return m_elem[a_index];
+}
+
+
+TMPL
+inline void QUAL::ResetAndFreeMemory(void)
+{
+  if(m_elem)
+  {
+    delete[] (char *) m_elem;
+    m_elem = NULL;
+  }
+  m_count = m_size = 0;
+}
+
+
+TMPL
+inline void QUAL::SetCount(gmuint a_count)
+{
+  if(a_count > m_size)
+  {
+    Resize(a_count);
+  }
+  m_count = a_count;
+}
+
+
+TMPL
+inline void QUAL::SetCountAndFreeMemory(gmuint a_count)
+{
+  Resize(a_count, true);
+  m_count = a_count;
+}
+
+
+TMPL
+inline void QUAL::Touch(gmuint a_element)
+{
+  if(a_element >= m_count)
+  {
+    SetCount(a_element + 1);
+  }
+}
+
+
+TMPL bool QUAL::IsValid(const T* a_elem) const
+{
+  gmuint index = (a_elem - m_elem);
+  return (index < m_count);
+}
+
+
+TMPL
+inline QUAL &QUAL::operator=(const QUAL &a_array)
+{
+  SetCount(a_array.m_count);
+  memcpy((char*)m_elem, (const char*)a_array.m_elem, m_count * sizeof(T));
+  return *this;
+}
+
+
+TMPL bool QUAL::FindRemove(const T &a_elem)
+{
+  gmuint index = FindIndex(a_elem);
+  if(index != NULL_INDEX)
+  {
+    Remove(index);
+    return true;
+  }
+  return false;
+}
+
+
+TMPL
+void QUAL::Resize(gmuint a_size, bool a_shrinkIfPossible)
+{
+  if(m_size >= a_size)
+  {
+    // TODO: handle a_shrinkIfPossible.
+    return;
+  }
+
+  // we need to grow, figure out a new size.
+  gmuint size = 0;
+  if(m_blockSize > 0)
+  {
+    size = ((a_size / m_blockSize) + 1) * m_blockSize;
+  }
+  else
+  {
+    size = gmLog2ge(gmMax<gmuint>(4, a_size + 1));
+  }
+
+  // alloc, copy, free
+  {
+    T * t = (T*) GM_NEW(char[sizeof(T) * size]);
+    if(m_elem)
+    {
+      memcpy(t, m_elem, m_count * sizeof(T));
+      delete[] (char *) m_elem;
+    }
+    m_elem = t;
+  }
+
+  m_size = size;
+}
+
+#undef QUAL
+#undef TMPL
+
+#endif

+ 135 - 0
gmsrc/src/gm/gmByteCode.cpp

@@ -0,0 +1,135 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmByteCode.h"
+
+
+#if GM_COMPILE_DEBUG
+
+void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength)
+{
+  union 
+  {
+    const gmuint32 * instruction32;
+    const gmuint8 * instruction;
+  };
+
+  instruction = (const gmuint8 *) a_byteCode;
+  const gmuint8 * end = instruction + a_byteCodeLength;
+  const gmuint8 * start = instruction;
+  const char * cp;
+  bool opiptr, opf32;
+
+  while(instruction < end)
+  {
+    opiptr = false;
+    opf32 = false;
+
+    int addr = instruction - start;
+
+    switch(*instruction)
+    {
+      case BC_NOP : cp = "nop"; break;
+      case BC_LINE : cp = "line"; break;
+
+      case BC_GETDOT : cp = "get dot"; opiptr = true; break;
+      case BC_SETDOT : cp = "set dot"; opiptr = true; break;
+      case BC_GETIND : cp = "get index"; break;
+      case BC_SETIND : cp = "set index"; break;
+
+      case BC_BRA : cp = "bra"; opiptr = true; break;
+      case BC_BRZ : cp = "brz"; opiptr = true; break;
+      case BC_BRNZ : cp = "brnz"; opiptr = true; break;
+      case BC_BRZK : cp = "brzk"; opiptr = true; break;
+      case BC_BRNZK : cp = "brnzk"; opiptr = true; break;
+      case BC_CALL : cp = "call"; opiptr = true; break;
+      case BC_RET : cp = "ret"; break;
+      case BC_RETV : cp = "retv"; break;
+      case BC_FOREACH : cp = "foreach"; opiptr = true; break;
+      
+      case BC_POP : cp = "pop"; break;
+      case BC_POP2 : cp = "pop2"; break;
+      case BC_DUP : cp = "dup"; break;
+      case BC_DUP2 : cp = "dup2"; break;
+      case BC_SWAP : cp = "swap"; break;
+      case BC_PUSHNULL : cp = "push null"; break;
+      case BC_PUSHINT : cp = "push int"; opiptr = true; break;
+      case BC_PUSHINT0 : cp = "push int 0"; break;
+      case BC_PUSHINT1 : cp = "push int 1"; break;
+      case BC_PUSHFP : cp = "push fp"; opf32 = true; break;
+      case BC_PUSHSTR : cp = "push str"; opiptr = true; break;
+      case BC_PUSHTBL : cp = "push tbl"; break;
+      case BC_PUSHFN : cp = "push fn"; opiptr = true; break;
+      case BC_PUSHTHIS : cp = "push this"; break;
+      
+      case BC_GETLOCAL : cp = "get local"; opiptr = true; break;
+      case BC_SETLOCAL : cp = "set local"; opiptr = true; break;
+      case BC_GETGLOBAL : cp = "get global"; opiptr = true; break;
+      case BC_SETGLOBAL : cp = "set global"; opiptr = true; break;
+      case BC_GETTHIS : cp = "get this"; opiptr = true; break;
+      case BC_SETTHIS : cp = "set this"; opiptr = true; break;
+      
+      case BC_OP_ADD : cp = "add"; break;
+      case BC_OP_SUB : cp = "sub"; break;
+      case BC_OP_MUL : cp = "mul"; break;
+      case BC_OP_DIV : cp = "div"; break;
+      case BC_OP_REM : cp = "rem"; break;
+
+      case BC_BIT_OR : cp = "bor"; break;
+      case BC_BIT_XOR : cp = "bxor"; break;
+      case BC_BIT_AND : cp = "band"; break;
+      case BC_BIT_INV : cp = "binv"; break;
+      case BC_BIT_SHL : cp = "bshl"; break;
+      case BC_BIT_SHR : cp = "bshr"; break;
+      
+      case BC_OP_NEG : cp = "neg"; break;
+      case BC_OP_POS : cp = "pos"; break;
+      case BC_OP_NOT : cp = "not"; break;
+      
+      case BC_OP_LT : cp = "lt"; break;
+      case BC_OP_GT : cp = "gt"; break;
+      case BC_OP_LTE : cp = "lte"; break;
+      case BC_OP_GTE : cp = "gte"; break;
+      case BC_OP_EQ : cp = "eq"; break;
+      case BC_OP_NEQ : cp = "neq"; break;
+
+#if GM_USE_FORK
+      case BC_FORK : cp = "fork"; opiptr = true; break;
+#endif //GM_USE_FORK
+
+      default : cp = "ERROR"; break;
+    }
+
+    ++instruction32;
+
+    if(opf32)
+    {
+      float fval = *((float *) instruction);
+      instruction += sizeof(gmint32);
+      fprintf(a_fp, "  %04d %s %f"GM_NL, addr, cp, fval);
+    }
+    else if (opiptr)
+    {
+      gmptr ival = *((gmptr *) instruction);
+      instruction += sizeof(gmptr);
+      fprintf(a_fp, "  %04d %s %d"GM_NL, addr, cp, ival);
+    }
+    else
+    {
+      fprintf(a_fp, "  %04d %s"GM_NL, addr, cp);
+    }
+  }
+}
+
+
+#endif // GM_COMPILE_DEBUG
+

+ 106 - 0
gmsrc/src/gm/gmByteCode.h

@@ -0,0 +1,106 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMBYTECODE_H_
+#define _GMBYTECODE_H_
+
+#include "gmConfig.h"
+
+/// \enum gmByteCode
+/// \brief gmByteCode are the op codes for the game monkey scripting.  The first byte codes MUST match the gmOperator
+///        enum.
+enum gmByteCode
+{
+  // BC_GETDOT to BC_NOP MUST MATCH ENUM GMOPERATOR
+  
+  BC_GETDOT = 0,      // tos '.' opptr, push result
+  BC_SETDOT,          // tos-1 '.' opptr = tos, tos -= 2
+  BC_GETIND,          // tos-1 = tos-1 [tos], --tos
+  BC_SETIND,          // tos-2 [tos-1] = tos, tos -= 3
+
+  // math     
+  BC_OP_ADD,
+  BC_OP_SUB,
+  BC_OP_MUL,
+  BC_OP_DIV,
+  BC_OP_REM,
+
+  // bit
+  BC_BIT_OR,
+  BC_BIT_XOR,
+  BC_BIT_AND,
+  BC_BIT_SHL,
+  BC_BIT_SHR,
+  BC_BIT_INV,
+              
+  // compare   
+  BC_OP_LT,
+  BC_OP_GT,
+  BC_OP_LTE,
+  BC_OP_GTE,
+  BC_OP_EQ,
+  BC_OP_NEQ,
+
+  // unary    
+  BC_OP_NEG,
+  BC_OP_POS,
+  BC_OP_NOT,
+
+  BC_NOP,
+  BC_LINE,            // indicates instruction is on a new code line to the last executed instruction. used in debug mode
+
+  // branch
+  BC_BRA,             // branch always
+  BC_BRZ,             // branch tos equal to zero, --tos
+  BC_BRNZ,            // branch tos not equal to zero, --tos
+  BC_BRZK,            // branch tos equal to zero keep value on stack
+  BC_BRNZK,           // branch tos not equal to zero keep value on stack
+  BC_CALL,            // call op16 num parameters
+  BC_RET,             // return null, ++tos
+  BC_RETV,            // return tos
+  BC_FOREACH,         // op16 op16, table, iterator, leave loop complete bool on stack.
+              
+  // stack    
+  BC_POP,             // --tos
+  BC_POP2,            // tos -=2
+  BC_DUP,             // tos + 1 = tos, ++tos
+  BC_DUP2,            // tos + 1 = tos -1, tos + 2 = tos, tos += 2
+  BC_SWAP,            // 
+  BC_PUSHNULL,        // push null,
+  BC_PUSHINT,         // push int opptr
+  BC_PUSHINT0,        // push 0
+  BC_PUSHINT1,        // push 1
+  BC_PUSHFP,          // push floating point op32
+  BC_PUSHSTR,         // push string opptr
+  BC_PUSHTBL,         // push table
+  BC_PUSHFN,          // push function opptr
+  BC_PUSHTHIS,        // push this
+
+  // get set
+  BC_GETLOCAL,        // get local op16 (stack offset) ++tos
+  BC_SETLOCAL,        // set local op16 (stack offset) --tos
+  BC_GETGLOBAL,       // get global opptr (symbol id) ++tos
+  BC_SETGLOBAL,       // set global opptr (symbol id) --tos
+  BC_GETTHIS,         // get this opptr (symbol id) ++tos
+  BC_SETTHIS,         // set this opptr (symbol id) --tos
+  
+#if GM_USE_FORK
+  BC_FORK,            // Fork
+#endif //GM_USE_FORK  
+};
+
+#if GM_COMPILE_DEBUG
+
+void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength);
+
+#endif // GM_COMPILE_DEBUG
+
+#endif

+ 159 - 0
gmsrc/src/gm/gmByteCodeGen.cpp

@@ -0,0 +1,159 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmByteCodeGen.h"
+
+
+
+gmByteCodeGen::gmByteCodeGen(void * a_context)
+{
+  m_tos = 0;
+  m_maxTos = 0;
+  m_emitCallback = NULL;
+  m_context = a_context;
+}
+
+
+
+void gmByteCodeGen::Reset(void * a_context)
+{
+  gmStreamBufferDynamic::Reset();
+  m_tos = 0;
+  m_maxTos = 0;
+  m_emitCallback = NULL;
+  m_context = a_context;
+}
+
+
+
+bool gmByteCodeGen::Emit(gmByteCode a_instruction)
+{
+  if(m_emitCallback) m_emitCallback(Tell(), m_context);
+  AdjustStack(a_instruction);
+  *this << (gmuint32) a_instruction;
+  return true;
+}
+
+
+
+bool gmByteCodeGen::Emit(gmByteCode a_instruction, gmuint32 a_operand32)
+{
+  if(m_emitCallback) m_emitCallback(Tell(), m_context);
+  AdjustStack(a_instruction);
+  *this << (gmuint32) a_instruction;
+  *this << a_operand32;
+  return true;
+}
+
+
+
+bool gmByteCodeGen::EmitPtr(gmByteCode a_instruction, gmptr a_operand)
+{
+  if(m_emitCallback) m_emitCallback(Tell(), m_context);
+  AdjustStack(a_instruction);
+  *this << ((gmuint32) a_instruction);
+  *this << a_operand;
+  return true;
+}
+
+
+unsigned int gmByteCodeGen::Skip(unsigned int p_n, unsigned char p_value)
+{
+  unsigned int oldPos = Tell();
+  if(p_n)
+  {
+    char * fill = (char *) alloca(p_n);
+    memset(fill, p_value, p_n);
+    Write(fill, p_n);
+  }
+  return oldPos;
+}
+
+
+
+void gmByteCodeGen::AdjustStack(gmByteCode a_instruction)
+{
+  switch(a_instruction)
+  {
+    case BC_NOP : break;
+    case BC_LINE : break;
+
+    case BC_GETDOT : m_tos += 0; break;
+    case BC_SETDOT : m_tos -= 2; break;
+    case BC_GETIND : --m_tos; break;
+    case BC_SETIND : m_tos -= 3; break;
+
+    case BC_BRA : break;
+    case BC_BRZ : --m_tos; break;
+    case BC_BRNZ : --m_tos; break;
+    case BC_BRZK : break;
+    case BC_BRNZK : break;
+    case BC_CALL : break;
+    case BC_RET : break;
+    case BC_RETV : break;
+    case BC_FOREACH : ++m_tos; break;
+  
+    case BC_POP : --m_tos; break;
+    case BC_POP2 : m_tos -= 2; break;
+    case BC_DUP : ++m_tos; break;
+    case BC_DUP2 : m_tos += 2; break;
+    case BC_SWAP : break;
+    case BC_PUSHNULL : ++m_tos; break;
+    case BC_PUSHINT : ++m_tos; break;
+    case BC_PUSHINT0 : ++m_tos; break;
+    case BC_PUSHINT1 : ++m_tos; break;
+    case BC_PUSHFP : ++m_tos; break;
+    case BC_PUSHSTR : ++m_tos; break;
+    case BC_PUSHTBL : ++m_tos; break;
+    case BC_PUSHFN : ++m_tos; break;
+    case BC_PUSHTHIS : ++m_tos; break;
+  
+    case BC_GETLOCAL : ++m_tos; break;
+    case BC_SETLOCAL : --m_tos; break;
+    case BC_GETGLOBAL : ++m_tos; break;
+    case BC_SETGLOBAL : --m_tos; break;
+    case BC_GETTHIS : ++m_tos; break;
+    case BC_SETTHIS : --m_tos; break;
+  
+    case BC_OP_ADD : --m_tos; break;
+    case BC_OP_SUB : --m_tos; break;
+    case BC_OP_MUL : --m_tos; break;
+    case BC_OP_DIV : --m_tos; break;
+    case BC_OP_REM : --m_tos; break;
+
+    case BC_BIT_OR : --m_tos; break;
+    case BC_BIT_XOR : --m_tos; break;
+    case BC_BIT_AND : --m_tos; break;
+    case BC_BIT_INV : --m_tos; break;
+    case BC_BIT_SHL : --m_tos; break;
+    case BC_BIT_SHR : --m_tos; break;
+  
+    case BC_OP_NEG : break;
+    case BC_OP_POS : break;
+    case BC_OP_NOT : break;
+  
+    case BC_OP_LT : --m_tos; break;
+    case BC_OP_GT : --m_tos; break;
+    case BC_OP_LTE : --m_tos; break;
+    case BC_OP_GTE : --m_tos; break;
+    case BC_OP_EQ : --m_tos; break;
+    case BC_OP_NEQ : --m_tos; break;
+
+#if GM_USE_FORK
+    case BC_FORK : m_tos += 2; break; // two variables are popped as a result of BC_FORK (one in each thread)
+#endif //GM_USE_FORK
+  }
+
+  if(m_tos > m_maxTos) m_maxTos = m_tos;
+}
+
+

+ 49 - 0
gmsrc/src/gm/gmByteCodeGen.h

@@ -0,0 +1,49 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+*/
+
+#ifndef _GMBYTECODEGEN_H_
+#define _GMBYTECODEGEN_H_
+
+#include "gmConfig.h"
+#include "gmStreamBuffer.h"
+#include "gmByteCode.h"
+
+/// \class gmByteCodeBuffer
+class gmByteCodeGen : public gmStreamBufferDynamic
+{
+public:
+  gmByteCodeGen(void * a_context = NULL);
+  virtual ~gmByteCodeGen() {}
+
+  void Reset(void * a_context = NULL);
+
+  bool Emit(gmByteCode a_instruction);
+  bool Emit(gmByteCode a_instruction, gmuint32 a_operand32);
+  bool EmitPtr(gmByteCode a_instruction, gmptr a_operand);
+
+  unsigned int Skip(unsigned int p_n, unsigned char p_value = 0);
+
+  /// \brief m_emitCallback will be called whenever code is emitted
+  void (GM_CDECL *m_emitCallback)(int a_address, void * a_context);
+
+  inline int GetMaxTos() const { return m_maxTos; }
+  inline int GetTos() const { return m_tos; }
+  inline void SetTos(int a_tos) { m_tos = a_tos; }
+
+protected:
+
+  void AdjustStack(gmByteCode a_instruction);
+
+  int m_tos;
+  int m_maxTos;
+  void * m_context;
+};
+
+#endif // _GMBYTECODEGEN_H_

+ 1595 - 0
gmsrc/src/gm/gmCodeGen.cpp

@@ -0,0 +1,1595 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmCodeGen.h"
+#include "gmCodeTree.h"
+#include "gmByteCodeGen.h"
+#include "gmArraySimple.h"
+#include "gmListDouble.h"
+
+
+//static const char * s_tempVarName0 = "__t0"; // Currently not used
+static const char * s_tempVarName1 = "__t1";
+
+#define SIZEOF_BC_BRA   8
+
+/// \brief gmSortDebugLines will sort debug line information
+static void gmSortDebugLines(gmArraySimple<gmLineInfo> &a_lineInfo)
+{
+  int count = a_lineInfo.Count();
+
+  // sort by address
+  int i;
+  for(i = 0; i < count; ++i)
+  {
+    int min = i, j;
+    for(j = i + 1; j < count; ++j)
+    {
+      if(a_lineInfo[j].m_address < a_lineInfo[min].m_address) 
+      {
+        min = j;
+      }
+    }
+    gmLineInfo t = a_lineInfo[min];
+    a_lineInfo[min] = a_lineInfo[i];
+    a_lineInfo[i] = t;
+  }
+
+  // remove duplicate line numbers
+  int s, d;
+  for(s = 1, d = 0; s < count; ++s)
+  {
+    if(a_lineInfo[s].m_lineNumber != a_lineInfo[d].m_lineNumber)
+    {
+      a_lineInfo[++d] = a_lineInfo[s];
+    }
+  }
+
+  a_lineInfo.SetCount(++d);
+}
+
+
+/*!
+  \class gmCodeGenPrivate
+  \brief implementation of gmCodeGen
+*/
+class gmCodeGenPrivate : public gmCodeGen
+{
+public:
+
+  gmCodeGenPrivate();
+  virtual ~gmCodeGenPrivate();
+
+  // implementation
+
+  virtual void FreeMemory();
+  virtual int Lock(const gmCodeTreeNode * a_codeTree, gmCodeGenHooks * a_hooks, bool a_debug, gmLog * a_log);
+  virtual int Unlock();
+
+  // helpers
+
+  bool Generate(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode, bool a_siblings = true);
+  bool GenDeclVariable(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprFunction(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprTable(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+#if GM_USE_FORK
+  bool GenStmtFork(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+#endif //GM_USE_FORK
+  bool GenStmtReturn(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtBreak(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtContinue(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtFor(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtForEach(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtWhile(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtDoWhile(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtIf(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenStmtCompound(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpDot(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpUnary(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpArrayIndex(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpAr(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpShift(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpComparison(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpBitwise(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpAnd(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpOr(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprOpAssign(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprConstant(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprIdentifier(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprCall(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+  bool GenExprThis(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode);
+
+  bool m_locked;
+  int m_errors;
+  gmLog * m_log;
+  gmCodeGenHooks * m_hooks;
+  bool m_debug;
+
+  // Variable
+  struct Variable
+  {
+    int m_offset;
+    gmCodeTreeVariableType m_type;
+    const char * m_symbol;
+  };
+
+  // FunctionState
+  class FunctionState : public gmListDoubleNode<FunctionState>
+  {
+  public:
+    FunctionState();
+    ~FunctionState() {}
+
+    void Reset();
+    int SetVariableType(const char * a_symbol, gmCodeTreeVariableType a_type);
+    // return -2 if the variable does not exist, -1 if it exists but is not a local
+    // set a type to var type if return >= -1
+    int GetVariableOffset(const char * a_symbol, gmCodeTreeVariableType &a_type);
+
+    const char * m_debugName; // name of the variable the function is assigned to.
+    gmArraySimple<Variable> m_variables;
+    int m_numLocals; // number of local variables including parameters.
+    gmByteCodeGen m_byteCode;
+
+    // line number debug.
+    int m_currentLine;
+    gmArraySimple<gmLineInfo> m_lineInfo;
+  };
+
+  // Patch
+  struct Patch
+  {
+    gmuint32 m_address;
+    int m_next;
+  };
+
+  // LoopInfo
+  struct LoopInfo
+  {
+    int m_breaks;
+    int m_continues;
+  };
+
+  int m_currentLoop; //!< loop top of stack
+  FunctionState * m_currentFunction; //!< function top of stack
+
+  gmListDouble<FunctionState> m_functionStack;
+  gmArraySimple<LoopInfo> m_loopStack;
+  gmArraySimple<Patch> m_patches;
+
+  
+  // helper functions
+  FunctionState * PushFunction();
+  FunctionState * PopFunction();
+  void PushLoop();
+  void PopLoop();
+  void ApplyPatches(int a_patches, gmByteCodeGen * a_byteCode, gmuint32 a_value);
+};
+
+
+//
+// gmLineNumberCallback is used to record byte code instruction addresses against source code line numbers.
+// The callback records entries into 
+//
+void GM_CDECL gmLineNumberCallback(int a_address, void * a_context)
+{
+  gmCodeGenPrivate::FunctionState * state = (gmCodeGenPrivate::FunctionState *) a_context;
+  gmLineInfo info, * lastEntry = NULL;
+  info.m_address = a_address;
+  info.m_lineNumber = state->m_currentLine;
+  if(state->m_lineInfo.Count() > 0)
+  {
+    lastEntry = &state->m_lineInfo[state->m_lineInfo.Count() - 1];
+  }
+  if(lastEntry == NULL || (lastEntry->m_address != a_address) || (lastEntry->m_lineNumber != state->m_currentLine))
+  {
+    state->m_lineInfo.InsertLast(info);
+  }
+}
+
+
+
+gmCodeGen& gmCodeGen::Get()
+{
+  static gmCodeGenPrivate codeGen;
+  return codeGen;
+}
+
+
+
+//
+//
+// Implementation of gmCodeGenPrivate
+//
+//
+
+gmCodeGenPrivate::gmCodeGenPrivate()
+{
+  m_locked = false;
+  m_errors = 0;
+  m_log = NULL;
+  m_hooks = NULL;
+  m_debug = false;
+
+  m_currentLoop = -1;
+  m_currentFunction = NULL;
+}
+
+
+
+gmCodeGenPrivate::~gmCodeGenPrivate()
+{
+  FreeMemory();
+}
+
+
+
+void gmCodeGenPrivate::FreeMemory()
+{
+  if(m_locked == false)
+  {
+    m_currentLoop = -1;
+    m_currentFunction = NULL;
+    m_loopStack.ResetAndFreeMemory();
+    m_functionStack.RemoveAndDeleteAll();
+    m_patches.ResetAndFreeMemory();
+  }
+}
+
+
+
+int gmCodeGenPrivate::Lock(const gmCodeTreeNode * a_codeTree, gmCodeGenHooks * a_hooks, bool a_debug, gmLog * a_log)
+{
+  if(m_locked == true) return 1;
+
+  // set up members
+  m_errors = 0;
+  m_locked = true;
+  m_log = a_log;
+  m_hooks = a_hooks;
+  m_debug = a_debug;
+
+  GM_ASSERT(m_hooks != NULL);
+
+  // set up memory and stacks.
+  m_currentLoop = -1;
+  m_currentFunction = NULL;
+  m_loopStack.Reset();
+  m_patches.Reset();
+
+  // set up the stacks for the first procedure.
+  m_hooks->Begin(m_debug);
+
+  PushFunction();
+  GM_ASSERT(m_currentFunction);
+
+  // generate the byte code for the root procedure
+  if(!Generate(a_codeTree, &m_currentFunction->m_byteCode))
+  {
+    ++m_errors;
+  }
+  else
+  {
+    m_currentFunction->m_byteCode.Emit(BC_RET);
+
+    // Create a locals table
+    const char ** locals = NULL;
+    if(m_debug)
+    {
+      locals = (const char **) alloca(sizeof(const char *) * m_currentFunction->m_numLocals);
+      memset(locals, 0, sizeof(const char *) * m_currentFunction->m_numLocals);
+      for(gmuint v = 0; v < m_currentFunction->m_variables.Count(); ++v)
+      {
+        Variable &variable = m_currentFunction->m_variables[v];
+        if(variable.m_offset != -1)
+        {
+          locals[variable.m_offset] = variable.m_symbol;
+        }
+      }
+    }
+    
+    // Fill out a function info struct and add the function to the code gen hooks.
+
+    gmSortDebugLines(m_currentFunction->m_lineInfo);
+
+    gmFunctionInfo info;
+    info.m_id = m_hooks->GetFunctionId();
+    info.m_root = true;
+    info.m_byteCode = m_currentFunction->m_byteCode.GetData();
+    info.m_byteCodeLength = m_currentFunction->m_byteCode.Tell();
+    info.m_numParams = 0;
+    info.m_numLocals = m_currentFunction->m_numLocals;
+    info.m_symbols = locals;
+    info.m_maxStackSize = m_currentFunction->m_byteCode.GetMaxTos();
+    info.m_lineInfoCount = m_currentFunction->m_lineInfo.Count();
+    info.m_lineInfo = m_currentFunction->m_lineInfo.GetData();
+    info.m_debugName = "__main";
+    m_hooks->AddFunction(info);
+
+    //gmByteCodePrint(stdout, info.m_byteCode, info.m_byteCodeLength);
+  }
+
+  PopFunction();
+
+  m_hooks->End(m_errors);
+
+  return m_errors;
+}
+
+
+
+int gmCodeGenPrivate::Unlock()
+{
+  m_errors = 0;
+  m_locked = false;
+  m_log = NULL;
+  m_hooks = NULL;
+  m_debug = false;
+  m_currentLoop = -1;
+  m_loopStack.Reset();
+  m_patches.Reset();
+  return 0;
+}
+
+
+
+bool gmCodeGenPrivate::Generate(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode, bool a_siblings)
+{
+  bool res = true;
+
+  while(a_node)
+  {
+    // record line number
+    static int s_line = 0;
+    if(m_currentFunction) m_currentFunction->m_currentLine = a_node->m_lineNumber;
+
+    // if we are in debug, emit a BC_LINE instruction
+    if(m_debug && (s_line != a_node->m_lineNumber) && 
+       !(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_COMPOUND))
+    {
+      a_byteCode->Emit(BC_LINE);
+      s_line = a_node->m_lineNumber;
+    }
+
+    switch(a_node->m_type)
+    {
+      case CTNT_DECLARATION :
+      {
+        switch(a_node->m_subType)
+        {
+          case CTNDT_VARIABLE : res = GenDeclVariable(a_node, a_byteCode); break;
+          default: 
+          {
+            GM_ASSERT(false);
+            return false;
+          }
+        }
+        break;
+      }
+      case CTNT_STATEMENT :
+      {
+        switch(a_node->m_subType)
+        {
+          case CTNST_RETURN : res = GenStmtReturn(a_node, a_byteCode); break;
+          case CTNST_BREAK : res = GenStmtBreak(a_node, a_byteCode); break;
+          case CTNST_CONTINUE : res = GenStmtContinue(a_node, a_byteCode); break;
+          case CTNST_FOR : res = GenStmtFor(a_node, a_byteCode); break;
+          case CTNST_FOREACH : res = GenStmtForEach(a_node, a_byteCode); break;
+          case CTNST_WHILE : res = GenStmtWhile(a_node, a_byteCode); break;
+          case CTNST_DOWHILE : res = GenStmtDoWhile(a_node, a_byteCode); break;
+          case CTNST_IF : res = GenStmtIf(a_node, a_byteCode); break;
+          case CTNST_COMPOUND : res = GenStmtCompound(a_node, a_byteCode); break;
+#if GM_USE_FORK
+          case CTNST_FORK : res = GenStmtFork(a_node, a_byteCode); break;
+#else //GM_USE_FORK
+          case CTNST_FORK: // Unsupported, but tokens exist
+          {
+            if(m_log && m_currentFunction)
+            {
+              m_log->LogEntry("error (%d) 'fork' instruction not supported", m_currentFunction->m_currentLine);
+            }
+            return false;
+          }
+#endif //GM_USE_FORK
+          default: 
+          {
+            GM_ASSERT(false);
+            return false;
+          }
+        }
+        break;
+      }
+      case CTNT_EXPRESSION : 
+      {
+        switch(a_node->m_subType)
+        {
+          case CTNET_OPERATION : 
+          {
+            switch(a_node->m_subTypeType)
+            {
+              case CTNOT_DOT :              res = GenExprOpDot(a_node, a_byteCode); break;
+              case CTNOT_UNARY_PLUS :
+              case CTNOT_UNARY_MINUS :
+              case CTNOT_UNARY_COMPLEMENT :
+              case CTNOT_UNARY_NOT : res = GenExprOpUnary(a_node, a_byteCode); break;
+              case CTNOT_ARRAY_INDEX :      res = GenExprOpArrayIndex(a_node, a_byteCode); break;
+              case CTNOT_TIMES :
+              case CTNOT_DIVIDE :
+              case CTNOT_REM :
+              case CTNOT_MINUS :
+              case CTNOT_ADD :              res = GenExprOpAr(a_node, a_byteCode); break;
+              case CTNOT_SHIFT_LEFT :
+              case CTNOT_SHIFT_RIGHT :      res = GenExprOpShift(a_node, a_byteCode); break;
+              case CTNOT_LT :
+              case CTNOT_GT :
+              case CTNOT_LTE :
+              case CTNOT_GTE :
+              case CTNOT_EQ :
+              case CTNOT_NEQ :              res = GenExprOpComparison(a_node, a_byteCode); break;
+              case CTNOT_BIT_AND :
+              case CTNOT_BIT_XOR :
+              case CTNOT_BIT_OR :           res = GenExprOpBitwise(a_node, a_byteCode); break;
+              case CTNOT_AND :              res = GenExprOpAnd(a_node, a_byteCode); break;
+              case CTNOT_OR :               res = GenExprOpOr(a_node, a_byteCode); break;
+              case CTNOT_ASSIGN :           res = GenExprOpAssign(a_node, a_byteCode); break;
+              default:
+              {
+                GM_ASSERT(false);
+                return false;
+              }
+            }
+            break;
+          }
+          case CTNET_CONSTANT : res = GenExprConstant(a_node, a_byteCode); break;
+          case CTNET_IDENTIFIER : res = GenExprIdentifier(a_node, a_byteCode); break;
+          case CTNET_CALL : res = GenExprCall(a_node, a_byteCode); break;
+          case CTNET_THIS : res = GenExprThis(a_node, a_byteCode); break;
+          case CTNET_FUNCTION : res = GenExprFunction(a_node, a_byteCode); break;
+          case CTNET_TABLE : res = GenExprTable(a_node, a_byteCode); break;
+          default :
+          {
+            GM_ASSERT(false);
+            return false;
+          }
+        }
+
+        break;
+      }
+      default: 
+      {
+        GM_ASSERT(false);
+        return false;
+      }
+    }
+
+    if(!res) 
+    {
+      return false;
+    }
+
+    if((a_node->m_flags & gmCodeTreeNode::CTN_POP) > 0)
+    {
+      a_byteCode->Emit(BC_POP);
+    }
+
+    if(a_siblings)
+    {
+      a_node = a_node->m_sibling;
+    }
+    else a_node = NULL;
+  }
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenDeclVariable(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_DECLARATION && a_node->m_subType == CTNDT_VARIABLE);
+  GM_ASSERT(m_currentFunction);
+  m_currentFunction->SetVariableType(a_node->m_children[0]->m_data.m_string, (gmCodeTreeVariableType) a_node->m_subTypeType);
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprFunction(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_FUNCTION);
+
+  gmptr id = m_hooks->GetFunctionId();
+  a_byteCode->EmitPtr(BC_PUSHFN, id);
+
+  // Create the function
+  PushFunction();
+
+  // Get a debug function name as the name of the variable the function is assigned to
+  if(m_debug && a_node->m_parent && a_node->m_parent->m_type == CTNT_EXPRESSION && a_node->m_parent->m_subType == CTNET_OPERATION &&
+     (a_node->m_parent->m_subTypeType == CTNOT_ASSIGN || a_node->m_parent->m_subTypeType == CTNOT_ASSIGN_FIELD) && a_node->m_parent->m_children[1] == a_node)
+  {
+    const gmCodeTreeNode * debugName = a_node->m_parent->m_children[0];
+    if(debugName && debugName->m_type == CTNT_EXPRESSION && debugName->m_subType == CTNET_IDENTIFIER)
+    {
+    }
+    else if(debugName->m_type == CTNT_EXPRESSION && debugName->m_subType == CTNET_OPERATION && 
+            debugName->m_subTypeType == CTNOT_DOT)
+    {
+      debugName = debugName->m_children[1];
+    }
+    else
+    {
+      debugName = NULL;
+    }
+
+    if(debugName)
+    {
+      GM_ASSERT(debugName->m_type == CTNT_EXPRESSION && debugName->m_subType == CTNET_IDENTIFIER);
+      m_currentFunction->m_debugName = debugName->m_data.m_string;
+    }
+  }
+
+  // Parameters
+  const gmCodeTreeNode * params = a_node->m_children[0];
+  int numParams = 0;
+  while(params)
+  {
+    const gmCodeTreeNode * param = params->m_children[0];
+    GM_ASSERT(param->m_type == CTNT_EXPRESSION && param->m_subType == CTNET_IDENTIFIER);
+    if(m_currentFunction->SetVariableType(param->m_data.m_string, CTVT_LOCAL) != numParams)
+    {
+      if(m_log) m_log->LogEntry("error (%d) parameter %s already declared", param->m_lineNumber, param->m_data.m_string);
+      PopFunction();
+      return false;
+    }
+    ++numParams;
+    params = params->m_sibling;
+  }
+
+  // Generate the code
+  bool res = Generate(a_node->m_children[1], &m_currentFunction->m_byteCode);
+
+  // Generate a return incase the function didnt have one.
+  m_currentFunction->m_byteCode.Emit(BC_RET);
+
+  if(res)
+  {
+    // Create a locals table
+    const char ** locals = NULL;
+    if(m_debug)
+    {
+      locals = (const char **) alloca(sizeof(const char *) * m_currentFunction->m_numLocals);
+      memset(locals, 0, sizeof(const char *) * m_currentFunction->m_numLocals);
+
+      for(gmuint v = 0; v < m_currentFunction->m_variables.Count(); ++v)
+      {
+        Variable &variable = m_currentFunction->m_variables[v];
+        if(variable.m_offset != -1)
+        {
+          locals[variable.m_offset] = variable.m_symbol;
+        }
+      }
+    }
+
+    // Add the function to the hooks.
+
+    gmSortDebugLines(m_currentFunction->m_lineInfo);
+    
+    gmFunctionInfo info;
+    info.m_id = id;
+    info.m_root = false;
+    info.m_byteCode = m_currentFunction->m_byteCode.GetData();
+    info.m_byteCodeLength = m_currentFunction->m_byteCode.Tell();
+    info.m_numParams = numParams;
+    info.m_numLocals = m_currentFunction->m_numLocals - numParams;
+    info.m_symbols = locals;
+    info.m_maxStackSize = m_currentFunction->m_byteCode.GetMaxTos();
+    info.m_lineInfoCount = m_currentFunction->m_lineInfo.Count();
+    info.m_lineInfo = m_currentFunction->m_lineInfo.GetData();
+    info.m_debugName = m_currentFunction->m_debugName;
+    m_hooks->AddFunction(info);
+
+    //gmByteCodePrint(stdout, info.m_byteCode, info.m_byteCodeLength);
+  }
+
+  PopFunction();
+  
+  return res;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprTable(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_TABLE);
+
+  gmuint32 index = 0;
+  const gmCodeTreeNode * fields = a_node->m_children[0];
+
+  // Create table
+  a_byteCode->Emit(BC_PUSHTBL);
+
+  // Create fields
+  while(fields)
+  {
+    a_byteCode->Emit(BC_DUP);
+    
+    if(fields->m_type == CTNT_EXPRESSION && fields->m_subType == CTNET_OPERATION && fields->m_subTypeType == CTNOT_ASSIGN_FIELD)
+    {
+      if(!Generate(fields->m_children[1], a_byteCode)) return false;
+      a_byteCode->EmitPtr(BC_SETDOT, m_hooks->GetSymbolId(fields->m_children[0]->m_data.m_string));
+    }
+    else
+    {
+      a_byteCode->EmitPtr(BC_PUSHINT, index++);
+      if(!Generate(fields, a_byteCode, false)) return false;
+      a_byteCode->Emit(BC_SETIND);
+    }
+
+    fields = fields->m_sibling;
+  }
+
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtReturn(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_RETURN);
+
+  if(a_node->m_children[0])
+  {
+    if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+    return a_byteCode->Emit(BC_RETV);
+  }
+  return a_byteCode->Emit(BC_RET);
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtBreak(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_BREAK);
+
+  if(m_currentLoop >= 0)
+  {
+    a_byteCode->Emit(BC_BRA);
+    Patch * patch = &m_patches.InsertLast();
+    patch->m_address = a_byteCode->Skip(sizeof(gmuint32));
+    patch->m_next = m_loopStack[m_currentLoop].m_breaks;
+    m_loopStack[m_currentLoop].m_breaks = m_patches.Count()-1;
+    return true;
+  }
+
+  if(m_log) m_log->LogEntry("error (%d) illegal break statement", a_node->m_lineNumber);
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtContinue(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_CONTINUE);
+
+  if(m_currentLoop >= 0)
+  {
+    a_byteCode->Emit(BC_BRA);
+    Patch * patch = &m_patches.InsertLast();
+    patch->m_address = a_byteCode->Skip(sizeof(gmuint32));
+    patch->m_next = m_loopStack[m_currentLoop].m_continues;
+    m_loopStack[m_currentLoop].m_continues = m_patches.Count()-1;
+    return true;
+  }
+
+  if(m_log) m_log->LogEntry("error (%d) illegal continue statement", a_node->m_lineNumber);
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtFor(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_FOR);
+
+  unsigned int loc1, loc2 = 0, continueAddress;
+
+  // Initialisers
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+
+  PushLoop();
+
+  loc1 = a_byteCode->Tell();
+
+  // Condition expression
+  if(!Generate(a_node->m_children[1], a_byteCode))
+  {
+    PopLoop();
+    return false;
+  }
+  if(a_node->m_children[1] != NULL) // no branch for no test.
+  {
+    loc2 = a_byteCode->Skip(SIZEOF_BC_BRA);
+  }
+
+  // Body
+  if(!Generate(a_node->m_children[3], a_byteCode))
+  {
+    PopLoop();
+    return false;
+  }
+
+  // Continue patch
+  continueAddress = a_byteCode->Tell();
+
+  // Loop Expression
+  if(!Generate(a_node->m_children[2], a_byteCode))
+  {
+    PopLoop();
+    return false;
+  }
+
+  a_byteCode->EmitPtr(BC_BRA, loc1);
+  loc1 = a_byteCode->Tell();
+  if(a_node->m_children[1] != NULL)
+  {
+    a_byteCode->Seek(loc2);
+    a_byteCode->EmitPtr(BC_BRZ, loc1);
+    a_byteCode->Seek(loc1);
+  }
+
+  ApplyPatches(m_loopStack[m_currentLoop].m_breaks, a_byteCode, loc1);
+  ApplyPatches(m_loopStack[m_currentLoop].m_continues, a_byteCode, continueAddress);
+
+  PopLoop();
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtForEach(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  unsigned int breakAddress, continueAddress, loc1, loc2;
+
+  // Generate table
+  if(!Generate(a_node->m_children[0], a_byteCode))
+  {
+    return false;
+  }
+
+  PushLoop();
+
+  // Push the first iterator
+  a_byteCode->Emit(BC_PUSHINT, (gmuint32) -2); // first iterator value.
+
+  continueAddress = a_byteCode->Tell();
+
+  // Generate call
+  const char * keyVar = s_tempVarName1;
+  if(a_node->m_children[2]) keyVar = a_node->m_children[2]->m_data.m_string;
+  const char * valueVar = a_node->m_children[1]->m_data.m_string;
+
+  gmuint16 keyOffset = (gmuint16) m_currentFunction->SetVariableType(keyVar, CTVT_LOCAL);
+  gmuint16 valueOffset = (gmuint16) m_currentFunction->SetVariableType(valueVar, CTVT_LOCAL);
+  gmuint32 opcode = (keyOffset << 16) | (valueOffset & 0xffff);
+
+  loc1 = a_byteCode->Tell();
+  a_byteCode->Emit(BC_FOREACH, opcode);
+
+  // Skip space for jump
+  loc2 = a_byteCode->Skip(SIZEOF_BC_BRA);
+
+  // Generate body
+  if(!Generate(a_node->m_children[3], a_byteCode))
+  {
+    PopLoop();
+    return false;
+  }
+
+  a_byteCode->Emit(BC_BRA, (gmuint32) loc1);
+  breakAddress = a_byteCode->Seek(loc2);
+  a_byteCode->EmitPtr(BC_BRZ, breakAddress);
+  a_byteCode->Seek(breakAddress);
+
+  // pop table and iterator
+  a_byteCode->Emit(BC_POP2);
+
+  ApplyPatches(m_loopStack[m_currentLoop].m_breaks, a_byteCode, breakAddress);
+  ApplyPatches(m_loopStack[m_currentLoop].m_continues, a_byteCode, continueAddress);
+
+  PopLoop();
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtWhile(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_WHILE);
+  
+  unsigned int loc1, loc2, continueAddress; 
+
+  PushLoop();
+
+  // Continue address
+  loc1 = continueAddress = a_byteCode->Tell();
+
+  // Condition expression
+  if(!Generate(a_node->m_children[0], a_byteCode)) 
+  {
+    PopLoop();
+    return false;
+  }
+
+  loc2 = a_byteCode->Skip(SIZEOF_BC_BRA);
+
+  // Loop body
+  if(!Generate(a_node->m_children[1], a_byteCode)) 
+  {
+    PopLoop();
+    return false;
+  }
+  
+  a_byteCode->EmitPtr(BC_BRA, loc1);
+  loc1 = a_byteCode->Seek(loc2);
+  a_byteCode->EmitPtr(BC_BRZ, loc1);
+  a_byteCode->Seek(loc1);
+
+  ApplyPatches(m_loopStack[m_currentLoop].m_breaks, a_byteCode, loc1);
+  ApplyPatches(m_loopStack[m_currentLoop].m_continues, a_byteCode, continueAddress);
+
+  PopLoop();
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtDoWhile(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_DOWHILE);
+  
+  unsigned int loc1, continueAddress; 
+
+  PushLoop();
+
+  loc1 = a_byteCode->Tell();
+
+  // Loop body
+  if(!Generate(a_node->m_children[1], a_byteCode)) 
+  {
+    PopLoop();
+    return false;
+  }
+
+  // Continue address
+  continueAddress = a_byteCode->Tell();
+
+  // Condition expression
+  if(!Generate(a_node->m_children[0], a_byteCode)) 
+  {
+    PopLoop();
+    return false;
+  }
+
+  a_byteCode->EmitPtr(BC_BRNZ, loc1);
+
+  loc1 = a_byteCode->Tell();
+
+  ApplyPatches(m_loopStack[m_currentLoop].m_breaks, a_byteCode, loc1);
+  ApplyPatches(m_loopStack[m_currentLoop].m_continues, a_byteCode, continueAddress);
+
+  PopLoop();
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenStmtIf(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_IF);
+
+  unsigned int loc1, loc2, loc3;
+  
+  if(a_node->m_children[2]) // Is this an if-else, or just an if
+  {
+    if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+    loc1 = a_byteCode->Skip(SIZEOF_BC_BRA);
+    if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+    loc2 = a_byteCode->Skip(SIZEOF_BC_BRA);
+    if(!Generate(a_node->m_children[2], a_byteCode)) return false;
+    loc3 = a_byteCode->Seek(loc1);
+    a_byteCode->EmitPtr(BC_BRZ, loc2+SIZEOF_BC_BRA);
+    a_byteCode->Seek(loc2);
+    a_byteCode->EmitPtr(BC_BRA, loc3);
+    a_byteCode->Seek(loc3);
+  }
+  else
+  {
+    if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+    loc1 = a_byteCode->Skip(SIZEOF_BC_BRA);
+    if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+    loc2 = a_byteCode->Seek(loc1);
+    m_currentFunction->m_currentLine = a_node->m_lineNumber;
+    a_byteCode->EmitPtr(BC_BRZ, loc2);
+    a_byteCode->Seek(loc2);
+  }
+
+  return true;
+}
+
+
+#if GM_USE_FORK
+bool gmCodeGenPrivate::GenStmtFork(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+   GM_ASSERT(a_node->m_type == CTNT_STATEMENT && a_node->m_subType == CTNST_FORK );
+   
+   gmuint32 loc1,loc2;
+
+   // create the var for the thread id
+   const char * valname = 0;
+   gmuint32 valref = 0;
+   if ( a_node->m_children[1])
+   {
+      valname = a_node->m_children[1]->m_data.m_string;
+      valref = m_currentFunction->SetVariableType( valname, CTVT_LOCAL );
+   }
+
+   loc1 = a_byteCode->Skip( SIZEOF_BC_BRA );
+   
+   if (!valname) a_byteCode->Emit( BC_POP );   // if not specified then just pop
+   else a_byteCode->Emit( BC_SETLOCAL, valref );   // store the thread id
+   if (!Generate(a_node->m_children[0], a_byteCode )) return false;
+   a_byteCode->Emit( BC_RET );
+   
+   loc2 = a_byteCode->Seek( loc1 );
+   a_byteCode->Emit( BC_FORK, loc2 );
+   a_byteCode->Seek( loc2 );
+   
+   if (!valname) a_byteCode->Emit( BC_POP );   // if not specified then just pop
+   else a_byteCode->Emit( BC_SETLOCAL, valref );   // store the thread id
+   
+   return true;
+}
+#endif //GM_USE_FORK
+
+
+bool gmCodeGenPrivate::GenStmtCompound(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  return Generate(a_node->m_children[0], a_byteCode);
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpDot(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION && a_node->m_subTypeType == CTNOT_DOT);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+
+  // make sure child 1 is an identifier
+  const gmCodeTreeNode * id = a_node->m_children[1];
+  if(id && id->m_type == CTNT_EXPRESSION && id->m_subType == CTNET_IDENTIFIER)
+  {
+    return a_byteCode->EmitPtr(BC_GETDOT, m_hooks->GetSymbolId(a_node->m_children[1]->m_data.m_string));
+  }
+
+  if(m_log) m_log->LogEntry("error (%d) illegal dot operator", a_node->m_lineNumber);
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpUnary(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNOT_UNARY_PLUS :       return a_byteCode->Emit(BC_OP_POS);
+    case CTNOT_UNARY_MINUS :      return a_byteCode->Emit(BC_OP_NEG);
+    case CTNOT_UNARY_NOT :        return a_byteCode->Emit(BC_OP_NOT);
+    case CTNOT_UNARY_COMPLEMENT : return a_byteCode->Emit(BC_BIT_INV);
+    default :
+    {
+      if(m_log) m_log->LogEntry("error (%d) unkown operator", a_node->m_lineNumber);
+    }
+  }
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpArrayIndex(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION && a_node->m_subTypeType == CTNOT_ARRAY_INDEX);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  return a_byteCode->Emit(BC_GETIND);
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpAr(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNOT_TIMES : return a_byteCode->Emit(BC_OP_MUL);
+    case CTNOT_DIVIDE : return a_byteCode->Emit(BC_OP_DIV);
+    case CTNOT_REM : return a_byteCode->Emit(BC_OP_REM);
+    case CTNOT_ADD : return a_byteCode->Emit(BC_OP_ADD);
+    case CTNOT_MINUS : return a_byteCode->Emit(BC_OP_SUB);
+    default :
+    {
+      if(m_log) m_log->LogEntry("error (%d) unkown arithmatic operator", a_node->m_lineNumber);
+    }
+  }
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpShift(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNOT_SHIFT_LEFT :  return a_byteCode->Emit(BC_BIT_SHL);
+    case CTNOT_SHIFT_RIGHT : return a_byteCode->Emit(BC_BIT_SHR);
+    default :
+    {
+      if(m_log) m_log->LogEntry("error (%d) unkown shift operator", a_node->m_lineNumber);
+    }
+  }
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpComparison(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNOT_LT :  return a_byteCode->Emit(BC_OP_LT);
+    case CTNOT_GT :  return a_byteCode->Emit(BC_OP_GT);
+    case CTNOT_LTE : return a_byteCode->Emit(BC_OP_LTE);
+    case CTNOT_GTE : return a_byteCode->Emit(BC_OP_GTE);
+    case CTNOT_EQ :  return a_byteCode->Emit(BC_OP_EQ);
+    case CTNOT_NEQ : return a_byteCode->Emit(BC_OP_NEQ);
+    default :
+    {
+      if(m_log) m_log->LogEntry("error (%d) unkown comparison operator", a_node->m_lineNumber);
+    }
+  }
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpBitwise(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNOT_BIT_AND : return a_byteCode->Emit(BC_BIT_AND);
+    case CTNOT_BIT_XOR : return a_byteCode->Emit(BC_BIT_XOR);
+    case CTNOT_BIT_OR  : return a_byteCode->Emit(BC_BIT_OR);
+    default :
+    {
+      if(m_log) m_log->LogEntry("error (%d) unkown bitwise operator", a_node->m_lineNumber);
+    }
+  }
+  return false;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpAnd(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION && a_node->m_subTypeType == CTNOT_AND);
+
+  unsigned int loc1, loc2;
+
+  // Generate expression 1
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  loc1 = a_byteCode->Skip(SIZEOF_BC_BRA);
+
+  // Generate expression 2
+  a_byteCode->Emit(BC_POP);
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  // Seek back and finish expression 1
+  loc2 = a_byteCode->Seek(loc1);
+  a_byteCode->EmitPtr(BC_BRZK, loc2);
+  a_byteCode->Seek(loc2);
+
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpOr(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION && a_node->m_subTypeType == CTNOT_OR);
+
+  unsigned int loc1, loc2;
+ 
+  // Generate expression 1
+  if(!Generate(a_node->m_children[0], a_byteCode)) return false;
+  loc1 = a_byteCode->Skip(SIZEOF_BC_BRA);
+
+  // Generate expression 2
+  a_byteCode->Emit(BC_POP);
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  // Seek back and finish expression 1
+  loc2 = a_byteCode->Seek(loc1);
+  a_byteCode->EmitPtr(BC_BRNZK, loc2);
+  a_byteCode->Seek(loc2);
+
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprOpAssign(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_OPERATION);
+
+  // value on left hand side must be an l-value... ie, a dot, array or identifier.
+  const gmCodeTreeNode * lValue = a_node->m_children[0];
+  int type = 0;
+
+  if(lValue->m_type == CTNT_EXPRESSION && lValue->m_subType == CTNET_OPERATION && lValue->m_subTypeType == CTNOT_DOT)
+  {
+    // Generate half l-value
+    if(!Generate(lValue->m_children[0], a_byteCode)) return false;
+    type = 0;
+  }
+  else if(lValue->m_type == CTNT_EXPRESSION && lValue->m_subType == CTNET_OPERATION && lValue->m_subTypeType == CTNOT_ARRAY_INDEX)
+  {
+    // Generate half l-value
+    if(!Generate(lValue->m_children[0], a_byteCode)) return false;
+    if(!Generate(lValue->m_children[1], a_byteCode)) return false;
+    type = 1;
+  }
+  else if(lValue->m_type == CTNT_EXPRESSION && lValue->m_subType == CTNET_IDENTIFIER)
+  {
+    type = 2;
+  }
+  else
+  {
+    if(m_log) m_log->LogEntry("error (%d) illegal l-value for '=' operator", a_node->m_lineNumber);
+    return false;
+  }
+
+  // Generate r-value
+  if(!Generate(a_node->m_children[1], a_byteCode)) return false;
+
+  // complete assignment
+  if(type == 0)
+  {
+    a_byteCode->EmitPtr(BC_SETDOT, m_hooks->GetSymbolId(lValue->m_children[1]->m_data.m_string));
+  }
+  else if(type == 1)
+  {
+    a_byteCode->Emit(BC_SETIND);
+  }
+  else if(type == 2)
+  {
+    gmCodeTreeVariableType vtype;
+    int offset = m_currentFunction->GetVariableOffset(lValue->m_data.m_string, vtype);
+
+    // if local, set local regardless
+    // if member set this
+    // if global, set global
+    // set and add local
+
+    if((lValue->m_flags & gmCodeTreeNode::CTN_MEMBER) > 0)
+    {
+      return a_byteCode->EmitPtr(BC_SETTHIS, m_hooks->GetSymbolId(lValue->m_data.m_string));
+    }
+    if(offset >= 0 && vtype == CTVT_LOCAL)
+    {
+      return a_byteCode->Emit(BC_SETLOCAL, (gmuint32) offset);
+    }
+    else if(offset == -1)
+    {
+      if(vtype == CTVT_MEMBER)
+      {
+        return a_byteCode->EmitPtr(BC_SETTHIS, m_hooks->GetSymbolId(lValue->m_data.m_string));
+      }
+      else if(vtype == CTVT_GLOBAL)
+      {
+        return a_byteCode->EmitPtr(BC_SETGLOBAL, m_hooks->GetSymbolId(lValue->m_data.m_string));
+      }
+      if(m_log) m_log->LogEntry("internal error");
+      return false;
+    }
+
+    offset = m_currentFunction->SetVariableType(lValue->m_data.m_string, CTVT_LOCAL);
+    return a_byteCode->Emit(BC_SETLOCAL, (gmuint32) offset);
+  }
+  else
+  {
+    // paranoia
+    if(m_log) m_log->LogEntry("internal error");
+    return false;
+  }
+
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprConstant(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_CONSTANT);
+
+  switch(a_node->m_subTypeType)
+  {
+    case CTNCT_INT : // INT
+    {
+      if(a_node->m_data.m_iValue == 0)
+      {
+        a_byteCode->Emit(BC_PUSHINT0);
+      }
+      else if(a_node->m_data.m_iValue == 1)
+      {
+        a_byteCode->Emit(BC_PUSHINT1);
+      }
+      else
+      {
+        a_byteCode->EmitPtr(BC_PUSHINT, *((gmptr *) &a_node->m_data.m_iValue));
+      }
+      break;
+    }
+    case CTNCT_FLOAT : // FLOAT
+    {
+      a_byteCode->Emit(BC_PUSHFP, *((gmuint32 *) ((void *) &a_node->m_data.m_fValue)));
+      break;
+    }
+    case CTNCT_STRING : // STRING
+    {
+      a_byteCode->EmitPtr(BC_PUSHSTR, m_hooks->GetStringId(a_node->m_data.m_string));
+      break;
+    }
+    case CTNCT_NULL : // NULL
+    {
+      a_byteCode->Emit(BC_PUSHNULL);
+      break;
+    }
+    default:
+    {
+      if(m_log) m_log->LogEntry("unkown constant type");
+      return false;
+    }
+  }
+  
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprIdentifier(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_IDENTIFIER);
+
+  // if local, get local regardless
+  // if member, get this
+  // if global, get global
+  // get global
+
+  if((a_node->m_flags & gmCodeTreeNode::CTN_MEMBER) > 0)
+  {
+    return a_byteCode->EmitPtr(BC_GETTHIS, m_hooks->GetSymbolId(a_node->m_data.m_string));
+  }
+
+  gmCodeTreeVariableType type;
+  int offset = m_currentFunction->GetVariableOffset(a_node->m_data.m_string, type);
+
+  if(offset >= 0 && type == CTVT_LOCAL)
+  {
+    return a_byteCode->Emit(BC_GETLOCAL, (gmuint32) offset);
+  }
+  else if(offset != -2)
+  {
+    if(type == CTVT_MEMBER)
+    {
+      return a_byteCode->EmitPtr(BC_GETTHIS, m_hooks->GetSymbolId(a_node->m_data.m_string));
+    }
+    else if(type == CTVT_GLOBAL)
+    {
+      return a_byteCode->EmitPtr(BC_GETGLOBAL, m_hooks->GetSymbolId(a_node->m_data.m_string));
+    }
+    if(m_log) m_log->LogEntry("internal error");
+    return false;
+  }
+
+  return a_byteCode->EmitPtr(BC_GETGLOBAL, m_hooks->GetSymbolId(a_node->m_data.m_string));
+}
+
+
+
+bool gmCodeGenPrivate::GenExprCall(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_CALL);
+
+  // mark the stack.
+  int stackLevel = a_byteCode->GetTos();
+
+  // if callee is a dot function, push left side of dot as 'this'
+  const gmCodeTreeNode * callee = a_node->m_children[0];
+
+  if(callee->m_type == CTNT_EXPRESSION && callee->m_subType == CTNET_OPERATION && callee->m_subTypeType == CTNOT_DOT)
+  {
+    if(!Generate(callee->m_children[0], a_byteCode)) return false;
+    a_byteCode->Emit(BC_DUP);
+    a_byteCode->EmitPtr(BC_GETDOT, m_hooks->GetSymbolId(callee->m_children[1]->m_data.m_string));
+  }
+  else
+  {
+    if(a_node->m_children[2])
+    {
+      if(!Generate(a_node->m_children[2], a_byteCode)) return false;
+    }
+    else
+    {
+#if GM_COMPILE_PASS_THIS_ALWAYS
+
+      a_byteCode->Emit(BC_PUSHTHIS);
+
+#else // !GM_COMPILE_PASS_THIS_ALWAYS
+
+      // if the lvalue is a member, pass 'this', otherwise pass 'null'
+      bool pushed = false;
+      if(callee->m_type == CTNT_EXPRESSION && callee->m_subType == CTNET_IDENTIFIER)
+      {
+        gmCodeTreeVariableType vtype;
+        int offset = m_currentFunction->GetVariableOffset(callee->m_data.m_string, vtype);
+        if(((callee->m_flags & gmCodeTreeNode::CTN_MEMBER) > 0) || (offset == -1 && vtype == CTVT_MEMBER))
+        {
+          a_byteCode->Emit(BC_PUSHTHIS);
+          pushed = true;
+        }
+      }
+      if(!pushed)
+      {
+        a_byteCode->Emit(BC_PUSHNULL);
+      }
+
+#endif // !GM_COMPILE_PASS_THIS_ALWAYS
+    }
+    if(!Generate(callee, a_byteCode)) return false;
+  }
+
+  // push parameters, count the number of parameters
+  gmuint32 numParams = 0;
+
+  const gmCodeTreeNode * params = a_node->m_children[1];
+
+  while(params)
+  {
+    ++numParams;
+    if(!Generate(params, a_byteCode, false)) return false;
+    params = params->m_sibling;
+  }
+
+  // call
+  a_byteCode->Emit(BC_CALL, (gmuint32) numParams);
+
+  // restore the stack level.
+  a_byteCode->SetTos(stackLevel + 1);
+
+  return true;
+}
+
+
+
+bool gmCodeGenPrivate::GenExprThis(const gmCodeTreeNode * a_node, gmByteCodeGen * a_byteCode)
+{
+  GM_ASSERT(a_node->m_type == CTNT_EXPRESSION && a_node->m_subType == CTNET_THIS);
+  return a_byteCode->Emit(BC_PUSHTHIS);
+}
+
+
+
+gmCodeGenPrivate::FunctionState::FunctionState()
+{
+  m_debugName = NULL;
+  m_numLocals = 0;
+  m_currentLine = 1;
+  m_byteCode.Reset(this);
+}
+
+
+
+void gmCodeGenPrivate::FunctionState::Reset()
+{
+  m_debugName = NULL;
+  m_variables.Reset();
+  m_numLocals = 0;
+  m_currentLine = 1;
+  m_byteCode.Reset(this);
+  m_lineInfo.Reset();
+}
+
+
+
+int gmCodeGenPrivate::FunctionState::GetVariableOffset(const char * a_symbol, gmCodeTreeVariableType &a_type)
+{
+  for(gmuint v = 0; v < m_variables.Count(); ++v)
+  {
+    Variable &variable = m_variables[v];
+    if(strcmp(variable.m_symbol, a_symbol) == 0)
+    {
+      a_type = variable.m_type;
+      if(variable.m_type == CTVT_LOCAL)
+      {
+        return variable.m_offset;
+      }
+      return -1;
+    }
+  }
+
+  a_type = CTVT_GLOBAL;
+  return -2;
+}
+
+
+
+int gmCodeGenPrivate::FunctionState::SetVariableType(const char * a_symbol, gmCodeTreeVariableType a_type)
+{
+  for(gmuint v = 0; v < m_variables.Count(); ++v)
+  {
+    Variable &variable = m_variables[v];
+    if(strcmp(variable.m_symbol, a_symbol) == 0)
+    {
+      variable.m_type = a_type;
+      // if this variable was previously not a local, be is now being declared as local, get a stack offset.
+      if(a_type == CTVT_LOCAL && variable.m_offset == -1)
+      {
+        variable.m_offset = m_numLocals++;
+      }
+      return variable.m_offset;
+    }
+  }
+
+  Variable &variable = m_variables.InsertLast();
+  // if the new variable is a local, get a stack offset for it.
+  if(a_type == CTVT_LOCAL)
+  {
+    variable.m_offset = m_numLocals++;
+  }
+  else variable.m_offset = -1;
+
+  variable.m_type = a_type;
+  variable.m_symbol = a_symbol;
+  return variable.m_offset;
+}
+
+
+
+gmCodeGenPrivate::FunctionState * gmCodeGenPrivate::PushFunction()
+{
+  if(m_currentFunction)
+  {
+    if(m_currentFunction != m_functionStack.GetLast())
+    {
+      m_currentFunction = m_functionStack.GetNext(m_currentFunction);
+    }
+    else
+    {
+      m_currentFunction = new FunctionState();   
+      m_functionStack.InsertLast(m_currentFunction);
+    }
+  }
+  else
+  {
+    if(m_functionStack.IsEmpty())
+    {
+      m_currentFunction = new FunctionState();   
+      m_functionStack.InsertLast(m_currentFunction);
+    }
+    else
+    {
+      m_currentFunction = m_functionStack.GetFirst();
+    }  
+  }
+
+  m_currentFunction->Reset();
+
+  m_currentFunction->m_byteCode.SetSwapEndianOnWrite(m_hooks->SwapEndian());
+
+  // if we are debugging, set up some line number debugging.
+  if(m_debug)
+  {
+    m_currentFunction->m_byteCode.m_emitCallback = gmLineNumberCallback;
+  }
+
+  return m_currentFunction;
+}
+
+
+
+gmCodeGenPrivate::FunctionState * gmCodeGenPrivate::PopFunction()
+{
+  if(m_currentFunction)
+  {
+    m_currentFunction->Reset();
+    m_currentFunction = m_functionStack.GetPrev(m_currentFunction);
+    if(!m_functionStack.IsValid(m_currentFunction))
+    {
+      m_currentFunction = NULL;
+    }
+  }
+
+  return m_currentFunction;
+}
+
+
+
+void gmCodeGenPrivate::PushLoop()
+{
+  LoopInfo * loop = &m_loopStack.InsertLast();
+  m_currentLoop = m_loopStack.Count()-1;
+  loop->m_breaks = -1;
+  loop->m_continues = -1;
+}
+
+
+void gmCodeGenPrivate::PopLoop()
+{
+  m_loopStack.RemoveLast();
+  if(m_loopStack.Count())
+  {
+    m_currentLoop = m_loopStack.Count() - 1;
+  }
+  else
+  {
+    m_currentLoop = -1;
+  }
+}
+
+
+void gmCodeGenPrivate::ApplyPatches(int a_patches, gmByteCodeGen * a_byteCode, gmuint32 a_value)
+{
+  unsigned int pos = a_byteCode->Tell();
+  while(a_patches >= 0)
+  {
+    Patch * curPatch = &m_patches[a_patches];
+
+    a_byteCode->Seek(curPatch->m_address);
+    *a_byteCode << a_value;
+    a_patches = curPatch->m_next;
+  }
+  a_byteCode->Seek(pos);
+}

+ 49 - 0
gmsrc/src/gm/gmCodeGen.h

@@ -0,0 +1,49 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCODEGEN_H_
+#define _GMCODEGEN_H_
+
+#include "gmConfig.h"
+#include "gmLog.h"
+#include "gmCodeGenHooks.h"
+
+// fwd decl
+struct gmCodeTreeNode;
+
+/// \class gmCodeGen
+/// \brief gmCodeGen will create byte code for a given code tree.  after parsing script into a code tree using gmCodeTree,
+///        turn it into byte code using this class.  After the code gen has been run, the gmCodeTree may be unlocked.
+///        Note that the code tree is parsed into a set of functions authored using a gmCodeGenHooks implementation.
+class gmCodeGen
+{
+public:
+
+  /// \brief Get() will return the singleton code generator.
+  static gmCodeGen& Get();
+
+  /// \brief FreeMemory() will free all memory allocated by the code tree.  must be unlocked
+  virtual void FreeMemory() = 0;
+
+  /// \brief Lock() will create the byte code for the given gode tree.
+  /// \param a_codeTree is the code tree.
+  /// \param a_hooks is the byte code authoring object.
+  /// \param a_debug is true if debug info is required.
+  /// \param a_log is the compile log.
+  /// \return the number of errors encounted
+  virtual int Lock(const gmCodeTreeNode * a_codeTree, gmCodeGenHooks * a_hooks, bool a_debug, gmLog * a_log) = 0;
+ 
+  /// \brief Unlock() will reset the code generator.
+  virtual int Unlock() = 0;
+};
+
+
+#endif // _GMCODEGEN_H_

+ 14 - 0
gmsrc/src/gm/gmCodeGenHooks.cpp

@@ -0,0 +1,14 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmCodeGenHooks.h"
+

+ 101 - 0
gmsrc/src/gm/gmCodeGenHooks.h

@@ -0,0 +1,101 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCODEGENHOOKS_H_
+#define _GMCODEGENHOOKS_H_
+
+#include "gmConfig.h"
+
+/// \struct gmLineInfo
+/// \brief gmLineInfo describes the debug info required for line number debugging
+struct gmLineInfo
+{
+  int m_address; //!< byte code address
+  int m_lineNumber; //!< code line number
+};
+
+/// \struct gmFunctionInfo
+/// \brief gmFunctionInfo
+struct gmFunctionInfo
+{
+  gmptr m_id;                     //!< unique id of the function (as used in BC_PUSHFN)
+  bool m_root;                    //!< is this function the root function '__main'
+
+  const void * m_byteCode;        //!< byte code
+  int m_byteCodeLength;           //!< byte code length in bytes
+  int m_numParams;                //!< parameter count
+  int m_numLocals;                //!< local variable count (includes registers)
+  int m_maxStackSize;             //!< required temporary storage
+
+  const char * m_debugName;       //!< name of variable function was assigned to... may be NULL
+  const char ** m_symbols;        //!< param and local variable names, sizeof m_numParams + m_numLocals; (indexed by stack offset)
+  int m_lineInfoCount;            //!< number of entries in the line info array
+  const gmLineInfo * m_lineInfo;  //!< line - instruction address mapping for debugging purposes.
+};
+
+/// \class gmCodeGenHooks
+/// \brief gmCodeGenHooks is an interface that is fed to the compiler.  basically the code gen hooks class allows you
+///        to compile script directly into the runtime vm, or into a libary.
+class gmCodeGenHooks
+{
+public:
+  gmCodeGenHooks() {}
+  virtual ~gmCodeGenHooks() {}
+
+  /// \brief Begin() will be called by gmCodeGen at the start of compilation.
+  /// \param a_debug is true if this is a debug build.
+  virtual bool Begin(bool a_debug) = 0;
+
+  /// \brief AddFunction() is called each time the byte code for a function has been created.  The memory passed in
+  ///        the info structure is not valid after AddFunction returns.
+  /// \return true on success
+  virtual bool AddFunction(gmFunctionInfo &a_functionInfo) = 0;
+
+  /// \brief End() is called by gmCodeGen at the end of a compilation.
+  /// \param a_errors is the number of compilation errors.
+  virtual bool End(int a_errors) = 0;
+
+  /// \brief GetFunctionId() is called for the creation of unique function ids.
+  /// \return a unique id.
+  virtual gmptr GetFunctionId() = 0;
+
+  /// \brief GetSymbolId() is called by the compiler to get a unique symbol id.  this sybol id is a machine size int
+  ///        id written into the byte code.
+  /// \return a unique id for each unique a_symbol.
+  virtual gmptr GetSymbolId(const char * a_symbol) = 0;
+
+  /// \brief GetStringId() is called by the compiler to get a constant string id.  the returned value is written into 
+  ///        the byte code for string lookups.
+  /// \return a unique id for each unique string.
+  virtual gmptr GetStringId(const char * a_string) = 0;
+
+  /// \brief SwapEndian() returns true if the byte code is being compiled for a machine of differing endian
+  virtual bool SwapEndian() const { return false; }
+};
+
+
+/// \class gmCodeGenHooksNull 
+/// \brief used for syntax checking etc.
+class gmCodeGenHooksNull : public gmCodeGenHooks
+{
+public:
+  gmCodeGenHooksNull() {}
+  virtual ~gmCodeGenHooksNull() {}
+
+  virtual bool Begin(bool a_debug) { return true; }
+  virtual bool AddFunction(gmFunctionInfo &a_functionInfo) { return true; }
+  virtual bool End(int a_errors) { return true; }
+  virtual gmptr GetFunctionId() { return 0; }
+  virtual gmptr GetSymbolId(const char * a_symbol) { return 0; }
+  virtual gmptr GetStringId(const char * a_string) { return 0; }
+};
+
+#endif // _GMCODEGENHOOKS_H_

+ 554 - 0
gmsrc/src/gm/gmCodeTree.cpp

@@ -0,0 +1,554 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmCodeTree.h"
+
+gmCodeTreeNode * g_codeTree = NULL;
+
+
+
+gmCodeTree::gmCodeTree() :
+  m_mem(1, GMCODETREE_CHAINSIZE)
+{
+  g_codeTree = NULL;
+  m_locked = false;
+  m_errors = 0;
+  m_log = 0;
+}
+
+
+
+gmCodeTree::~gmCodeTree()
+{
+  Unlock();
+}
+
+
+
+gmCodeTree &gmCodeTree::Get()
+{
+  static gmCodeTree codeTree;
+  return codeTree;
+}
+
+
+
+void gmCodeTree::FreeMemory()
+{
+  if(m_locked == false)
+  {
+    m_mem.ResetAndFreeMemory();
+  }
+}
+
+
+
+int gmCodeTree::Lock(const char * a_script, gmLog * a_log)
+{
+  if(m_locked == true) return 1;
+
+  m_errors = 0;
+  m_locked = true;
+  m_log = a_log;
+  g_codeTree = NULL;
+  //gmdebug = 1;
+  gmlineno = 1;
+
+  // create a scan buffer
+  YY_BUFFER_STATE buffer = gm_scan_string(a_script);
+  if(buffer)
+  {
+    m_errors = gmparse();
+    gm_delete_buffer(buffer);
+  }
+  return m_errors;
+}
+
+
+
+int gmCodeTree::Unlock()
+{
+  m_mem.Reset();
+  g_codeTree = NULL;
+  m_locked = false;
+  m_errors = 0;
+  m_log = NULL;
+  return 0;
+}
+
+
+
+const gmCodeTreeNode * gmCodeTree::GetCodeTree() const
+{
+  return g_codeTree;
+}
+
+
+
+void * gmCodeTree::Alloc(int a_size, int a_align)
+{
+  return m_mem.AllocBytes(a_size, a_align);
+}
+
+
+
+#if GM_COMPILE_DEBUG
+
+const char * gmGetOperatorTypeName(gmCodeTreeNodeOperationType a_type)
+{
+  switch(a_type)
+  {
+    case CTNOT_INVALID : return "CTNOT_INVALID";
+    case CTNOT_DOT : return "CTNOT_DOT";
+    case CTNOT_UNARY_PLUS : return "CTNOT_UNARY_PLUS";
+    case CTNOT_UNARY_MINUS : return "CTNOT_UNARY_MINUS";
+    case CTNOT_UNARY_NOT : return "CTNOT_UNARY_NOT";
+    case CTNOT_UNARY_COMPLEMENT : return "CTNOT_UNARY_COMPLEMENT";
+    case CTNOT_ARRAY_INDEX : return "CTNOT_ARRAY_INDEX";
+    case CTNOT_TIMES : return "CTNOT_TIMES";
+    case CTNOT_DIVIDE : return "CTNOT_DIVIDE";
+    case CTNOT_REM : return "CTNOT_REM";
+    case CTNOT_ADD : return "CTNOT_ADD";
+    case CTNOT_MINUS : return "CTNOT_MINUS";
+    case CTNOT_LT : return "CTNOT_LT";
+    case CTNOT_GT : return "CTNOT_GT";
+    case CTNOT_LTE : return "CTNOT_LTE";
+    case CTNOT_GTE : return "CTNOT_GTE";
+    case CTNOT_EQ : return "CTNOT_EQ";
+    case CTNOT_NEQ : return "CTNOT_NEQ";
+    case CTNOT_AND : return "CTNOT_AND";
+    case CTNOT_OR : return "CTNOT_OR";
+    case CTNOT_BIT_OR : return "CTNOT_BIT_OR";
+    case CTNOT_BIT_XOR : return "CTNOT_BIT_XOR";
+    case CTNOT_BIT_AND : return "CTNOT_BIT_AND";
+    case CTNOT_SHIFT_LEFT : return "CTNOT_SHIFT_LEFT";
+    case CTNOT_SHIFT_RIGHT : return "CTNOT_SHIFT_RIGHT";
+    case CTNOT_ASSIGN : return "CTNOT_ASSIGN";
+    case CTNOT_ASSIGN_FIELD : return "CTNOT_ASSIGN_FIELD";
+    default : break;
+  }
+  return "UNKNOWN OPERATOR TYPE";
+};
+
+
+
+static void PrintRecursive(const gmCodeTreeNode * a_node, FILE * a_fp, bool a_firstCall)
+{
+  if(a_node)
+  {
+    static int indent;
+    int i;
+
+    if(a_firstCall)
+    {
+      indent = 0;
+    }
+
+    indent += 2;
+
+    while(a_node != NULL)
+    {
+      for(i = 0; i < indent; ++i)
+        fprintf(a_fp, " ");
+
+      if(a_node->m_type == CTNT_DECLARATION)
+      {
+        //
+        // DECLARATIONS
+        //
+        switch(a_node->m_subType)
+        {
+          case CTNDT_PARAMETER : fprintf(a_fp, "CTNDT_PARAMETER:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNDT_VARIABLE : fprintf(a_fp, "CTNDT_VARIABLE:%04d, type %d"GM_NL, a_node->m_lineNumber, a_node->m_subTypeType); break;
+          default : fprintf(a_fp, "UNKNOWN DECLARATION:"GM_NL); break;
+        }
+      }
+      else if(a_node->m_type == CTNT_STATEMENT)
+      {
+        //
+        // STATEMENTS
+        //
+        switch(a_node->m_subType)
+        {
+          case CTNST_RETURN : fprintf(a_fp, "CTNST_RETURN:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_BREAK : fprintf(a_fp, "CTNST_BREAK:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_CONTINUE : fprintf(a_fp, "CTNST_CONTINUE:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_FOR : fprintf(a_fp, "CTNST_FOR:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_FOREACH : fprintf(a_fp, "CTNST_FOREACH:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_WHILE : fprintf(a_fp, "CTNST_WHILE:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_DOWHILE : fprintf(a_fp, "CTNST_DOWHILE:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_IF : fprintf(a_fp, "CTNST_IF:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNST_COMPOUND : fprintf(a_fp, "CTNST_COMPOUND:%04d"GM_NL, a_node->m_lineNumber); break;
+#if GM_USE_FORK
+          case CTNST_FORK : fprintf(a_fp, "CTNST_FORK:%04d"GM_NL, a_node->m_lineNumber); break;
+#endif //GM_USE_FORK
+          default : fprintf(a_fp, "UNKNOWN STATEMENT:"GM_NL); break;
+        }
+      }
+      else if(a_node->m_type == CTNT_EXPRESSION)
+      {
+        //
+        // EXPRESSIONS
+        //
+        switch(a_node->m_subType)
+        {
+          case CTNET_OPERATION :
+          {
+            if(a_node->m_subTypeType < CTNOT_MAX)
+            {
+              fprintf(a_fp, "CTNET_OPERATION:%04d : %s"GM_NL, a_node->m_lineNumber, gmGetOperatorTypeName((gmCodeTreeNodeOperationType) a_node->m_subTypeType));
+            }
+            else
+            { 
+              fprintf(a_fp, "UNKNOWN CTNET_OPERATION"GM_NL);
+            }
+            break;
+          }
+
+          case CTNET_CONSTANT :
+          {
+            switch(a_node->m_subTypeType)
+            {
+              case CTNCT_INT : fprintf(a_fp, "CTNCT_INT:%04d : %d"GM_NL, a_node->m_lineNumber, a_node->m_data.m_iValue); break;
+              case CTNCT_FLOAT : fprintf(a_fp, "CTNCT_FLOAT:%04d : %f"GM_NL, a_node->m_lineNumber, a_node->m_data.m_fValue); break;
+              case CTNCT_STRING : fprintf(a_fp, "CTNCT_STRING:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
+              case CTNCT_NULL : fprintf(a_fp, "CTNCT_NULL:%04d"GM_NL, a_node->m_lineNumber); break;
+              default: fprintf(a_fp, "UNKNOWN CTNET_CONSTANT"GM_NL);
+            }
+            break;
+          }
+
+          case CTNET_IDENTIFIER : fprintf(a_fp, "CTNET_IDENTIFIER:%04d : %s"GM_NL, a_node->m_lineNumber, a_node->m_data.m_string); break;
+          case CTNET_THIS : fprintf(a_fp, "CTNET_THIS:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNET_CALL : fprintf(a_fp, "CTNET_CALL:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNET_FUNCTION : fprintf(a_fp, "CTNET_FUNCTION:%04d"GM_NL, a_node->m_lineNumber); break;
+          case CTNET_TABLE : fprintf(a_fp, "CTNET_TABLE:%04d"GM_NL, a_node->m_lineNumber); break;
+          default : fprintf(a_fp, "UNKNOWN EXPRESSION:"GM_NL); break;
+        }
+      }
+      else
+      {
+        fprintf(a_fp, "UNKNOWN NODE TYPE"GM_NL);
+      }
+
+      // print the child nodes
+      for(i = 0; i < GMCODETREE_NUMCHILDREN; ++i)
+      {
+        if(a_node->m_children[i])
+        {
+          PrintRecursive(a_node->m_children[i], a_fp, false);
+        }
+      }
+      a_node = a_node->m_sibling;
+
+    } // while(a_node != NULL)
+
+    indent -= 2;
+  }
+}
+
+
+void gmCodeTree::Print(FILE * a_fp)
+{
+  if(m_locked)
+  {
+    PrintRecursive(g_codeTree, a_fp, true);
+  }
+}
+
+
+#endif // GM_COMPILE_DEBUG
+
+
+
+gmCodeTreeNode * gmCodeTreeNode::Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType)
+{
+  gmCodeTreeNode * node = (gmCodeTreeNode *) gmCodeTree::Get().Alloc(sizeof(gmCodeTreeNode), GM_DEFAULT_ALLOC_ALIGNMENT);
+  GM_ASSERT(node != NULL);
+  memset(node, 0, sizeof(gmCodeTreeNode));
+  node->m_type = a_type;
+  node->m_subType = a_subType;
+  node->m_lineNumber = a_lineNumber;
+  node->m_subTypeType = a_subTypeType;
+  node->m_flags = 0;
+  return node;
+}
+
+
+
+void gmCodeTreeNode::SetChild(int a_index, gmCodeTreeNode * a_node)
+{
+  GM_ASSERT(a_index >= 0 && a_index < GMCODETREE_NUMCHILDREN);
+
+  m_children[a_index] = a_node;
+  if(a_node != NULL)
+  {
+    a_node->m_parent = this;
+  }
+}
+
+
+static bool gmFold(float &a_r, float a_a, int a_op)
+{
+  switch(a_op)
+  {
+    case CTNOT_UNARY_PLUS : a_r = a_a; break;
+    case CTNOT_UNARY_MINUS : a_r = -a_a; break;
+    default: return false;
+  }
+  return true;
+}
+
+
+static bool gmFold(int &a_r, int a_a, int a_op)
+{
+  switch(a_op)
+  {
+    case CTNOT_UNARY_PLUS : a_r = a_a; break;
+    case CTNOT_UNARY_MINUS : a_r = -a_a; break;
+    case CTNOT_UNARY_NOT : a_r = !a_a; break;
+    case CTNOT_UNARY_COMPLEMENT : a_r = ~a_a; break;
+    default: return false;
+  }
+  return true;
+}
+
+
+#include <math.h>
+static bool gmFold(float &a_r, float a_a, float a_b, int a_op)
+{
+  switch(a_op)
+  {
+    case CTNOT_TIMES : a_r = a_a * a_b; break;
+    case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
+    case CTNOT_REM : a_r = fmodf(a_a, a_b); break;
+    case CTNOT_ADD : a_r = a_a + a_b; break;
+    case CTNOT_MINUS : a_r = a_a - a_b; break;
+    default: return false;
+  }
+  return true;
+}
+
+
+static bool gmFold(int &a_r, int a_a, int a_b, int a_op)
+{
+  switch(a_op)
+  {
+    case CTNOT_TIMES : a_r = a_a * a_b; break;
+    case CTNOT_DIVIDE : if(a_b == 0) return false; a_r = a_a / a_b; break;
+    case CTNOT_REM : a_r = a_a % a_b; break;
+    case CTNOT_ADD : a_r = a_a + a_b; break;
+    case CTNOT_MINUS : a_r = a_a - a_b; break;
+    case CTNOT_BIT_OR : a_r = a_a | a_b; break;
+    case CTNOT_BIT_XOR : a_r = a_a ^ a_b; break;
+    case CTNOT_BIT_AND : a_r = a_a & a_b; break;
+    case CTNOT_SHIFT_LEFT : a_r = a_a << a_b; break;
+    case CTNOT_SHIFT_RIGHT : a_r = a_a >> a_b; break;
+    default: return false;
+  }
+  return true;
+}
+
+
+bool gmCodeTreeNode::ConstantFold()
+{
+  if(m_type == CTNT_EXPRESSION && m_subType == CTNET_OPERATION)
+  {
+    bool possibleUnaryFold = false;
+    bool possibleFold = false;
+    bool intOnly = false;
+
+    switch(m_subTypeType)
+    {
+      case CTNOT_UNARY_PLUS :
+      case CTNOT_UNARY_MINUS :
+        possibleUnaryFold = true;
+        break;
+      case CTNOT_UNARY_NOT :
+      case CTNOT_UNARY_COMPLEMENT :
+        possibleUnaryFold = true;
+        intOnly = true;
+        break;
+      case CTNOT_TIMES :
+      case CTNOT_DIVIDE :
+      case CTNOT_REM :
+      case CTNOT_ADD :
+      case CTNOT_MINUS :
+        possibleFold = true;
+        break;
+      case CTNOT_BIT_OR :
+      case CTNOT_BIT_XOR :
+      case CTNOT_BIT_AND :
+      case CTNOT_SHIFT_LEFT :
+      case CTNOT_SHIFT_RIGHT :
+        possibleFold = true;
+        intOnly = true;
+        break;
+      default:
+        break;
+    }
+
+    if(possibleUnaryFold)
+    {
+      gmCodeTreeNode * l = m_children[0];
+      if(l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT)
+      {
+        if(l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly))
+        {
+          // we can fold....
+          m_children[0] = NULL;
+          m_subType = CTNET_CONSTANT;
+
+          if(l->m_subTypeType == CTNCT_INT)
+          {
+            gmFold(m_data.m_iValue, l->m_data.m_iValue, m_subTypeType);
+            m_subTypeType = CTNCT_INT;
+          }
+          else if(l->m_subTypeType == CTNCT_FLOAT)
+          {
+            gmFold(m_data.m_fValue, l->m_data.m_fValue, m_subTypeType);
+            m_subTypeType = CTNCT_FLOAT;
+          }
+          return true;
+        }
+      }
+    }
+    else if(possibleFold)
+    {
+      gmCodeTreeNode * l = m_children[0], * r = m_children[1];
+      if((l && l->m_type == CTNT_EXPRESSION && l->m_subType == CTNET_CONSTANT) && 
+         (r && r->m_type == CTNT_EXPRESSION && r->m_subType == CTNET_CONSTANT))
+      {
+        if((l->m_subTypeType == CTNCT_INT || (l->m_subTypeType == CTNCT_FLOAT && !intOnly)) && 
+           (r->m_subTypeType == CTNCT_INT || (r->m_subTypeType == CTNCT_FLOAT && !intOnly)))
+        {
+          // we can fold....
+          m_children[0] = NULL; m_children[1] = NULL;
+          m_subType = CTNET_CONSTANT;
+          if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_INT)
+          {
+            gmFold(m_data.m_iValue, l->m_data.m_iValue, r->m_data.m_iValue, m_subTypeType);
+            m_subTypeType = CTNCT_INT;
+          }
+          else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_FLOAT)
+          {
+            gmFold(m_data.m_fValue, l->m_data.m_fValue, r->m_data.m_fValue, m_subTypeType);
+            m_subTypeType = CTNCT_FLOAT;
+          }
+          else if(l->m_subTypeType == CTNCT_INT && r->m_subTypeType == CTNCT_FLOAT)
+          {
+            gmFold(m_data.m_fValue, (float) l->m_data.m_iValue, r->m_data.m_fValue, m_subTypeType);
+            m_subTypeType = CTNCT_FLOAT;
+          }
+          else if(l->m_subTypeType == CTNCT_FLOAT && r->m_subTypeType == CTNCT_INT)
+          {
+            gmFold(m_data.m_fValue, l->m_data.m_fValue, (float) r->m_data.m_iValue, m_subTypeType);
+            m_subTypeType = CTNCT_FLOAT;
+          }
+          return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+
+void gmProcessSingleQuoteString(char * a_string)
+{
+  char * c = a_string;
+  char * r = a_string;
+
+  while(*c)
+  {
+    if(c[0] == '`' && c[1] == '`' && c[2])
+    {
+      *(r++) = *c;
+      c += 2;
+      continue;
+    }
+    else if(c[0] == '`')
+    {
+      ++c;
+      continue;
+    }
+
+    *(r++) = *(c++);
+  }
+  *r = '\0';
+}
+
+
+
+void gmProcessDoubleQuoteString(char * a_string)
+{
+  char * c = a_string;
+  char * r = a_string;
+
+  while(*c)
+  {
+    if(c[0] == '\"')
+    {
+      ++c;
+      continue;
+    }
+    else if(c[0] == '\\')
+    {
+      switch(c[1])
+      {
+        case '0' : case '1' : case '2' : case '3' : case '4' : case '5' :
+        case '6' : case '7' : case '8' : case '9' :
+        {
+          char buffer[4]; int i = 0;
+          while(i < 3 && isdigit(c[i+1]))
+          {
+            buffer[i] = c[i+1];
+            ++i;
+          }
+          buffer[i] = '\0';
+          *r = (char) (atoi(buffer) & 0xff);
+          c += (i - 1);
+          break;
+        }
+        case 'a' : *r = '\a'; break;
+        case 'b' : *r = '\b'; break;
+        case 'f' : *r = '\f'; break;
+        case 'n' : *r = '\n'; break;
+        case 'r' : *r = '\r'; break;
+        case 't' : *r = '\t'; break;
+        case 'v' : *r = '\v'; break;
+        case '\'' : *r = '\''; break;
+        case '\"' : *r = '\"'; break;
+        case '\\' : *r = '\\'; break;
+        default: *r = c[1];
+      }
+      ++r;
+      c += 2;
+      continue;
+    }
+    *(r++) = *(c++);
+  }
+  *r = '\0';
+}
+
+
+
+int gmerror(char * a_message)
+{
+  gmCodeTree & ct = gmCodeTree::Get();
+  if(ct.GetLog())
+  {
+    ct.GetLog()->LogEntry("error (%d) %s", gmlineno, a_message);
+  }
+  return 0;
+}
+

+ 255 - 0
gmsrc/src/gm/gmCodeTree.h

@@ -0,0 +1,255 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCODETREE_H_
+#define _GMCODETREE_H_
+
+#include "gmConfig.h"
+#include "gmMem.h"
+#include "gmMemChain.h"
+#include "gmLog.h"
+#include "gmScanner.h"
+
+#define GMCODETREE_NUMCHILDREN  4
+
+// fwd decl
+struct gmCodeTreeNode;
+
+/// \class gmCodeTree
+/// \brief gmCodeTree is a singleton class for creating code trees.
+class gmCodeTree
+{
+protected:
+  gmCodeTree();
+
+public:
+  ~gmCodeTree();
+
+  /// \brief Get() will return the singlton parser.
+  static gmCodeTree &Get();
+
+  /// \brief FreeMemory() will free all memory allocated by the code tree.  must be unlocked
+  void FreeMemory();
+
+  /// \brief Lock() will create a code tree for the passed script.  Note that the code tree is valid until
+  ///        Unlock() is called.
+  /// \param a_script is a null terminated script string.
+  /// \return the number of errors encounted when parsing.
+  /// \sa Unlock()
+  int Lock(const char * a_script, gmLog * a_log = NULL);
+
+  /// \brief Unlock() will unlock the singleton code tree such that it may be used again.
+  /// \return 0 on success
+  /// \sa Lock()
+  int Unlock();
+
+  /// \brief GetCodeTree() will return the code tree resulting from a Lock() operation.
+  /// \return NULL on failure
+  /// \sa Lock()
+  const gmCodeTreeNode * GetCodeTree() const;
+
+  inline gmLog * GetLog() const { return m_log; }
+
+  /// \brief Alloc() will return memory from the code tree memory pool.  This method is used by the
+  ///        parser when building the code tree.
+  /// \return NULL on failure
+  void * Alloc(int a_size, int a_align = GM_DEFAULT_ALLOC_ALIGNMENT);
+
+#if GM_COMPILE_DEBUG
+  
+  /// \brief Print() will write the tree to the given file.  this is purely for debugging.
+  /// \param a_fp is an open file for writing.
+  void Print(FILE * a_fp);
+
+#endif // GM_COMPILE_DEBUG
+
+private:
+
+  bool m_locked;
+  int m_errors;
+  gmLog * m_log;
+  gmMemChain m_mem;
+};
+
+
+
+/// \enum gmCodeTreeNodeType
+/// \brief gmCodeTreeNodeType indicates the type of a gmCodeTreeNode.
+enum gmCodeTreeNodeType
+{
+  CTNT_INVALID = 0,
+  CTNT_DECLARATION,
+  CTNT_STATEMENT,
+  CTNT_EXPRESSION,
+};
+
+
+
+/// \enum gmCodeTreeNodeDeclarationType
+/// \brief if a tree node is of type CTNT_DECLARATION, gmCodeTreeNodeDeclarationType are the sub types
+enum gmCodeTreeNodeDeclarationType
+{
+  CTNDT_PARAMETER = 0,
+  CTNDT_VARIABLE,
+};
+
+
+
+/// \enum gmCodeTreeVariableType
+/// \brief if a treenode is CTNT_DECLARATION, CTNDT_VARIABLE, this is the type of variable decl.
+enum gmCodeTreeVariableType
+{
+  CTVT_LOCAL = 0,
+  CTVT_GLOBAL,
+  CTVT_MEMBER,
+};
+
+
+
+/// \enum gmCodeTreeNodeStatementType
+/// \brief if a tree node is of type CTNT_STATEMENT, gmCodeTreeNodeStatementType are the sub types
+enum gmCodeTreeNodeStatementType
+{
+  CTNST_INVALID = 0,
+  CTNST_RETURN,
+  CTNST_BREAK,
+  CTNST_CONTINUE,
+  CTNST_FOR,
+  CTNST_FOREACH,
+  CTNST_WHILE,
+  CTNST_DOWHILE,
+  CTNST_IF,
+  CTNST_COMPOUND,
+  CTNST_FORK,                                     // If GM_USE_FORK
+};
+
+
+
+/// \enum gmCodeTreeNodeExpressionType
+/// \brief
+enum gmCodeTreeNodeExpressionType
+{
+  CTNET_INVALID = 0,
+  CTNET_OPERATION,
+  CTNET_CONSTANT,
+  CTNET_IDENTIFIER,
+  CTNET_THIS,
+  CTNET_CALL,
+  CTNET_FUNCTION,
+  CTNET_TABLE,
+};
+
+
+
+/// \enum gmCodeTreeNodeOperationType
+/// \brief
+enum gmCodeTreeNodeOperationType
+{
+  CTNOT_INVALID = 0,
+  CTNOT_DOT,
+  CTNOT_UNARY_PLUS,
+  CTNOT_UNARY_MINUS,
+  CTNOT_UNARY_COMPLEMENT,
+  CTNOT_UNARY_NOT, 
+  CTNOT_ARRAY_INDEX,
+  CTNOT_TIMES,
+  CTNOT_DIVIDE,
+  CTNOT_REM,
+  CTNOT_ADD,
+  CTNOT_MINUS,
+  CTNOT_LT,
+  CTNOT_GT,
+  CTNOT_LTE,
+  CTNOT_GTE,
+  CTNOT_EQ,
+  CTNOT_NEQ,
+  CTNOT_AND,
+  CTNOT_OR,
+  CTNOT_BIT_OR,
+  CTNOT_BIT_XOR,
+  CTNOT_BIT_AND,
+  CTNOT_SHIFT_LEFT,
+  CTNOT_SHIFT_RIGHT,
+  CTNOT_ASSIGN,
+  CTNOT_ASSIGN_FIELD,
+  CTNOT_MAX,
+};
+
+
+
+/// \enum gmCodeTreeNodeConstantType
+enum gmCodeTreeNodeConstantType
+{
+  CTNCT_INVALID = 0,
+  CTNCT_INT,
+  CTNCT_FLOAT,
+  CTNCT_STRING,
+  CTNCT_NULL,
+};
+
+
+
+/// \union gmCodeTreeNodeUnion
+/// \brief
+union gmCodeTreeNodeData
+{
+  char * m_string;
+  int m_iValue;
+  float m_fValue;
+};
+
+
+
+/// \struct gmCodeTreeNode
+/// \brief gmCodeTreeNode is the tree node structure used to represent the game monkey script syntax tree.
+struct gmCodeTreeNode
+{
+  // flags
+  enum
+  {
+    CTN_POP     = (1 << 0),
+    CTN_MEMBER  = (1 << 1),
+  };
+
+  /// \brief Create() will create a tree node.  the singleton gmCodeTree must be locked.
+  /// \return a tree node
+  static gmCodeTreeNode * Create(gmCodeTreeNodeType a_type, int a_subType, int a_lineNumber, int a_subTypeType = 0);
+
+  /// \brief SetChild() will set the child at the given index.
+  /// \param a_node is the child node, whose parent pointer will be assigned to this.
+  void SetChild(int a_index, gmCodeTreeNode * a_node);
+
+  /// \brief ConstantFold() will pull child nodes into this node, and make this node a constant if possible
+  bool ConstantFold();
+
+  gmCodeTreeNodeType m_type;
+  int m_subType;
+  int m_subTypeType;
+  int m_flags;
+
+  gmCodeTreeNode * m_children[GMCODETREE_NUMCHILDREN];
+  gmCodeTreeNode * m_sibling;
+  gmCodeTreeNode * m_parent;
+
+  int m_lineNumber;
+  gmCodeTreeNodeData m_data;
+};
+
+//
+// misc lexing and parsing functions.
+//
+
+void gmProcessSingleQuoteString(char * a_string);
+void gmProcessDoubleQuoteString(char * a_string);
+int gmerror(char * a_message);
+int gmparse(void);
+
+#endif // _GMCODETREE_H_

+ 109 - 0
gmsrc/src/gm/gmConfig.h

@@ -0,0 +1,109 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCONFIG_H_
+#define _GMCONFIG_H_
+
+// Include the platform config.
+// All platform configuration exists in gmconfig_p.h
+#include "gmConfig_p.h"
+
+#include <stdlib.h> // atoi, strtoul (binds: rand, srand)
+#include <stdio.h> // fprintf, sprintf, _snprintf, _vnsprintf
+#include <string.h> // stricmp, strcmp, strcpy, strlen, strcat, memset, memcpy (binds: strlwr, wtrupr, strspn, strcspn, strchr, strstr)
+#include <stdarg.h> // va_start, va_end
+#include <ctype.h> // isdigit
+#include <math.h> // floorf, fmodf
+
+
+/// \enum gmEndian Endian byte order
+enum gmEndian
+{
+  GM_ENDIAN_BIG = 0,      //!< MOTOROLA (MAC), NINTENDO GC
+  GM_ENDIAN_LITTLE = 1    //!< x86, XBOX, PS2
+};
+
+//
+// Game Monkey Configuration
+//
+
+// COMPILE
+
+#define GM_COMPILE_DEBUG            1         // define for compile debugging code, ie, printing code trees, byte code etc.
+
+// COMPILE LOG
+
+#define GMLOG_CHAINSIZE             2048      // memory chunk resolution for the compile log
+
+// COMPILE PARSER
+
+#define GMCODETREE_CHAINSIZE        4096      // memory chunk resolution for compiler code tree nodes.
+
+// COMPILER CODE GENERATOR
+
+#define GM_COMPILE_PASS_THIS_ALWAYS 0         // set to 1 to pass current this to each function call
+
+// RUNTIME THREAD
+
+#define GMTHREAD_INITIALBYTESIZE    512       // initial stack byte size for a single thread
+#define GMTHREAD_MAXBYTESIZE        128000    //1024  // max stack byte size for a single thread (Sample scripts like it big)
+
+// MACHINE
+
+#define GMMACHINE_REMOVECOMPILER    0         // Remove compiler code, will only be able to run precompiled libs
+#define GMMACHINE_GMCHECKDIVBYZERO  0         // Let GM operator check for divide by zero and possibly cause GM run time exception (rather than OS exception)
+#define GMMACHINE_NULL_VAR_CTOR     0         // Nullify gmVariable in constructor.  Not recommended for real-time / time critical applications.
+#define GMMACHINE_USERTYPEGROWBY    16        // allocate user types in chunks of this size
+#define GMMACHINE_OBJECTCHUNKSIZE   32        // default object chunk allocation size
+#define GMMACHINE_TBLCHUNKSIZE      32        // table object chunk allocation size
+#define GMMACHINE_STRINGCHUNKSIZE   128       // default object chunk allocation size
+#define GMMACHINE_STACKFCHUNKSIZE   128       // stack frame chunk size
+#define GMMACHINE_AUTOMEM           true      // automatically decide garbage collection limit
+#define GMMACHINE_AUTOMEMMULTIPY    2.5f      // after gc cycle, set limit = current * GMMACHINE_AUTOMEMMULTIPY (This is for atomic GC)
+#define GMMACHINE_AUTOMEMALLOWSHRINK 0        // Allow memory liimits to shrink, otherwise memory will grow when needed only
+#define GMMACHINE_INITIALGCHARDLIMIT 128*1024  // default gc hard memory limit.
+#define GMMACHINE_INITIALGCSOFTLIMIT (GMMACHINE_INITIALGCHARDLIMIT * 9 / 10) // default gc soft memory limit
+#define GMMACHINE_STRINGHASHSIZE    8192      // this will be dynamic... todo
+#define GMMACHINE_MAXKILLEDTHREADS  16        // max size of the free thread list (don't make too large, ie, < 32)
+#define GMMACHINE_GCEVERYALLOC      0         // define this to check garbage collection every allocate.
+#define GMMACHINE_SUPERPARANOIDGC   0         // validate references (only for debugging purposes)
+#define GMMACHINE_THREEPASSGC       0         // 1 for safe gc of persisting objects that reference other objects, 
+                                              // ie, persisting tables.  if you only have persisting simple objects, ie
+                                              // strings, set to 0 for faster garbage collection.
+
+// Auto GC Calibration values
+#define GMMACHINE_GC_HARD_MEM_INC_FRAC_OF_USED      1.5f   // what to set hard limit to above used mem when growing hard limit
+#define GMMACHINE_GC_HARD_MEM_DEC_FRAC_OF_USED      1.5f   // what to set hard limit to above used mem when shinking hard limit
+#define GMMACHINE_GC_SOFT_MEM_DEFAULT_FRAC_OF_HARD  (9.0f/10.0f) // what to set soft limit as frac of hard limit by default
+#define GMMACHINE_GC_HARD_MEM_SHRINK_THRESH         0.5f   // threshold at which hard limit should shrink
+#define GMMACHINE_GC_SOFT_MEM_MIN_FRAC              0.25f  // minimum soft limit as frac of hard limit to shrink soft limit
+#define GMMACHINE_GC_SOFT_MEM_DEC_FRAC              0.1f   // amount to shrink soft limit as frac of soft/hard
+#define GMMACHINE_GC_MIN_FRAMES_SINCE_RESTART       100    // if gc is restarting within this many frames/calls, it is probably configured bad
+#define GM_GC_DEFAULT_WORK_INCREMENT                200    // Desired number of objects to trace per frame
+#define GM_GC_DEFAULT_DESTRUCT_INCREMENT            200    // Desired number of old objects to free per frame
+
+#define GMMACHINE_CPPOWNEDGMOBJHASHSIZE 1024  // default hash table size for objects owned by cpp code, necessary for GC.
+
+// DEBUGGING
+
+#define GMDEBUG_SUPPORT             1         // allow use with the gm debugger
+
+
+// GARBAGE COLLECTOR
+#define GM_USE_INCGC                1         // use incremental garbage collector
+
+
+#define GM_BOOL_OP                  1         // Spport for a bool operator on user types for use in if statements. For full effect, users will want to implement operators [bool, ==, !=, !]
+#define GM_USE_FORK                 1         // Support fork instruction 
+#define GM_USER_FOREACH             1         // Support foreach for user types
+#define GM_USE_ENDON                1         // Support endon() to kill thread when signalled
+
+#endif // _GMCONFIG_H_

+ 80 - 0
gmsrc/src/gm/gmCrc.cpp

@@ -0,0 +1,80 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmCrc.h"
+
+#define _gmUPDC32(A,C) (s_gmCrc32Table[((C) ^ ((gmuint8) A)) & 0xff] ^ ((C) >> 8))
+
+// CRC polynomial 0xedb88320 
+static gmuint32 s_gmCrc32Table[] = 
+  { 
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+  };
+
+
+
+gmuint32 gmCrc32String(const char *p_string)
+{
+  register gmuint32 crc32;
+
+  crc32 = 0xffffffff;
+
+  for (; *p_string; ++p_string)
+  {
+    crc32 = _gmUPDC32(*p_string, crc32);
+  }
+
+  return ~crc32;
+}
+

+ 19 - 0
gmsrc/src/gm/gmCrc.h

@@ -0,0 +1,19 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMCRC_H_
+#define _GMCRC_H_
+
+#include "gmConfig.h"
+
+gmuint32 gmCrc32String(const char *p_string);
+
+#endif // _GMCRC_H_

+ 737 - 0
gmsrc/src/gm/gmDebug.cpp

@@ -0,0 +1,737 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmDebug.h"
+#include "gmConfig.h"
+#include "gmMachine.h"
+#include "gmThread.h"
+
+#if GMDEBUG_SUPPORT
+
+
+#define ID_mrun GM_MAKE_ID32('m','r','u','n')
+#define ID_msin GM_MAKE_ID32('m','s','i','n')
+#define ID_msou GM_MAKE_ID32('m','s','o','u')
+#define ID_msov GM_MAKE_ID32('m','s','o','v')
+#define ID_mgct GM_MAKE_ID32('m','g','c','t')
+#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
+#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
+#define ID_mgti GM_MAKE_ID32('m','g','t','i')
+#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
+#define ID_msbp GM_MAKE_ID32('m','s','b','p')
+#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
+#define ID_mend GM_MAKE_ID32('m','e','n','d')
+
+#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
+#define ID_dexc GM_MAKE_ID32('d','e','x','c')
+#define ID_drun GM_MAKE_ID32('d','r','u','n')
+#define ID_dstp GM_MAKE_ID32('d','s','t','p')
+#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
+#define ID_dctx GM_MAKE_ID32('d','c','t','x')
+#define ID_call GM_MAKE_ID32('c','a','l','l')
+#define ID_vari GM_MAKE_ID32('v','a','r','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_dsri GM_MAKE_ID32('d','s','r','i')
+#define ID_srci GM_MAKE_ID32('s','r','c','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_dthi GM_MAKE_ID32('d','t','h','i')
+#define ID_thri GM_MAKE_ID32('t','h','r','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_derr GM_MAKE_ID32('d','e','r','r')
+#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
+#define ID_dack GM_MAKE_ID32('d','a','c','k')
+#define ID_dend GM_MAKE_ID32('d','e','n','d')
+
+
+//
+// functions to handle incomming commands from a debugger
+//
+
+void gmMachineRun(gmDebugSession * a_session, int a_threadId);
+void gmMachineStepInto(gmDebugSession * a_session, int a_threadId);
+void gmMachineStepOver(gmDebugSession * a_session, int a_threadId);
+void gmMachineStepOut(gmDebugSession * a_session, int a_threadId);
+void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe);
+void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId);
+void gmMachineGetSourceInfo(gmDebugSession * a_session);
+void gmMachineGetThreadInfo(gmDebugSession * a_session);
+void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId);
+void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
+void gmMachineBreak(gmDebugSession * a_session, int a_threadId);
+void gmMachineQuit(gmDebugSession * a_session);
+
+//
+// functions to package outgoing messages to a debugger
+//
+
+void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
+void gmDebuggerRun(gmDebugSession * a_session, int a_threadId);
+void gmDebuggerStop(gmDebugSession * a_session, int a_threadId);
+void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
+void gmDebuggerException(gmDebugSession * a_session, int a_threadId);
+
+void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame);
+void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
+void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
+void gmDebuggerEndContext(gmDebugSession * a_session);
+
+void gmDebuggerBeginSourceInfo(gmDebugSession * a_session);
+void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName);
+void gmDebuggerEndSourceInfo(gmDebugSession * a_session);
+
+void gmDebuggerBeginThreadInfo(gmDebugSession * a_session);
+void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState);
+void gmDebuggerEndThreadInfo(gmDebugSession * a_session);
+
+void gmDebuggerError(gmDebugSession * a_session, const char * a_error);
+void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message);
+void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg);
+void gmDebuggerQuit(gmDebugSession * a_session);
+
+//
+// debug machine callback
+//
+
+enum gmdThreadFlags
+{
+  TF_STEPOVER  = (1 << 0),
+  TF_STEPINTO  = (1 << 1),
+  TF_STEPOUT   = (1 << 2),
+  TF_BREAK     = (1 << 3),
+};
+
+// the following callbacks return true if the thread is to yield after completion of the callback.
+static bool LineCallback(gmThread * a_thread)
+{
+  gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
+  GM_ASSERT(session);
+
+  if(!(a_thread->m_debugFlags & TF_STEPOVER) ||
+     (a_thread->m_debugUser != ((a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0)))
+  {
+    int * bp = session->FindBreakPoint((void *) a_thread->GetInstruction());
+    if(bp == NULL)
+      return false;
+
+    if(*bp && *bp != a_thread->GetId())
+      return false;
+  }
+
+  a_thread->m_debugFlags = TF_BREAK;
+  const gmFunctionObject * fn = a_thread->GetFunctionObject();
+  gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
+  return true;
+}
+
+static bool CallCallback(gmThread * a_thread)
+{
+  gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
+  GM_ASSERT(session);
+  if(a_thread->m_debugFlags & TF_STEPINTO)
+  {
+    a_thread->m_debugFlags = TF_BREAK;
+    const gmFunctionObject * fn = a_thread->GetFunctionObject();
+    gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
+    return true;
+  }
+  return false;
+}
+
+static bool RetCallback(gmThread * a_thread)
+{
+  gmDebugSession * session = (gmDebugSession *) a_thread->GetMachine()->m_debugUser;
+  GM_ASSERT(session);
+  if(((a_thread->m_debugFlags & TF_STEPOUT) && (a_thread->m_debugUser == a_thread->GetIntBase())) ||
+     ((a_thread->m_debugFlags & TF_STEPOVER) && (a_thread->m_debugUser == a_thread->GetIntBase())))
+  {
+    a_thread->m_debugFlags = TF_BREAK;
+    const gmFunctionObject * fn = a_thread->GetFunctionObject();
+    gmDebuggerBreak(session, a_thread->GetId(), fn->GetSourceId(), fn->GetLine(a_thread->GetInstruction()));
+    return true;
+  }
+  return false;
+}
+
+static bool IsBrokenCallback(gmThread * a_thread)
+{
+  return (a_thread->m_debugFlags & TF_BREAK) > 0;
+}
+
+static gmMachineCallback s_prevMachineCallback = NULL;
+bool GM_CDECL gmdMachineCallback(gmMachine * a_machine, gmMachineCommand a_command, const void * a_context)
+{
+  gmDebugSession * session = (gmDebugSession *) a_machine->m_debugUser;
+  const gmThread * thread = (const gmThread *) a_context;
+
+  // chain callback
+  if(s_prevMachineCallback) s_prevMachineCallback(a_machine, a_command, a_context);
+
+  // do we have a debug session?
+  if(session == NULL) return false;
+
+  // command
+  switch(a_command)
+  {
+    case MC_THREAD_EXCEPTION :
+    {
+      // send thread exception message
+      gmDebuggerException(session, thread->GetId());
+      a_machine->GetLog();
+      bool first = true;
+      const char * entry;
+      while((entry = a_machine->GetLog().GetEntry(first)))
+      {
+        gmDebuggerError(session, entry);
+      }
+      return true;
+    }
+    case MC_THREAD_CREATE :
+    {
+      gmDebuggerRun(session, thread->GetId());
+      break;
+    }
+    case MC_THREAD_DESTROY :
+    {
+      gmDebuggerStop(session, thread->GetId());
+      break;
+    }
+    default : break;
+  };
+
+  return false;
+}
+
+//
+// debug session
+//
+
+gmDebugSession::gmDebugSession() : 
+  m_breaks(32)
+{
+  m_machine = NULL;
+}
+
+
+gmDebugSession::~gmDebugSession()
+{
+  m_breaks.RemoveAndDeleteAll();
+}
+
+
+void gmDebugSession::Update()
+{
+  for(;;)
+  {
+    int len;
+    const void * msg = m_pumpMessage(this, len);
+    if(msg == NULL)
+      break;
+
+    m_in.Open(msg, len);
+
+    // parse the message
+    int id, pa, pb, pc, pd;
+    Unpack(id);
+    switch(id)
+    {
+      case ID_mrun :
+        Unpack(id);
+        gmMachineRun(this, id);
+        break;
+      case ID_msin :
+        Unpack(id);
+        gmMachineStepInto(this, id);
+        break;
+      case ID_msou :
+        Unpack(id);
+        gmMachineStepOut(this, id);
+        break;
+      case ID_msov :
+        Unpack(id);
+        gmMachineStepOver(this, id);
+        break;
+      case ID_mgct :
+        Unpack(id).Unpack(pa);
+        gmMachineGetContext(this, id, pa);
+        break;
+      case ID_mgsr :
+        Unpack(id);
+        gmMachineGetSource(this, id);
+        break;
+      case ID_mgsi :
+        gmMachineGetSourceInfo(this);
+        break;
+      case ID_mgti :
+        gmMachineGetThreadInfo(this);
+        break;
+      case ID_mgvi :
+        Unpack(id);
+        gmMachineGetVariableInfo(this, id);
+        break;
+      case ID_msbp :
+        Unpack(pa).Unpack(pb).Unpack(pc).Unpack(id).Unpack(pd);
+        gmMachineSetBreakPoint(this, pa, pb, pc, id, pd);
+        break;
+      case ID_mbrk :
+        Unpack(id);
+        gmMachineBreak(this, id);
+        break;
+      case ID_mend :
+        gmMachineQuit(this);
+        break;
+      default:;
+    }
+  }
+}
+
+
+bool gmDebugSession::Open(gmMachine * a_machine)
+{
+  Close();
+  m_machine = a_machine;
+  m_machine->m_debugUser = this;
+  m_machine->m_line = LineCallback;
+  m_machine->m_call = CallCallback;
+  m_machine->m_isBroken = IsBrokenCallback;
+  m_machine->m_return = RetCallback;
+  s_prevMachineCallback = a_machine->s_machineCallback;
+  a_machine->s_machineCallback = gmdMachineCallback;
+  return true;
+}
+
+
+static bool threadIterClose(gmThread * a_thread, void * a_context)
+{
+  a_thread->m_debugFlags = 0;
+  a_thread->m_debugUser = 0;
+  return true;
+}
+
+
+bool gmDebugSession::Close()
+{
+  if(m_machine && m_machine->m_debugUser == this)
+  {
+    gmDebuggerQuit(this);
+
+    m_machine->m_debugUser = NULL;
+    m_machine->s_machineCallback = s_prevMachineCallback;
+
+    m_machine->m_line = NULL;
+    m_machine->m_call = NULL;
+    m_machine->m_return = NULL;
+    m_machine->m_isBroken = NULL;
+
+    m_machine->KillExceptionThreads();
+    m_machine->ForEachThread(threadIterClose, NULL);
+    m_machine = NULL;
+
+    m_breaks.RemoveAndDeleteAll();
+    m_out.ResetAndFreeMemory();
+
+    return true;
+  }
+
+  m_breaks.RemoveAndDeleteAll();
+  m_out.ResetAndFreeMemory();
+
+  return false;
+}
+
+
+gmDebugSession &gmDebugSession::Pack(int a_val)
+{
+  m_out << a_val;
+  return *this;
+}
+
+
+gmDebugSession &gmDebugSession::Pack(const char * a_val)
+{
+  if(a_val)
+    m_out.Write(a_val, strlen(a_val) + 1);
+  else
+    m_out.Write("", 1);
+  return *this;
+}
+
+
+void gmDebugSession::Send()
+{
+  m_sendMessage(this, m_out.GetData(), m_out.GetSize());
+  m_out.Reset();
+}
+
+
+gmDebugSession &gmDebugSession::Unpack(int &a_val)
+{
+  if(m_in.Read(&a_val, 4) != 4) a_val = 0;
+  return *this;
+}
+
+
+gmDebugSession &gmDebugSession::Unpack(const char * &a_val)
+{
+  // this is dangerous!!!
+  a_val = &m_in.GetData()[m_in.Tell()];
+  int len = strlen(a_val);
+  m_in.Seek(m_in.Tell() + len + 1);
+  return *this;
+}
+
+
+bool gmDebugSession::AddBreakPoint(const void * a_bp, int a_threadId)
+{
+  BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
+  if(bp) return false;
+  bp = GM_NEW( BreakPoint() );
+  bp->m_bp = a_bp;
+  bp->m_threadId = a_threadId;
+  m_breaks.Insert(bp);
+  return true;
+}
+
+
+int * gmDebugSession::FindBreakPoint(const void * a_bp)
+{
+  BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
+  if(bp)
+  {
+    return &bp->m_threadId;
+  }
+  return NULL;
+}
+
+
+bool gmDebugSession::RemoveBreakPoint(const void * a_bp)
+{
+  BreakPoint * bp = m_breaks.Find((void *const&)a_bp);
+  if(bp)
+  {
+    m_breaks.Remove(bp);
+    delete bp;
+    return true;
+  }
+  return false;
+}
+
+//
+// implementation
+//
+
+void gmMachineRun(gmDebugSession * a_session, int a_threadId)
+{
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    thread->m_debugFlags = 0;
+  }
+}
+
+void gmMachineStepInto(gmDebugSession * a_session, int a_threadId)
+{
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
+    thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
+  }
+}
+
+void gmMachineStepOver(gmDebugSession * a_session, int a_threadId)
+{
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
+    thread->m_debugFlags = TF_STEPOVER;
+  }
+}
+
+void gmMachineStepOut(gmDebugSession * a_session, int a_threadId)
+{
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
+    thread->m_debugFlags = TF_STEPOUT;
+  }
+}
+
+void gmMachineGetContext(gmDebugSession * a_session, int a_threadId, int a_callframe)
+{
+  const int buffSize = 256;
+  char buff[buffSize];  // buff is used for AsString
+
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    // count the number of frames on the thread
+    int numFrames = 0;
+    const gmStackFrame * frame = thread->GetFrame();
+    while(frame)
+    {
+      ++numFrames;
+      frame = frame->m_prev;
+    }
+
+    // if a valid frame was requested, fill out a context.
+    if(a_callframe >= 0 && a_callframe <= numFrames)
+    {
+      gmDebuggerBeginContext(a_session, a_threadId, a_callframe);
+
+      // pack frames
+      frame = thread->GetFrame();
+      numFrames = 0;
+
+      gmVariable * base = thread->GetBase();
+      const gmuint8 * ip = thread->GetInstruction();
+
+      while(frame)
+      {
+        // get the function object
+        gmVariable * fnVar = base - 1;
+        if(fnVar->m_type == GM_FUNCTION)
+        {
+          gmFunctionObject * fn = (gmFunctionObject *) GM_MOBJECT(thread->GetMachine(), fnVar->m_value.m_ref);
+
+          // this
+          base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
+          gmDebuggerContextCallFrame(a_session, numFrames, fn->GetDebugName(), fn->GetSourceId(), fn->GetLine(ip), "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
+
+          if(numFrames == a_callframe)
+          {
+            // this is the active frame, fill out the variables
+            int i;
+            for(i = 0; i < fn->GetNumParamsLocals(); ++i)
+            {
+              base[i].AsStringWithType(thread->GetMachine(), buff, buffSize);
+              gmDebuggerContextVariable(a_session, fn->GetSymbol(i), buff, (base[i].IsReference()) ? base[i].m_value.m_ref : 0);
+            }
+          }
+        }
+        else
+        {
+          base[-2].AsStringWithType(thread->GetMachine(), buff, buffSize);
+          gmDebuggerContextCallFrame(a_session, numFrames, "unknown", 0, 0, "this", buff, (base[-2].IsReference()) ? base[-2].m_value.m_ref : 0);
+        }
+
+        // next call frame
+        ++numFrames;
+        base = thread->GetBottom() + frame->m_returnBase;
+        ip = frame->m_returnAddress;
+        frame = frame->m_prev;
+      }
+
+      gmDebuggerEndContext(a_session);
+    }
+  }
+}
+
+void gmMachineGetSource(gmDebugSession * a_session, int a_sourceId)
+{
+  const char * source;
+  const char * filename;
+  if(a_session->GetMachine()->GetSourceCode(a_sourceId, source, filename))
+  {
+    gmDebuggerSource(a_session, a_sourceId, filename, source);
+  }
+}
+
+void gmMachineGetSourceInfo(gmDebugSession * a_session)
+{
+  // todo
+}
+
+
+static bool threadIter(gmThread * a_thread, void * a_context)
+{
+  gmDebugSession * session = (gmDebugSession *) a_context;
+  int state = 0; // 0 - running, 1 - blocked, 2 - sleeping, 3 - exception, 4 - debug
+  if(a_thread->m_debugFlags)
+    state = 4;
+  else if(a_thread->GetState() == gmThread::EXCEPTION)
+    state = 3;
+  else if(a_thread->GetState() == gmThread::RUNNING)
+    state = 0;
+  else if(a_thread->GetState() == gmThread::BLOCKED)
+    state = 1;
+  else if(a_thread->GetState() == gmThread::SLEEPING)
+    state = 2;
+  else
+    state = 3;
+  gmDebuggerThreadInfo(session, a_thread->GetId(), state);
+  return true;
+}
+
+
+void gmMachineGetThreadInfo(gmDebugSession * a_session)
+{
+  gmDebuggerBeginThreadInfo(a_session);
+  a_session->GetMachine()->ForEachThread(threadIter, a_session);
+  gmDebuggerEndThreadInfo(a_session);
+}
+
+void gmMachineGetVariableInfo(gmDebugSession * a_session, int a_variableId)
+{
+  // todo
+}
+
+void gmMachineSetBreakPoint(gmDebugSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
+{
+  bool sendAck = false;
+
+  // get break point
+  const void * bp = (const void *) a_session->GetMachine()->GetInstructionAtBreakPoint(a_sourceId, a_lineNumber);
+  if(bp)
+  {
+    // get to next instruction
+    bp = (const void *) (((const char *) bp) + 4);
+
+    int * id = a_session->FindBreakPoint(bp);
+    if(id)
+    {
+      if(!a_enabled)
+      {
+        a_session->RemoveBreakPoint(bp);
+        sendAck = true;
+      }
+    }
+    else
+    {
+      if(a_session->AddBreakPoint(bp, a_threadId))
+      {
+        sendAck = true;
+      }
+    }
+  }
+
+  if(sendAck)
+    gmDebuggerAck(a_session, a_responseId, 1);
+  else
+    gmDebuggerAck(a_session, a_responseId, 0);
+}
+
+void gmMachineBreak(gmDebugSession * a_session, int a_threadId)
+{
+  gmThread * thread = a_session->GetMachine()->GetThread(a_threadId);
+  if(thread)
+  {
+    thread->m_debugUser = (thread->GetFrame()) ? thread->GetFrame()->m_returnBase : 0;
+    thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
+  }
+}
+
+void gmMachineQuit(gmDebugSession * a_session)
+{
+  a_session->Close();
+}
+
+void gmDebuggerBreak(gmDebugSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber) {
+  a_session->Pack(ID_dbrk).Pack(a_threadId).Pack(a_sourceId).Pack(a_lineNumber).Send();
+}
+void gmDebuggerException(gmDebugSession * a_session, int a_threadId) {
+  a_session->Pack(ID_dexc).Pack(a_threadId).Send();
+}
+void gmDebuggerRun(gmDebugSession * a_session, int a_threadId) {
+  a_session->Pack(ID_drun).Pack(a_threadId).Send();
+}
+void gmDebuggerStop(gmDebugSession * a_session, int a_threadId) {
+  a_session->Pack(ID_dstp).Pack(a_threadId).Send();
+}
+void gmDebuggerSource(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source) {
+  a_session->Pack(ID_dsrc).Pack(a_sourceId).Pack(a_sourceName).Pack(a_source).Send();
+}
+void gmDebuggerBeginContext(gmDebugSession * a_session, int a_threadId, int a_callFrame) {
+  a_session->Pack(ID_dctx).Pack(a_threadId).Pack(a_callFrame);
+}
+void gmDebuggerContextCallFrame(gmDebugSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId) {
+  a_session->Pack(ID_call).Pack(a_callFrame).Pack(a_functionName).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_thisSymbol).Pack(a_thisValue).Pack(a_thisId);
+}
+void gmDebuggerContextVariable(gmDebugSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId) {
+  a_session->Pack(ID_vari).Pack(a_varSymbol).Pack(a_varValue).Pack(a_varId);
+}
+void gmDebuggerEndContext(gmDebugSession * a_session) {
+  a_session->Pack(ID_done).Send();
+}
+void gmDebuggerBeginSourceInfo(gmDebugSession * a_session) {
+  a_session->Pack(ID_dsri);
+}
+void gmDebuggerSourceInfo(gmDebugSession * a_session, int a_sourceId, const char * a_sourceName) {
+  a_session->Pack(ID_srci).Pack(a_sourceId).Pack(a_sourceName);
+}
+void gmDebuggerEndSourceInfo(gmDebugSession * a_session) {
+  a_session->Pack(ID_done).Send();
+}
+void gmDebuggerBeginThreadInfo(gmDebugSession * a_session) {
+  a_session->Pack(ID_dthi);
+}
+void gmDebuggerThreadInfo(gmDebugSession * a_session, int a_threadId, int a_threadState) {
+  a_session->Pack(ID_thri).Pack(a_threadId).Pack(a_threadState);
+}
+void gmDebuggerEndThreadInfo(gmDebugSession * a_session) {
+  a_session->Pack(ID_done).Send();
+}
+void gmDebuggerError(gmDebugSession * a_session, const char * a_error) {
+  a_session->Pack(ID_derr).Pack(a_error).Send();
+}
+void gmDebuggerMessage(gmDebugSession * a_session, const char * a_message) {
+  a_session->Pack(ID_dmsg).Pack(a_message).Send();
+}
+void gmDebuggerAck(gmDebugSession * a_session, int a_response, int a_posNeg) {
+  a_session->Pack(ID_dack).Pack(a_response).Pack(a_posNeg).Send();
+}
+void gmDebuggerQuit(gmDebugSession * a_session) {
+  a_session->Pack(ID_dend).Send();
+}
+
+
+//
+// lib binding
+//
+
+
+int GM_CDECL gmdDebug(gmThread * a_thread)
+{
+  // if the machine has a debug session, attach a debug hook to the thread
+  if(a_thread->GetMachine()->m_debugUser && a_thread->GetMachine()->GetDebugMode())
+  {
+    a_thread->m_debugUser = (a_thread->GetFrame()) ? a_thread->GetFrame()->m_returnBase : 0;
+    a_thread->m_debugFlags = TF_STEPINTO | TF_STEPOVER;
+  }
+  return GM_OK;
+}
+
+
+
+static gmFunctionEntry s_debugLib[] = 
+{ 
+  /*gm
+    \lib gm
+    \brief functions in the gm lib are all global scope
+  */
+ 
+   /*gm
+    \function debug
+    \brief debug will cause a the debugger to break at this point while running.
+  */
+  
+  {"debug", gmdDebug},
+};
+
+
+
+void gmBindDebugLib(gmMachine * a_machine)
+{
+  a_machine->RegisterLibrary(s_debugLib, sizeof(s_debugLib) / sizeof(s_debugLib[0]));
+}
+
+#endif

+ 88 - 0
gmsrc/src/gm/gmDebug.h

@@ -0,0 +1,88 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMDEBUG_H_
+#define _GMDEBUG_H_
+
+#include "gmConfig.h"
+#include "gmStreamBuffer.h"
+#include "gmHash.h"
+
+class gmMachine;
+class gmDebugSession;
+
+// bind debug lib
+void gmBindDebugLib(gmMachine * a_machine);
+
+// callbacks used to hook up comms
+typedef void (GM_CDECL *gmSendDebuggerMessage)(gmDebugSession * a_session, const void * a_command, int a_len);
+typedef const void * (GM_CDECL *gmPumpDebuggerMessage)(gmDebugSession * a_session, int &a_len);
+
+#if GMDEBUG_SUPPORT
+
+/// \class gmDebugSession
+class gmDebugSession
+{
+public:
+
+  gmDebugSession();
+  ~gmDebugSession();
+
+  /// \brief Update() must be called to pump messages
+  void Update();
+
+  /// \brief Open() will start debugging on a_machine
+  bool Open(gmMachine * a_machine);
+  
+  /// \brief Close() will stop debugging
+  bool Close();
+
+  /// \brief GetMachine()
+  inline gmMachine * GetMachine() const { return m_machine; }
+
+  gmSendDebuggerMessage m_sendMessage;
+  gmPumpDebuggerMessage m_pumpMessage;
+  void * m_user;
+
+  // send message helpers
+  gmDebugSession &Pack(int a_val);
+  gmDebugSession &Pack(const char * a_val);
+  void Send();
+
+  // rcv message helpers
+  gmDebugSession &Unpack(int &a_val);
+  gmDebugSession &Unpack(const char * &a_val);
+
+  // helpers
+  bool AddBreakPoint(const void * a_bp, int a_threadId);
+  int * FindBreakPoint(const void * a_bp); // return thread id
+  bool RemoveBreakPoint(const void * a_bp);
+
+private:
+
+  class BreakPoint : public gmHashNode<void *, BreakPoint>
+  {
+  public:
+    inline const void * GetKey() const { return m_bp; }
+    const void * m_bp;
+    int m_threadId;
+  };
+
+  gmMachine * m_machine;
+  
+  gmHash<void *, BreakPoint> m_breaks;
+  gmStreamBufferDynamic m_out;
+  gmStreamBufferStatic m_in;
+};
+
+#endif
+
+#endif // _GMDEBUG_H_

+ 321 - 0
gmsrc/src/gm/gmDebugger.cpp

@@ -0,0 +1,321 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include <string.h>
+#include "gmDebugger.h"
+
+//
+// Please note that gmDebugger.c/.h are for implementing
+// a debugger application and should not be included
+// in an normal GM application build.
+//
+
+#ifndef GM_MAKE_ID32
+  #define GM_MAKE_ID32( a, b, c, d )  ( ((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
+#endif //GM_MAKE_ID32
+
+#define ID_mrun GM_MAKE_ID32('m','r','u','n')
+#define ID_msin GM_MAKE_ID32('m','s','i','n')
+#define ID_msou GM_MAKE_ID32('m','s','o','u')
+#define ID_msov GM_MAKE_ID32('m','s','o','v')
+#define ID_mgct GM_MAKE_ID32('m','g','c','t')
+#define ID_mgsr GM_MAKE_ID32('m','g','s','r')
+#define ID_mgsi GM_MAKE_ID32('m','g','s','i')
+#define ID_mgti GM_MAKE_ID32('m','g','t','i')
+#define ID_mgvi GM_MAKE_ID32('m','g','v','i')
+#define ID_msbp GM_MAKE_ID32('m','s','b','p')
+#define ID_mbrk GM_MAKE_ID32('m','b','r','k')
+#define ID_mend GM_MAKE_ID32('m','e','n','d')
+
+#define ID_dbrk GM_MAKE_ID32('d','b','r','k')
+#define ID_dexc GM_MAKE_ID32('d','e','x','c')
+#define ID_drun GM_MAKE_ID32('d','r','u','n')
+#define ID_dstp GM_MAKE_ID32('d','s','t','p')
+#define ID_dsrc GM_MAKE_ID32('d','s','r','c')
+#define ID_dctx GM_MAKE_ID32('d','c','t','x')
+#define ID_call GM_MAKE_ID32('c','a','l','l')
+#define ID_vari GM_MAKE_ID32('v','a','r','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_dsri GM_MAKE_ID32('d','s','r','i')
+#define ID_srci GM_MAKE_ID32('s','r','c','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_dthi GM_MAKE_ID32('d','t','h','i')
+#define ID_thri GM_MAKE_ID32('t','h','r','i')
+#define ID_done GM_MAKE_ID32('d','o','n','e')
+#define ID_derr GM_MAKE_ID32('d','e','r','r')
+#define ID_dmsg GM_MAKE_ID32('d','m','s','g')
+#define ID_dack GM_MAKE_ID32('d','a','c','k')
+#define ID_dend GM_MAKE_ID32('d','e','n','d')
+
+//
+// Please note that gmDebugger.c/.h are for implementing
+// a debugger application and should not be included
+// in an normal GM application build.
+//
+
+
+gmDebuggerSession::gmDebuggerSession()
+{
+  m_outSize = 256;
+  m_out = (void*) new char[m_outSize];
+  m_outCursor = 0;
+  m_in = NULL;
+  m_inCursor = m_inSize = NULL;
+}
+
+
+gmDebuggerSession::~gmDebuggerSession()
+{
+  if(m_out)
+  {
+    delete [] (char*)m_out;
+  }
+}
+
+
+void gmDebuggerSession::Update()
+{
+  for(;;)
+  {
+    m_in = m_pumpMessage(this, m_inSize);
+    if(m_in == NULL) break;
+    m_inCursor = 0;
+
+    int id, pa, pb, pc;
+    const char * sa, * sb, * sc;
+
+    Unpack(id);
+    switch(id)
+    {
+      case ID_dbrk :
+        Unpack(id).Unpack(pa).Unpack(pb);
+        gmDebuggerBreak(this, id, pa, pb);
+        break;
+      case ID_drun :
+        Unpack(id);
+        gmDebuggerRun(this, id);
+        break;
+      case ID_dstp :
+        Unpack(id);
+        gmDebuggerStop(this, id);
+        break;
+      case ID_dsrc :
+        Unpack(id).Unpack(sa).Unpack(sb);
+        gmDebuggerSource(this, id, sa, sb);
+        break;
+      case ID_dexc :
+        Unpack(id);
+        gmDebuggerException(this, id);
+        break;
+      case ID_dctx :
+        Unpack(id).Unpack(pa); // thread id, callframe
+        gmDebuggerBeginContext(this, id, pa);
+        for(;;)
+        {
+          Unpack(id);
+          if(id == ID_call)
+          {
+            Unpack(id).Unpack(sa).Unpack(pa).Unpack(pb).Unpack(sb).Unpack(sc).Unpack(pc);
+            gmDebuggerContextCallFrame(this, id, sa, pa, pb, sb, sc, pc);
+          }
+          else if(id == ID_vari)
+          {
+            Unpack(sa).Unpack(sb).Unpack(pa);
+            gmDebuggerContextVariable(this, sa, sb, pa);
+          }
+          else if(id == ID_done) break;
+          else break;
+        }
+        gmDebuggerEndContext(this);
+        break;
+      case ID_dsri :
+        // todo
+        break;
+      case ID_dthi :
+        gmDebuggerBeginThreadInfo(this);
+        for(;;)
+        {
+          Unpack(id);
+          if(id == ID_thri)
+          {
+            Unpack(pa).Unpack(pb);
+            gmDebuggerThreadInfo(this, pa, pb);
+          }
+          else if(id == ID_done) break;
+          else break;
+        }
+        gmDebuggerEndThreadInfo(this);
+        break;
+      case ID_derr :
+        Unpack(sa);
+        gmDebuggerError(this, sa);
+        break;
+      case ID_dmsg :
+        Unpack(sa);
+        gmDebuggerMessage(this, sa);
+        break;
+      case ID_dack :
+        Unpack(pa).Unpack(pb);
+        gmDebuggerAck(this, pa, pb);
+        break;
+      case ID_dend :
+        gmDebuggerQuit(this);
+        break;
+      default:;
+    }
+  }
+}
+
+
+bool gmDebuggerSession::Open()
+{
+  m_outCursor = 0;
+  return true;
+}
+
+
+bool gmDebuggerSession::Close()
+{
+  return true;
+}
+
+
+gmDebuggerSession &gmDebuggerSession::Pack(int a_val)
+{
+  Need(4);
+  memcpy((char *) m_out + m_outCursor, &a_val, 4);
+  m_outCursor += 4;
+  return *this;
+}
+
+
+gmDebuggerSession &gmDebuggerSession::Pack(const char * a_val)
+{
+  if(a_val)
+  {
+    int len = strlen(a_val) + 1;
+    Need(len);
+    memcpy((char *) m_out + m_outCursor, a_val, len);
+    m_outCursor += len;
+  }
+  else
+  {
+    Need(1);
+    memcpy((char *) m_out + m_outCursor, "", 1);
+    m_outCursor += 1;
+  }
+  return *this;
+}
+
+
+void gmDebuggerSession::Send()
+{
+  m_sendMessage(this, m_out, m_outCursor);
+  m_outCursor = 0;
+}
+
+
+gmDebuggerSession &gmDebuggerSession::Unpack(int &a_val)
+{
+  if(m_inCursor + 4 <= m_inSize)
+  {
+    memcpy(&a_val, (const char *) m_in + m_inCursor, 4);
+    m_inCursor += 4;
+  }
+  else
+  {
+    a_val = 0;
+  }
+  return *this;
+}
+
+
+gmDebuggerSession &gmDebuggerSession::Unpack(const char * &a_val)
+{
+  a_val = (const char *) m_in + m_inCursor;
+  m_inCursor += strlen(a_val) + 1;
+  return *this;
+}
+
+
+void gmDebuggerSession::Need(int a_bytes)
+{
+  if((m_outCursor + a_bytes) >= m_outSize)
+  {
+    int newSize = m_outSize + a_bytes + 256;
+    void * buffer = (void*)new char[newSize];
+    memcpy(buffer, m_out, m_outCursor);
+    delete [] (char*)m_out;
+    m_out = buffer;
+    m_outSize = newSize;
+  }
+}
+
+
+void gmMachineRun(gmDebuggerSession * a_session, int a_threadId)
+{
+  a_session->Pack(ID_mrun).Pack(a_threadId).Send();
+}
+
+void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId)
+{
+  a_session->Pack(ID_msin).Pack(a_threadId).Send();
+}
+
+void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId)
+{
+  a_session->Pack(ID_msov).Pack(a_threadId).Send();
+}
+
+void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId)
+{
+  a_session->Pack(ID_msou).Pack(a_threadId).Send();
+}
+
+void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe)
+{
+  a_session->Pack(ID_mgct).Pack(a_threadId).Pack(a_callframe).Send();
+}
+
+void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId)
+{
+  a_session->Pack(ID_mgsr).Pack(a_sourceId).Send();
+}
+
+void gmMachineGetSourceInfo(gmDebuggerSession * a_session)
+{
+  a_session->Pack(ID_mgsi).Send();
+}
+
+void gmMachineGetThreadInfo(gmDebuggerSession * a_session)
+{
+  a_session->Pack(ID_mgti).Send();
+}
+
+void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId)
+{
+  a_session->Pack(ID_mgvi).Pack(a_variableId).Send();
+}
+
+void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled)
+{
+  a_session->Pack(ID_msbp).Pack(a_responseId).Pack(a_sourceId).Pack(a_lineNumber).Pack(a_threadId).Pack(a_enabled).Send();
+}
+
+void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId)
+{
+  a_session->Pack(ID_mbrk).Pack(a_threadId).Send();
+}
+
+void gmMachineQuit(gmDebuggerSession * a_session)
+{
+  a_session->Pack(ID_mend).Send();
+}

+ 114 - 0
gmsrc/src/gm/gmDebugger.h

@@ -0,0 +1,114 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMDEBUGGER_H_
+#define _GMDEBUGGER_H_
+
+//
+// Please note that gmDebugger.c/.h are for implementing
+// a debugger application and should not be included
+// in an normal GM application build.
+//
+
+
+class gmDebuggerSession;
+
+// callbacks used to hook up comms
+typedef void (*gmSendMachineMessage)(gmDebuggerSession * a_session, const void * a_command, int a_len);
+typedef const void * (*gmPumpMachineMessage)(gmDebuggerSession * a_session, int &a_len);
+
+/// \class gmDebuggerSession
+class gmDebuggerSession
+{
+public:
+
+  gmDebuggerSession();
+  ~gmDebuggerSession();
+
+  /// \brief Update() must be called to pump messages
+  void Update();
+
+  /// \brief Open() will start debugging
+  bool Open();
+  
+  /// \brief Close() will stop debugging
+  bool Close();
+
+  gmSendMachineMessage m_sendMessage;
+  gmPumpMachineMessage m_pumpMessage;
+  void * m_user; // hook to your debugger
+
+  // send message helpers
+  gmDebuggerSession &Pack(int a_val);
+  gmDebuggerSession &Pack(const char * a_val);
+  void Send();
+
+  // rcv message helpers
+  gmDebuggerSession &Unpack(int &a_val);
+  gmDebuggerSession &Unpack(const char * &a_val);
+
+private:
+
+  void * m_out;
+  int m_outCursor, m_outSize;
+  void Need(int a_bytes);
+
+  const void * m_in;
+  int m_inCursor, m_inSize;
+};
+
+//
+// the debugger must implement the following functions
+//
+
+extern void gmDebuggerBreak(gmDebuggerSession * a_session, int a_threadId, int a_sourceId, int a_lineNumber);
+extern void gmDebuggerRun(gmDebuggerSession * a_session, int a_threadId);
+extern void gmDebuggerStop(gmDebuggerSession * a_session, int a_threadId);
+extern void gmDebuggerSource(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName, const char * a_source);
+extern void gmDebuggerException(gmDebuggerSession * a_session, int a_threadId);
+
+extern void gmDebuggerBeginContext(gmDebuggerSession * a_session, int a_threadId, int a_callFrame);
+extern void gmDebuggerContextCallFrame(gmDebuggerSession * a_session, int a_callFrame, const char * a_functionName, int a_sourceId, int a_lineNumber, const char * a_thisSymbol, const char * a_thisValue, int a_thisId);
+extern void gmDebuggerContextVariable(gmDebuggerSession * a_session, const char * a_varSymbol, const char * a_varValue, int a_varId);
+extern void gmDebuggerEndContext(gmDebuggerSession * a_session);
+
+extern void gmDebuggerBeginSourceInfo(gmDebuggerSession * a_session);
+extern void gmDebuggerSourceInfo(gmDebuggerSession * a_session, int a_sourceId, const char * a_sourceName);
+extern void gmDebuggerEndSourceInfo(gmDebuggerSession * a_session);
+
+extern void gmDebuggerBeginThreadInfo(gmDebuggerSession * a_session);
+extern void gmDebuggerThreadInfo(gmDebuggerSession * a_session, int a_threadId, int a_threadState);
+extern void gmDebuggerEndThreadInfo(gmDebuggerSession * a_session);
+
+extern void gmDebuggerError(gmDebuggerSession * a_session, const char * a_error);
+extern void gmDebuggerMessage(gmDebuggerSession * a_session, const char * a_message);
+extern void gmDebuggerAck(gmDebuggerSession * a_session, int a_response, int a_posNeg);
+extern void gmDebuggerQuit(gmDebuggerSession * a_session);
+
+//
+// the debugger can use the following functions to send messages to the machine
+//
+
+void gmMachineRun(gmDebuggerSession * a_session, int a_threadId);
+void gmMachineStepInto(gmDebuggerSession * a_session, int a_threadId);
+void gmMachineStepOver(gmDebuggerSession * a_session, int a_threadId);
+void gmMachineStepOut(gmDebuggerSession * a_session, int a_threadId);
+void gmMachineGetContext(gmDebuggerSession * a_session, int a_threadId, int a_callframe);
+void gmMachineGetSource(gmDebuggerSession * a_session, int a_sourceId);
+void gmMachineGetSourceInfo(gmDebuggerSession * a_session);
+void gmMachineGetThreadInfo(gmDebuggerSession * a_session);
+void gmMachineGetVariableInfo(gmDebuggerSession * a_session, int a_variableId);
+void gmMachineSetBreakPoint(gmDebuggerSession * a_session, int a_responseId, int a_sourceId, int a_lineNumber, int a_threadId, int a_enabled);
+void gmMachineBreak(gmDebuggerSession * a_session, int a_threadId);
+void gmMachineQuit(gmDebuggerSession * a_session);
+
+#endif
+

+ 282 - 0
gmsrc/src/gm/gmFunctionObject.cpp

@@ -0,0 +1,282 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmFunctionObject.h"
+#include "gmMachine.h"
+
+gmFunctionObject::gmFunctionObject()
+{
+  m_cFunction = NULL;
+  m_cUserData = NULL;
+  m_debugInfo = NULL;
+  m_byteCode = NULL;
+  m_byteCodeLength = 0;
+  m_maxStackSize = 1; // return value
+  m_numLocals = 0;
+  m_numParams = 0;
+  m_numParamsLocals = 0;
+  m_numReferences = 0;
+  m_references = NULL;
+}
+
+void gmFunctionObject::Destruct(gmMachine * a_machine)
+{
+  if(m_references)
+  {
+    a_machine->Sys_Free(m_references);
+    m_references = NULL;
+  }
+  if(m_byteCode)
+  {
+    a_machine->Sys_Free(m_byteCode);
+    m_byteCode = NULL;
+  }
+  if(m_debugInfo)
+  {
+    if(m_debugInfo->m_debugName) { a_machine->Sys_Free(m_debugInfo->m_debugName); }
+    if(m_debugInfo->m_lineInfo) { a_machine->Sys_Free(m_debugInfo->m_lineInfo); }
+    if(m_debugInfo->m_symbols)
+    {
+      int i;
+      for(i = 0; i < m_numParamsLocals; ++i)
+      {
+        a_machine->Sys_Free(m_debugInfo->m_symbols[i]);
+      }
+      a_machine->Sys_Free(m_debugInfo->m_symbols);
+    }
+    a_machine->Sys_Free(m_debugInfo);
+    m_debugInfo = NULL;
+  }
+
+#if GM_USE_INCGC
+  a_machine->DestructDeleteObject(this);
+#endif //GM_USE_INCGC
+}
+
+#if GM_USE_INCGC
+
+
+bool gmFunctionObject::Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
+{
+  int i;
+  for(i = 0; i < m_numReferences; ++i)
+  {
+    gmObject * object = a_machine->GetObject(m_references[i]);
+    a_gc->GetNextObject(object);
+    ++a_workDone;
+  }
+    
+  ++a_workDone;
+  return true;
+}
+
+#else //GM_USE_INCGC
+
+void gmFunctionObject::Mark(gmMachine * a_machine, gmuint32 a_mark)
+{
+  if(m_mark != GM_MARK_PERSIST) m_mark = a_mark;
+  int i;
+  for(i = 0; i < m_numReferences; ++i)
+  {
+    gmObject * object = a_machine->GetObject(m_references[i]);
+    if(object->NeedsMark(a_mark)) object->Mark(a_machine, a_mark);
+  }
+}
+#endif //GM_USE_INCGC
+
+
+bool gmFunctionObject::Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId)
+{
+  // byte code
+  if(a_info.m_byteCodeLength)
+  {
+    m_byteCode = (gmuint8 *) a_machine->Sys_Alloc(a_info.m_byteCodeLength);
+    memcpy(m_byteCode, a_info.m_byteCode, a_info.m_byteCodeLength);
+    m_byteCodeLength = a_info.m_byteCodeLength;
+  }
+  else
+  {
+    m_byteCode = NULL;
+    m_byteCodeLength = 0;
+  }
+
+  // stack info
+  m_maxStackSize = a_info.m_maxStackSize;
+  m_numLocals = a_info.m_numLocals;
+  m_numParams = a_info.m_numParams;
+  m_numParamsLocals = a_info.m_numParams + a_info.m_numLocals;
+
+  // references
+  m_numReferences = 0;
+  m_references = NULL;
+
+  if(m_byteCode)
+  {
+    // find the objects this function references by iterating over the byte code and collecting them.
+    // we could perform this step in the compilation phase if we don't want to iterate over the byte code.
+    
+    gmptr * references = (gmptr *) GM_NEW( char[a_info.m_byteCodeLength] );
+
+    union
+    {
+      const gmuint8 * instruction;
+      const gmuint32 * instruction32;
+    };
+
+    instruction = (const gmuint8 *) m_byteCode;
+    const gmuint8 * end = instruction + m_byteCodeLength;
+    for(;instruction < end;)
+    {
+      switch(*(instruction32++))
+      {
+        case BC_GETDOT :
+        case BC_SETDOT :
+        case BC_BRA :
+        case BC_BRZ :
+        case BC_BRNZ :
+        case BC_BRZK :
+        case BC_BRNZK :
+        case BC_FOREACH :
+        case BC_PUSHINT :
+        case BC_GETGLOBAL :
+        case BC_SETGLOBAL :
+        case BC_GETTHIS :
+        case BC_SETTHIS : instruction += sizeof(gmptr); break;
+        case BC_PUSHFP : instruction += sizeof(gmfloat); break;
+      
+        case BC_CALL :
+        case BC_GETLOCAL :
+        case BC_SETLOCAL : instruction += sizeof(gmuint32); break;
+
+        case BC_PUSHSTR :
+        case BC_PUSHFN :
+        {
+          // if the reference does not already exist, add it.
+          gmptr reference = *((gmptr *) instruction); 
+          instruction += sizeof(gmptr);
+          int i;
+          for(i = 0; i < m_numReferences; ++i)
+          {
+            if(references[i] == reference) break;
+          }
+          if(i == m_numReferences) references[m_numReferences++] = reference;
+          break;
+        }
+
+        default : break;
+      }
+    }
+
+    if(m_numReferences > 0)
+    {
+      m_references = (gmptr *) a_machine->Sys_Alloc(sizeof(gmptr) * m_numReferences);
+      memcpy(m_references, references, sizeof(gmptr) * m_numReferences);
+    }
+
+    delete [] (char*) references;
+  }
+  
+  // debug info
+  m_debugInfo = NULL;
+  if(a_debug)
+  {
+    m_debugInfo = (gmFunctionObjectDebugInfo *) a_machine->Sys_Alloc(sizeof(gmFunctionObjectDebugInfo));
+    memset(m_debugInfo, 0, sizeof(gmFunctionObjectDebugInfo));
+
+    // source code id
+    m_debugInfo->m_sourceId = a_sourceId;
+
+    // debug name
+    if(a_info.m_debugName)
+    {
+      int len = strlen(a_info.m_debugName) + 1;
+      m_debugInfo->m_debugName = (char *) a_machine->Sys_Alloc(len);
+      memcpy(m_debugInfo->m_debugName, a_info.m_debugName, len);
+    }
+
+    // symbols
+    if(a_info.m_symbols)
+    {
+      m_debugInfo->m_symbols = (char **) a_machine->Sys_Alloc(sizeof(char *) * m_numParamsLocals);
+      int i;
+      for(i = 0; i < m_numParamsLocals; ++i)
+      {
+        int len = strlen(a_info.m_symbols[i]) + 1;
+        m_debugInfo->m_symbols[i] = (char *) a_machine->Sys_Alloc(len);
+        memcpy(m_debugInfo->m_symbols[i], a_info.m_symbols[i], len);
+      }
+    }
+
+    // line number debugging.
+    if(a_info.m_lineInfo)
+    {
+      // alloc and copy
+      m_debugInfo->m_lineInfo = (gmLineInfo *) a_machine->Sys_Alloc(sizeof(gmLineInfo) * a_info.m_lineInfoCount);
+      memcpy(m_debugInfo->m_lineInfo, a_info.m_lineInfo, sizeof(gmLineInfo) * a_info.m_lineInfoCount);
+      m_debugInfo->m_lineInfoCount = a_info.m_lineInfoCount;
+    }
+  }
+  
+  return true;
+}
+
+
+
+int gmFunctionObject::GetLine(int a_address) const
+{
+  if(m_debugInfo && m_debugInfo->m_lineInfo)
+  {
+    int i;
+    for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
+    {
+      if(a_address < m_debugInfo->m_lineInfo[i].m_address)
+      {
+        // return entry before
+        if(i > 0) --i;
+        return m_debugInfo->m_lineInfo[i].m_lineNumber;
+      }
+    }
+    return m_debugInfo->m_lineInfo[i - 1].m_lineNumber;
+  }
+  return 0;
+}
+
+
+
+const void * gmFunctionObject::GetInstructionAtLine(int a_line) const
+{
+  if(m_debugInfo && m_debugInfo->m_lineInfo && m_byteCode)
+  {
+    // serach for the first address using this line.
+    int i;
+    for(i = 0; i < m_debugInfo->m_lineInfoCount; ++i)
+    {
+      if(m_debugInfo->m_lineInfo[i].m_lineNumber == a_line)
+      {
+        return (void *) ((char *) m_byteCode + m_debugInfo->m_lineInfo[i].m_address);
+      }
+    }
+  }
+  return NULL;
+}
+
+
+
+gmuint32 gmFunctionObject::GetSourceId() const
+{
+  if(m_debugInfo)
+  {
+    return m_debugInfo->m_sourceId;
+  }
+  return 0;
+}
+

+ 163 - 0
gmsrc/src/gm/gmFunctionObject.h

@@ -0,0 +1,163 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMFUNCTIONOBJECT_H_
+#define _GMFUNCTIONOBJECT_H_
+
+#include "gmConfig.h"
+#include "gmVariable.h"
+#include "gmCodeGenHooks.h"
+#include "gmMem.h"
+
+// fwd decls
+class gmThread;
+
+enum gmCFunctionReturn
+{
+  GM_OK = 0,
+  GM_EXCEPTION = -1,
+  GM_SYS_YIELD = -2, // system only
+  GM_SYS_BLOCK = -3, // system only
+  GM_SYS_SLEEP = -4, // system only
+  GM_SYS_KILL =  -5, // system only
+  GM_SYS_STATE = -6, // system only
+};
+
+/*!
+  \brief gmCFunction is the function type for binding c functions to gm.
+  \return gmCFunctionReturn
+*/
+typedef int (GM_CDECL *gmCFunction)(gmThread *);
+
+/*!
+  \class gmFunctionObject
+  \brief 
+*/
+class gmFunctionObject : public gmObject
+{
+public:
+
+  virtual int GetType() const { return GM_FUNCTION; }
+
+  virtual void Destruct(gmMachine * a_machine);
+#if GM_USE_INCGC
+  virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
+#else //GM_USE_INCGC
+  virtual void Mark(gmMachine * a_machine, gmuint32 a_mark);
+#endif //GM_USE_INCGC
+
+
+  /*!
+    \brief Init() will initialise a function object.
+    \param a_debug is true if this is a debug build
+    \param a_info is a function info struct as built by gmCodeGenHooks.
+    \param a_sourceId is an unique id specifiying the source code of the function, is used for debugging.
+    \return true on success.
+  */
+  bool Init(gmMachine * a_machine, bool a_debug, gmFunctionInfo &a_info, gmuint32 a_sourceId = 0);
+
+  /*!
+    \brief GetMaxStackSize
+    \return the maximum stack growth not including parameters or locals
+  */
+  inline int GetMaxStackSize() const { return m_maxStackSize; }
+
+   /// \brief GetNumLocals
+  inline int GetNumLocals() const { return m_numLocals; }
+
+  /// \brief GetNumParams
+  inline int GetNumParams() const { return m_numParams; }
+
+  /// \brief GetNumParamsLocals()
+  inline int GetNumParamsLocals() const { return m_numParamsLocals; }
+
+  /// \brief GetByteCode()
+  inline const void * GetByteCode() const { return m_byteCode; }
+
+  /// \brief GetDebugName()
+  inline const char * GetDebugName() const;
+
+  /// \brief GetLine() will return the source line for the given address
+  int GetLine(int a_address) const;
+  int GetLine(const void * a_instruction) const { return GetLine((const char * ) a_instruction - (char *) m_byteCode); }
+
+  /// \brief GetInstructionAtLine() will return the instruction at the given line, or NULL of line was not within this function
+  const void * GetInstructionAtLine(int a_line) const;
+
+  /// \brief GetSourceId() will get the source code id when in debug mode, else 0
+  gmuint32 GetSourceId() const;
+
+  /// \brief GetSymbol() will return the symbol name at the given offset.
+  inline const char * GetSymbol(int a_offset) const;
+
+  // public data
+  gmCFunction m_cFunction;
+  const void* m_cUserData;
+
+protected:
+
+  /// \brief Non-public constructor.  Create via gmMachine.
+  gmFunctionObject();
+  friend class gmMachine;
+
+private:
+
+  /*!
+    \brief gmFunctionObjectDebugInfo stores debugging info for a debug build
+  */
+  struct gmFunctionObjectDebugInfo
+  {
+    char * m_debugName;
+    char ** m_symbols;
+    int m_lineInfoCount;
+    gmuint32 m_sourceId; // source code id.
+    gmLineInfo * m_lineInfo;
+  };
+
+  gmFunctionObjectDebugInfo * m_debugInfo;
+  void * m_byteCode;
+  int m_byteCodeLength;
+  int m_maxStackSize;
+  int m_numLocals;
+  int m_numParams;
+  int m_numParamsLocals; //!< m_numLocals + m_numParams
+  int m_numReferences; //!< number of references within the byte code.
+  gmptr * m_references; //!< references from the byte code
+};
+
+//
+//
+// INLINE IMPLEMENTATION
+//
+//
+
+inline const char * gmFunctionObject::GetDebugName() const
+{
+  if(m_debugInfo && m_debugInfo->m_debugName)
+  {
+    return m_debugInfo->m_debugName;
+  }
+  return "__unknown";
+}
+
+
+
+inline const char * gmFunctionObject::GetSymbol(int a_offset) const
+{
+  if(m_debugInfo && m_debugInfo->m_symbols && (a_offset >= 0) && (a_offset < m_numParamsLocals))
+  {
+    return m_debugInfo->m_symbols[a_offset];
+  }
+  return "__unknown";
+}
+
+
+#endif // _GMFUNCTIONOBJECT_H_

+ 15 - 0
gmsrc/src/gm/gmHash.cpp

@@ -0,0 +1,15 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmHash.h"
+
+

+ 348 - 0
gmsrc/src/gm/gmHash.h

@@ -0,0 +1,348 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMHASH_H_
+#define _GMHASH_H_
+
+#include "gmConfig.h"
+#include "gmIterator.h"
+
+#define TMPL template<class KEY, class T, class HASHER>
+#define QUAL gmHash<KEY, T, HASHER>
+#define NQUAL gmHashNode<KEY, T, HASHER>
+
+TMPL class gmHash;
+class gmDefaultHasher;
+
+/// \class gmHashNode
+/// \brief inherit gmHashNode
+template<class KEY, class T, class HASHER = gmDefaultHasher>
+class gmHashNode
+{
+public:
+  inline gmHashNode() {}
+
+  /// \brief Return whatever is the 'key' for this type.  Used by gmHash
+  //const KEY& GetKey() const = 0;
+
+private:
+
+  T * m_next;
+  friend class QUAL;
+};
+
+/// \class gmHash
+/// \brief templated intrusive hash class
+///        HASHER must provide static gmuint ::Hash(const KEY &a_key) and int ::Compare(const KEY &a_key, const KEY &a_key)
+template<class KEY, class T, class HASHER = gmDefaultHasher>
+class gmHash
+{
+public:
+
+  /// \class Iterator
+  class Iterator
+  {
+  public:
+
+    GM_INCLUDE_ITERATOR_KERNEL(T)
+
+    inline Iterator() 
+    {
+      m_hash = NULL;
+      m_elem = NULL;
+      m_slot = 0;
+    }
+
+    inline Iterator(const gmHash * a_hash)
+    {
+      m_hash = a_hash;
+      m_slot = 0;
+      m_elem = NULL;
+      Inc();
+    }
+
+    inline void Inc(void)
+    {
+      GM_ASSERT(m_hash);
+      if(m_elem)
+      {
+        m_elem = m_hash->GetNext(m_elem);
+      }
+      while(m_elem == NULL && m_slot < m_hash->m_size)
+      {
+        m_elem = m_hash->m_table[m_slot++];
+      }
+    }
+  
+    inline void Dec(void) { GM_ASSERT(false); }
+    inline T * Resolve(void) { return m_elem; }
+    inline const T * Resolve(void) const { return m_elem; }
+    inline bool IsValid() const { return (m_elem != NULL); }
+ 
+  private:
+
+    const gmHash * m_hash;
+    T * m_elem;
+    unsigned int m_slot;
+  };
+
+  // members
+
+  gmHash(gmuint a_size);
+  ~gmHash();
+
+  void RemoveAll();
+  void RemoveAndDeleteAll();
+
+  /// \brief Insert() will insert an item into the hash table. 
+  /// \return non-null on failure, in which case the returned item is the duplicate existing in the hash
+  T * Insert(T * a_node);
+
+  /// \brief Remove() will remove an item from the hash table
+  /// \return the removed item
+  T * Remove(T * a_node);
+
+  /// \brief Remove() will remove an item from the hash table via an iterator
+  /// \return the removed item
+  T * Remove(Iterator & a_it);
+
+  /// \brief RemoveKey() will remove an item by key
+  /// \return the removed item
+  T * RemoveKey(const KEY &a_key);
+
+  /// \brief Find()
+  T * Find(const KEY &a_key);
+
+  inline gmuint Count() const { return m_count; }
+  inline Iterator First() const { return Iterator(this); }
+
+private:
+
+  T * GetNext(T * a_elem) const { return a_elem->NQUAL::m_next; }
+  T ** m_table;
+  gmuint m_count;
+  gmuint m_size;
+
+  friend class Iterator;
+};
+
+
+/// \class gmDefaultHasher
+/// \brief use the gmDefaultHasher as the HASHER template arg for the common hashing keys
+class gmDefaultHasher
+{
+public:
+
+  static inline gmuint Hash(const char * a_key)
+  {
+    gmuint key = 0;
+    const char * cp = (const char *) a_key;
+
+    while(*cp != '\0') 
+    {
+      key = (key + ((key << 5) + *cp));
+      ++cp;
+    }
+    return key;
+  }
+
+  static inline int Compare(const char * a_keyA, const char * a_keyB)
+  {
+    return strcmp(a_keyA, a_keyB);
+  }
+
+  static inline gmuint Hash(int a_key) 
+  {
+    return (gmuint) a_key;
+  }
+
+  static inline int Compare(int a_keyA, int a_keyB)
+  {
+    return (a_keyA - a_keyB);
+  }
+  
+  static inline gmuint Hash(const void * a_key) 
+  {
+    return (gmuint) (((gmuint) a_key) / sizeof(double));
+  }
+
+  static inline int Compare(const void * a_keyA, const void * a_keyB)
+  {
+    return (int) ((char *) a_keyA - (char *) a_keyB);
+  }
+};
+
+
+
+TMPL
+QUAL::gmHash(gmuint a_size)
+{
+  // make sure size is power of 2
+  GM_ASSERT((a_size & (a_size - 1)) == 0);
+  m_size = a_size;
+  m_table = GM_NEW(T * [a_size]);
+  int i = m_size;
+  while(i--)
+  {
+    m_table[i] = NULL;
+  }
+  m_count = 0;
+}
+
+TMPL
+QUAL::~gmHash()
+{
+  delete [] m_table;
+}
+
+
+TMPL
+void QUAL::RemoveAll()
+{
+  int i = m_size;
+  while(i--)
+  {
+    m_table[i] = NULL;
+  }
+  m_count = 0;
+}
+
+
+TMPL
+void QUAL::RemoveAndDeleteAll()
+{
+  // iterate over table and delete all
+  int i = m_size;
+  T * node, * next;
+  while(i--)
+  {
+    node = m_table[i];
+    while(node)
+    {
+      next = node->NQUAL::m_next;
+      delete node;
+      node = next;
+    }
+    m_table[i] = NULL;
+  }
+  m_count = 0;
+}
+
+
+TMPL
+T * QUAL::Insert(T * a_node)
+{
+  gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
+  T ** node = &m_table[slot];
+
+  while(*node)
+  {
+    int compare = HASHER::Compare(a_node->GetKey(), (*node)->GetKey());
+    if(compare == 0) return (*node);
+    else if(compare < 0) break;
+    node = &((*node)->NQUAL::m_next);
+  }
+
+  a_node->NQUAL::m_next = *node;
+  *node = a_node;
+  ++m_count;
+  return NULL;
+}
+
+
+TMPL
+T * QUAL::Remove(T * a_node)
+{
+  gmuint slot = HASHER::Hash(a_node->GetKey()) & (m_size - 1);
+  T ** node = &m_table[slot];
+
+  while(*node)
+  {
+    if(a_node == *node)
+    {
+      *node = a_node->NQUAL::m_next;
+      --m_count;
+      return a_node;
+    }
+    node = &((*node)->NQUAL::m_next);
+  }
+  return NULL;
+}
+
+
+TMPL
+T * QUAL::Remove(Iterator & a_it)
+{
+  T * node = a_it.Resolve();
+  if(node)
+  {
+    a_it.Inc();
+    return Remove(node);
+  }
+  return NULL;
+}
+
+
+
+TMPL
+T * QUAL::RemoveKey(const KEY &a_key)
+{
+  gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
+  T ** node = &m_table[slot];
+  T * found;
+
+  while(*node)
+  {
+    int compare = HASHER::Compare((*node)->GetKey(), a_key);
+    if(compare == 0)
+    {
+      --m_count;
+      found = *node;
+      *node = found->NQUAL::m_next;
+      return (found);
+    }
+    else if(compare > 0)
+    {
+      return NULL;
+    }
+    node = &((*node)->NQUAL::m_next);
+  }
+  return NULL;
+}
+
+
+TMPL
+T * QUAL::Find(const KEY &a_key)
+{
+  gmuint slot = HASHER::Hash(a_key) & (m_size - 1);
+  T * node = m_table[slot];
+
+  while(node)
+  {
+    int compare = HASHER::Compare(static_cast<T*>(node)->GetKey(), a_key);
+    if(compare == 0)
+    {
+      return node;
+    }
+    else if(compare > 0)
+    {
+      return NULL;
+    }
+    node = node->NQUAL::m_next;
+  }
+  return NULL;
+}
+
+#undef TMPL
+#undef QUAL
+#undef NQUAL
+
+#endif // _GMHASH_H_

+ 835 - 0
gmsrc/src/gm/gmIncGC.cpp

@@ -0,0 +1,835 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmIncGC.h"
+
+// NOTES: 
+
+// o Q: What about when we turn GC off manually to prevent new objects from disappearing ?
+//   A: This can't happen with allocate black AS LONG AS the collect is not called during processing, only at yield.
+//   Still, would be nice to put this functionality in for non-game use.
+//
+
+// How to make new types compatible with GC.
+//
+// 1) Implement the following user type callbacks:
+//    typedef void (GM_CDECL *gmGCDestructCallback)(gmMachine * a_machine, gmUserObject* a_object);
+//    typedef bool (GM_CDECL *gmGCTraceCallback)(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone);
+//
+//    o The Destruct function merely destructs and frees the user object. (Same as it used to.)
+//
+//    o The Trace function calls gc->GetNextObject(obj) where obj is each gmObject* at that level.
+//    You should increment a_workDone for each call and once extra (so min value should be +1).
+//    The Trace has the potential to be re-called for multiple increments, but this functionality is not necessary and has not been tested.
+//    Always return true when finished tracing.  Returning false signals that you are not finished and it should be re-called.
+//
+// 2) If you make a table or array type class that contains variables that could be gmObjects,
+//    call gc->WriteBarrier(obj) where obj is the old gmObject about to be overwritten in a SetInd or SetDot call etc.
+//
+// Note that permanant strings are stored in a separate list so they are ignored by the GC.
+
+//////////////////////////////////////////////////
+// gmGCColorSet
+//////////////////////////////////////////////////
+
+gmGCColorSet::gmGCColorSet()
+{
+  Init(NULL);
+}
+
+
+gmGCColorSet::~gmGCColorSet()
+{
+}
+
+
+void gmGCColorSet::Init(gmGarbageCollector* a_gc)
+{
+  m_gc = a_gc;
+
+  // Note that only the Scan and Free markers actually move
+  //
+  //  | GRAY BLACK FREE | WHITE |
+  //  ^      ^     ^    ^       ^
+  //  G      S     F    W       T
+  //
+  m_gray = &m_headObject;
+  m_white = &m_separatorObject;
+  m_free = m_white;
+  m_scan = m_free;
+  m_tail = &m_tailObject;
+
+  m_tailObject.SetPrev(&m_separatorObject);
+  m_tailObject.SetNext(NULL);
+  
+  m_separatorObject.SetPrev(&m_headObject);
+  m_separatorObject.SetNext(&m_tailObject);
+  
+  m_headObject.SetPrev(NULL);
+  m_headObject.SetNext(&m_separatorObject);
+
+  // Make persistList into a list node
+  m_persistList.SetNext(&m_persistList);
+  m_persistList.SetPrev(&m_persistList);
+
+#if GM_GC_STATS
+  m_numAllocated = 0;
+#endif //GM_GC_STATS
+}
+
+
+#if GM_GC_DEBUG
+bool gmGCColorSet::VerifyIntegrity()
+{
+  // Scan through list make sure all pointers are in valid positions and all objects are colored correctly
+  gmGCObjBase* curObj = m_gray->GetNext();
+  int curCol = GM_GC_DEBUG_COL_GRAY;
+  while(curObj != m_tail)
+  {
+    if(curObj == m_scan)
+    {
+      curCol = GM_GC_DEBUG_COL_BLACK;
+    }
+    if(curObj == m_free)
+    {
+      curCol = GM_GC_DEBUG_COL_FREE;
+    }
+    if(curObj == m_white)
+    {
+      curCol = GM_GC_DEBUG_COL_WHITE;
+    }
+    if(curObj != &m_separatorObject)
+    {
+      GM_ASSERT(curObj->m_curPosColor == curCol);
+    }
+
+    curObj = curObj->GetNext();
+  }
+  
+  return true;
+}
+#endif //GM_GC_DEBUG
+
+
+void gmGCColorSet::DestructPersistantObjects()
+{
+  int count=0;
+
+  gmGCObjBase* curObj = m_persistList.GetNext();
+  while(curObj != &m_persistList)
+  {
+    gmGCObjBase* objToDestruct = curObj;
+    curObj = curObj->GetNext();
+
+    objToDestruct->Destruct(m_gc->GetVM());
+    ++count;
+  }
+
+  // Reset persist list (Make persistList into a list node)
+  m_persistList.SetNext(&m_persistList);
+  m_persistList.SetPrev(&m_persistList);
+}
+
+
+int gmGCColorSet::FollowPointers(int a_maxBytesToTrace)
+{
+  int workDone = 0;
+
+  if(m_gc->GetTraceState().m_object->Trace(m_gc->GetVM(), m_gc, a_maxBytesToTrace, workDone))
+  {
+    m_gc->GetTraceState().m_done = true;
+  }
+
+  return workDone;
+}
+
+
+bool gmGCColorSet::BlackenNextGray(int& a_workDone, int a_workLeftToGo)
+{
+  if(m_gc->GetTraceState().m_done == true)
+  {
+    if(m_scan->GetPrev() == m_gray) // No grays to blacken.
+    { 
+      a_workDone = 0;
+      return false;
+    } 
+    else 
+    {
+      m_scan = m_scan->GetPrev();
+      m_gc->GetTraceState().m_object = m_scan;
+    
+#if GM_GC_DEBUG
+      GM_ASSERT(m_scan->m_curPosColor == GM_GC_DEBUG_COL_GRAY);
+      m_scan->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
+#endif //GM_GC_DEBUG
+
+      a_workDone = FollowPointers(a_workLeftToGo);
+
+      return true;  // Gray was blackened
+    }
+  } 
+  else 
+  {
+    // Resume previously interrupted scanning of an object
+    a_workDone = FollowPointers(a_workLeftToGo);
+    return true;
+  }
+} 
+
+
+void gmGCColorSet::GrayThisObject(gmGCObjBase* a_obj)
+{
+  gmGCObjBase* objPrev = a_obj->GetPrev();
+  gmGCObjBase* objNext = a_obj->GetNext();
+
+  // This routine should never get called with a shaded object
+  GM_ASSERT(!m_gc->IsShaded(a_obj));
+
+#if GM_GC_DEBUG
+  GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
+  a_obj->m_curPosColor = GM_GC_DEBUG_COL_GRAY;
+#endif //GM_GC_DEBUG
+
+  // Set object`s color to shaded first.
+  a_obj->SetColor(m_gc->GetCurShadeColor());
+
+  // The object must be shaded
+  GM_ASSERT(m_gc->IsShaded(a_obj));
+
+  // Splice the object out of the white list
+  // This can be done unconditionally as no set pointers can point to any object in this set.
+  objPrev->SetNext(objNext);
+  objNext->SetPrev(objPrev);
+
+  // Put the object into the correct place in the gray list
+
+#if DEPTH_FIRST
+  // Put the gray object at the head of the gray list.
+  a_obj->SetPrev(m_scan->GetPrev());
+  a_obj->SetNext(m_scan);
+  m_scan->GetPrev()->SetNext(a_obj);
+  m_scan->SetPrev(a_obj);
+#else // BREADTH_FIRST
+  // Put the gray object at the tail of the gray list.
+  a_obj->SetPrev(m_gray);
+  a_obj->SetNext(m_gray->GetNext());
+  m_gray->GetNext()->SetPrev(a_obj);
+  m_gray->SetNext(a_obj);
+#endif // BREADTH_FIRST
+
+#if GM_GC_DEBUG
+  // Slow, paranoid check
+  VerifyIntegrity();
+#endif //GM_GC_DEBUG
+}
+
+  
+void gmGCColorSet::Revive(gmGCObjBase* a_obj)
+{
+  // NOTE: Once objects are in the free list, we can't trust the color mark, 
+  //       it may have been set either side of the flip.
+  //       We should only revive 'free', but we can't simply tell where in the list the object is.
+  
+  // Always mark black, this is only done for strings, and we are logically re-allocating the dead object
+
+#if GM_GC_DEBUG 
+  a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK; // Blacken as if re-allocated
+#endif //GM_GC_DEBUG
+
+  // Set object`s color to shaded first.
+  a_obj->SetColor(m_gc->GetCurShadeColor());
+
+  // Fix scan (NOTE: If scan == obj, obj must already be black, so could skip rest of this function)
+  if( m_scan == a_obj )
+  {
+    m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
+  }
+  // We don't care if (m_gc->GetTraceState().m_object == a_obj ) as this is Done and write only, and can only be string, not potentially a resumable object.
+#if GM_GC_DEBUG
+  if( m_gc->GetTraceState().m_object == a_obj )
+  {
+    GM_ASSERT( m_gc->GetTraceState().m_done );
+  }
+#endif // GM_GC_DEBUG
+
+  // Fix sentinels
+  if( m_free == a_obj )
+  {
+    m_free = m_free->GetNext();
+  }
+
+  // Splice the object out of the free/white list (Or anywhere in this case)
+  gmGCObjBase* objPrev = a_obj->GetPrev();
+  gmGCObjBase* objNext = a_obj->GetNext();
+  objPrev->SetNext(objNext);
+  objNext->SetPrev(objPrev);
+
+  // Insert at the end of black list
+  a_obj->SetNext(m_free);             // Next is first Free
+  a_obj->SetPrev(m_free->GetPrev());  // Prev is last Black
+  m_free->GetPrev()->SetNext(a_obj);  // Last Black next is now this
+  m_free->SetPrev(a_obj);             // Free prev is now this
+
+  // If there were no blacks, move scan forward to prevent scanning this new black
+  if( m_scan == m_free )
+  {
+    m_scan = a_obj;
+  }
+}
+
+
+void gmGCColorSet::ReclaimGarbage()
+{
+  GM_ASSERT(m_scan->GetPrev() == m_gray);
+
+#if GM_GC_DEBUG
+  {
+    // Traverse the newly found garbage objects just to make sure there
+    // aren't any live objects in there.  Used for debugging only.
+    for(gmGCObjBase* temp = m_white->GetNext();
+        temp != m_tail;
+        temp = temp->GetNext())
+    {
+      GM_ASSERT(!m_gc->IsShaded(temp));
+    }
+  }
+#endif
+
+  if (m_white->GetNext() != m_tail)       // There are garbage objects
+  {
+#if GM_GC_DEBUG
+    //flag white->tail as white?
+    for(gmGCObjBase* temp = m_white->GetNext();
+        temp != m_tail;
+        temp = temp->GetNext())
+    {
+      GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
+      temp->m_curPosColor = GM_GC_DEBUG_COL_FREE;
+    }
+#endif //GM_GC_DEBUG
+
+    bool fixScan = false;
+    if( m_scan == m_free )  // There are no black objects
+    {  
+      fixScan = true;    
+    }
+
+    // Reclaim the garbage.
+    // Insert old White->Tail at start of Free (Free may == White)
+    gmGCObjBase* firstFree = m_white->GetNext();
+    
+    firstFree->SetPrev(m_free->GetPrev());
+    m_free->GetPrev()->SetNext(firstFree);
+    
+    m_tail->GetPrev()->SetNext(m_free);
+    m_free->SetPrev(m_tail->GetPrev());
+    
+    m_free = firstFree;
+    
+    if( fixScan )
+    {
+      m_scan = m_free;
+    }
+
+    m_white->SetNext(m_tail);
+    m_tail->SetPrev(m_white);
+  }
+
+  // Whiten the live objects.
+  if (m_scan != m_free)   // There are live (black) objects
+  {
+
+#if GM_GC_DEBUG
+    //flag scan->free as white?
+    for(gmGCObjBase* temp = m_scan;//m_scan->GetNext();
+        temp != m_free;
+        temp = temp->GetNext())
+    {
+      GM_ASSERT(temp->m_curPosColor == GM_GC_DEBUG_COL_BLACK);
+      temp->m_curPosColor = GM_GC_DEBUG_COL_WHITE;
+    }
+#endif //GM_GC_DEBUG
+
+    GM_ASSERT(m_white->GetNext() == m_tail);
+    GM_ASSERT(m_tail->GetPrev() == m_white);
+
+    m_scan->GetPrev()->SetNext(m_free);
+    m_free->GetPrev()->SetNext(m_tail);
+    m_tail->SetPrev(m_free->GetPrev());
+    m_free->SetPrev(m_scan->GetPrev());
+    m_scan->SetPrev(m_white);
+    m_white->SetNext(m_scan);
+    m_scan = m_free;
+  }
+
+  GM_ASSERT(m_gray->GetNext() == m_scan);
+
+#if GM_GC_DEBUG
+  {
+    int count = 0;
+    for(gmGCObjBase* temp = m_free; temp != m_white; temp = temp->GetNext())
+    {
+      ++count;
+    }
+  }
+#endif
+}
+
+
+int gmGCColorSet::DestructSomeFreeObjects(int a_maxToDestruct)
+{
+  int numDestructed = 0;
+  // Go through the free list (perhaps over multiple installments in future) and call Destruct() on them.
+  if(m_free != m_white)
+  {
+    gmGCObjBase* beforeFree = m_free->GetPrev(); // Save previous node so we can relink after removing some
+    bool fixScan = false;
+    if(m_scan == m_free) // Will need to fix the scan ptr later if this is so.
+    {
+      fixScan = true;
+    }
+
+    while(m_free != m_white)
+    {
+      gmGCObjBase* objToRecycle = m_free;
+      m_free = m_free->GetNext();
+
+#if GM_GC_DEBUG
+      //GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_WHITE);
+      GM_ASSERT(objToRecycle->m_curPosColor == GM_GC_DEBUG_COL_FREE);
+      objToRecycle->m_curPosColor = GM_GC_DEBUG_COL_INVALID;
+#endif //GM_GC_DEBUG
+
+#if GM_GC_KEEP_PERSISTANT_SEPARATE
+      GM_ASSERT(!objToRecycle->GetPersist());
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+#if GM_GC_STATS
+      --m_numAllocated;
+#endif //GM_GC_STATS
+
+      objToRecycle->Destruct(m_gc->GetVM());
+      ++numDestructed;
+      --a_maxToDestruct;
+      if(a_maxToDestruct <= 0)
+      {
+        // Relink since we removed elements
+        beforeFree->SetNext(m_free);
+        m_free->SetPrev(beforeFree);
+        if(fixScan)
+        {
+          m_scan = m_free;
+        }
+        return numDestructed; // Work is done for now.
+      }
+    }
+    // Relink since we removed elements
+    beforeFree->SetNext(m_free);
+    m_free->SetPrev(beforeFree);
+    if(fixScan)
+    {
+      m_scan = m_free;
+    }
+  }
+  return numDestructed;
+}
+
+
+void gmGCColorSet::Allocate(gmGCObjBase* a_obj)
+{
+#if GM_GC_STATS
+  ++m_numAllocated;
+#endif //GM_GC_STATS
+
+  a_obj->SetPersist(false);
+
+  a_obj->SetColor(m_gc->GetCurShadeColor());
+
+#if GM_GC_DEBUG
+  GM_ASSERT(a_obj->m_curPosColor == GM_GC_DEBUG_COL_INVALID);
+  a_obj->m_curPosColor = GM_GC_DEBUG_COL_BLACK;
+#endif //GM_GC_DEBUG
+  
+  //Insert at the end of black list
+  a_obj->SetNext(m_free);             //Next is first Free
+  a_obj->SetPrev(m_free->GetPrev());  //Prev is last Black
+  m_free->GetPrev()->SetNext(a_obj);  //Last Black next is now this
+  m_free->SetPrev(a_obj);             //Free prev is now this
+ 
+  //If there were no blacks, move scan forward to prevent scanning this new black
+  if(m_scan == m_free)
+  {
+    m_scan = a_obj;
+  }
+}
+
+
+void gmGCColorSet::DestructAll()
+{
+  int count = 0;
+
+  DestructPersistantObjects();
+
+  // Black and Gray
+  gmGCObjBase* curGrayOrBlack = m_gray->GetNext();
+  while(curGrayOrBlack != m_free)
+  {
+    gmGCObjBase* objToRecycle = curGrayOrBlack;
+    curGrayOrBlack = curGrayOrBlack->GetNext();
+#if GM_GC_KEEP_PERSISTANT_SEPARATE
+    GM_ASSERT(!objToRecycle->GetPersist());
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+    objToRecycle->Destruct(m_gc->GetVM());
+#if GM_GC_STATS
+    --m_numAllocated;
+#endif //GM_GC_STATS
+    ++count;
+  }
+
+  // Whites
+  gmGCObjBase* curWhite = m_white->GetNext();
+  while(curWhite != m_tail)
+  {
+    gmGCObjBase* objToRecycle = curWhite;
+    curWhite = curWhite->GetNext();
+
+#if GM_GC_KEEP_PERSISTANT_SEPARATE
+    GM_ASSERT(!objToRecycle->GetPersist());
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+    objToRecycle->Destruct(m_gc->GetVM());
+#if GM_GC_STATS
+    --m_numAllocated;
+#endif //GM_GC_STATS
+    ++count;
+  }
+
+  // Free list
+  gmGCObjBase* curFree = m_free;
+  while(curFree != m_white)
+  {
+    gmGCObjBase* objToRecycle = curFree;
+    curFree = curFree->GetNext();
+    {
+#if GM_GC_KEEP_PERSISTANT_SEPARATE
+      GM_ASSERT(!objToRecycle->GetPersist());
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+      objToRecycle->Destruct(m_gc->GetVM());
+#if GM_GC_STATS
+      --m_numAllocated;
+#endif //GM_GC_STATS
+      ++count;
+    }
+  }
+
+  Init(m_gc);
+}
+
+//////////////////////////////////////////////////
+// gmGarbageCollector
+//////////////////////////////////////////////////
+
+gmGarbageCollector::gmGarbageCollector()
+{
+  Init(NULL, NULL);
+}
+
+
+gmGarbageCollector::~gmGarbageCollector()
+{
+}
+
+
+void gmGarbageCollector::Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine)
+{
+  m_curShadeColor = 0; // Another color is !0
+  m_workPerIncrement = GM_GC_DEFAULT_WORK_INCREMENT;
+  m_maxObjsToDestructPerIncrement = GM_GC_DEFAULT_DESTRUCT_INCREMENT;
+  m_workLeftToGo = 0;
+  m_fullThrottle = false;
+  m_gcTurnedOff = true; // Start in OFF state, machine will turn on when needed.
+  m_firstCollectionIncrement = true;
+  m_doneTracing = false;
+  m_colorSet.Init(this);
+  m_traceState.Reset();
+  m_flipCallback = NULL;
+  m_scanRootsCallback = a_scanRootsCallback;
+  m_gmMachine = a_gmMachine;
+}
+
+
+bool gmGarbageCollector::BlackenGrays() 
+{
+  int workDone;
+
+  while(m_colorSet.AnyGrays()) 
+  {
+    // gmGCColorSet::BlackenNextGray returns 1 if there was a gray to
+    // blacken (even if it couldn't finish blackening it), and a 0 otherwise.
+
+    workDone = m_workLeftToGo;
+
+    while(m_colorSet.BlackenNextGray(workDone, m_workLeftToGo))
+    {
+      m_workLeftToGo -= workDone;
+      if (m_workLeftToGo <= 0)
+      {
+        // Quit early
+        return true;  // We have completed one increment of work
+      }
+    }
+  };
+  
+  return false;
+}
+
+
+bool gmGarbageCollector::Collect()
+{
+  if(m_fullThrottle)
+  {
+    m_workLeftToGo = GM_MAX_INT32;
+  }
+  else
+  {
+    m_workLeftToGo = m_workPerIncrement;
+  }
+
+  m_doneTracing = false;
+
+  if(m_firstCollectionIncrement)
+  {
+    // Scan each root object and gray it
+    GM_ASSERT(m_scanRootsCallback);
+    m_scanRootsCallback(m_gmMachine, this);
+
+    m_firstCollectionIncrement = false;
+    return false;
+  }
+
+  // If any grays exist, scan them first
+  if(m_colorSet.AnyGrays())
+  {
+    if(BlackenGrays()) // Returns 0 if no more grays, and 1 if done with an increment of collection.
+    {
+      return false; // Out of time, so exit function
+    }
+  }
+
+  m_doneTracing = true;
+
+  // Let the collect continue until garbage memory has been reclaimed
+  // This could be done as an external phase
+  if(ReclaimSomeFreeObjects())
+  {
+    return false;
+  }
+
+#if GM_GC_TURN_OFF_ABLE  
+  // Turn off gc until almost out of memory.
+  // Can only do when allocating black.
+  m_gcTurnedOff = true;
+#else //GM_GC_TURN_OFF_ABLE  
+  Flip();
+#endif //GM_GC_TURN_OFF_ABLE  
+
+  return true;
+}
+
+
+void gmGarbageCollector::Flip()
+{
+  m_firstCollectionIncrement = true;
+
+  if(m_flipCallback)
+  {
+    m_flipCallback();
+  }
+
+#if GM_GC_TURN_OFF_ABLE  
+  // The garbage collector can only be turned off if we are allocating black.
+  m_gcTurnedOff = false; // Turn the garbage collector back on.
+#endif //GM_GC_TURN_OFF_ABLE  
+  
+  m_colorSet.ReclaimGarbage();
+  
+  ToggleCurShadeColor();
+}
+
+
+// This function is called when there are no free objects in the colorset.
+// If the gc is turned off, it calls flip to reclaim any garbage objects
+// that have been found by the garbage collector.
+void gmGarbageCollector::ReclaimObjectsAndRestartCollection()
+{
+#if GM_GC_TURN_OFF_ABLE
+  // The garbage collector only gets turned off if we are allocating
+  // black.  GC is turned off after finishing  tracing and before 
+  // doing a gc flip. So if there are no free objects left, first 
+  // flip the GC and turn white objects into free.  Hopefully, this 
+  // will provide more free objects for allocation.
+
+  if(m_gcTurnedOff) 
+  {
+    Flip();
+  }
+#endif //GM_GC_TURN_OFF_ABLE
+}
+
+
+/// \brief Destruct all objects.
+void gmGarbageCollector::DestructAll()
+{
+  m_colorSet.DestructAll();
+
+  //Reset some of our members
+  m_curShadeColor = 0;
+  m_workPerIncrement = 100;
+  m_maxObjsToDestructPerIncrement = 100;
+  m_workLeftToGo = 0;
+  m_doneTracing = false;
+  m_fullThrottle = false;
+  m_firstCollectionIncrement = true;
+  m_traceState.Reset();
+}
+
+
+void gmGarbageCollector::FullCollect()
+{
+  m_fullThrottle = true;
+
+  if(IsOff()) // If GC is off
+  {
+    ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
+  }
+
+  while(!Collect())
+  {
+    // Do the collect phase
+  }
+  ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
+
+  // Collect a second time to catch floating black objects
+  while(!Collect())
+  {
+    // Do the collect phase
+  }
+  ReclaimObjectsAndRestartCollection(); // Do flip and turn it back on
+
+  // NOTE: The GC is now restarted and in an 'On' state, meaning it will now collect again from the machine.
+  //       This behavior may not be desirable, so this function really needs more analysis to determine the
+  //       optimum sequence for a full collect with minimal redundancy.
+
+  // Free memory of garbage objects
+  while(ReclaimSomeFreeObjects())
+  {
+    // Reclaim all garbage
+  }
+  m_fullThrottle = false;
+}
+
+
+
+//////////////////////////////////////////////////
+// Helper functions for VM and debugger
+//////////////////////////////////////////////////
+#include "gmVariable.h"
+#include "gmFunctionObject.h"
+
+const void* gmGCColorSet::GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line)
+{
+  gmGCObjBase* cur;
+  
+  // Search Gray to Free
+  cur = m_gray->GetNext();
+  while(cur != m_free)
+  {
+    gmObject* object = (gmObject*)cur;
+    if(object->GetType() == GM_FUNCTION)
+    {
+      gmFunctionObject * function = (gmFunctionObject *) object;
+      if(function->GetSourceId() == a_sourceId)
+      {
+        const void * instr = function->GetInstructionAtLine(a_line);
+        if(instr)
+        {
+          return instr;
+        }
+      }
+    }
+    cur = cur->GetNext();
+  }
+
+  // Search White
+  cur = m_white->GetNext();
+  while(cur != m_tail)
+  {
+    gmObject* object = (gmObject*)cur;
+    if(object->GetType() == GM_FUNCTION)
+    {
+      gmFunctionObject * function = (gmFunctionObject *) object;
+      if(function->GetSourceId() == a_sourceId)
+      {
+        const void * instr = function->GetInstructionAtLine(a_line);
+        if(instr)
+        {
+          return instr;
+        }
+      }
+    }
+    cur = cur->GetNext();
+  }
+
+  return NULL;
+}
+
+
+gmObject* gmGCColorSet::CheckReference(gmptr a_ref)
+{
+  gmGCObjBase* cur;
+
+  // Search Gray to Free
+  cur = m_gray->GetNext();
+  while(cur != m_free)
+  {
+    gmObject* object = (gmObject*)cur;
+    if((gmptr)object == a_ref)
+    {
+      return object;
+    }
+    cur = cur->GetNext();
+  }
+
+  // Search White
+  cur = m_white->GetNext();
+  while(cur != m_tail)
+  {
+    gmObject* object = (gmObject*)cur;
+    if((gmptr)object == a_ref)
+    {
+      return object;
+    }
+    cur = cur->GetNext();
+  }
+
+  // Search Persistant list
+  cur = m_persistList.GetNext();
+  while(cur != &m_persistList)
+  {
+    gmObject* object = (gmObject*)cur;
+    if((gmptr)object == a_ref)
+    {
+      return object;
+    }
+    cur = cur->GetNext();
+  }
+
+  return NULL;
+}

+ 436 - 0
gmsrc/src/gm/gmIncGC.h

@@ -0,0 +1,436 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMINCGC_H_
+#define _GMINCGC_H_
+
+#include "gmConfig.h"
+
+// Configuration options
+#define GM_GC_TURN_OFF_ABLE 1                     // Let GC turn off after completion, can be turned back on when memory low
+#define GM_GC_KEEP_PERSISTANT_SEPARATE 1          // Keep persistant object in separate list for efficiency
+#define DEPTH_FIRST 1                             // Depth first or bredth first tracing
+#ifdef GM_DEBUG_BUILD
+  #define GM_GC_STATS 1                           // Some stats
+#else //GM_DEBUG_BUILD
+  #define GM_GC_STATS 0
+#endif //GM_DEBUG_BUILD
+
+#define GM_GC_DEBUG 0                             // GC Debugging paranoid check code.  Only set to 1 when debugging the GC routines.
+
+// Fwd decls
+class gmgmGCObjBase;
+class gmGarbageCollector;
+class gmMachine;
+class gmObject;
+
+typedef void (GM_CDECL *GCFlipCallBack)();
+typedef void (GM_CDECL *gmGCScanRootsCallBack)(gmMachine* a_machine, gmGarbageCollector* a_gc);
+
+
+// Incremental garbage collection method:
+//
+// A tricolor marking scheme is used with new objects allocated Black.
+// A write barrier is used to maintain integrity of the list.
+// The list looks like:
+//
+//  | GRAY BLACK FREE | WHITE |
+//  ^      ^     ^    ^       ^
+//  G      S     F    W       T
+//
+//  Legend:
+//  G Gray (head) pointer
+//  S Scan pointer
+//  F Free pointer
+//  W White pointer
+//  T Tail pointer
+//  | List sentinels
+//
+// Note that only the Scan and Free markers actually move
+// The objects are classified between the pointer pairs as follows:
+//
+// GRAY:  Gray  (exclusive)  to Scan  (exclusive)
+// BLACK: Scan  (inclusive)  to Free  (exclusive)
+// FREE:  Free  (inclusive)  to White (exclusive)
+// WHITE: White (exclusive)  to Tail  (exclusive)
+//
+
+//////////////////////////////////////////////////
+// gmgmGCObjBase
+//////////////////////////////////////////////////
+
+#if GM_GC_DEBUG
+enum
+{
+  GM_GC_DEBUG_COL_INVALID, //0
+  GM_GC_DEBUG_COL_GRAY,    //1
+  GM_GC_DEBUG_COL_BLACK,   //2
+  GM_GC_DEBUG_COL_WHITE,   //3
+  GM_GC_DEBUG_COL_FREE,    //4
+};
+#endif //GM_GC_DEBUG
+
+/// \brief All GC objects are dervied from this class
+class gmGCObjBase
+{
+public:
+
+#if GM_GC_DEBUG
+  gmGCObjBase()
+  {
+    m_curPosColor = GM_GC_DEBUG_COL_INVALID;
+  }
+  int m_curPosColor;
+#endif //GM_GC_DEBUG
+
+  inline void SetColor(int a_color)               {m_color = (char)a_color;}
+  inline int GetColor()                           {return (int)m_color;}
+  inline gmGCObjBase* GetPrev() const             {return m_prev;}
+  inline void SetPrev(gmGCObjBase* a_prev)        {m_prev = a_prev;}
+  inline gmGCObjBase* GetNext() const             {return m_next;}
+  inline void SetNext(gmGCObjBase* a_next)        {m_next = a_next;}
+
+  inline char GetPersist()                        {return m_persist;}
+  inline void SetPersist(bool a_flag)             {m_persist = a_flag;}
+
+  /// \brief Called when GC wants to free this memory
+  virtual void Destruct(gmMachine * a_machine)    {}
+
+  /// \brief Trace pointers this object contains to other objects.
+  /// It must call gmGarbageCollector::GetNextObject() on each pointer,
+  /// until it has traced all pointers, or used up the a_workLeftToGo count.
+  /// \param a_workLeftToGo Number of pointers to trace.
+  /// \param a_workDone, The number of pointers traced.
+  /// \return true if finished tracing this object, false if not finished.
+  virtual bool Trace(gmMachine * a_machine, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone) 
+  {
+    //NOTE: Use a_gc->GetTraceState().m_context to help resume incremental tracing.
+    a_workDone = 1;
+    return true;
+  }
+
+private:
+
+  gmGCObjBase* m_prev;                            ///< Point to previous object in color set
+  gmGCObjBase* m_next;                            ///< Point to next object in color set
+  char m_color;                                   ///< Is gray or black flag, really only need by 1 bit
+  char m_persist;                                 ///< This object is persistant
+  char m_pad[2];                                  ///< Pad to dword
+};
+
+//////////////////////////////////////////////////
+// gmGCColorSet
+//////////////////////////////////////////////////
+
+/// \brief Tri color managing class
+class gmGCColorSet
+{
+public:
+
+  /// \brief Constructor
+  gmGCColorSet();
+  /// \brief Destructor
+  virtual ~gmGCColorSet();
+
+  /// \brief Initialize members
+  void Init(gmGarbageCollector* a_gc);
+
+  /// \brief Returns true if there are any gray objects in this size class, false otherwise.
+  inline  bool AnyGrays(void)
+  {
+    return (m_gray->GetNext() != m_scan);
+  }
+
+  int FollowPointers(int a_maxBytesToTrace);
+
+  /// \brief Blacken the next object (at the scan pointer) in the gray set.
+  /// Object is blackened and its children are grayed.
+  /// Returns 0 when no grays are left.
+  bool BlackenNextGray(int& a_workDone, int a_workLeftToGo);
+
+  /// \brief Called by GCGetNextObject() that is called by user code while tracing over a object.
+  inline void GrayAWhite(gmGCObjBase* a_obj);
+
+  /// \brief Gray this object.
+  void GrayThisObject(gmGCObjBase* a_obj);
+  
+  /// \brief Called on a new object being allocated.
+  void Allocate(gmGCObjBase* a_obj);
+
+  /// \brief This routine reclaims the garbage memory for the system.
+  void ReclaimGarbage();
+
+  /// \brief Destruct some free objects
+  /// \return The number of objects destructed
+  int DestructSomeFreeObjects(int a_maxToDestruct);
+
+  /// \brief Destruct all objects.
+  void DestructAll();
+
+  /// \brief Make an object persistant by moving it into the persistant list.
+  void MakePersistant(gmGCObjBase* a_obj)
+  {
+    // Fix scan (If scan == obj, obj must already be black, as we have just allocated or revived this string)
+    if( m_scan == a_obj )
+    {
+      m_scan = m_scan->GetNext(); // Not Prev as scan should be the start of black inclusive
+    }
+    // Fix sentinels
+    if( m_free == a_obj )
+    {
+      m_free = m_free->GetNext();
+    }
+ 
+    // Unlink from current list
+    a_obj->GetNext()->SetPrev(a_obj->GetPrev());
+    a_obj->GetPrev()->SetNext(a_obj->GetNext());
+ 
+    // Insert at start of persist list
+    a_obj->SetNext(m_persistList.GetNext());
+    a_obj->SetPrev(&m_persistList);
+   
+    m_persistList.GetNext()->SetPrev(a_obj);
+    m_persistList.SetNext(a_obj);
+  }
+
+  /// \brief Destruct all objects in persistant list
+  void DestructPersistantObjects();
+
+  /// \brief Revive an object before it is finalized
+  void Revive(gmGCObjBase* a_obj);
+
+  /// \brief Check if reference is valid for VM
+  gmObject* CheckReference(gmptr a_ref);
+  
+  /// \brief Get instruction at point for VM Debugger.
+  const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line);
+
+#if GM_GC_DEBUG
+  bool VerifyIntegrity();
+#endif //GM_GC_DEBUG
+
+protected:
+
+  gmGCObjBase* m_gray;
+  gmGCObjBase* m_scan;
+  gmGCObjBase* m_free;
+  gmGCObjBase* m_white;
+  gmGCObjBase* m_tail;
+
+  gmGCObjBase m_tailObject;
+  gmGCObjBase m_headObject;
+  gmGCObjBase m_separatorObject;
+
+  gmGCObjBase m_persistList;
+
+  gmGarbageCollector* m_gc;
+#if GM_GC_STATS
+  int m_numAllocated;
+#endif //GM_GC_STATS
+};
+
+
+//////////////////////////////////////////////////
+// gmGarbageCollector
+//////////////////////////////////////////////////
+
+struct gmGCTraceState
+{
+  gmGCTraceState()
+  {
+    Reset();
+  }
+
+  void Reset()
+  {
+    m_done = true;
+    m_object = NULL;
+    m_context = NULL;
+  }
+
+  bool m_done;
+  gmGCObjBase* m_object;
+  void* m_context;
+};
+
+
+/// \brief Incremental garbage collection
+/// Method: Tri-Color, Non-copying, Write barrier, Root Snapshot.
+class gmGarbageCollector
+{
+public:
+
+  /// \brief Constructor
+  gmGarbageCollector();
+  
+  /// \brief Destructor
+  virtual ~gmGarbageCollector();
+
+  /// \brief Initialize the garbage collection system
+  void Init(gmGCScanRootsCallBack a_scanRootsCallback, gmMachine* a_gmMachine);
+
+  /// \brief Perform write barrier operation on Left and/or Right side objects.
+  inline void WriteBarrier(gmGCObjBase* a_lObj /*, gmGCObjBase* a_rObj*/);
+
+  /// \brief Call to start collection
+  /// \return true if collection completed, false if more work to do.
+  bool Collect();
+
+  /// \brief Do a full collect and don't return until done.
+  void FullCollect();
+
+  /// \brief Called during trace by client code, and by scan roots callback.  
+  /// This grays a white object.
+  inline void GetNextObject(gmGCObjBase* a_obj)   {m_colorSet.GrayAWhite(a_obj);}
+
+  /// \brief Called on a new object being allocated
+  inline void AllocateObject(gmGCObjBase* a_obj)  {m_colorSet.Allocate(a_obj);}
+
+  /// \brief Get the current shade color since it is flipped each cycle.
+  inline int GetCurShadeColor()                   {return (m_curShadeColor);}
+
+  /// \brief Is the object colored gray or black?
+  inline bool IsShaded(gmGCObjBase* a_obj)        {return (a_obj->GetColor() == m_curShadeColor);}
+
+  /// \brief if GC is turned off, reclaim garbage memory
+  void ReclaimObjectsAndRestartCollection();
+
+  /// \brief Get the trace state to resume incremental collecting
+  inline gmGCTraceState& GetTraceState()          {return m_traceState;}
+
+  /// \brief Set the amount of work to do per increment of collecting
+  inline void SetWorkPerIncrement(int a_workPerIncrement)     {m_workPerIncrement = a_workPerIncrement;}
+  /// \brief Set the amount of objects to destruct per increment of collecting
+  inline void SetDestructPerIncrement(int a_destructPerIncrement)     {m_maxObjsToDestructPerIncrement = a_destructPerIncrement;}
+
+  /// \brief Get the amount of work to do per increment of collecting
+  inline int GetWorkPerIncrement()                {return m_workPerIncrement;}
+  /// \brief Get the amount of objects to destruct per increment of collecting
+  inline int GetDestructPerIncrement()            {return m_maxObjsToDestructPerIncrement;}
+
+  /// \brief Set function to be called before flip when dead objects are reclaimed. 
+  /// Optional, so pass NULL to disable.
+  inline void SetFlipCallback(GCFlipCallBack a_flipCallback)  {m_flipCallback = a_flipCallback;}
+
+  /// \brief Is collector currently turned off?
+  inline bool IsOff()                             {return m_gcTurnedOff;}
+
+  /// \brief Destruct all objects.
+  void DestructAll();
+
+  /// \brief Reclaim some free objects.
+  int ReclaimSomeFreeObjects()                    {return m_colorSet.DestructSomeFreeObjects(m_maxObjsToDestructPerIncrement);}
+
+  /// \brief Make an object persistant by moving it into the persistant list.
+  void MakeObjectPersistant(gmGCObjBase* a_obj)   {m_colorSet.MakePersistant(a_obj);}
+
+  /// \brief Get the virtual machine for language
+  inline gmMachine* GetVM()                       {return m_gmMachine;}
+
+  /// \brief Check if reference is valid for VM
+  gmObject* CheckReference(gmptr a_ref)           {return m_colorSet.CheckReference(a_ref);}
+  
+  /// \brief Get instruction at point for VM Debugger.
+  const void * GetInstructionAtBreakPoint(gmuint32 a_sourceId, int a_line) {return m_colorSet.GetInstructionAtBreakPoint(a_sourceId, a_line);}
+
+  /// \brief Revive a dead object (only used to re-live a shared string before it is finalized)
+  void Revive(gmGCObjBase* a_obj)                 
+  { 
+    if( !a_obj->GetPersist() ) 
+    {
+      m_colorSet.Revive(a_obj); 
+    } 
+  }
+
+protected:
+
+  /// \brief Has Collect() completed yet?
+  inline bool IsDoneTracing()                     {return m_doneTracing;}
+
+  /// \brief Called by Collect()
+  bool BlackenGrays();
+
+  /// \brief Flip system and reclaim garbage.
+  void Flip();
+
+  /// \brief Toggle bit used to represent 'colored'
+  inline void ToggleCurShadeColor()               {m_curShadeColor = !m_curShadeColor;}
+
+  gmGCColorSet m_colorSet;                        ///< Tri color helper class
+  int m_curShadeColor;                            ///< Cur color used to shade this generation
+  int m_workPerIncrement;                         ///< How much work to do per increment
+  int m_workLeftToGo;                             ///< How much work left in this increment
+  int m_maxObjsToDestructPerIncrement;            ///< How much destructing work to do this frame?
+  bool m_gcTurnedOff;                             ///< Is the GC currently turned off?
+  bool m_firstCollectionIncrement;                ///< Using snapshot method, scan roots atomically first
+  bool m_fullThrottle;                            ///< Set to true when forcing a full collection
+  bool m_doneTracing;                             ///< Has Collect() completed yet?
+  gmGCTraceState m_traceState;                    ///< Allows trace to resume where it left off
+
+  GCFlipCallBack m_flipCallback;                  ///< Called before flip, when dead objects are reclaimed. Default is NULL.
+  gmGCScanRootsCallBack m_scanRootsCallback;      ///< Called at start of Collect() to add gray all roots. MUST be implemented.
+  gmMachine* m_gmMachine;                         ///< Virtual machine to pass around
+};
+
+//////////////////////////////////////////////////
+// inline functions
+//////////////////////////////////////////////////
+
+void gmGCColorSet::GrayAWhite(gmGCObjBase* a_obj)
+{
+#if  GM_GC_KEEP_PERSISTANT_SEPARATE
+  if(a_obj->GetPersist()) // Don't do anything with persistant objects
+  {
+    return;
+  }
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+
+  // If right object is not shaded, shade it
+  if(!m_gc->IsShaded(a_obj))
+  {
+    GrayThisObject(a_obj);
+  }
+}
+
+
+void gmGarbageCollector::WriteBarrier(gmGCObjBase* a_lObj/*, gmGCObjBase* a_rObj*/)
+{
+  // If we are allocating black and the collector is off, do nothing
+  if(m_gcTurnedOff) 
+  { 
+    return;
+  }
+
+  // We don't need to use write barrier on root objects, so check for it if we can.
+  // if(IsRoot(a_lObj)) { return; }
+
+  // Note: Left side is logically the old right side
+
+  // This is a snapshot write barrier.  There is not old pointer to overwrite, so do nothing.
+  if(!a_lObj) 
+  { 
+    return; 
+  }
+
+#if GM_GC_KEEP_PERSISTANT_SEPARATE
+  if(a_lObj->GetPersist()) // Don't do anything with persistant objects
+  {
+    return;
+  }
+#endif //GM_GC_KEEP_PERSISTANT_SEPARATE
+
+  if(!IsShaded(a_lObj)) 
+  { 
+    m_colorSet.GrayThisObject(a_lObj);
+  }
+}
+
+#endif //_GMINCGC_H_

+ 51 - 0
gmsrc/src/gm/gmIterator.h

@@ -0,0 +1,51 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#ifndef _GMITERATOR_H_
+#define _GMITERATOR_H_
+
+//
+// Intrusive Container Iterator
+//
+
+//
+// Iterator must declare the following functions
+//
+// void Inc()
+// void Dec()
+// T* Resolve()
+// const T* Resolve() const
+// bool IsValid() const
+//
+
+#define GM_INCLUDE_ITERATOR_KERNEL(T)                                \
+inline void operator++() { Inc(); }                                  \
+inline void operator--() { Dec(); }                                  \
+inline void operator++(int) { Inc(); }                               \
+inline void operator--(int) { Dec(); }                               \
+inline T& operator*(void) { return *Resolve(); }                     \
+inline const T& operator*(void) const { return *Resolve(); }         \
+inline T* operator->(void) { return Resolve(); }                     \
+inline const T* operator->(void) const { return Resolve(); }         \
+                                                                     \
+inline operator bool(void) { return IsValid(); }                     \
+inline operator bool(void) const { return IsValid(); }               \
+inline bool operator !(void) { return !IsValid(); }                  \
+inline bool operator !(void) const { return !IsValid(); }            \
+                                                                     \
+private:                                                             \
+inline operator unsigned int(void) { return 0xDEADBEEF; }            \
+inline operator int(void) { return 0xDEADBEEF; }                     \
+inline operator unsigned int(void) const { return 0xDEADBEEF; }      \
+inline operator int(void) const { return 0xDEADBEEF; }               \
+public:
+
+#endif // _GMITERATOR_H_

+ 443 - 0
gmsrc/src/gm/gmLibHooks.cpp

@@ -0,0 +1,443 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmLibHooks.h"
+#include "gmMachine.h"
+#include "gmFunctionObject.h"
+#include "gmStringObject.h"
+
+#define ID_func GM_MAKE_ID32('f','u','n','c')
+#define ID_gml0 GM_MAKE_ID32('g','m','l','0')
+
+
+gmLibHooks::gmLibHooks(gmStream &a_stream, const char * a_source) :
+  m_allocator(1, GMCODETREE_CHAINSIZE)
+{
+  m_stream = &a_stream;
+  m_source = a_source;
+  m_swapEndian = m_stream->GetSwapEndianOnWrite();
+}
+
+
+gmLibHooks::~gmLibHooks()
+{
+}
+
+
+bool gmLibHooks::Begin(bool a_debug)
+{
+  m_debug = a_debug;
+  m_symbolOffset = 0;
+  m_functionId = 0;
+  m_functionStream.Reset();
+  m_functionStream.SetSwapEndianOnWrite(m_swapEndian);
+  return true;
+}
+
+
+bool gmLibHooks::AddFunction(gmFunctionInfo &a_info)
+{
+  // write the function into the stream
+  m_functionStream << (gmuint32) ID_func;
+  m_functionStream << (gmuint32) a_info.m_id;
+  m_functionStream << (gmuint32) ((a_info.m_root) ? 1 : 0);
+  m_functionStream << (gmuint32) a_info.m_numParams;
+  m_functionStream << (gmuint32) a_info.m_numLocals;
+  m_functionStream << (gmuint32) a_info.m_maxStackSize;
+  m_functionStream << (gmuint32) a_info.m_byteCodeLength;
+  m_functionStream.Write(a_info.m_byteCode, a_info.m_byteCodeLength);
+
+  if(m_debug)
+  {
+    int numSymbols = a_info.m_numLocals + a_info.m_numParams, i;
+
+    // debug name
+    m_functionStream << (gmuint32) GetSymbolId(a_info.m_debugName);
+
+    // line info
+    m_functionStream << (gmuint32) a_info.m_lineInfoCount;
+    for(i = 0; i < a_info.m_lineInfoCount; ++i)
+    {
+      m_functionStream << (gmuint32) a_info.m_lineInfo[i].m_address;
+      m_functionStream << (gmuint32) a_info.m_lineInfo[i].m_lineNumber;
+    }
+
+    // symbol info
+    for(i = 0; i < numSymbols; ++i)
+    {
+      if(a_info.m_symbols)
+      {
+        m_functionStream << (gmuint32) GetSymbolId(a_info.m_symbols[i]);
+      }
+      else
+      {
+        m_functionStream << (gmuint32) (~0);
+      }
+    }
+  }
+  return true;
+}
+
+
+bool gmLibHooks::End(int a_errors)
+{
+  if(a_errors == 0)
+  {
+    gmuint32 offsets[3] = {0}, offsetPos;
+    
+    gmuint32 t = ID_gml0, t1 = 0;
+    *m_stream << t;
+    t = (m_debug) ? 1 : 0;
+    *m_stream << t;
+
+    offsetPos = m_stream->Tell();
+    *m_stream << offsets[0] << offsets[1] << offsets[2];
+
+    // write the string table
+    offsets[0] = m_stream->Tell();
+    t = m_symbolOffset;
+    *m_stream << t;
+    USymbol * symbol = m_symbols.GetLast();
+    while(m_symbols.IsValid(symbol))
+    {
+      m_stream->Write(symbol->m_string, strlen(symbol->m_string) + 1);
+      symbol = m_symbols.GetPrev(symbol);
+    }
+    GM_ASSERT(m_stream->Tell() == m_symbolOffset + offsets[0] + sizeof(gmuint32));
+
+    // write the source code
+    if(m_debug && m_source)
+    {
+      offsets[1] = m_stream->Tell();
+      t = strlen(m_source) + 1;
+      t1 = 0;
+      *m_stream << t << t1;
+      m_stream->Write(m_source, t);
+    }
+    else
+    {
+      offsets[1] = 0;
+    }
+
+    // write the functions
+    offsets[2] = m_stream->Tell();
+    t = m_functionId;
+    *m_stream << t;
+    m_stream->Write(m_functionStream.GetData(), m_functionStream.GetSize());
+    m_functionStream.ResetAndFreeMemory();
+
+    // write the offsets
+    m_stream->Seek(offsetPos);
+    *m_stream << offsets[0] << offsets[1] << offsets[2];
+
+    return true;
+  }
+  return false;
+}
+
+
+gmptr gmLibHooks::GetFunctionId()
+{
+  return m_functionId++;
+}
+
+
+gmptr gmLibHooks::GetSymbolId(const char * a_symbol)
+{
+  if(a_symbol == NULL) a_symbol = "";
+
+  // see if we already have sybmol
+  USymbol * symbol = m_symbols.GetFirst();
+  while(m_symbols.IsValid(symbol))
+  {
+    if(strcmp(symbol->m_string, a_symbol) == 0)
+    {
+      return symbol->m_offset;
+    }
+    symbol = m_symbols.GetNext(symbol);
+  }
+
+  // add a new symbol
+  unsigned int len = strlen(a_symbol) + 1;
+  symbol = (USymbol *) m_allocator.AllocBytes(sizeof(USymbol), GM_DEFAULT_ALLOC_ALIGNMENT);
+  symbol->m_string = (char *) m_allocator.AllocBytes(len, GM_DEFAULT_ALLOC_ALIGNMENT);
+  memcpy(symbol->m_string, a_symbol, len);
+  symbol->m_offset = m_symbolOffset;
+  m_symbolOffset += len;
+  m_symbols.InsertFirst(symbol);
+  return symbol->m_offset;
+}
+
+
+gmptr gmLibHooks::GetStringId(const char * a_string)
+{
+  return GetSymbolId(a_string);
+}
+
+
+//
+//
+// Library Loading
+//
+//
+
+struct gmlHeader
+{
+  gmuint32 m_id;
+  gmuint32 m_flags;
+  gmuint32 m_stOffset;
+  gmuint32 m_scOffset;
+  gmuint32 m_fnOffset;
+};
+
+struct gmlStrings
+{
+  gmuint32 m_size;
+};
+
+struct gmlLineInfo
+{
+  gmuint32 m_byteCodeAddress;
+  gmuint32 m_lineNumber;
+};
+
+struct gmlSource
+{
+  gmuint32 m_size;
+  gmuint32 m_flags;
+};
+
+struct gmlFunction
+{
+  gmuint32 m_func;
+  gmuint32 m_id;
+  gmuint32 m_flags;
+  gmuint32 m_numParams;
+  gmuint32 m_numLocals;
+  gmuint32 m_maxStackSize;
+  gmuint32 m_byteCodeLen;
+};
+
+
+gmFunctionObject * gmLibHooks::BindLib(gmMachine &a_machine, gmStream &a_stream, const char * a_filename)
+{
+  gmlHeader header;
+  gmlStrings strings;
+  gmlSource source;
+  gmlFunction function;
+  gmFunctionObject * functionObject = NULL;
+  gmFunctionObject ** functionObjects = NULL;
+  bool error = true, debug = false;
+  char * stringTable = NULL;
+  char * sourceCode = NULL;
+  char * byteCode = NULL;
+  unsigned int i, j;
+  gmuint32 numFunctions = 0;
+  gmuint32 sourceCodeId = 0;
+  gmuint32 scratchSize = 2048;
+  gmuint8 * scratch = GM_NEW( gmuint8[scratchSize] );
+
+  // Turn garbage collection off.
+  bool gc = a_machine.IsGCEnabled();
+  a_machine.EnableGC(false);
+
+  // Ensure stream is read from the start
+  a_stream.Seek( 0 );
+
+  // Load the gmlib header
+  if((a_stream.Read(&header, sizeof(header)) != sizeof(header)) || header.m_id != ID_gml0) { goto done; }
+  debug = (header.m_flags & 1);
+
+  // Load the string table
+  a_stream.Seek(header.m_stOffset);
+  if(a_stream.Read(&strings, sizeof(strings)) != sizeof(strings)) { goto done; }
+  stringTable = GM_NEW( char[strings.m_size] );
+  if(a_stream.Read(stringTable, strings.m_size) != strings.m_size) { goto done; }
+
+  // Read the source code 
+  if(header.m_scOffset && a_machine.GetDebugMode())
+  {
+    a_stream.Seek(header.m_scOffset);
+    if(a_stream.Read(&source, sizeof(source)) != sizeof(source)) { goto done; }
+    sourceCode = GM_NEW( char[source.m_size] );
+    if(a_stream.Read(sourceCode, source.m_size) != source.m_size) { goto done; }
+    sourceCodeId = a_machine.AddSourceCode(sourceCode, a_filename);
+    delete[] sourceCode;
+    sourceCode = NULL;
+  }
+
+  // Read in the functions
+  a_stream.Seek(header.m_fnOffset);
+  if(a_stream.Read(&numFunctions, sizeof(numFunctions)) != sizeof(numFunctions)) { goto done; }
+
+  // Allocate n function objects.
+  functionObjects = GM_NEW( gmFunctionObject *[numFunctions] );
+  for(i = 0; i < numFunctions; ++i)
+  {
+    functionObjects[i] = a_machine.AllocFunctionObject();
+  }
+
+  // Load each function
+  for(i = 0; i < numFunctions; ++i)
+  {
+    if((a_stream.Read(&function, sizeof(function)) != sizeof(function)) || function.m_func != ID_func) { goto done; }
+    // Read in the byte code
+    if(byteCode) { delete[] byteCode; }
+    // use the byte code allocation for the debug params and locals symbol array
+    byteCode = GM_NEW( char[function.m_byteCodeLen] );
+    if(a_stream.Read(byteCode, function.m_byteCodeLen) != function.m_byteCodeLen) { goto done; }
+
+    // Load all symbols
+    union
+    {
+      gmuint8 * instruction;
+      gmuint32 * instruction32;
+    };
+
+    instruction = (gmuint8 *) byteCode;
+    gmuint8 * end = instruction + function.m_byteCodeLen;
+    for(;instruction < end;)
+    {
+      switch(*(instruction32++))
+      {
+        case BC_BRA :
+        case BC_BRZ :
+        case BC_BRNZ :
+        case BC_BRZK :
+        case BC_BRNZK :
+        case BC_FOREACH :
+        case BC_PUSHINT :
+        case BC_PUSHFP : instruction += sizeof(gmfloat); break;
+
+        case BC_CALL :
+        case BC_GETLOCAL :
+        case BC_SETLOCAL : instruction += sizeof(gmuint32); break;
+
+        case BC_GETDOT :
+        case BC_SETDOT :
+        case BC_GETTHIS :
+        case BC_SETTHIS :
+        case BC_GETGLOBAL :
+        case BC_SETGLOBAL :
+        {
+          gmptr * reference = (gmptr *) instruction; 
+          GM_ASSERT(*reference >= 0 && *reference < (gmptr) strings.m_size);
+          *reference = a_machine.AllocPermanantStringObject(&stringTable[*reference])->GetRef();
+          instruction += sizeof(gmptr);
+          break;
+        }
+        case BC_PUSHSTR :
+        {
+          gmptr * reference = (gmptr *) instruction; 
+          GM_ASSERT(*reference >= 0 && *reference < (gmptr) strings.m_size);
+          *reference = a_machine.AllocStringObject(&stringTable[*reference])->GetRef();
+          instruction += sizeof(gmptr);
+          break;
+        }
+
+        case BC_PUSHFN :
+        {
+          gmptr * reference = (gmptr *) instruction; 
+          GM_ASSERT(*reference >= 0 && *reference < (gmptr) numFunctions);
+          *reference = functionObjects[*reference]->GetRef();
+          instruction += sizeof(gmptr);
+          break;
+        }
+
+        default : break;
+      }
+    }
+
+    // Initialise our function object.
+    gmFunctionInfo functionInfo;
+    gmFunctionObject * currFunction = functionObjects[function.m_id];
+    functionInfo.m_id = currFunction->GetRef();
+    functionInfo.m_root = (function.m_flags & 1);
+    functionInfo.m_byteCode = byteCode;
+    functionInfo.m_byteCodeLength = function.m_byteCodeLen;
+    functionInfo.m_numParams = function.m_numParams;
+    functionInfo.m_numLocals = function.m_numLocals;
+    functionInfo.m_maxStackSize = function.m_maxStackSize;
+    functionInfo.m_symbols = NULL;
+    functionInfo.m_lineInfo = NULL;
+
+    // We have now loaded all objects into the byte code....  Load the debug info
+    if(debug)
+    {
+      gmuint32 stringOffset, lineInfoCount, numSymbols = function.m_numLocals + function.m_numParams;
+
+      // debug name
+      if(a_stream.Read(&stringOffset, sizeof(stringOffset)) != sizeof(stringOffset)) { goto done; }
+      GM_ASSERT(stringOffset < strings.m_size);
+      functionInfo.m_debugName = &stringTable[stringOffset];
+
+      // Make sure our scratch memory is large enough
+      if(a_stream.Read(&lineInfoCount, sizeof(lineInfoCount)) != sizeof(lineInfoCount)) { goto done; }
+      gmuint32 reqdScratchSize = (lineInfoCount * sizeof(gmLineInfo)) + (sizeof(const char *) * numSymbols);
+      if(scratchSize < reqdScratchSize)
+      {
+        scratchSize = reqdScratchSize;
+        delete[] scratch;
+        scratch = GM_NEW( gmuint8[scratchSize] );
+      }
+      gmLineInfo * lineInfo = (gmLineInfo *) scratch;
+      functionInfo.m_lineInfo = lineInfo;
+      functionInfo.m_symbols = (const char **) (scratch + (lineInfoCount * sizeof(gmLineInfo)));
+      functionInfo.m_lineInfoCount = lineInfoCount;
+
+      // Debug line info
+      for(j = 0; j < lineInfoCount; ++j)
+      {
+        gmlLineInfo libLineInfo;
+        if(a_stream.Read(&libLineInfo, sizeof(libLineInfo)) != sizeof(libLineInfo)) { goto done; }
+        lineInfo[j].m_address = libLineInfo.m_byteCodeAddress;
+        lineInfo[j].m_lineNumber = libLineInfo.m_lineNumber;
+      }
+
+      // Debug symbols
+      for(j = 0; j < numSymbols; ++j)
+      {
+        if(a_stream.Read(&stringOffset, sizeof(stringOffset)) != sizeof(stringOffset)) { goto done; }
+        GM_ASSERT(stringOffset < strings.m_size);
+        functionInfo.m_symbols[j] = &stringTable[stringOffset];
+      }
+
+    }
+
+    // AND FINALLY, INITIALISE OUR FUNCTION
+    currFunction->Init(&a_machine, debug && a_machine.GetDebugMode(), functionInfo, sourceCodeId);
+    if(functionInfo.m_root)
+    {
+      functionObject = currFunction;
+    }
+  }
+
+  error = false;
+
+done:
+
+  // turn gc off.
+  a_machine.EnableGC(gc);
+  if(stringTable) { delete[] stringTable; }
+  if(sourceCode) { delete[] sourceCode; }
+  if(functionObjects) { delete[] functionObjects; }
+  if(byteCode) { delete[] byteCode; }
+  if(scratch) { delete[] scratch; }
+
+  if(error)
+  {
+    a_machine.GetLog().LogEntry("Error loading library");
+    return NULL;
+  }
+  return functionObject;
+}
+

+ 124 - 0
gmsrc/src/gm/gmLibHooks.h

@@ -0,0 +1,124 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmCodeGenHooks.h"
+#include "gmListDouble.h"
+#include "gmMemChain.h"
+#include "gmStream.h"
+#include "gmStreamBuffer.h"
+
+class gmMachine;
+class gmFunctionObject;
+
+/// \class gmLibHooks
+/// \brief gmLibHooks is a compiler hook class that allows compiling to a lib for disk storage.
+class gmLibHooks : public gmCodeGenHooks
+{
+public:
+
+  // a_source and must exist untill destruction of the libhooks
+  gmLibHooks(gmStream &a_stream, const char * a_source); 
+  virtual ~gmLibHooks();
+
+  virtual bool Begin(bool a_debug);
+  virtual bool AddFunction(gmFunctionInfo &a_info);
+  virtual bool End(int a_errors);
+  virtual gmptr GetFunctionId();
+  virtual gmptr GetSymbolId(const char * a_symbol);
+  virtual gmptr GetStringId(const char * a_string);
+  virtual bool SwapEndian() const { return m_swapEndian; }
+
+  /// \brief BindLib will bind the lib to the machine, and return the root function for executing.
+  static gmFunctionObject * BindLib(gmMachine &a_machine, gmStream &a_stream, const char * a_filename);
+
+private:
+
+  class USymbol : public gmListDoubleNode<USymbol>
+  {
+  public: 
+    USymbol();
+    ~USymbol();
+    char * m_string;
+    gmptr m_offset; // offset into symbol table.
+  };
+
+  gmStream * m_stream;
+  bool m_swapEndian;
+  bool m_debug;
+  const char * m_source;
+  gmptr m_symbolOffset;
+  gmptr m_functionId;
+  gmStreamBufferDynamic m_functionStream;
+  gmListDouble<USymbol> m_symbols;
+  gmMemChain m_allocator;
+};
+
+/*
+
+  Library file format for .gmlib files (gm library files) (native endian)
+
+  // header
+
+  'gml0'                      [4 bytes]
+  flags                       [4 bytes]  // 0x01 - debug lib, 
+
+  string_table_offset         [4 bytes]  // relative to start of lib
+  source_code_offset          [4 bytes]  // 0 for not there, otherwise relative to start of lib
+  functions_offset            [4 bytes]  // relative to start of lib
+
+  string_table
+  {
+    string_table_size         [4 bytes]
+    data                      [1 byte ] * string_table_size of 0 terminated strings
+  }
+
+  source_code
+  {
+    source_code_size          [4 bytes]
+    flags                     [4 bytes] // possibly encryption and compression flags
+    data                      [1 byte ] * source code size.
+  }
+
+  functions
+  {
+    num_functions             [4 bytes]  // number of functions
+
+    function[]
+    {
+      'func'                  [4 bytes]
+      function_id             [4 bytes] // FUNCTION IDS ARE FROM 0-(num_functions-1), BC_PUSHFN calls reference this id.
+      flags                   [4 bytes] // 0x01 - is root
+      num_params              [4 bytes]
+      num_locals              [4 bytes]
+      max_stack_size          [4 bytes]
+      byte_code_length        [4 bytes]
+      byte_code               [1 byte ] * byte_code_length // contains offsets rel to string tab 
+                                                           // and function ids for certian op codes.
+
+      // byte code must be changed on load such that BC_PUSHFN calls reference their respective function object, 
+      // and BC_PUSHSTR reference their string object created by their string table offset.
+
+      if(debug lib)
+      {
+        debug_name_offset     [4 bytes]
+        line_info_count       [4 bytes]
+        line_info[]
+        {
+          byte_code_address   [4 bytes]
+          line_number         [4 bytes]
+        }
+        symbol_name_offsets[] [4 bytes] * (num_params + num_locals) (~0) on no offset
+      }
+    }
+  }
+
+*/

+ 14 - 0
gmsrc/src/gm/gmListDouble.cpp

@@ -0,0 +1,14 @@
+/*
+    _____               __  ___          __            ____        _      __
+   / ___/__ ___ _  ___ /  |/  /__  ___  / /_____ __ __/ __/_______(_)__  / /_
+  / (_ / _ `/  ' \/ -_) /|_/ / _ \/ _ \/  '_/ -_) // /\ \/ __/ __/ / _ \/ __/
+  \___/\_,_/_/_/_/\__/_/  /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
+                                               /___/             /_/
+                                             
+  See Copyright Notice in gmMachine.h
+
+*/
+
+#include "gmConfig.h"
+#include "gmListDouble.h"
+

Some files were not shown because too many files changed in this diff