This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -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

View File

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

View File

@@ -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]

View File

@@ -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={}

View File

@@ -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]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,324 @@
-----------------------------------------------------------
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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,27 @@
-----------------------------------------------------------
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 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.

View File

@@ -0,0 +1,308 @@
<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY><BR><HR>
<H1>gm</H1>
<HR><BR>
<HR>
<a name="gm::array"><H3>array</H3>
</a><B><EM>Brief:</B></EM> array will create a fixed size array object <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> array <BR><BR><HR>
<H1>array</H1>
<HR><BR>
<HR>
<a name="array::Size"><H3>Size</H3>
</a><B><EM>Brief:</B></EM> Size will return the current size of the fixed array <BR><B><EM>Return:</B></EM> int array size <BR><HR>
<a name="array::Resize"><H3>Resize</H3>
</a><B><EM>Brief:</B></EM> Resize will resize the array to a new size <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> null <BR><HR>
<a name="array::Shift"><H3>Shift</H3>
</a><B><EM>Brief:</B></EM> Shift will shift slide the array elements by a delta, nulls are shifted in <BR><B><EM>Param:</B></EM> int delta <BR><B><EM>Return:</B></EM> null <BR><HR>
<a name="array::Move"><H3>Move</H3>
</a><B><EM>Brief:</B></EM> Move will perform a non destructive move on the array <BR><B><EM>Param:</B></EM> int dst <BR><B><EM>Param:</B></EM> int src <BR><B><EM>Param:</B></EM> int size <BR><B><EM>Return:</B></EM> null <BR><BR><HR>
<H1>math</H1>
<HR><BR>
<HR>
<a name="math::abs"><H3>abs</H3>
</a><B><EM>Brief:</B></EM> abs will return the absolute value of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float abs(param) <BR><HR>
<a name="math::sqrt"><H3>sqrt</H3>
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float sqrt(param) <BR><HR>
<a name="math::sqrt"><H3>sqrt</H3>
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float A <BR><B><EM>Param:</B></EM> int\float B <BR><B><EM>Return:</B></EM> int\float A to the power of B <BR><HR>
<a name="math::floor"><H3>floor</H3>
</a><B><EM>Brief:</B></EM> floor <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float floor(A) <BR><HR>
<a name="math::ceil"><H3>ceil</H3>
</a><B><EM>Brief:</B></EM> ceil <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float ceil(A) <BR><HR>
<a name="math::round"><H3>round</H3>
</a><B><EM>Brief:</B></EM> round <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float round(A) <BR><HR>
<a name="math::degtorad"><H3>degtorad</H3>
</a><B><EM>Brief:</B></EM> degtorad will convert degrees to radians <BR><B><EM>Param:</B></EM> float\int deg <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::radtodeg"><H3>radtodeg</H3>
</a><B><EM>Brief:</B></EM> radtodeg will convert radians to degrees <BR><B><EM>Param:</B></EM> float\int rad <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::cos"><H3>cos</H3>
</a><B><EM>Brief:</B></EM> cos will return the radian cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::sin"><H3>sin</H3>
</a><B><EM>Brief:</B></EM> sin will return the radian sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::tan"><H3>tan</H3>
</a><B><EM>Brief:</B></EM> tan will return the radian tan (sin/cos) <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::acos"><H3>acos</H3>
</a><B><EM>Brief:</B></EM> acos will return the radian arc cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::asin"><H3>asin</H3>
</a><B><EM>Brief:</B></EM> asin will return the radian arc sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::atan"><H3>atan</H3>
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::atan"><H3>atan</H3>
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent of x / y <BR><B><EM>Param:</B></EM> float x <BR><B><EM>Param:</B></EM> float y <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::log"><H3>log</H3>
</a><B><EM>Brief:</B></EM> log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base <BR><B><EM>Param:</B></EM> float natural \ base <BR><B><EM>Param:</B></EM> float value (optional) <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::min"><H3>min</H3>
</a><B><EM>Brief:</B></EM> min will return the min of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int min(A, B) <BR><HR>
<a name="math::max"><H3>max</H3>
</a><B><EM>Brief:</B></EM> max will return the max of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int max(A, B) <BR><HR>
<a name="math::clamp"><H3>clamp</H3>
</a><B><EM>Brief:</B></EM> clamp will return the clamed value. clamp(min, val, max) <BR><B><EM>Param:</B></EM> float\int MIN <BR><B><EM>Param:</B></EM> float\int VALUE <BR><B><EM>Param:</B></EM> float\int MAX <BR><B><EM>Return:</B></EM> float\int value clamped to min, max <BR><HR>
<a name="math::randint"><H3>randint</H3>
</a><B><EM>Brief:</B></EM> randint will return a random int from lower inclusive to upper. <BR><B><EM>Param:</B></EM> int lower inclusive <BR><B><EM>Param:</B></EM> int upper <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="math::randfloat"><H3>randfloat</H3>
</a><B><EM>Brief:</B></EM> randfloat will return a random float from lower inclusive to upper. <BR><B><EM>Param:</B></EM> float lower inclusive <BR><B><EM>Param:</B></EM> float upper <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::randseed"><H3>randseed</H3>
</a><B><EM>Brief:</B></EM> randseed will seed the random number generator <BR><B><EM>Param:</B></EM> int seed <BR><BR><HR>
<H1>string</H1>
<HR><BR>
<B><EM>Brief:</B></EM> string operations often store a copy of the string on the stack, so keep string sizes reasonable <BR><HR>
<a name="string::IsEmpty"><H3>IsEmpty</H3>
</a><B><EM>Brief:</B></EM> IsEmpty will test to see if the string is 0 length <BR><B><EM>Return:</B></EM> non-zero if the string is empty <BR><HR>
<a name="string::Length"><H3>Length</H3>
</a><B><EM>Brief:</B></EM> Length will return the length of the string not including the null terminating character <BR><B><EM>Return:</B></EM> int length <BR><HR>
<a name="string::Left"><H3>Left</H3>
</a><B><EM>Brief:</B></EM> Left will return the left count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Right"><H3>Right</H3>
</a><B><EM>Brief:</B></EM> Right will return the right count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::RightAt"><H3>RightAt</H3>
</a><B><EM>Brief:</B></EM> RightAt will return the charaters right of and including the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Mid"><H3>Mid</H3>
</a><B><EM>Brief:</B></EM> Mid will return count characters from the start index <BR><B><EM>Param:</B></EM> int startIndex <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Compare"><H3>Compare</H3>
</a><B><EM>Brief:</B></EM> Compare will perform a string compare <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
<a name="string::CompareNoCase"><H3>CompareNoCase</H3>
</a><B><EM>Brief:</B></EM> CompareNoCase will perform a string compare (case insensitive) <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
<a name="string::Int"><H3>Int</H3>
</a><B><EM>Brief:</B></EM> Int will return the int value of the string <BR><B><EM>Return:</B></EM> int value <BR><HR>
<a name="string::Float"><H3>Float</H3>
</a><B><EM>Brief:</B></EM> Float will return the float value of the string <BR><B><EM>Return:</B></EM> float value <BR><HR>
<a name="string::String"><H3>String</H3>
</a><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Upper"><H3>Upper</H3>
</a><B><EM>Brief:</B></EM> Upper will return the string as uppercase <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Lower"><H3>Lower</H3>
</a><B><EM>Brief:</B></EM> Lower will return the string as lowercase <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SpanIncluding"><H3>SpanIncluding</H3>
</a><B><EM>Brief:</B></EM> SpanIncluding will return this string while characters are within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SpanExcluding"><H3>SpanExcluding</H3>
</a><B><EM>Brief:</B></EM> SpanExcluding will return this string while characters are not within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::AppendPath"><H3>AppendPath</H3>
</a><B><EM>Brief:</B></EM> AppendPath will append a path make sure one '\' is maintained <BR><B><EM>Param:</B></EM> string path to append <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::ReplaceCharsInSet"><H3>ReplaceCharsInSet</H3>
</a><B><EM>Brief:</B></EM> ReplaceCharsInSet will replace all chars in this that are within the charset with the given int char <BR><B><EM>Param:</B></EM> int char to replace with <BR><B><EM>Param:</B></EM> string charset of chars to replace <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Find"><H3>Find</H3>
</a><B><EM>Brief:</B></EM> Find will find the first occurance of the passed string within this string <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Param:</B></EM> int start index optional (0) <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
<a name="string::Reverse"><H3>Reverse</H3>
</a><B><EM>Brief:</B></EM> Reverse characters of a string <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::ReverseFind"><H3>ReverseFind</H3>
</a><B><EM>Brief:</B></EM> ReverseFind will find the first occurance of the passed string within this string starting from the right <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
<a name="string::GetAt"><H3>GetAt</H3>
</a><B><EM>Brief:</B></EM> GetAt will return the char at the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> int char, or null if index was out of range <BR><HR>
<a name="string::SetAt"><H3>SetAt</H3>
</a><B><EM>Brief:</B></EM> SetAt will return the string with the character set at the given position <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Param:</B></EM> int char <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::TrimLeft"><H3>TrimLeft</H3>
</a><B><EM>Brief:</B></EM> TrimLeft will return the string with the chars from the passed char set trimmed from the left <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::TrimRight"><H3>TrimRight</H3>
</a><B><EM>Brief:</B></EM> TrimRight will return the string with the chars from the passed char set trimmed from the right <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetFilenameNoExt"><H3>GetFilenameNoExt</H3>
</a><B><EM>Brief:</B></EM> GetFilenameNoExt will return the filename part of a path string <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetFilename"><H3>GetFilename</H3>
</a><B><EM>Brief:</B></EM> GetFilename will return the filename part of a path string incl. extension <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetExtension"><H3>GetExtension</H3>
</a><B><EM>Brief:</B></EM> GetExtension will return the file extension <BR><B><EM>Param:</B></EM> int inclDot optional (0) 1 will include '.', 0 won't <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SetExtension"><H3>SetExtension</H3>
</a><B><EM>Brief:</B></EM> SetExtension returns a string with the extension change to the given one. <BR><B><EM>Param:</B></EM> string ext optional (null) the new extension, with or without the dot. null to remove extension. <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetPath"><H3>GetPath</H3>
</a><B><EM>Brief:</B></EM> GetPath will return the file path from a path string <BR><B><EM>Param:</B></EM> int inclSlash optional (0) will include a '\' on the end of the path <BR><B><EM>Return:</B></EM> string <BR><BR><HR>
<H1>system</H1>
<HR><BR>
<B><EM>Brief:</B></EM> system functions are bound in a "system" table. <BR><HR>
<a name="system::Exec"><H3>Exec</H3>
</a><B><EM>Brief:</B></EM> Exec will execute a system command <BR><B><EM>Param:</B></EM> string params will be concatinated together with a single space to form the final system command string <BR><B><EM>Return:</B></EM> integer value returned from system exec call, -1 on error <BR><HR>
<a name="system::DoFile"><H3>DoFile</H3>
</a><B><EM>Brief:</B></EM> DoFile will execute the gm script in the named file <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int optional (1) as 1 will execute string before returning, 0 will execute later. <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> thread id of new thread created to execute file <BR><HR>
<a name="system::File"><H3>File</H3>
</a><B><EM>Brief:</B></EM> File will create a file object <BR><B><EM>Return:</B></EM> file object <BR><HR>
<a name="system::FileExists"><H3>FileExists</H3>
</a><B><EM>Brief:</B></EM> FileExists will test to see if a file exists <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Return:</B></EM> 1 if the file exists, otherwise 0 <BR><HR>
<a name="system::FileFindFirst"><H3>FileFindFirst</H3>
</a><B><EM>Brief:</B></EM> FileFindFirst will start a file search will test to see if a file exists <BR><B><EM>Param:</B></EM> string filesearch (may contain wildcards, eg, `c:\temp\*.txt`) <BR><B><EM>Return:</B></EM> fileFind object. fileFind object has .filename and .size member <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
<a name="system::FileFindNext"><H3>FileFindNext</H3>
</a><B><EM>Brief:</B></EM> FileFindNext will get the next file matching the file find <BR><B><EM>Param:</B></EM> fileFind object returned by FileFindFirst call or FileFindNext call <BR><B><EM>Return:</B></EM> fileFind object <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
<a name="system::FileInfo"><H3>FileInfo</H3>
</a><B><EM>Brief:</B></EM> FileInfo will return a file info object that has readonly members for .creationDate <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size <BR><HR>
<a name="system::CreateFolder"><H3>CreateFolder</H3>
</a><B><EM>Brief:</B></EM> CreateFolder will create a file path if it does not already exist <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> int 0 on failure, 1 on successful create, 2 if folder already exists <BR><HR>
<a name="system::DeleteFolder"><H3>DeleteFolder</H3>
</a><B><EM>Brief:</B></EM> DeleteFolder will remove a file path <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Param:</B></EM> int remove subfiles and folders optional (0) <BR><B><EM>Return:</B></EM> int 1 on success, 0 con failure <BR><HR>
<a name="system::Time"><H3>Time</H3>
</a><B><EM>Brief:</B></EM> Time will return a unix style time_t as an int <BR><B><EM>Return:</B></EM> the current time <BR><HR>
<a name="system::FormatTime"><H3>FormatTime</H3>
</a><B><EM>Brief:</B></EM> FormatTime will take a int (time_t) value and format according to the passed format string. <BR><B><EM>Param:</B></EM> int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time <BR><B><EM>Param:</B></EM> 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 <20> 31)<BR> %H : Hour in 24-hour format (00 <20> 23)<BR> %I : Hour in 12-hour format (01 <20> 12)<BR> %j : Day of year as decimal number (001 <20> 366)<BR> %m : Month as decimal number (01 <20> 12)<BR> %M : Minute as decimal number (00 <20> 59)<BR> %p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR> %S : Second as decimal number (00 <20> 59)<BR> %U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR> %w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR> %W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR> %x : Date representation for current locale<BR> %X : Time representation for current locale<BR> %y : Year without century, as decimal number (00 <20> 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> <BR><B><EM>Return:</B></EM> the time as a string. <BR><BR><HR>
<H1>fileFind</H1>
<HR><BR>
<B><EM>Brief:</B></EM> fileFind object has a "filename" and "size" member <BR><HR>
<a name="fileFind::GetAttribute"><H3>GetAttribute</H3>
</a><B><EM>Brief:</B></EM> GetAttribute will test a file attribute. <BR><B><EM>Param:</B></EM> int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory <BR><B><EM>Return:</B></EM> 1 if the attribute is set, 0 otherwise <BR><BR><HR>
<H1>file</H1>
<HR><BR>
<HR>
<a name="file::Open"><H3>Open</H3>
</a><B><EM>Brief:</B></EM> Open will open a file in binary mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
<a name="file::OpenText"><H3>OpenText</H3>
</a><B><EM>Brief:</B></EM> OpenText will open a file in text mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
<a name="file::Close"><H3>Close</H3>
</a><B><EM>Brief:</B></EM> Close will close a file <BR><HR>
<a name="file::IsOpen"><H3>IsOpen</H3>
</a><B><EM>Brief:</B></EM> IsOpen will test to see if a file is open <BR><B><EM>Return:</B></EM> 1 if the file is open, 0 otherwise <BR><HR>
<a name="file::Seek"><H3>Seek</H3>
</a><B><EM>Brief:</B></EM> Move to position within file <BR><B><EM>Param:</B></EM> int offset positional offset relative to origin <BR><B><EM>Param:</B></EM> int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins. <BR><B><EM>Return:</B></EM> int 1 if operation succeeded, 0 if failed. <BR><HR>
<a name="file::Tell"><H3>Tell</H3>
</a><B><EM>Brief:</B></EM> Tell will return the current cursor position of the file <BR><B><EM>Return:</B></EM> int the current cursor position, -1 on error <BR><HR>
<a name="file::ReadLine"><H3>ReadLine</H3>
</a><B><EM>Brief:</B></EM> ReadLine will read a line of text from the file. <BR><B><EM>Param:</B></EM> int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed <BR><B><EM>Return:</B></EM> string, or null on eof <BR><HR>
<a name="file::ReadChar"><H3>ReadChar</H3>
</a><B><EM>Brief:</B></EM> ReadChar will read a char from the file <BR><B><EM>Return:</B></EM> int char or null on eof <BR><HR>
<a name="file::WriteString"><H3>WriteString</H3>
</a><B><EM>Brief:</B></EM> WriteString will write a string to the file <BR><B><EM>Param:</B></EM> string to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><HR>
<a name="file::WriteChar"><H3>WriteChar</H3>
</a><B><EM>Brief:</B></EM> WriteChar will write a char to the file <BR><B><EM>Param:</B></EM> int char to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><BR><HR>
<H1>Vector3</H1>
<HR><BR>
<HR>
<a name="Vector3::Vector3"><H3>Vector3</H3>
</a><B><EM>Brief:</B></EM> Create a Vector3 object <BR><B><EM>Param:</B></EM> float x or [0] optional (0) <BR><B><EM>Param:</B></EM> float y or [1] optional (0) <BR><B><EM>Param:</B></EM> float z or [2] optional (0) <BR><BR><HR>
<H1>Vector3</H1>
<HR><BR>
<B><EM>Brief:</B></EM> Vector3 math class <BR><HR>
<a name="Vector3::DominantAxis"><H3>DominantAxis</H3>
</a><B><EM>Brief:</B></EM> Find the index of the largest vector component. <BR><B><EM>This:</B></EM> Vector to evaluate. <BR><B><EM>Return:</B></EM> int Index of largest component. <BR><HR>
<a name="Vector3::Dot"><H3>Dot</H3>
</a><B><EM>Brief:</B></EM> Calculate the Dot (or Inner) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> float result. <BR><HR>
<a name="Vector3::Length"><H3>Length</H3>
</a><B><EM>Brief:</B></EM> Length will return the length of the vector. <BR><B><EM>Return:</B></EM> float Dot product result that is cosine of the angle between the two vectors. <BR><HR>
<a name="Vector3::Cross"><H3>Cross</H3>
</a><B><EM>Brief:</B></EM> Calculate the Cross (or Outer) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> Vector3 Cross product resultant vector that is perpendicular to the two input vectors and length sine of the angle between them. <BR><HR>
<a name="Vector3::Normalize"><H3>Normalize</H3>
</a><B><EM>Brief:</B></EM> Return a unit length copy of this vector. <BR><B><EM>This:</B></EM> Vector to be copied. <BR><B><EM>Return:</B></EM> Vector3 Unit length copy of this vector. <BR><HR>
<a name="Vector3::LengthSquared"><H3>LengthSquared</H3>
</a><B><EM>Brief:</B></EM> Return the squared length of the vector. <BR><B><EM>Return:</B></EM> float Squared length of the vector. <BR><HR>
<a name="Vector3::ProjectFrom"><H3>ProjectFrom</H3>
</a><B><EM>Brief:</B></EM> Project a direction from a point. <BR><B><EM>This:</B></EM> Vector3 Direction. <BR><B><EM>Param:</B></EM> Vector3 Start point; <BR><B><EM>Param:</B></EM> float Distance or time. <BR><B><EM>Return:</B></EM> Vector3 Projected result. <BR><HR>
<a name="Vector3::Clone"><H3>Clone</H3>
</a><B><EM>Brief:</B></EM> Return a copy of this vector. <BR><B><EM>Return:</B></EM> A copy of this vector. <BR><HR>
<a name="Vector3::Set"><H3>Set</H3>
</a><B><EM>Brief:</B></EM> Set vector from other vector or 3 components. <BR><HR>
<a name="Vector3::LerpToPoint"><H3>LerpToPoint</H3>
</a><B><EM>Brief:</B></EM> Linear interpolate between two 'point' vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
<a name="Vector3::SlerpToVector"><H3>SlerpToVector</H3>
</a><B><EM>Brief:</B></EM> Spherical linear interpolate between two vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
<a name="Vector3::RotateAxisAngle"><H3>RotateAxisAngle</H3>
</a><B><EM>Brief:</B></EM> Rotate around Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> Vector3 Unit length axis of rotation. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateX"><H3>RotateX</H3>
</a><B><EM>Brief:</B></EM> Rotate around X Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateX"><H3>RotateX</H3>
</a><B><EM>Brief:</B></EM> Rotate around Y Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateZ"><H3>RotateZ</H3>
</a><B><EM>Brief:</B></EM> Rotate around Z Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::SetAdd"><H3>SetAdd</H3>
</a><B><EM>Brief:</B></EM> Add two vectors, store result in this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><HR>
<a name="Vector3::Add"><H3>Add</H3>
</a><B><EM>Brief:</B></EM> Add vector to this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 vector to add. <BR><BR><HR>
<H1>gm</H1>
<HR><BR>
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
<a name="gm::debug"><H3>debug</H3>
</a><B><EM>Brief:</B></EM> debug will cause a the debugger to break at this point while running. <BR><BR><HR>
<H1>gm</H1>
<HR><BR>
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
<a name="gm::gmVersion"><H3>gmVersion</H3>
</a><B><EM>Brief:</B></EM> gmVersion will return the gmMachine version string. version string is major type . minor type as a string and was added at version 1.1 <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="gm::typeId"><H3>typeId</H3>
</a><B><EM>Brief:</B></EM> typeId will return the type id of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> integer type <BR><HR>
<a name="gm::typeName"><H3>typeName</H3>
</a><B><EM>Brief:</B></EM> typeName will return the type name of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="gm::typeRegisterOperator"><H3>typeRegisterOperator</H3>
</a><B><EM>Brief:</B></EM> typeRegisterOperator will register an operator for a type <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string operator name is one of "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod", "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv", "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not" <BR><B><EM>Param:</B></EM> function <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
<a name="gm::typeRegisterVariable"><H3>typeRegisterVariable</H3>
</a><B><EM>Brief:</B></EM> typeRegisterVariable will register a variable with a type such that (type).varname will return the variable <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string var name <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
<a name="gm::sysCollectGarbage"><H3>sysCollectGarbage</H3>
</a><B><EM>Brief:</B></EM> sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used <BR><B><EM>Param:</B></EM> forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled. <BR><B><EM>Return:</B></EM> 1 if the gc was run, 0 otherwise <BR><HR>
<a name="gm::sysGetMemoryUsage"><H3>sysGetMemoryUsage</H3>
</a><B><EM>Brief:</B></EM> sysGetMemoryUsage will return the current memory used in bytes <BR><B><EM>Return:</B></EM> int memory usage <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageHard"><H3>sysSetDesiredMemoryUsageHard</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageSoft"><H3>sysSetDesiredMemoryUsageSoft</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
<a name="gm::sysGetDesiredMemoryUsageHard"><H3>sysGetDesiredMemoryUsageHard</H3>
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
<a name="gm::sysGetDesiredMemoryUsageSoft"><H3>sysGetDesiredMemoryUsageSoft</H3>
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageAuto"><H3>sysSetDesiredMemoryUsageAuto</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections. <BR><B><EM>Param:</B></EM> int enable or disable flag <BR><HR>
<a name="gm::sysGetStatsGCNumFullCollects"><H3>sysGetStatsGCNumFullCollects</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured. <BR><B><EM>Return:</B></EM> int Number of times full collect has occured. <BR><HR>
<a name="gm::sysGetStatsGCNumIncCollects"><H3>sysGetStatsGCNumIncCollects</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured. This number may increase in twos as the GC has multiple phases which appear as restarts. <BR><B><EM>Return:</B></EM> int Number of times incremental collect has occured. <BR><HR>
<a name="gm::sysGetStatsGCNumWarnings"><H3>sysGetStatsGCNumWarnings</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured. If this number is large and growing rapidly, the GC soft and hard limits need to be configured better. Do not be concerned if this number grows slowly. <BR><B><EM>Return:</B></EM> int Number of warnings garbage collect has generated. <BR><HR>
<a name="gm::sysIsGCRunning"><H3>sysIsGCRunning</H3>
</a><B><EM>Brief:</B></EM> Returns true if GC is running a cycle. <BR><HR>
<a name="gm::sysTime"><H3>sysTime</H3>
</a><B><EM>Brief:</B></EM> sysTime will return the machine time in milli seconds <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::doString"><H3>doString</H3>
</a><B><EM>Brief:</B></EM> doString will execute the passed gm script <BR><B><EM>Param:</B></EM> string script <BR><B><EM>Param:</B></EM> int optional (1) set as true and the string will execute before returning to this thread <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> int thread id of thread created for string execution <BR><HR>
<a name="gm::globals"><H3>globals</H3>
</a><B><EM>Brief:</B></EM> globals will return the globals table <BR><B><EM>Return:</B></EM> table containing all global variables <BR><HR>
<a name="gm::threadTime"><H3>threadTime</H3>
</a><B><EM>Brief:</B></EM> threadTime will return the thread execution time in milliseconds <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::threadId"><H3>threadId</H3>
</a><B><EM>Brief:</B></EM> threadId will return the thread id of the current executing script <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::threadAllIds"><H3>threadAllIds</H3>
</a><B><EM>Brief:</B></EM> threadIds returns a table of thread Ids <BR><B><EM>Return:</B></EM> table of thread Ids <BR><HR>
<a name="gm::threadKill"><H3>threadKill</H3>
</a><B><EM>Brief:</B></EM> threadKill will kill the thread with the given id <BR><B><EM>Param:</B></EM> int threadId optional (0) will kill this thread <BR><HR>
<a name="gm::threadKillAll"><H3>threadKillAll</H3>
</a><B><EM>Brief:</B></EM> threadKillAll will kill all the threads except the current one <BR><B><EM>Param:</B></EM> bool optional (false) will kill this thread if true <BR><HR>
<a name="gm::thread"><H3>thread</H3>
</a><B><EM>Brief:</B></EM> thread will start a new thread <BR><B><EM>Param:</B></EM> function entry point of the thread <BR><B><EM>Param:</B></EM> ... parameters to pass to the entry function <BR><B><EM>Return:</B></EM> int threadid <BR><HR>
<a name="gm::yield"><H3>yield</H3>
</a><B><EM>Brief:</B></EM> yield will hand execution control to the next thread <BR><HR>
<a name="gm::exit"><H3>exit</H3>
</a><B><EM>Brief:</B></EM> exit will kill this thread <BR><HR>
<a name="gm::assert"><H3>assert</H3>
</a><B><EM>Brief:</B></EM> assert <BR><B><EM>Param:</B></EM> int expression if true, will do nothing, if false, will cause an exception <BR><HR>
<a name="gm::sleep"><H3>sleep</H3>
</a><B><EM>Brief:</B></EM> sleep will sleep this thread for the given number of seconds <BR><B><EM>Param:</B></EM> int\float seconds <BR><HR>
<a name="gm::signal"><H3>signal</H3>
</a><B><EM>Brief:</B></EM> signal will signal the given variable, this will unblock dest threads that are blocked on the same variable. <BR><B><EM>Param:</B></EM> var <BR><B><EM>Param:</B></EM> int destThreadId optional (0) 0 will signal all threads <BR><HR>
<a name="gm::block"><H3>block</H3>
</a><B><EM>Brief:</B></EM> block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null. <BR><B><EM>Param:</B></EM> ... vars <BR><B><EM>Return:</B></EM> the unblocking var <BR><HR>
<a name="gm::stateSet"><H3>stateSet</H3>
</a><B><EM>Brief:</B></EM> stateSet will collapse the stack to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
<a name="gm::stateSetOnThread"><H3>stateSetOnThread</H3>
</a><B><EM>Brief:</B></EM> stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> int thread id <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
<a name="gm::stateGet"><H3>stateGet</H3>
</a><B><EM>Brief:</B></EM> stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get state on. \reutrn function \ null <BR><HR>
<a name="gm::stateGetLast"><H3>stateGetLast</H3>
</a><B><EM>Brief:</B></EM> stateGetLast will return the last state function of this thread <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get last state on. \reutrn function \ null <BR><HR>
<a name="gm::stateSetExitFunction"><H3>stateSetExitFunction</H3>
</a><B><EM>Brief:</B></EM> stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread switches state <BR><B><EM>Param:</B></EM> function <BR><HR>
<a name="gm::tableCount"><H3>tableCount</H3>
</a><B><EM>Brief:</B></EM> tableCount will return the number of elements in a table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::tableDuplicate"><H3>tableDuplicate</H3>
</a><B><EM>Brief:</B></EM> tableDuplicate will duplicate the passed table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> table <BR><HR>
<a name="gm::print"><H3>print</H3>
</a><B><EM>Brief:</B></EM> print will print the given vars to the print handler. passed strings are concatinated together with a seperating space. <BR><B><EM>Param:</B></EM> ... strings <BR><HR>
<a name="gm::format"><H3>format</H3>
</a><B><EM>Brief:</B></EM> format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e <BR><B><EM>Param:</B></EM> string <BR></BODY></HTML>

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

View File

@@ -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. )
-----------------------------------------------------------

View File

@@ -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());

View File

@@ -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.

View File

@@ -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.`);

View File

@@ -0,0 +1,308 @@
<HTML><HEAD><TITLE>GM Documentation</TITLE></HEAD><BODY><BR><HR>
<H1>gm</H1>
<HR><BR>
<HR>
<a name="gm::array"><H3>array</H3>
</a><B><EM>Brief:</B></EM> array will create a fixed size array object <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> array <BR><BR><HR>
<H1>array</H1>
<HR><BR>
<HR>
<a name="array::Size"><H3>Size</H3>
</a><B><EM>Brief:</B></EM> Size will return the current size of the fixed array <BR><B><EM>Return:</B></EM> int array size <BR><HR>
<a name="array::Resize"><H3>Resize</H3>
</a><B><EM>Brief:</B></EM> Resize will resize the array to a new size <BR><B><EM>Param:</B></EM> int size optional (0) <BR><B><EM>Return:</B></EM> null <BR><HR>
<a name="array::Shift"><H3>Shift</H3>
</a><B><EM>Brief:</B></EM> Shift will shift slide the array elements by a delta, nulls are shifted in <BR><B><EM>Param:</B></EM> int delta <BR><B><EM>Return:</B></EM> null <BR><HR>
<a name="array::Move"><H3>Move</H3>
</a><B><EM>Brief:</B></EM> Move will perform a non destructive move on the array <BR><B><EM>Param:</B></EM> int dst <BR><B><EM>Param:</B></EM> int src <BR><B><EM>Param:</B></EM> int size <BR><B><EM>Return:</B></EM> null <BR><BR><HR>
<H1>math</H1>
<HR><BR>
<HR>
<a name="math::abs"><H3>abs</H3>
</a><B><EM>Brief:</B></EM> abs will return the absolute value of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float abs(param) <BR><HR>
<a name="math::sqrt"><H3>sqrt</H3>
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float <BR><B><EM>Return:</B></EM> int\float sqrt(param) <BR><HR>
<a name="math::sqrt"><H3>sqrt</H3>
</a><B><EM>Brief:</B></EM> sqrt will return the square root of the passed int \ float <BR><B><EM>Param:</B></EM> int\float A <BR><B><EM>Param:</B></EM> int\float B <BR><B><EM>Return:</B></EM> int\float A to the power of B <BR><HR>
<a name="math::floor"><H3>floor</H3>
</a><B><EM>Brief:</B></EM> floor <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float floor(A) <BR><HR>
<a name="math::ceil"><H3>ceil</H3>
</a><B><EM>Brief:</B></EM> ceil <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float ceil(A) <BR><HR>
<a name="math::round"><H3>round</H3>
</a><B><EM>Brief:</B></EM> round <BR><B><EM>Param:</B></EM> float A <BR><B><EM>Return:</B></EM> float round(A) <BR><HR>
<a name="math::degtorad"><H3>degtorad</H3>
</a><B><EM>Brief:</B></EM> degtorad will convert degrees to radians <BR><B><EM>Param:</B></EM> float\int deg <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::radtodeg"><H3>radtodeg</H3>
</a><B><EM>Brief:</B></EM> radtodeg will convert radians to degrees <BR><B><EM>Param:</B></EM> float\int rad <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::cos"><H3>cos</H3>
</a><B><EM>Brief:</B></EM> cos will return the radian cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::sin"><H3>sin</H3>
</a><B><EM>Brief:</B></EM> sin will return the radian sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::tan"><H3>tan</H3>
</a><B><EM>Brief:</B></EM> tan will return the radian tan (sin/cos) <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::acos"><H3>acos</H3>
</a><B><EM>Brief:</B></EM> acos will return the radian arc cosine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::asin"><H3>asin</H3>
</a><B><EM>Brief:</B></EM> asin will return the radian arc sine <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::atan"><H3>atan</H3>
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent <BR><B><EM>Param:</B></EM> float <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::atan"><H3>atan</H3>
</a><B><EM>Brief:</B></EM> atan will return the radian arc tangent of x / y <BR><B><EM>Param:</B></EM> float x <BR><B><EM>Param:</B></EM> float y <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::log"><H3>log</H3>
</a><B><EM>Brief:</B></EM> log will return the natural logarithm of 1 parameter, or (base, value) the logarithm to base <BR><B><EM>Param:</B></EM> float natural \ base <BR><B><EM>Param:</B></EM> float value (optional) <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::min"><H3>min</H3>
</a><B><EM>Brief:</B></EM> min will return the min of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int min(A, B) <BR><HR>
<a name="math::max"><H3>max</H3>
</a><B><EM>Brief:</B></EM> max will return the max of the 2 passed values <BR><B><EM>Param:</B></EM> float\int A <BR><B><EM>Param:</B></EM> float\int B <BR><B><EM>Return:</B></EM> float \ int max(A, B) <BR><HR>
<a name="math::clamp"><H3>clamp</H3>
</a><B><EM>Brief:</B></EM> clamp will return the clamed value. clamp(min, val, max) <BR><B><EM>Param:</B></EM> float\int MIN <BR><B><EM>Param:</B></EM> float\int VALUE <BR><B><EM>Param:</B></EM> float\int MAX <BR><B><EM>Return:</B></EM> float\int value clamped to min, max <BR><HR>
<a name="math::randint"><H3>randint</H3>
</a><B><EM>Brief:</B></EM> randint will return a random int from lower inclusive to upper. <BR><B><EM>Param:</B></EM> int lower inclusive <BR><B><EM>Param:</B></EM> int upper <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="math::randfloat"><H3>randfloat</H3>
</a><B><EM>Brief:</B></EM> randfloat will return a random float from lower inclusive to upper. <BR><B><EM>Param:</B></EM> float lower inclusive <BR><B><EM>Param:</B></EM> float upper <BR><B><EM>Return:</B></EM> float <BR><HR>
<a name="math::randseed"><H3>randseed</H3>
</a><B><EM>Brief:</B></EM> randseed will seed the random number generator <BR><B><EM>Param:</B></EM> int seed <BR><BR><HR>
<H1>string</H1>
<HR><BR>
<B><EM>Brief:</B></EM> string operations often store a copy of the string on the stack, so keep string sizes reasonable <BR><HR>
<a name="string::IsEmpty"><H3>IsEmpty</H3>
</a><B><EM>Brief:</B></EM> IsEmpty will test to see if the string is 0 length <BR><B><EM>Return:</B></EM> non-zero if the string is empty <BR><HR>
<a name="string::Length"><H3>Length</H3>
</a><B><EM>Brief:</B></EM> Length will return the length of the string not including the null terminating character <BR><B><EM>Return:</B></EM> int length <BR><HR>
<a name="string::Left"><H3>Left</H3>
</a><B><EM>Brief:</B></EM> Left will return the left count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Right"><H3>Right</H3>
</a><B><EM>Brief:</B></EM> Right will return the right count charaters of the string <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::RightAt"><H3>RightAt</H3>
</a><B><EM>Brief:</B></EM> RightAt will return the charaters right of and including the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Mid"><H3>Mid</H3>
</a><B><EM>Brief:</B></EM> Mid will return count characters from the start index <BR><B><EM>Param:</B></EM> int startIndex <BR><B><EM>Param:</B></EM> int count <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Compare"><H3>Compare</H3>
</a><B><EM>Brief:</B></EM> Compare will perform a string compare <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
<a name="string::CompareNoCase"><H3>CompareNoCase</H3>
</a><B><EM>Brief:</B></EM> CompareNoCase will perform a string compare (case insensitive) <BR><B><EM>Param:</B></EM> string to compare <BR><B><EM>Return:</B></EM> -1 if the this < compare string, 0 if the strings are equal, 1 otherwise <BR><HR>
<a name="string::Int"><H3>Int</H3>
</a><B><EM>Brief:</B></EM> Int will return the int value of the string <BR><B><EM>Return:</B></EM> int value <BR><HR>
<a name="string::Float"><H3>Float</H3>
</a><B><EM>Brief:</B></EM> Float will return the float value of the string <BR><B><EM>Return:</B></EM> float value <BR><HR>
<a name="string::String"><H3>String</H3>
</a><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Upper"><H3>Upper</H3>
</a><B><EM>Brief:</B></EM> Upper will return the string as uppercase <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Lower"><H3>Lower</H3>
</a><B><EM>Brief:</B></EM> Lower will return the string as lowercase <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SpanIncluding"><H3>SpanIncluding</H3>
</a><B><EM>Brief:</B></EM> SpanIncluding will return this string while characters are within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SpanExcluding"><H3>SpanExcluding</H3>
</a><B><EM>Brief:</B></EM> SpanExcluding will return this string while characters are not within the passed string <BR><B><EM>Param:</B></EM> string charset <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::AppendPath"><H3>AppendPath</H3>
</a><B><EM>Brief:</B></EM> AppendPath will append a path make sure one '\' is maintained <BR><B><EM>Param:</B></EM> string path to append <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::ReplaceCharsInSet"><H3>ReplaceCharsInSet</H3>
</a><B><EM>Brief:</B></EM> ReplaceCharsInSet will replace all chars in this that are within the charset with the given int char <BR><B><EM>Param:</B></EM> int char to replace with <BR><B><EM>Param:</B></EM> string charset of chars to replace <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::Find"><H3>Find</H3>
</a><B><EM>Brief:</B></EM> Find will find the first occurance of the passed string within this string <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Param:</B></EM> int start index optional (0) <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
<a name="string::Reverse"><H3>Reverse</H3>
</a><B><EM>Brief:</B></EM> Reverse characters of a string <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::ReverseFind"><H3>ReverseFind</H3>
</a><B><EM>Brief:</B></EM> ReverseFind will find the first occurance of the passed string within this string starting from the right <BR><B><EM>Param:</B></EM> string search string <BR><B><EM>Return:</B></EM> int index of first occurance, or -1 if the string was not found <BR><HR>
<a name="string::GetAt"><H3>GetAt</H3>
</a><B><EM>Brief:</B></EM> GetAt will return the char at the given index <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Return:</B></EM> int char, or null if index was out of range <BR><HR>
<a name="string::SetAt"><H3>SetAt</H3>
</a><B><EM>Brief:</B></EM> SetAt will return the string with the character set at the given position <BR><B><EM>Param:</B></EM> int index <BR><B><EM>Param:</B></EM> int char <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::TrimLeft"><H3>TrimLeft</H3>
</a><B><EM>Brief:</B></EM> TrimLeft will return the string with the chars from the passed char set trimmed from the left <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::TrimRight"><H3>TrimRight</H3>
</a><B><EM>Brief:</B></EM> TrimRight will return the string with the chars from the passed char set trimmed from the right <BR><B><EM>Param:</B></EM> string charset optional (" \r\n\v\t") <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetFilenameNoExt"><H3>GetFilenameNoExt</H3>
</a><B><EM>Brief:</B></EM> GetFilenameNoExt will return the filename part of a path string <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetFilename"><H3>GetFilename</H3>
</a><B><EM>Brief:</B></EM> GetFilename will return the filename part of a path string incl. extension <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetExtension"><H3>GetExtension</H3>
</a><B><EM>Brief:</B></EM> GetExtension will return the file extension <BR><B><EM>Param:</B></EM> int inclDot optional (0) 1 will include '.', 0 won't <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::SetExtension"><H3>SetExtension</H3>
</a><B><EM>Brief:</B></EM> SetExtension returns a string with the extension change to the given one. <BR><B><EM>Param:</B></EM> string ext optional (null) the new extension, with or without the dot. null to remove extension. <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="string::GetPath"><H3>GetPath</H3>
</a><B><EM>Brief:</B></EM> GetPath will return the file path from a path string <BR><B><EM>Param:</B></EM> int inclSlash optional (0) will include a '\' on the end of the path <BR><B><EM>Return:</B></EM> string <BR><BR><HR>
<H1>system</H1>
<HR><BR>
<B><EM>Brief:</B></EM> system functions are bound in a "system" table. <BR><HR>
<a name="system::Exec"><H3>Exec</H3>
</a><B><EM>Brief:</B></EM> Exec will execute a system command <BR><B><EM>Param:</B></EM> string params will be concatinated together with a single space to form the final system command string <BR><B><EM>Return:</B></EM> integer value returned from system exec call, -1 on error <BR><HR>
<a name="system::DoFile"><H3>DoFile</H3>
</a><B><EM>Brief:</B></EM> DoFile will execute the gm script in the named file <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int optional (1) as 1 will execute string before returning, 0 will execute later. <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> thread id of new thread created to execute file <BR><HR>
<a name="system::File"><H3>File</H3>
</a><B><EM>Brief:</B></EM> File will create a file object <BR><B><EM>Return:</B></EM> file object <BR><HR>
<a name="system::FileExists"><H3>FileExists</H3>
</a><B><EM>Brief:</B></EM> FileExists will test to see if a file exists <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Return:</B></EM> 1 if the file exists, otherwise 0 <BR><HR>
<a name="system::FileFindFirst"><H3>FileFindFirst</H3>
</a><B><EM>Brief:</B></EM> FileFindFirst will start a file search will test to see if a file exists <BR><B><EM>Param:</B></EM> string filesearch (may contain wildcards, eg, `c:\temp\*.txt`) <BR><B><EM>Return:</B></EM> fileFind object. fileFind object has .filename and .size member <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
<a name="system::FileFindNext"><H3>FileFindNext</H3>
</a><B><EM>Brief:</B></EM> FileFindNext will get the next file matching the file find <BR><B><EM>Param:</B></EM> fileFind object returned by FileFindFirst call or FileFindNext call <BR><B><EM>Return:</B></EM> fileFind object <BR><B><EM>See Also:</B></EM> fileFind <BR><HR>
<a name="system::FileInfo"><H3>FileInfo</H3>
</a><B><EM>Brief:</B></EM> FileInfo will return a file info object that has readonly members for .creationDate <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> fileInfo object, fileInfo object has a .creationTime, .accessedTime, .modifiedTime and a .size <BR><HR>
<a name="system::CreateFolder"><H3>CreateFolder</H3>
</a><B><EM>Brief:</B></EM> CreateFolder will create a file path if it does not already exist <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Return:</B></EM> int 0 on failure, 1 on successful create, 2 if folder already exists <BR><HR>
<a name="system::DeleteFolder"><H3>DeleteFolder</H3>
</a><B><EM>Brief:</B></EM> DeleteFolder will remove a file path <BR><B><EM>Param:</B></EM> string path <BR><B><EM>Param:</B></EM> int remove subfiles and folders optional (0) <BR><B><EM>Return:</B></EM> int 1 on success, 0 con failure <BR><HR>
<a name="system::Time"><H3>Time</H3>
</a><B><EM>Brief:</B></EM> Time will return a unix style time_t as an int <BR><B><EM>Return:</B></EM> the current time <BR><HR>
<a name="system::FormatTime"><H3>FormatTime</H3>
</a><B><EM>Brief:</B></EM> FormatTime will take a int (time_t) value and format according to the passed format string. <BR><B><EM>Param:</B></EM> int time (-1) is a (time_t) to be converted to a string, passing -1 gets current time <BR><B><EM>Param:</B></EM> 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 <20> 31)<BR> %H : Hour in 24-hour format (00 <20> 23)<BR> %I : Hour in 12-hour format (01 <20> 12)<BR> %j : Day of year as decimal number (001 <20> 366)<BR> %m : Month as decimal number (01 <20> 12)<BR> %M : Minute as decimal number (00 <20> 59)<BR> %p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR> %S : Second as decimal number (00 <20> 59)<BR> %U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR> %w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR> %W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR> %x : Date representation for current locale<BR> %X : Time representation for current locale<BR> %y : Year without century, as decimal number (00 <20> 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> <BR><B><EM>Return:</B></EM> the time as a string. <BR><BR><HR>
<H1>fileFind</H1>
<HR><BR>
<B><EM>Brief:</B></EM> fileFind object has a "filename" and "size" member <BR><HR>
<a name="fileFind::GetAttribute"><H3>GetAttribute</H3>
</a><B><EM>Brief:</B></EM> GetAttribute will test a file attribute. <BR><B><EM>Param:</B></EM> int char attribute 'r' readonly, 'a' archive, 's' system, 'h' hidden, 'c' compressed, 'd' directory <BR><B><EM>Return:</B></EM> 1 if the attribute is set, 0 otherwise <BR><BR><HR>
<H1>file</H1>
<HR><BR>
<HR>
<a name="file::Open"><H3>Open</H3>
</a><B><EM>Brief:</B></EM> Open will open a file in binary mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
<a name="file::OpenText"><H3>OpenText</H3>
</a><B><EM>Brief:</B></EM> OpenText will open a file in text mode <BR><B><EM>Param:</B></EM> string filename <BR><B><EM>Param:</B></EM> int readonly optional (1) <BR><B><EM>Return:</B></EM> 1 if the open was successful, 0 otherwise <BR><HR>
<a name="file::Close"><H3>Close</H3>
</a><B><EM>Brief:</B></EM> Close will close a file <BR><HR>
<a name="file::IsOpen"><H3>IsOpen</H3>
</a><B><EM>Brief:</B></EM> IsOpen will test to see if a file is open <BR><B><EM>Return:</B></EM> 1 if the file is open, 0 otherwise <BR><HR>
<a name="file::Seek"><H3>Seek</H3>
</a><B><EM>Brief:</B></EM> Move to position within file <BR><B><EM>Param:</B></EM> int offset positional offset relative to origin <BR><B><EM>Param:</B></EM> int origin eg. myFile.SEEK_CUR, myFile.SEEK_END, myFile.SEEK_SET for current, end, start origins. <BR><B><EM>Return:</B></EM> int 1 if operation succeeded, 0 if failed. <BR><HR>
<a name="file::Tell"><H3>Tell</H3>
</a><B><EM>Brief:</B></EM> Tell will return the current cursor position of the file <BR><B><EM>Return:</B></EM> int the current cursor position, -1 on error <BR><HR>
<a name="file::ReadLine"><H3>ReadLine</H3>
</a><B><EM>Brief:</B></EM> ReadLine will read a line of text from the file. <BR><B><EM>Param:</B></EM> int keep optional (0) as 1 will keep the "\n" char on the line, otherwise it is removed <BR><B><EM>Return:</B></EM> string, or null on eof <BR><HR>
<a name="file::ReadChar"><H3>ReadChar</H3>
</a><B><EM>Brief:</B></EM> ReadChar will read a char from the file <BR><B><EM>Return:</B></EM> int char or null on eof <BR><HR>
<a name="file::WriteString"><H3>WriteString</H3>
</a><B><EM>Brief:</B></EM> WriteString will write a string to the file <BR><B><EM>Param:</B></EM> string to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><HR>
<a name="file::WriteChar"><H3>WriteChar</H3>
</a><B><EM>Brief:</B></EM> WriteChar will write a char to the file <BR><B><EM>Param:</B></EM> int char to write to file <BR><B><EM>Return:</B></EM> 1 on success, null on error <BR><BR><HR>
<H1>Vector3</H1>
<HR><BR>
<HR>
<a name="Vector3::Vector3"><H3>Vector3</H3>
</a><B><EM>Brief:</B></EM> Create a Vector3 object <BR><B><EM>Param:</B></EM> float x or [0] optional (0) <BR><B><EM>Param:</B></EM> float y or [1] optional (0) <BR><B><EM>Param:</B></EM> float z or [2] optional (0) <BR><BR><HR>
<H1>Vector3</H1>
<HR><BR>
<B><EM>Brief:</B></EM> Vector3 math class <BR><HR>
<a name="Vector3::DominantAxis"><H3>DominantAxis</H3>
</a><B><EM>Brief:</B></EM> Find the index of the largest vector component. <BR><B><EM>This:</B></EM> Vector to evaluate. <BR><B><EM>Return:</B></EM> int Index of largest component. <BR><HR>
<a name="Vector3::Dot"><H3>Dot</H3>
</a><B><EM>Brief:</B></EM> Calculate the Dot (or Inner) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> float result. <BR><HR>
<a name="Vector3::Length"><H3>Length</H3>
</a><B><EM>Brief:</B></EM> Length will return the length of the vector. <BR><B><EM>Return:</B></EM> float Dot product result that is cosine of the angle between the two vectors. <BR><HR>
<a name="Vector3::Cross"><H3>Cross</H3>
</a><B><EM>Brief:</B></EM> Calculate the Cross (or Outer) Product of two vectors. <BR><B><EM>This:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><B><EM>Return:</B></EM> Vector3 Cross product resultant vector that is perpendicular to the two input vectors and length sine of the angle between them. <BR><HR>
<a name="Vector3::Normalize"><H3>Normalize</H3>
</a><B><EM>Brief:</B></EM> Return a unit length copy of this vector. <BR><B><EM>This:</B></EM> Vector to be copied. <BR><B><EM>Return:</B></EM> Vector3 Unit length copy of this vector. <BR><HR>
<a name="Vector3::LengthSquared"><H3>LengthSquared</H3>
</a><B><EM>Brief:</B></EM> Return the squared length of the vector. <BR><B><EM>Return:</B></EM> float Squared length of the vector. <BR><HR>
<a name="Vector3::ProjectFrom"><H3>ProjectFrom</H3>
</a><B><EM>Brief:</B></EM> Project a direction from a point. <BR><B><EM>This:</B></EM> Vector3 Direction. <BR><B><EM>Param:</B></EM> Vector3 Start point; <BR><B><EM>Param:</B></EM> float Distance or time. <BR><B><EM>Return:</B></EM> Vector3 Projected result. <BR><HR>
<a name="Vector3::Clone"><H3>Clone</H3>
</a><B><EM>Brief:</B></EM> Return a copy of this vector. <BR><B><EM>Return:</B></EM> A copy of this vector. <BR><HR>
<a name="Vector3::Set"><H3>Set</H3>
</a><B><EM>Brief:</B></EM> Set vector from other vector or 3 components. <BR><HR>
<a name="Vector3::LerpToPoint"><H3>LerpToPoint</H3>
</a><B><EM>Brief:</B></EM> Linear interpolate between two 'point' vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
<a name="Vector3::SlerpToVector"><H3>SlerpToVector</H3>
</a><B><EM>Brief:</B></EM> Spherical linear interpolate between two vectors. <BR><B><EM>This:</B></EM> Vector3 From vector. <BR><B><EM>Param:</B></EM> Vector3 To vector. <BR><B><EM>Param:</B></EM> float Fraction or time between 0 and 1. <BR><B><EM>Return:</B></EM> Vector3 Resulting inbetween vector. <BR><HR>
<a name="Vector3::RotateAxisAngle"><H3>RotateAxisAngle</H3>
</a><B><EM>Brief:</B></EM> Rotate around Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> Vector3 Unit length axis of rotation. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateX"><H3>RotateX</H3>
</a><B><EM>Brief:</B></EM> Rotate around X Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateX"><H3>RotateX</H3>
</a><B><EM>Brief:</B></EM> Rotate around Y Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::RotateZ"><H3>RotateZ</H3>
</a><B><EM>Brief:</B></EM> Rotate around Z Axis by Angle. <BR><B><EM>This:</B></EM> Vector3 Vector to rotate. <BR><B><EM>Param:</B></EM> float Angle amount to rotate. <BR><B><EM>Return:</B></EM> Vector3 Resulting rotated vector. <BR><HR>
<a name="Vector3::SetAdd"><H3>SetAdd</H3>
</a><B><EM>Brief:</B></EM> Add two vectors, store result in this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 First vector. <BR><B><EM>Param:</B></EM> Vector3 Second vector. <BR><HR>
<a name="Vector3::Add"><H3>Add</H3>
</a><B><EM>Brief:</B></EM> Add vector to this. Demonstrate relative efficiency compared to operator version. <BR><B><EM>This:</B></EM> Vector3 Result vector. <BR><B><EM>Param:</B></EM> Vector3 vector to add. <BR><BR><HR>
<H1>gm</H1>
<HR><BR>
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
<a name="gm::debug"><H3>debug</H3>
</a><B><EM>Brief:</B></EM> debug will cause a the debugger to break at this point while running. <BR><BR><HR>
<H1>gm</H1>
<HR><BR>
<B><EM>Brief:</B></EM> functions in the gm lib are all global scope <BR><HR>
<a name="gm::gmVersion"><H3>gmVersion</H3>
</a><B><EM>Brief:</B></EM> gmVersion will return the gmMachine version string. version string is major type . minor type as a string and was added at version 1.1 <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="gm::typeId"><H3>typeId</H3>
</a><B><EM>Brief:</B></EM> typeId will return the type id of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> integer type <BR><HR>
<a name="gm::typeName"><H3>typeName</H3>
</a><B><EM>Brief:</B></EM> typeName will return the type name of the passed var <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> string <BR><HR>
<a name="gm::typeRegisterOperator"><H3>typeRegisterOperator</H3>
</a><B><EM>Brief:</B></EM> typeRegisterOperator will register an operator for a type <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string operator name is one of "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod", "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv", "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not" <BR><B><EM>Param:</B></EM> function <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
<a name="gm::typeRegisterVariable"><H3>typeRegisterVariable</H3>
</a><B><EM>Brief:</B></EM> typeRegisterVariable will register a variable with a type such that (type).varname will return the variable <BR><B><EM>Param:</B></EM> int typeid <BR><B><EM>Param:</B></EM> string var name <BR><B><EM>Param:</B></EM> var <BR><B><EM>Return:</B></EM> 1 on success, otherwise 0 <BR><HR>
<a name="gm::sysCollectGarbage"><H3>sysCollectGarbage</H3>
</a><B><EM>Brief:</B></EM> sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used <BR><B><EM>Param:</B></EM> forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled. <BR><B><EM>Return:</B></EM> 1 if the gc was run, 0 otherwise <BR><HR>
<a name="gm::sysGetMemoryUsage"><H3>sysGetMemoryUsage</H3>
</a><B><EM>Brief:</B></EM> sysGetMemoryUsage will return the current memory used in bytes <BR><B><EM>Return:</B></EM> int memory usage <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageHard"><H3>sysSetDesiredMemoryUsageHard</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageSoft"><H3>sysSetDesiredMemoryUsageSoft</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run. <BR><B><EM>Param:</B></EM> int desired mem usage in bytes <BR><HR>
<a name="gm::sysGetDesiredMemoryUsageHard"><H3>sysGetDesiredMemoryUsageHard</H3>
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
<a name="gm::sysGetDesiredMemoryUsageSoft"><H3>sysGetDesiredMemoryUsageSoft</H3>
</a><B><EM>Brief:</B></EM> sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes. Note that this value is used to start garbage collection, it is not a strict limit. <BR><B><EM>Return:</B></EM> int Desired memory usage in bytes. <BR><HR>
<a name="gm::sysSetDesiredMemoryUsageAuto"><H3>sysSetDesiredMemoryUsageAuto</H3>
</a><B><EM>Brief:</B></EM> sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections. <BR><B><EM>Param:</B></EM> int enable or disable flag <BR><HR>
<a name="gm::sysGetStatsGCNumFullCollects"><H3>sysGetStatsGCNumFullCollects</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured. <BR><B><EM>Return:</B></EM> int Number of times full collect has occured. <BR><HR>
<a name="gm::sysGetStatsGCNumIncCollects"><H3>sysGetStatsGCNumIncCollects</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured. This number may increase in twos as the GC has multiple phases which appear as restarts. <BR><B><EM>Return:</B></EM> int Number of times incremental collect has occured. <BR><HR>
<a name="gm::sysGetStatsGCNumWarnings"><H3>sysGetStatsGCNumWarnings</H3>
</a><B><EM>Brief:</B></EM> sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured. If this number is large and growing rapidly, the GC soft and hard limits need to be configured better. Do not be concerned if this number grows slowly. <BR><B><EM>Return:</B></EM> int Number of warnings garbage collect has generated. <BR><HR>
<a name="gm::sysIsGCRunning"><H3>sysIsGCRunning</H3>
</a><B><EM>Brief:</B></EM> Returns true if GC is running a cycle. <BR><HR>
<a name="gm::sysTime"><H3>sysTime</H3>
</a><B><EM>Brief:</B></EM> sysTime will return the machine time in milli seconds <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::doString"><H3>doString</H3>
</a><B><EM>Brief:</B></EM> doString will execute the passed gm script <BR><B><EM>Param:</B></EM> string script <BR><B><EM>Param:</B></EM> int optional (1) set as true and the string will execute before returning to this thread <BR><B><EM>Param:</B></EM> ref optional (null) set 'this' <BR><B><EM>Return:</B></EM> int thread id of thread created for string execution <BR><HR>
<a name="gm::globals"><H3>globals</H3>
</a><B><EM>Brief:</B></EM> globals will return the globals table <BR><B><EM>Return:</B></EM> table containing all global variables <BR><HR>
<a name="gm::threadTime"><H3>threadTime</H3>
</a><B><EM>Brief:</B></EM> threadTime will return the thread execution time in milliseconds <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::threadId"><H3>threadId</H3>
</a><B><EM>Brief:</B></EM> threadId will return the thread id of the current executing script <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::threadAllIds"><H3>threadAllIds</H3>
</a><B><EM>Brief:</B></EM> threadIds returns a table of thread Ids <BR><B><EM>Return:</B></EM> table of thread Ids <BR><HR>
<a name="gm::threadKill"><H3>threadKill</H3>
</a><B><EM>Brief:</B></EM> threadKill will kill the thread with the given id <BR><B><EM>Param:</B></EM> int threadId optional (0) will kill this thread <BR><HR>
<a name="gm::threadKillAll"><H3>threadKillAll</H3>
</a><B><EM>Brief:</B></EM> threadKillAll will kill all the threads except the current one <BR><B><EM>Param:</B></EM> bool optional (false) will kill this thread if true <BR><HR>
<a name="gm::thread"><H3>thread</H3>
</a><B><EM>Brief:</B></EM> thread will start a new thread <BR><B><EM>Param:</B></EM> function entry point of the thread <BR><B><EM>Param:</B></EM> ... parameters to pass to the entry function <BR><B><EM>Return:</B></EM> int threadid <BR><HR>
<a name="gm::yield"><H3>yield</H3>
</a><B><EM>Brief:</B></EM> yield will hand execution control to the next thread <BR><HR>
<a name="gm::exit"><H3>exit</H3>
</a><B><EM>Brief:</B></EM> exit will kill this thread <BR><HR>
<a name="gm::assert"><H3>assert</H3>
</a><B><EM>Brief:</B></EM> assert <BR><B><EM>Param:</B></EM> int expression if true, will do nothing, if false, will cause an exception <BR><HR>
<a name="gm::sleep"><H3>sleep</H3>
</a><B><EM>Brief:</B></EM> sleep will sleep this thread for the given number of seconds <BR><B><EM>Param:</B></EM> int\float seconds <BR><HR>
<a name="gm::signal"><H3>signal</H3>
</a><B><EM>Brief:</B></EM> signal will signal the given variable, this will unblock dest threads that are blocked on the same variable. <BR><B><EM>Param:</B></EM> var <BR><B><EM>Param:</B></EM> int destThreadId optional (0) 0 will signal all threads <BR><HR>
<a name="gm::block"><H3>block</H3>
</a><B><EM>Brief:</B></EM> block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null. <BR><B><EM>Param:</B></EM> ... vars <BR><B><EM>Return:</B></EM> the unblocking var <BR><HR>
<a name="gm::stateSet"><H3>stateSet</H3>
</a><B><EM>Brief:</B></EM> stateSet will collapse the stack to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
<a name="gm::stateSetOnThread"><H3>stateSetOnThread</H3>
</a><B><EM>Brief:</B></EM> stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions. <BR><B><EM>Param:</B></EM> int thread id <BR><B><EM>Param:</B></EM> function new state function to execute <BR><B><EM>Param:</B></EM> ... params for new state function <BR><HR>
<a name="gm::stateGet"><H3>stateGet</H3>
</a><B><EM>Brief:</B></EM> stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get state on. \reutrn function \ null <BR><HR>
<a name="gm::stateGetLast"><H3>stateGetLast</H3>
</a><B><EM>Brief:</B></EM> stateGetLast will return the last state function of this thread <BR><B><EM>Param:</B></EM> a_threadId Optional Id of thread to get last state on. \reutrn function \ null <BR><HR>
<a name="gm::stateSetExitFunction"><H3>stateSetExitFunction</H3>
</a><B><EM>Brief:</B></EM> stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread switches state <BR><B><EM>Param:</B></EM> function <BR><HR>
<a name="gm::tableCount"><H3>tableCount</H3>
</a><B><EM>Brief:</B></EM> tableCount will return the number of elements in a table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> int <BR><HR>
<a name="gm::tableDuplicate"><H3>tableDuplicate</H3>
</a><B><EM>Brief:</B></EM> tableDuplicate will duplicate the passed table object <BR><B><EM>Param:</B></EM> table <BR><B><EM>Return:</B></EM> table <BR><HR>
<a name="gm::print"><H3>print</H3>
</a><B><EM>Brief:</B></EM> print will print the given vars to the print handler. passed strings are concatinated together with a seperating space. <BR><B><EM>Param:</B></EM> ... strings <BR><HR>
<a name="gm::format"><H3>format</H3>
</a><B><EM>Brief:</B></EM> format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e <BR><B><EM>Param:</B></EM> string <BR></BODY></HTML>

View File

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

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><functions><function name="gm::array"/><function name="array::Size"/><function name="array::Resize"/><function name="array::Shift"/><function name="array::Move"/><function name="math::abs"/><function name="math::sqrt"/><function name="math::sqrt"/><function name="math::floor"/><function name="math::ceil"/><function name="math::round"/><function name="math::degtorad"/><function name="math::radtodeg"/><function name="math::cos"/><function name="math::sin"/><function name="math::tan"/><function name="math::acos"/><function name="math::asin"/><function name="math::atan"/><function name="math::atan"/><function name="math::log"/><function name="math::min"/><function name="math::max"/><function name="math::clamp"/><function name="math::randint"/><function name="math::randfloat"/><function name="math::randseed"/><function name="string::IsEmpty"/><function name="string::Length"/><function name="string::Left"/><function name="string::Right"/><function name="string::RightAt"/><function name="string::Mid"/><function name="string::Compare"/><function name="string::CompareNoCase"/><function name="string::Int"/><function name="string::Float"/><function name="string::String"/><function name="string::Upper"/><function name="string::Lower"/><function name="string::SpanIncluding"/><function name="string::SpanExcluding"/><function name="string::AppendPath"/><function name="string::ReplaceCharsInSet"/><function name="string::Find"/><function name="string::Reverse"/><function name="string::ReverseFind"/><function name="string::GetAt"/><function name="string::SetAt"/><function name="string::TrimLeft"/><function name="string::TrimRight"/><function name="string::GetFilenameNoExt"/><function name="string::GetFilename"/><function name="string::GetExtension"/><function name="string::SetExtension"/><function name="string::GetPath"/><function name="system::Exec"/><function name="system::DoFile"/><function name="system::File"/><function name="system::FileExists"/><function name="system::FileFindFirst"/><function name="system::FileFindNext"/><function name="system::FileInfo"/><function name="system::CreateFolder"/><function name="system::DeleteFolder"/><function name="system::Time"/><function name="system::FormatTime"/><function name="fileFind::GetAttribute"/><function name="file::Open"/><function name="file::OpenText"/><function name="file::Close"/><function name="file::IsOpen"/><function name="file::Seek"/><function name="file::Tell"/><function name="file::ReadLine"/><function name="file::ReadChar"/><function name="file::WriteString"/><function name="file::WriteChar"/><function name="Vector3::Vector3"/><function name="Vector3::DominantAxis"/><function name="Vector3::Dot"/><function name="Vector3::Length"/><function name="Vector3::Cross"/><function name="Vector3::Normalize"/><function name="Vector3::LengthSquared"/><function name="Vector3::ProjectFrom"/><function name="Vector3::Clone"/><function name="Vector3::Set"/><function name="Vector3::LerpToPoint"/><function name="Vector3::SlerpToVector"/><function name="Vector3::RotateAxisAngle"/><function name="Vector3::RotateX"/><function name="Vector3::RotateX"/><function name="Vector3::RotateZ"/><function name="Vector3::SetAdd"/><function name="Vector3::Add"/><function name="gm::debug"/><function name="gm::gmVersion"/><function name="gm::typeId"/><function name="gm::typeName"/><function name="gm::typeRegisterOperator"/><function name="gm::typeRegisterVariable"/><function name="gm::sysCollectGarbage"/><function name="gm::sysGetMemoryUsage"/><function name="gm::sysSetDesiredMemoryUsageHard"/><function name="gm::sysSetDesiredMemoryUsageSoft"/><function name="gm::sysGetDesiredMemoryUsageHard"/><function name="gm::sysGetDesiredMemoryUsageSoft"/><function name="gm::sysSetDesiredMemoryUsageAuto"/><function name="gm::sysGetStatsGCNumFullCollects"/><function name="gm::sysGetStatsGCNumIncCollects"/><function name="gm::sysGetStatsGCNumWarnings"/><function name="gm::sysIsGCRunning"/><function name="gm::sysTime"/><function name="gm::doString"/><function name="gm::globals"/><function name="gm::threadTime"/><function name="gm::threadId"/><function name="gm::threadAllIds"/><function name="gm::threadKill"/><function name="gm::threadKillAll"/><function name="gm::thread"/><function name="gm::yield"/><function name="gm::exit"/><function name="gm::assert"/><function name="gm::sleep"/><function name="gm::signal"/><function name="gm::block"/><function name="gm::stateSet"/><function name="gm::stateSetOnThread"/><function name="gm::stateGet"/><function name="gm::stateGetLast"/><function name="gm::stateSetExitFunction"/><function name="gm::tableCount"/><function name="gm::tableDuplicate"/><function name="gm::print"/><function name="gm::format"/></functions>

View File

@@ -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();

View File

@@ -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();
}

View File

@@ -0,0 +1,413 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmArrayLib.h"
#include "gmThread.h"
#include "gmMachine.h"
#include "gmHelpers.h"
// Must be last header
#include "memdbgon.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

View File

@@ -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_

View File

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

View File

@@ -0,0 +1,434 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
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;
if ( !a_tableObj )
{
a_tableObj = a_machine->GetGlobals();
}
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_

View File

@@ -0,0 +1,215 @@
//
// gmGCRoot.cpp
//
#include "gmGCRoot.h"
// Must be last header
#include "memdbgon.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

View File

@@ -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

View File

@@ -0,0 +1,33 @@
//
// gmGCRootUtil.cpp
//
#include "gmGCRoot.h"
#include "gmGCRootUtil.h"
// Must be last header
#include "memdbgon.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);
}
}

View File

@@ -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

View File

@@ -0,0 +1,64 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmHelpers.h"
// Must be last header
#include "memdbgon.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));
}

View File

@@ -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_

View File

@@ -0,0 +1,860 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
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>
// Must be last header
#include "memdbgon.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
*/
#if 0 // Using Source versions [2/13/2008 tom]
{"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},
#endif
};
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]));
}

View File

@@ -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_

File diff suppressed because it is too large Load Diff

View File

@@ -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_

View File

@@ -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))
{
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 <20> 31)<BR>
%H : Hour in 24-hour format (00 <20> 23)<BR>
%I : Hour in 12-hour format (01 <20> 12)<BR>
%j : Day of year as decimal number (001 <20> 366)<BR>
%m : Month as decimal number (01 <20> 12)<BR>
%M : Minute as decimal number (00 <20> 59)<BR>
%p : Current locale<6C>s A.M./P.M. indicator for 12-hour clock<BR>
%S : Second as decimal number (00 <20> 59)<BR>
%U : Week of year as decimal number, with Sunday as first day of week (00 <20> 53)<BR>
%w : Weekday as decimal number (0 <20> 6; Sunday is 0)<BR>
%W : Week of year as decimal number, with Monday as first day of week (00 <20> 53)<BR>
%x : Date representation for current locale<BR>
%X : Time representation for current locale<BR>
%y : Year without century, as decimal number (00 <20> 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

View File

@@ -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_

File diff suppressed because it is too large Load Diff

View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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
};

View File

@@ -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]));
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>
{{{
}}}
###############################################################################

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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

View File

@@ -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]));
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

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

View File

@@ -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

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>
{{{
}}}
###############################################################################

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;
}
$

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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

View File

@@ -0,0 +1,134 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmByteCode.h"
// Must be last header
#include "memdbgon.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;
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

View File

@@ -0,0 +1,102 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
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_COMPILE_DEBUG
void gmByteCodePrint(FILE * a_fp, const void * a_byteCode, int a_byteCodeLength);
#endif // GM_COMPILE_DEBUG
#endif

View File

@@ -0,0 +1,158 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmByteCodeGen.h"
// Must be last header
#include "memdbgon.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(m_tos > m_maxTos) m_maxTos = m_tos;
}

View File

@@ -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_

File diff suppressed because it is too large Load Diff

View File

@@ -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_

View File

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

View File

@@ -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_

View File

@@ -0,0 +1,555 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCodeTree.h"
// Must be last header
#include "memdbgon.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;
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;
}

View File

@@ -0,0 +1,254 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
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,
};
/// \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_

View File

@@ -0,0 +1,106 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
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, ==, !=, !]
#endif // _GMCONFIG_H_

View File

@@ -0,0 +1,84 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmCrc.h"
// Must be last header
#include "memdbgon.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;
}

View File

@@ -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_

View File

@@ -0,0 +1,741 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmDebug.h"
#include "gmConfig.h"
#include "gmMachine.h"
#include "gmThread.h"
// Must be last header
#include "memdbgon.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

View File

@@ -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_

View File

@@ -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();
}

View File

@@ -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

View File

@@ -0,0 +1,286 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmFunctionObject.h"
#include "gmMachine.h"
// Must be last header
#include "memdbgon.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;
}

View File

@@ -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_

View File

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

View File

@@ -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_

View File

@@ -0,0 +1,838 @@
/*
_____ __ ___ __ ____ _ __
/ ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
/ (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
\___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
/___/ /_/
See Copyright Notice in gmMachine.h
*/
#include "gmConfig.h"
#include "gmIncGC.h"
// Must be last header
#include "memdbgon.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;
}

View File

@@ -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_

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