Difference between revisions of "SCM language"

From GTAMods Wiki
Jump to navigation Jump to search
(Changed again the explicit name of the SCM abbreviation (thanks to Deji) and other important things.)
Line 1: Line 1:
 
{{This|This section deals with the native SCM syntax of GTA 3 series, nothing other than [[GTA 3|III]], [[GTA VC|VC]], [[GTA SA|SA]], [[GTA LCS|LCS]] and [[GTA VCS|VCS]].<br/>
 
{{This|This section deals with the native SCM syntax of GTA 3 series, nothing other than [[GTA 3|III]], [[GTA VC|VC]], [[GTA SA|SA]], [[GTA LCS|LCS]] and [[GTA VCS|VCS]].<br/>
 
It may contain non-standard SCM definitions as R* hasn't published enough documentation about it yet.}}
 
It may contain non-standard SCM definitions as R* hasn't published enough documentation about it yet.}}
{{TocRight}}On the occasion of the GTAIII's tenth anniversary, after a long period of darkness where we fell about the real ''SCM'' syntax, R* finally treated us by attaching part of its own original source code into the GTAIII Anniversary game, available for iOs and Android devices. As far back as 2001, a snip of some debugging scripts has been already provided with ''main.sc'' and ''debug.sc'' files. However, many secrets are unrevealed yet, thus some things cannot be documented fully and so they can be only guessed. The ''SCM'' format abbreviation is one of countless proofs of this inconvenience, which may stand for ''Script Controlled Missions''. Other doubts come with source files, whose ''SC'' extension appears to be very close to ''Script Control''. At first, someone could have asked oneself how R* named its own programming language: ''R#''? ''R-Sharp''? ''Rockstar Sharp''? Who knows. We simply call it '''SCM language''' due to the lack of information.
+
{{TocRight}}On the occasion of the GTAIII's tenth anniversary, after a long period of darkness where we fell about the real ''SCM'' syntax, R* finally treated us by attaching part of its own original source code into the GTAIII Anniversary game, available for iOS and Android devices. As far back as 2001, a snip of some debugging scripts has been already provided with ''main.sc'' and ''debug.sc'' files. However, many secrets are unrevealed yet, thus some things cannot be documented fully and so they can be only guessed. The ''SCM'' format abbreviation is one of countless proofs of this inconvenience, which may stand for ''Script Control Merged''. Other doubts come with source files, whose ''SC'' extension appears to be very close to ''Script Control''. At first, someone could have asked oneself how R* named its own programming language: ''R#''? ''R-Sharp''? ''Rockstar Sharp''? Who knows. We simply call it '''SCM language''' due to the lack of information. Nevertheless, the '''SCR language''' name is cropping up gradually.
  
  
Line 46: Line 46:
 
===Curly brackets===
 
===Curly brackets===
  
''Curly brackets'' (or ''multiline brackets'') act like a [[#Scope|local variable scope]]. Essentially, they enclose the code where [[#Local|local variables]] are used, including [[#Timers|timers]]. They can be opened and closed many times in a script:
+
''Curly brackets'' (or ''multiline brackets'') act like a [[#Scope|local variable scope]]. Essentially, they enclose the code where [[#Local|local variables]] are used, including [[#Timers|timers]]. They can be opened and closed many times in a [[#Structure|script]]:
  
 
  {
 
  {
Line 63: Line 63:
 
==Labels==
 
==Labels==
  
A ''label'' is a ''sequence of characters'' which identifiy a location of the source code useful for ''jumps''. To define a label just append ''':''' (colon) to its name:
+
A ''label'' is a ''sequence of characters'' which identifiy a location of the source code useful for ''jumps''. It can be accessed by any part of the source code. To define a label just append ''':''' (colon) to its name:
  
 
  [...]
 
  [...]
Line 107: Line 107:
 
====CONST====
 
====CONST====
  
'''CONST''' is a ''special'' type that handles ''32-bit signed integer'' values. It is used only to compare [[#Constants|constants]] of ''model identifiers'', ''task statuses'' or ''ped events''.
+
'''CONST''' is a ''special'' type that handles ''32-bit signed integer'' values. It is used only to compare [[#Constants|constants]] of ''model identifiers'', ''task statuses'', ''ped events'' and such. It mightn't be used directly but divided into other subordinal types. At the moment, it is merely a suggestion because constants are processed in bulk apparently instead of file by file.
  
 
;Limit
 
;Limit
:It is supported since {{icon|vc}}.
+
:'''CONST''' variables are supported since {{icon|vc}}.
  
 
====STRING====
 
====STRING====
Line 117: Line 117:
  
 
;Limit
 
;Limit
:It is supported in {{icon|sa}} and {{icon|vcs}}.
+
:'''STRING''' variables are supported in {{icon|sa}} and {{icon|vcs}}.
  
 
====NAME====
 
====NAME====
  
The '''NAME''' type handles ''16-byte strings''. Like the previous type, it often holds ''15 characters plus the null-terminator''. It is used to store ''model and texture names of player clothes'' or ''animation names''.
+
The '''NAME''' type handles ''16-byte strings''. Like the previous type, it often holds ''15 characters plus the null-terminator''. It is used to store ''model and texture names of player clothes'' or ''animation names''. It may be a ghost type as R*'s compiler might choose between the two string types automatically according to the length of the string itself.
  
 
;Limit
 
;Limit
Line 237: Line 237:
 
** '''CLEAR_BIT'''
 
** '''CLEAR_BIT'''
 
** '''IS_STRING_NULL'''
 
** '''IS_STRING_NULL'''
** '''COMBINE_STRING'''
+
** '''STRING_STRING'''
  
 
{{incomplete}}
 
{{incomplete}}
Line 247: Line 247:
 
===GOTO===
 
===GOTO===
  
A '''GOTO''' is the jump to the [[#Labels|label]] of any location of the source code. It is also used internally to build other [[#Statements|statements]]:
+
A '''GOTO''' is the jump to the [[#Labels|label]] of any location of the source code. It is also used internally to build other [[#Statements|statements]] or singularly but then it mustn't point off the current context:
  
  jump:
+
  jump0:
  GOTO jump_2
+
  GOTO jumpN
 
   
 
   
 
  [...]
 
  [...]
 
   
 
   
  jump_2:
+
  jumpN:
  GOTO jump
+
  GOTO jump0
 
 
===GOSUB===
 
----
 
A '''GOSUB''' is the jump to the [[#Labels|label]] of a ''subroutine'', which executes some code and returns back to the place it was called with '''RETURN'''.
 
 
 
====Embedded====
 
----
 
''Embedded'' '''GOSUBS''' are those enclosed into a file script. If [[#Local|local variables]] are used within the subroutine, you should put it into the [[#Scope|local scope]] of the calling [[#Structure|script]]. When included in a [[#Modules|module]], they seem to behave like [[#Embedded_2|embedded scripts]].
 
 
 
=====Trigger=====
 
 
 
GOSUB jump
 
 
 
=====Code=====
 
 
 
jump:
 
[...]
 
RETURN
 
 
 
====File====
 
----
 
''File'' '''GOSUBS''' are those included into a script file singularly. It's likely '''GOSUB_AT_FILE''' loads a [[#Modules|module]] that can call other embedded gosubs or [[#Scripts|''scripts'']].
 
 
 
=====Trigger=====
 
 
 
GOSUB_AT_FILE gosub.sc
 
 
 
=====Code=====
 
 
 
// File: gosub.sc
 
 
[...]
 
RETURN
 
  
 
==Constants==
 
==Constants==
Line 332: Line 299:
 
==Structure==
 
==Structure==
  
The source code is split up into several ''SC'' files which comprehend ''scripts'', ''missions'', ''allocating scripts'' and ''modules''.
+
The source code is split up into several ''SC'' files which comprehend ''main'', ''gosubs'', ''scripts'', ''missions'', ''streams'' and ''functions''.
 +
 
 +
===Main===
 +
----
 +
''Main'' is the most significant part of the whole source. It consists of a single file that can include many script files through '''LAUNCH_*''' [[#Natives|natives]]. It is characterized by the absence of the [[#Local|local scope]].
  
===Scripts===
+
===Gosubs===
 
----
 
----
A ''script'' is a code block which takes part of a ''queue of other scripts''. As long as it isn't terminated with '''TERMINATE_THIS_SCRIPT''' or '''TERMINATE_ALL_SCRIPTS_WITH_NAME''' (elsewhere in another script), its execution never expires till the end of the game process. Each one works independently, even though they are able to share [[#Global|global variables]].
+
A ''gosub'' is the jump to the [[#Labels|label]] of a ''subroutine'', which executes some code and returns back to the place where it was called with '''RETURN'''.  
  
 
====Embedded====
 
====Embedded====
 
----
 
----
''Embedded scripts'' are those started by ''SC'' files where their body is included into. Note '''START_NEW_SCRIPT''' has an undefined amount of [[#Arguments|arguments]] which match with each [[#Local|local variable]] of the starting script.
+
''Embedded gosubs'' are those enclosed into a script file. They allow the use of the [[#Scope|local scope]] for [[#Timers|timers]] but they cannot declare [[#Local|local variables]]. Actually, they can inherit the local scope of the parent script but then they cannot be called by any other script.
  
 
=====Trigger=====
 
=====Trigger=====
  
  START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
+
  GOSUB gosub
  
 
=====Code=====
 
=====Code=====
  
  script:
+
  gosub:
SCRIPT_NAME script
 
 
script_loop:
 
 
  {
 
  {
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 
 
 
     [...]
 
     [...]
 
  }
 
  }
  //GOTO script_loop
+
  RETURN
TERMINATE_THIS_SCRIPT
 
  
 
====File====
 
====File====
 
----
 
----
''File scripts'' are those which enclose none or more embedded scripts other than their ''main script''. The '''''MISSION''' opening and closing [[#Natives|natives]]'' let us supposing scripts that own a ''SC'' file are computed as missions. '''MISSION_START''' is a special directive which doesn't get compiled, whereas '''MISSION_END''' is an alias of '''TERMINATE_THIS_SCRIPT'''. Yet, you can also start the scripts embedded in other files.
+
''File gosubs'' are those included into a script file singularly. '''LAUNCH_GOSUB''' jumps to the desired gosub defined into the input file that can call other embedded gosubs or scripts.
  
 
=====Trigger=====
 
=====Trigger=====
  
  START_NEW_SCRIPT_FROM_FILE script.sc
+
  LAUNCH_GOSUB gosub0 gosub.sc
  
 
=====Code=====
 
=====Code=====
  
  // File: script.sc
+
  // File: gosub.sc
 
MISSION_START
 
 
   
 
   
  [VAR_{vartype} {varname0}[,] [... {varnameN}]]
+
  :gosub0
 
SCRIPT_NAME main
 
 
main_loop:
 
 
  {
 
  {
 
     [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 
     [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 
   
 
   
     START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
+
     GOSUB gosubN
 
   
 
   
 
     [...]
 
     [...]
 
  }
 
  }
  //GOTO main_loop
+
  RETURN
MISSION_END
 
 
   
 
   
  script:
+
  gosubN:
SCRIPT_NAME script
 
 
script_loop:
 
 
  {
 
  {
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 
 
 
     [...]
 
     [...]
 
  }
 
  }
  //GOTO script_loop
+
  RETURN
TERMINATE_THIS_SCRIPT
+
 
 +
;Limit
 +
:Gosubs file were introduced since {{icon|3}}. They were unused in {{icon|vc}} and got removed in {{icon|sa}}, but then they were reimplemented in {{icon|lcs}} and {{icon|vcs}}.
  
===Missions===
+
===Scripts===
 
----
 
----
A ''mission'' is a script which takes part of the ''mission block''. When it is launched, the pointer is moved to the corresponding mission [[#Offsets|offset]] located somewhere in the mission block. Do not forget to begin a mission with '''MISSION_START''' (ghost [[#Natives|native]]) and stop its execution with '''MISSION_END'''. Although missions are file scripts, you are able to start embedded scripts, whether they are put in the current file or not.
+
A ''script'' is a code block which takes part of a ''queue of other scripts''. As long as it isn't terminated with '''TERMINATE_THIS_SCRIPT''' or '''TERMINATE_ALL_SCRIPTS_WITH_NAME''' (elsewhere in another script), its execution never expires till the end of the game process. Each one works independently, even though they are able to share [[#Global|global variables]].
  
====Trigger====
+
====Embedded====
 +
----
 +
''Embedded scripts'' are those started by ''SC'' files where their body is included into. Note '''START_NEW_SCRIPT''' has an undefined amount of [[#Arguments|arguments]] which match with each [[#Local|local variable]] of the starting script.
  
  LAUNCH_MISSION mission.sc
+
=====Trigger=====
 +
 
 +
  START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
  
====Code====
+
=====Code=====
  
  // File: mission.sc
+
  script:
+
  SCRIPT_NAME script
MISSION_START
 
 
GOSUB mission_start
 
 
IF HAS_DEATHARREST_BEEN_EXECUTED
 
    GOSUB mission_failed
 
ENDIF
 
 
GOSUB mission_cleanup
 
 
MISSION_END
 
 
[VAR_{vartype} {varname0}[,] [... {varnameN}]]
 
 
mission_start:
 
 
REGISTER_MISSION_GIVEN
 
  SCRIPT_NAME mission
 
 
// Variables initialization
 
 
   
 
   
 +
script_loop:
 
  {
 
  {
 
     [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 
     [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
Line 439: Line 379:
 
     [...]
 
     [...]
 
  }
 
  }
GOTO mission_passed
+
  //GOTO script_loop
+
  TERMINATE_THIS_SCRIPT
mission_failed:
 
[...]
 
RETURN
 
 
mission_passed:
 
REGISTER_MISSION_PASSED mission
 
  //PLAYER_MADE_PROGRESS 1
 
[...]
 
RETURN
 
 
mission_cleanup:
 
// Mark everything as no longer needed
 
[...]
 
  RETURN
 
  
===Allocating scripts===
+
====File====
 
----
 
----
{{incomplete}}
+
''File scripts'' are those which enclose none or more embedded scripts other than their ''main script''. The '''''MISSION''' opening and closing [[#Natives|natives]]'' let us supposing scripts that own a ''SC'' file are computed as missions. '''MISSION_START''' is a special directive which doesn't get compiled, whereas '''MISSION_END''' is an alias of '''TERMINATE_THIS_SCRIPT'''. Yet, you can also start the scripts embedded in other files.
  
===Modules===
+
=====Trigger=====
----
 
The module has been introduced since {{icon|3}} but it was never completed. It is unused in {{icon|vc}} and got removed in {{icon|sa}}, but then it was fully implemented in {{icon|lcs}} and {{icon|vcs}}.
 
  
{{incomplete}}
+
LAUNCH_SCRIPT script.sc
  
==Statements==
+
=====Code=====
  
As usual, the evolution of something implies its development over the years. Alongside, the ''statements'' implementation has been distributed equally into every chapter. Their definitions are similar to those used in ''pseudocodes'' resulting in a ''raw source code''.
+
// File: script.sc
 
+
===IF===
+
MISSION_START
 
+
'''IF''' is one of the most widespread ''conditional statements'' which executes some codes by evaluating a boolean flag, also known as a condition. According to the returning value, either the ''consequence'' or the ''alternative'' will be performed. The condition result can be inverted by appending the '''NOT''' clause before. More conditions require the use of ''logical operators'', they are '''AND''', when verifying if all ''checks'' are true, and '''OR''', while testing if one of all checks is true. The syntax below summarize the whole explanation:
+
[VAR_{vartype} {varname0}[,] [... {varnameN}]]
 
+
IF [NOT] {condition0}
+
SCRIPT_NAME main
  [AND|OR [NOT] {condition8}]
+
     {consequence}
+
main_loop:
  [ELSE
+
{
     {alternative}]
+
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
  ENDIF
+
   
 +
    START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
 +
 +
     [...]
 +
}
 +
  //GOTO main_loop
 +
MISSION_END
 +
 +
script:
 +
SCRIPT_NAME script
 +
 +
script_loop:
 +
{
 +
     [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 +
 +
    [...]
 +
}
 +
//GOTO script_loop
 +
  TERMINATE_THIS_SCRIPT
  
;Limit
+
===Missions===
:More than ''9 conditions'' per statement are't supported.
+
----
 +
A ''mission'' is a script which takes part of the ''mission block''. When it is launched, the pointer is moved to the corresponding mission [[#Offsets|offset]] located somewhere in the mission block. Do not forget to begin a mission with '''MISSION_START''' (ghost [[#Natives|native]]) and stop its execution with '''MISSION_END'''. Although missions are script files, you are able to start embedded scripts, whether they are put in the current file or not.
  
===WHILE===
+
====Trigger====
  
Alike the '''IF''' ''construct'', '''WHILE''' is a conditional statement. The only difference consists in how it performs the ''consequence'', that is it ''loops'' every line of code built into if the boolean flag is true:
+
LAUNCH_MISSION mission.sc
  
WHILE [NOT] {condition0}
+
====Code====
[AND|OR [NOT] {condition8}]
 
    {consequence}
 
ENDWHILE
 
  
===FOR===
+
// File: mission.sc
 
+
Similar to the '''WHILE''' construct, '''FOR''' ''iterates'' some code depending on an incremental variable which goes through the given range, whose ending value must be subsequent to its beginning.
+
MISSION_START
 
+
  FOR {varname} FROM {value0} TO {valueN}
+
GOSUB mission_start
    {code}
+
  ENDFOR
+
IF HAS_DEATHARREST_BEEN_EXECUTED
 
+
    GOSUB mission_failed
;Limits
+
ENDIF
:It is supported since {{icon|vc}}.
+
:The range accepts only integer values.
+
GOSUB mission_cleanup
:Values must be subsequent.
+
:The code will be read at least once in any case.
+
MISSION_END
 
+
===CASE===
+
  [VAR_{vartype} {varname0}[,] [... {varnameN}]]
 
+
Basically, '''CASE''' is a ''group of concatenated '''IF''' statements''. When a condition is false the next ''case'' gets performed, otherwise the ''consequence'' is executed and the code jumps to the end of the construct. If none of the cases is true, an '''ELSE''' clause may be carried out:
+
mission_start:
 +
 +
REGISTER_MISSION_GIVEN
 +
  SCRIPT_NAME mission
 +
 +
// Variables initialization
 +
 +
{
 +
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]
 +
 +
    [...]
 +
}
 +
GOTO mission_passed
 +
 +
mission_failed:
 +
[...]
 +
RETURN
 +
 +
mission_passed:
 +
REGISTER_MISSION_PASSED mission
 +
//PLAYER_MADE_PROGRESS 1
 +
[...]
 +
RETURN
 +
 +
mission_cleanup:
 +
// Mark everything as no longer needed
 +
[...]
 +
RETURN
 +
 
 +
===Streams===
 +
----
 +
{{incomplete}}
  
CASE {varname}
+
===Functions===
    WHEN {value0}
+
----
        {consequence}
+
{{incomplete}}
    [WHEN {valueN}
 
        {consequence}]
 
    [ELSE
 
        {alternative}]
 
ENDCASE
 
  
;Limits
+
==Statements==
:It is supported since {{icon|sa}}.
 
:The '''WHEN''' clause allows the use of integer values only.
 
:In {{icon|sa}}, values must be sorted.
 
:Multiple cases per ''consequence'' aren't allowed.
 
  
===FUNCTION===
+
As usual, the evolution of something implies its development over the years. Alongside, the ''statements'' implementation has been distributed equally into every chapter. Their definitions are similar to those used in ''pseudocodes'' resulting in a ''raw source code''.
  
{{incomplete}}
+
===IF===
  
=Decompiling=
+
'''IF''' is one of the most widespread ''conditional statements'' which executes some codes by evaluating a boolean flag, also known as a condition. According to the returning value, either the ''consequence'' or the ''alternative'' will be performed. The condition result can be inverted by appending the '''NOT''' clause before. More conditions require the use of ''logical operators'', they are '''AND''', when verifying if all ''checks'' are true, and '''OR''', while testing if one of all checks is true. The syntax below summarize the whole explanation:
  
==Structure==
+
IF [NOT] {condition0}
 +
[AND|OR [NOT] {condition8}]
 +
    {consequence}
 +
[ELSE
 +
    {alternative}]
 +
ENDIF
  
For any information about the SCM file format, read [[Mission_Scripting (Overview)|this]] article.
+
;Limit
 +
:More than ''9 conditions'' per statement are't supported.
  
==Identifiers==
+
===WHILE===
  
''Undefined constants'' of model identifiers, whose name refers to a ''DFF'' which is presumably archived into any of the ''IMGs'', loaded by the game, are overwritten by a decrementing value in the order they get compiled. These ''model names'' are then put into the second segment of the ''SCM header''.
+
Alike the '''IF''' ''construct'', '''WHILE''' is a conditional statement. The only difference consists in how it performs the ''consequence'', that is it ''loops'' every line of code built into if the boolean flag is true:
  
==Offsets==
+
WHILE [NOT] {condition0}
 +
[AND|OR [NOT] {condition8}]
 +
    {consequence}
 +
ENDWHILE
  
An ''offset'' is a ''32-bit signed integer'' value which points to a location of the source code. Those of [[#Scripts|scripts]] and [[#Modules|modules]] are ''absolute offsets'' that start from the beginning of the main script, while the ones of [[#Missions|missions]] and [[#Allocating Scripts|allocating scripts]] are ''relative offsets'' starting from their beginning. They always take 4-bytes when they are compiled as [[#Arguments|arguments]].
+
===DO-WHILE===
  
==Variables==
+
{{incomplete}}
  
The following table shows the [[#Variables|variables]] range of the [[#Scope|local scope]] for each game version:
+
===FOR===
  
{|class=wikitable style=text-align:center
+
Similar to the '''WHILE''' construct, '''FOR''' ''iterates'' some code depending on an incremental variable which goes through the given range, whose ending value must be subsequent to its beginning.
!width=100px|Scope
 
!width=50px|{{icon|3}}
 
!width=50px|{{icon|vc}}
 
!width=50px|{{icon|sa}}
 
!width=50px|{{icon|lcs}}
 
!width=50px|{{icon|vcs}}
 
|-
 
|align=left|Script||0-15||0-15||0-31||0-95||0-95
 
|-
 
|align=left|Mission{{ref|mblock|[*]}}||0-15||0-15||0-1023||0-95||0-95
 
|-
 
|align=left|Allocating script||n/a||n/a||0-31||n/a||n/a
 
|-
 
|align=left|Module||0-15||0-15||n/a||0-95||0-95
 
|-
 
|align=left|Function||n/a||n/a||n/a||0-95||0-95
 
|-
 
|align=left|Timer||16-17||16-17||32-33||10-11?||254-255
 
|}
 
  
{{note|mblock}} Although the [[#Missions|mission]] block is allocated as an ''unique [[#Scripts|script]]'', [[#Local|locals]] point to the same storage location, therefore they are some kind of [[#Global|globals]] for missions only.
+
FOR {varname} FROM {value0} TO {valueN}
 +
    {code}
 +
ENDFOR
  
==Commands==
+
;Limits
 +
:It is supported since {{icon|sa}}.
 +
:The range accepts only integer values.
 +
:Values must be subsequent.
 +
:The code will be read at least once in any case.
  
A ''command'' is a ''16-bit unsigned integer'' value referring to a portion of code the game executes when it is called by passing an undefined or absent amount of ''arguments''. The maximum number of available commands is ''0x7FFF'', since the last bit (''0x8000'') is set whenever they are used as ''negative conditions'' (those with the '''NOT''' clause).
+
===CASE===
  
===Arguments===
+
Basically, '''CASE''' is a ''group of concatenated '''IF''' statements''. When a condition is false the next ''case'' gets performed, otherwise the ''consequence'' is executed and the code jumps to the end of the construct. If none of the cases is true, an '''ELSE''' clause may be carried out:
  
An ''argument'' is some data given as input to a command. Normally, commands have a defined amount of arguments up to 32. Those not, such as '''START_NEW_SCRIPT''', can pass as many arguments as the available [[#Local|local variables]] are, except [[#Timers|timers]]. This limitation is game specific.
+
CASE {varname}
 +
    WHEN {value0}
 +
        {consequence}
 +
    [WHEN {valueN}
 +
        {consequence}]
 +
    [ELSE
 +
        {alternative}]
 +
ENDCASE
 +
 
 +
;Limits
 +
:It is supported since {{icon|sa}}.
 +
:The '''WHEN''' clause allows the use of integer values only.
 +
:In {{icon|sa}}, values must be sorted.
 +
:Multiple cases per ''consequence'' aren't allowed.
 +
 
 +
=Decompiling=
 +
 
 +
==Structure==
 +
 
 +
For any information about the SCM file format, read [[Mission_Scripting (Overview)|this]] article. Take into account the compiling order of each ''SC'' file is ''main-gosubs-scripts-missions'' apart from the reading order of the commands used to include them. [[#Streams|streams]] are compiled individually into the ''script.img'' file. On the other hand, [[#Functions|functions]] are compiled like [[#Embedded|embedded gosubs]].
 +
 
 +
==Identifiers==
  
===Internal===
+
''Undefined constants'' of model identifiers, whose name refers to a ''DFF'' which is presumably archived into any of the ''IMGs'', loaded by the game, are overwritten by a decrementing value in the order they get compiled. These ''model names'' are then put into the second segment of the ''SCM header''. Those of [[#Missions|missions]] and [[#Streams|streams]] respect the same rule except the fact they are turned into a 0-based growing identifier.
 +
 
 +
==Offsets==
 +
 
 +
An ''offset'' is a ''32-bit signed integer'' value which points to a location of the source code. Those of [[#Gosubs|gosub]] and [[#Scripts|script]] files are ''absolute offsets'' that start from the beginning of the main script, while the ones of [[#Missions|mission]] and [[#Streams|stream]] files are ''relative offsets'' starting from their beginning. They always take 4-bytes when they are compiled as [[#Arguments|arguments]].
 +
 
 +
==Variables==
  
Here is the list of all commands handled arbitrarily by a hypothetical compiler:
+
The following table shows the [[#Variables|variables]] range of the [[#Scope|local scope]] for each game version:
  
{|class=wikitable
+
{|class=wikitable style=text-align:center
 +
!width=100px|Scope
 +
!width=50px|{{icon|3}}
 +
!width=50px|{{icon|vc}}
 +
!width=50px|{{icon|sa}}
 +
!width=50px|{{icon|lcs}}
 +
!width=50px|{{icon|vcs}}
 +
|-
 +
|align=left|Gosub||0-15||0-15||n/a||0-95||0-95
 +
|-
 +
|align=left|Script||0-15||0-15||0-31||0-95||0-95
 +
|-
 +
|align=left|Mission{{ref|mblock|[*]}}||0-15||0-15||0-1023||0-95||0-95
 +
|-
 +
|align=left|Stream||n/a||n/a||0-31||n/a||n/a
 +
|-
 +
|align=left|Function||n/a||n/a||n/a||0-95||0-95
 +
|-
 +
!colspan=6|
 +
|-
 +
|align=left|Timer||16-17||16-17||32-33||10-11?||254-255
 +
|}
 +
 
 +
{{note|mblock}} Although the [[#Missions|mission]] block is allocated as an ''unique [[#Scripts|script]]'', [[#Local|locals]] point to the same storage location, therefore they are some kind of [[#Global|globals]] for missions uniquely.
 +
 
 +
==Commands==
 +
 
 +
A ''command'' is a ''16-bit unsigned integer'' value referring to a portion of code the game executes when it is called by passing an undefined or absent amount of ''arguments''. The maximum number of available commands is ''0x7FFF'', since the last bit (''0x8000'') is set whenever they are used as ''negative conditions'' (those with the '''NOT''' clause).
 +
 
 +
===Arguments===
 +
 
 +
An ''argument'' is some data given as input to a command. Normally, commands have a defined amount of arguments up to 32. Those not, such as '''START_NEW_SCRIPT''', can pass as many arguments as the available [[#Local|local variables]] are, except [[#Timers|timers]]. This limitation is game specific.
 +
 
 +
===Internal===
 +
 
 +
Here is the list of all commands handled arbitrarily by a hypothetical compiler:
 +
 
 +
{|class=wikitable
 
|-
 
|-
 
!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description
 
!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description!!Acronym!!Description
Line 624: Line 646:
 
|Local '''NAME'''
 
|Local '''NAME'''
 
!AN
 
!AN
|Allocating script number
+
|Stream number
 
|}
 
|}
  
Line 1,364: Line 1,386:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!START_NEW_SCRIPT_FROM_FILE{{ref|likely|[*]}}
+
!LAUNCH_SCRIPT{{ref|likely|[*]}}
 
|00D7
 
|00D7
 
!1
 
!1
Line 1,398: Line 1,420:
 
|colspan="36"|{{icon|3}} {{icon|vc}}
 
|colspan="36"|{{icon|3}} {{icon|vc}}
 
|-  
 
|-  
!GOSUB_AT_FILE{{ref|likely|[*]}}
+
!LAUNCH_GOSUB{{ref|likely|[*]}}
 
|02CD
 
|02CD
 
!2
 
!2
Line 1,792: Line 1,814:
 
|colspan="30"|
 
|colspan="30"|
 
|-  
 
|-  
!rowspan="2"|COMBINE_STRING{{ref|likely|[*]}}
+
!rowspan="2"|STRING_STRING
 
|098B
 
|098B
 
!rowspan="2"|3
 
!rowspan="2"|3
Line 1,886: Line 1,908:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!START_NEW_SCRIPT_FROM_FILE{{ref|likely|[*]}}
+
!LAUNCH_SCRIPT{{ref|likely|[*]}}
 
|00DC
 
|00DC
 
!1
 
!1
Line 1,892: Line 1,914:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!GOSUB_AT_FILE{{ref|likely|[*]}}
+
!LAUNCH_GOSUB{{ref|likely|[*]}}
 
|02D2
 
|02D2
 
!2
 
!2
Line 1,905: Line 1,927:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!rowspan="2"|Call Function{{ref|transf|[*]}}
+
!rowspan="2"|CALL_FUNCTION{{ref|transf|[*]}}
 
|05AE
 
|05AE
 
!rowspan="2"|4
 
!rowspan="2"|4
Line 2,696: Line 2,718:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!START_NEW_SCRIPT_FROM_FILE{{ref|likely|[*]}}
+
!LAUNCH_SCRIPT{{ref|likely|[*]}}
 
|0079
 
|0079
 
!1
 
!1
Line 2,702: Line 2,724:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!GOSUB_AT_FILE{{ref|likely|[*]}}
+
!LAUNCH_GOSUB{{ref|likely|[*]}}
 
|01BA
 
|01BA
 
!2
 
!2
Line 2,715: Line 2,737:
 
|colspan="31"|
 
|colspan="31"|
 
|-  
 
|-  
!rowspan="2"|Call Function{{ref|transf|[*]}}
+
!rowspan="2"|CALL_FUNCTION{{ref|transf|[*]}}
 
|037A
 
|037A
 
!rowspan="2"|1
 
!rowspan="2"|1
Line 2,737: Line 2,759:
 
{{note|likely}} It is a likely definition of the standard [[#Natives|native]].
 
{{note|likely}} It is a likely definition of the standard [[#Natives|native]].
  
{{note|dbgstr}} The native still accepts an argument which can admit up to ''128 characters''. In the compiling process, the argument is skipped but its string is copied to a predefined ''128-bytes buffer'', compiled afterwards. These are the predetermined bytes that seem to do nothing:
+
{{note|dbgstr}} The native still accepts an argument which can admit up to ''127 characters plus the null-terminator''. In the compiling process, the argument is skipped but its string is copied to a predefined ''128-bytes buffer'', compiled afterwards. These are the predetermined bytes that seem to do nothing:
 +
 
 +
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: silver">'''00'''</span> 00 00 00
 +
<span style="color: blue">'''09 2E 00 00'''</span> 00 00 00 00 1C FB 12 00 D8 A8 41 00
 +
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: silver">'''01'''</span> 00 00 00
 +
<span style="color: blue">'''09 2E 00 00'''</span> 00 00 00 00 1C FB 12 00 D8 A8 41 00
 +
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: silver">'''02'''</span> 00 00 00
 +
<span style="color: blue">'''09 2E 00 00'''</span> 00 00 00 00 1C FB 12 00 D8 A8 41 00
 +
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: silver">'''03'''</span> 00 00 00
 +
<span style="color: blue">'''09 2E 00 00'''</span> 00 00 00 00 1C FB 12 00 D8 A8 41 00
  
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: blue">'''00'''</span> 00 00 00
+
Dynamic values within a block are marked in <span style="color: silver">'''grey'''</span>, while those which differs among each block are coloured in <span style="color: blue">'''blue'''</span>.
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
 
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: blue">'''01'''</span> 00 00 00
 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
 
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: blue">'''02'''</span> 00 00 00
 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
 
00 00 41 00 09 2E 00 00 00 00 00 00 <span style="color: blue">'''03'''</span> 00 00 00
 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
 
  
 
{{note|transf}} It gets transformed when compiling.
 
{{note|transf}} It gets transformed when compiling.
Line 2,836: Line 2,860:
 
  {006D}
 
  {006D}
 
  {....}
 
  {....}
  {....}
+
  {....}
  {004D}
+
  {004D}
  {....}
+
  {....}
  {0002}
+
  {0002}
   ----  
+
   ----  
|
+
|
   ----  
+
   ----  
  {00DB}
+
  {00DB}
  {....}
+
  {....}
  {....}
+
  {....}
  {004D}
+
  {004D}
  {....}
+
  {....}
  {0002}
+
  {0002}
   ----  
+
   ----  
|
+
|
   ----  
+
   ----  
  {0078}
+
  {0078}
  {....}
+
  {....}
  {....}
+
  {....}
  {0022}
+
  {0022}
  {....}
+
  {....}
  {0002}
+
  {0002}
   ----  
+
   ----  
|
+
|
  WHILE:
+
  WHILE:
  {value}
+
  {value}
     [NOT] {condition0}
+
     [NOT] {condition0}
     [[NOT] {condition8}]
+
     [[NOT] {condition8}]
  ENDWHILE
+
  ENDWHILE
     {consequence}
+
     {consequence}
     GOTO WHILE
+
     GOTO WHILE
  ENDWHILE:
+
  ENDWHILE:
|}
+
|}
 +
 
 +
===DO-WHILE===
 +
 
 +
{{incomplete}}
  
 
===FOR===
 
===FOR===
Line 2,876: Line 2,904:
 
{|width=100%
 
{|width=100%
 
!width=50%|Decompiled
 
!width=50%|Decompiled
!colspan=2|G/L {{icon|3}} {{icon|vc}} {{icon|sa}} {{icon|lcs}}
+
!colspan=2|G/L {{icon|sa}} {{icon|lcs}}
 
!width=1px|G/L {{icon|vcs}}
 
!width=1px|G/L {{icon|vcs}}
 
!Compiled
 
!Compiled
Line 3,038: Line 3,066:
 
  ENDCASE0:
 
  ENDCASE0:
 
|}
 
|}
 
===FUNCTION===
 
 
{{incomplete}}
 
  
 
==Optimization==
 
==Optimization==
  
In {{icon|lcs}} and {{icon|vcs}}, whenever a ''single condition'' is checked ''00DB'' ({{icon|lcs}}) and ''0078'' ({{icon|vcs}}) don't get compiled cause no ''logical operator'' ('''AND''', '''OR''') is used and so they become really useless. Its lack increase the script efficiency a lot. However, the jump of the '''ELSE''' clause of an [[#IF|IF]] statement which points to the end of the construct is still compiled after a [[#GOTO|GOTO]].
+
In {{icon|lcs}} and {{icon|vcs}}, whenever a ''single condition'' is checked ''00DB'' ({{icon|lcs}}) and ''0078'' ({{icon|vcs}}) don't get compiled cause no ''logical operator'' ('''AND''', '''OR''') is used and so they become really useless. Its lack increase the script efficiency a lot. However, the jump of the '''ELSE''' clause of an [[#IF|IF]] statement which points to the end of the construct is still compiled after a [[#GOTO|GOTO]]. Furthermore, ''Stories Games'' come with an improved data type managing which causes a considerable decrease of the compiled file size.
  
 
=External links=
 
=External links=
  
 
*{{Icon|3}} [http://alexander.sannybuilder.com/?category=various&altname=gta_3_script_sources SC source files (no main.sc)]
 
*{{Icon|3}} [http://alexander.sannybuilder.com/?category=various&altname=gta_3_script_sources SC source files (no main.sc)]
*{{Icon|3}} [http://pastebin.com/raw.php?i=HwDP132U GXT source code (american.gxt)]
 
  
 
{{N|SA|VC|3}}
 
{{N|SA|VC|3}}
  
 
[[Category:Mission Script]]
 
[[Category:Mission Script]]

Revision as of 12:00, 27 October 2012

This section deals with the native SCM syntax of GTA 3 series, nothing other than III, VC, SA, LCS and VCS.
It may contain non-standard SCM definitions as R* hasn't published enough documentation about it yet.

On the occasion of the GTAIII's tenth anniversary, after a long period of darkness where we fell about the real SCM syntax, R* finally treated us by attaching part of its own original source code into the GTAIII Anniversary game, available for iOS and Android devices. As far back as 2001, a snip of some debugging scripts has been already provided with main.sc and debug.sc files. However, many secrets are unrevealed yet, thus some things cannot be documented fully and so they can be only guessed. The SCM format abbreviation is one of countless proofs of this inconvenience, which may stand for Script Control Merged. Other doubts come with source files, whose SC extension appears to be very close to Script Control. At first, someone could have asked oneself how R* named its own programming language: R#? R-Sharp? Rockstar Sharp? Who knows. We simply call it SCM language due to the lack of information. Nevertheless, the SCR language name is cropping up gradually.


Preliminary remarks


This article makes use of formatted codes to improve the reading comprehension. Note that:

  • Square brackets mean everything inside may be omitted.
  • Curly brackets denote the presence of useful codes but not necessarily needed.
  • Vertical bars divide what can be chosen alternatively.

Fundamentals

Comments

A comment is an additional text that may be helpful for the code writer or other users, in short for the reader. It is the only part of the source code which gets always ignored when compiling.

Inline

An inline comment, denoted by // (two slashes), makes everything that follows some plain text:

[...] // Some inline comment

Multiline

A multiline comment embraces a particular area of the source code, starting by the opening tag /* (slash and asterisk) and ending with */ (asterisk and slash):

[...]
/*
 * Some multiline comment
 * ...
 */
[...]

Currently, more than one multiline comment is allowed per line:

[...] /* Some inline comment */ [...] /* Some inline comment */ [...]
Limit
Comments cannot be nested.

Brackets

Brackets play an important role, especially when organizing multiple scripts in a single file becomes necessary.

Curly brackets

Curly brackets (or multiline brackets) act like a local variable scope. Essentially, they enclose the code where local variables are used, including timers. They can be opened and closed many times in a script:

{
    [...]
}

Round brackets

Actually, the round brakets (or multi inline brackets) behaviour sounds trivial, that's to say they simply highlight one or more arguments per native, individually or together. In GTA III, they appear to be used only for SETUP_ZONE_PED_INFO (in a various order) and GXT keys:

SETUP_ZONE_PED_INFO FISHFAC DAY (0) 0 0 0 (0 0 0 0) 0
PRINT_BIG (T4X4_1) 5000 2

They might lock up null values.

Labels

A label is a sequence of characters which identifiy a location of the source code useful for jumps. It can be accessed by any part of the source code. To define a label just append : (colon) to its name:

[...]

{lblname}:

[...]

At the compiling time, they are automatically converted into an offset.

Variables

A variable is a storage location assigned to a symbolic name which contains a value of any type.

Scope


The usage of a variable depends on the scope, that is the context where a specific variable is declared. At this point, we can distinguish the global and local scope.

Global

The global scope grasps the whole source code. Variables defined as globals are visible in any script. They are declared by appending the VAR prefix.

Local


The local scope wraps a localized part of the source code. Variables defined as locals are visible only in the code enclosed by curly brackets. They are declared by appending the LVAR prefix.

Timers

A timer is a singular local variable whose value rises automatically. It starts incrementing since the beginning of the script where it has been placed. There are 2 usable timers which are already defined as TIMERA and TIMERB, therefore they do not need to be declared.

Types


Among the available types, some are equivalent to the most known programming types. Their length is up to 4, 8 and 16 bytes. Each type is appended as a suffix in the variable declaration.

INT

The INT type handles 32-bit signed integer values. It is also used to store values with less bytes, such as a bool.

FLOAT

The FLOAT type handles 32-bit floating-point values. As it normally does, decimal precision is usually stuck to 6 digits beyond which it may get lost.

CONST

CONST is a special type that handles 32-bit signed integer values. It is used only to compare constants of model identifiers, task statuses, ped events and such. It mightn't be used directly but divided into other subordinal types. At the moment, it is merely a suggestion because constants are processed in bulk apparently instead of file by file.

Limit
CONST variables are supported since Vice City.

STRING

The STRING type handles 8-byte strings. Generally, a string is an array of 1-byte characters. It often requires 7 characters plus the null-terminator (a blank byte meaning the end of the string). It is used to hold GXT keys (those of town zones, interiors, help textes or dialogue subtitles) or script names.

Limit
STRING variables are supported in San Andreas and Vice City Stories.

NAME

The NAME type handles 16-byte strings. Like the previous type, it often holds 15 characters plus the null-terminator. It is used to store model and texture names of player clothes or animation names. It may be a ghost type as R*'s compiler might choose between the two string types automatically according to the length of the string itself.

Limit
It is supported in San Andreas.

Declaration


Defining a variable means assigning a token string to a memory cell at the compiling time. Variables must be declared in the following manner:

{varscope}_{vartype} {varname0}[,] [... {varnameN}]

As mentioned in the sections above, local variables have to be put within curly brackets:

{
    {varscope}_{vartype} {varname0}[,] [... {varnameN}]

    [...]
}

Inline variable declaration is allowed, you just have to separate them by spaces or tabulations. Adding a preceding comma before these characters is optional.

Limit
Whereas the variable buffer is limited, you can declare a certain amount of globals and locals. Numerical (INT, FLOAT) and special (CONST) types take 1 variable, while string types (STRING, NAME) occupy respectively 2 and 4 variables to store some data (have a look here for further details).

Arrays

A array is a collection of variables having the same type which can be accessed by an index, an integer value enclosed by square brackets:

{varscope}_{vartype} {varname0}{[arrsize0]}[,] [... {varnameN}{[arrsizeN]}]
Limit
It is supported since San Andreas.

Operators

In general, operators help programmers to do math calculations or operations of any other kind.

Assigning

Assigning is both initializing a variable and giving any value to the variable itself or the content of another one:

{varname} = {anyvalue|varname}

Calculating

Calculating is achieved by using the most common algebric operations. Sadly, the game has only support for adding, subtracting, multiplying and dividing:

{varname} += {anyvalue|varname}
{varname} -= {anyvalue|varname}
{varname} *= {anyvalue|varname}
{varname} /= {anyvalue|varname}

Yet, you can combine assigning and calculating as follows:

{varname} = {anyvalue|varname} + {anyvalue|varname}
{varname} = {anyvalue|varname} - {anyvalue|varname}
{varname} = {anyvalue|varname} * {anyvalue|varname}
{varname} = {anyvalue|varname} / {anyvalue|varname}

String (STRING, NAME) and special (CONST) types are free from these calculations. For a better code understanding, the expression of incrementing or decrementing by a unit can be written as:

{varname} ++
{varname} --
++ {varname}
-- {varname}

The compiler doesn't care about pre-increment or post-increment. Both are translated into:

{varname} += 1
{varname} -= 1
Limit
Multiple operations per line are not allowed.

Converting

Converting means casting a variable having a particular type into another. You can convert integer to floating-point values or viceversa:

{varname} =# {varname}

Ofcourse, the left variable will contain the result of the conversion of the right one.

Comparing

Comparing is the evaluation of a boolean flag regarding the comparison between two values or variables:

{varname} = {anyvalue|varname}  // equal to
{varname} > {anyvalue|varname}  // greater than
{varname} < {anyvalue|varname}  // lesser than
{varname} >= {anyvalue|varname} // greater than or equal to
{varname} <= {anyvalue|varname} // lesser than or equal to

Theorically, string types (STRING, NAME) should be compared with the first operator only, but the presence of the COMPARE_STRING increase the doubts concerning the existence of such operator for strings.

Natives

A native is a symbolic name associated to a command which executes a portion of code that specifies the operation to be performed by passing zero or more arguments. Commands do not return values that can be assigned to a variable, even though the boolean flag is kept whenever they are used as conditions. It follows the common programming syntax adopted for procedure or function calls:

{nativename} [{anyvalue0|varname0} ... {anyvalueN|varnameN}]

Built-in

Built-in natives are those which are associated to more commands that differ each other by argument types or actions:

  • GTA III Vice City San Andreas Liberty City Stories Vice City Stories:
    • TIMESTEP
    • TIMESTEPUNWARPED
    • ABSI
    • ABSF
  • San Andreas:
    • COMPARE_STRING
    • SAVE_STRING_TO_DEBUG_FILE
    • IS_BIT_SET
    • SET_BIT
    • CLEAR_BIT
    • IS_STRING_NULL
    • STRING_STRING

This section is incomplete. You can help by fixing and expanding it.

WAIT

A WAIT skips the execution of a script according to some milliseconds after which it will resume again. Indeed, it is absolutely necessary into infinite loops or those that break after more than one frame, such as the WHILE statement. In this case, a value equal to 0 is passed.

GOTO

A GOTO is the jump to the label of any location of the source code. It is also used internally to build other statements or singularly but then it mustn't point off the current context:

jump0:
GOTO jumpN

[...]

jumpN:
GOTO jump0

Constants

A constant is a symbolic name associated to a specific value. When compiling, their caption is converted in the assigned value. They are listed into TXT files, whose name follows the Pascal Notation which implyies that all first letters of the words are in uppercase and other characters are lowercase (eg. AudioEvents.txt). These files respect the syntax below:

{constname0} {constvalue0}

{constnameN} {constvalueN}

Constant names and values are divided by as many spaces or tabulations as you want. Constant lines are distinguished by two \n (new line) characters. The model names which aren't assigned to a constant are still valid (see Identifiers for further information).

Others

The arguments of some natives accept only constant values of a single namespace. Instead, the CONST type can hold every constant of any namespace.

This section is incomplete. You can help by fixing and expanding it.

Formatting

Everything is case-insensitive, that means the uppercase and lowercase letters are computed as the same character. Usually, the source code is formatted as shown in this table:

Entity Uppercase Lowercase
Label X
Declaration X
Variable X
Native X
Constant X
Statement X

Compiling

Structure

The source code is split up into several SC files which comprehend main, gosubs, scripts, missions, streams and functions.

Main


Main is the most significant part of the whole source. It consists of a single file that can include many script files through LAUNCH_* natives. It is characterized by the absence of the local scope.

Gosubs


A gosub is the jump to the label of a subroutine, which executes some code and returns back to the place where it was called with RETURN.

Embedded


Embedded gosubs are those enclosed into a script file. They allow the use of the local scope for timers but they cannot declare local variables. Actually, they can inherit the local scope of the parent script but then they cannot be called by any other script.

Trigger
GOSUB gosub
Code
gosub:
{
    [...]
}
RETURN

File


File gosubs are those included into a script file singularly. LAUNCH_GOSUB jumps to the desired gosub defined into the input file that can call other embedded gosubs or scripts.

Trigger
LAUNCH_GOSUB gosub0 gosub.sc
Code
// File: gosub.sc

:gosub0
{
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]

    GOSUB gosubN

    [...]
}
RETURN

gosubN:
{
    [...]
}
RETURN
Limit
Gosubs file were introduced since GTA III. They were unused in Vice City and got removed in San Andreas, but then they were reimplemented in Liberty City Stories and Vice City Stories.

Scripts


A script is a code block which takes part of a queue of other scripts. As long as it isn't terminated with TERMINATE_THIS_SCRIPT or TERMINATE_ALL_SCRIPTS_WITH_NAME (elsewhere in another script), its execution never expires till the end of the game process. Each one works independently, even though they are able to share global variables.

Embedded


Embedded scripts are those started by SC files where their body is included into. Note START_NEW_SCRIPT has an undefined amount of arguments which match with each local variable of the starting script.

Trigger
START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]
Code
script:
SCRIPT_NAME script

script_loop:
{
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]

    [...]
}
//GOTO script_loop
TERMINATE_THIS_SCRIPT

File


File scripts are those which enclose none or more embedded scripts other than their main script. The MISSION opening and closing natives let us supposing scripts that own a SC file are computed as missions. MISSION_START is a special directive which doesn't get compiled, whereas MISSION_END is an alias of TERMINATE_THIS_SCRIPT. Yet, you can also start the scripts embedded in other files.

Trigger
LAUNCH_SCRIPT script.sc
Code
// File: script.sc

MISSION_START

[VAR_{vartype} {varname0}[,] [... {varnameN}]]

SCRIPT_NAME main

main_loop:
{
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]

    START_NEW_SCRIPT script [{anyvalue0|varname0} ... {anyvalueN|varnameN}]

    [...]
}
//GOTO main_loop
MISSION_END

script:
SCRIPT_NAME script

script_loop:
{
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]

    [...]
}
//GOTO script_loop
TERMINATE_THIS_SCRIPT

Missions


A mission is a script which takes part of the mission block. When it is launched, the pointer is moved to the corresponding mission offset located somewhere in the mission block. Do not forget to begin a mission with MISSION_START (ghost native) and stop its execution with MISSION_END. Although missions are script files, you are able to start embedded scripts, whether they are put in the current file or not.

Trigger

LAUNCH_MISSION mission.sc

Code

// File: mission.sc

MISSION_START

GOSUB mission_start

IF HAS_DEATHARREST_BEEN_EXECUTED
    GOSUB mission_failed
ENDIF

GOSUB mission_cleanup

MISSION_END

[VAR_{vartype} {varname0}[,] [... {varnameN}]]

mission_start:

REGISTER_MISSION_GIVEN
SCRIPT_NAME mission

// Variables initialization

{
    [LVAR_{vartype} {varname0}[,] [... {varnameN}]]

    [...]
}
GOTO mission_passed

mission_failed:
[...]
RETURN

mission_passed:
REGISTER_MISSION_PASSED mission
//PLAYER_MADE_PROGRESS 1
[...]
RETURN

mission_cleanup:
// Mark everything as no longer needed
[...]
RETURN

Streams


This section is incomplete. You can help by fixing and expanding it.

Functions


This section is incomplete. You can help by fixing and expanding it.

Statements

As usual, the evolution of something implies its development over the years. Alongside, the statements implementation has been distributed equally into every chapter. Their definitions are similar to those used in pseudocodes resulting in a raw source code.

IF

IF is one of the most widespread conditional statements which executes some codes by evaluating a boolean flag, also known as a condition. According to the returning value, either the consequence or the alternative will be performed. The condition result can be inverted by appending the NOT clause before. More conditions require the use of logical operators, they are AND, when verifying if all checks are true, and OR, while testing if one of all checks is true. The syntax below summarize the whole explanation:

IF [NOT] {condition0}
[AND|OR [NOT] {condition8}]
    {consequence}
[ELSE
    {alternative}]
ENDIF
Limit
More than 9 conditions per statement are't supported.

WHILE

Alike the IF construct, WHILE is a conditional statement. The only difference consists in how it performs the consequence, that is it loops every line of code built into if the boolean flag is true:

WHILE [NOT] {condition0}
[AND|OR [NOT] {condition8}]
    {consequence}
ENDWHILE

DO-WHILE

This section is incomplete. You can help by fixing and expanding it.

FOR

Similar to the WHILE construct, FOR iterates some code depending on an incremental variable which goes through the given range, whose ending value must be subsequent to its beginning.

FOR {varname} FROM {value0} TO {valueN}
    {code}
ENDFOR
Limits
It is supported since San Andreas.
The range accepts only integer values.
Values must be subsequent.
The code will be read at least once in any case.

CASE

Basically, CASE is a group of concatenated IF statements. When a condition is false the next case gets performed, otherwise the consequence is executed and the code jumps to the end of the construct. If none of the cases is true, an ELSE clause may be carried out:

CASE {varname}
    WHEN {value0}
        {consequence}
    [WHEN {valueN}
        {consequence}]
    [ELSE
        {alternative}]
ENDCASE
Limits
It is supported since San Andreas.
The WHEN clause allows the use of integer values only.
In San Andreas, values must be sorted.
Multiple cases per consequence aren't allowed.

Decompiling

Structure

For any information about the SCM file format, read this article. Take into account the compiling order of each SC file is main-gosubs-scripts-missions apart from the reading order of the commands used to include them. streams are compiled individually into the script.img file. On the other hand, functions are compiled like embedded gosubs.

Identifiers

Undefined constants of model identifiers, whose name refers to a DFF which is presumably archived into any of the IMGs, loaded by the game, are overwritten by a decrementing value in the order they get compiled. These model names are then put into the second segment of the SCM header. Those of missions and streams respect the same rule except the fact they are turned into a 0-based growing identifier.

Offsets

An offset is a 32-bit signed integer value which points to a location of the source code. Those of gosub and script files are absolute offsets that start from the beginning of the main script, while the ones of mission and stream files are relative offsets starting from their beginning. They always take 4-bytes when they are compiled as arguments.

Variables

The following table shows the variables range of the local scope for each game version:

Scope GTA III Vice City San Andreas Liberty City Stories Vice City Stories
Gosub 0-15 0-15 n/a 0-95 0-95
Script 0-15 0-15 0-31 0-95 0-95
Mission[*] 0-15 0-15 0-1023 0-95 0-95
Stream n/a n/a 0-31 n/a n/a
Function n/a n/a n/a 0-95 0-95
Timer 16-17 16-17 32-33 10-11? 254-255

^ Although the mission block is allocated as an unique script, locals point to the same storage location, therefore they are some kind of globals for missions uniquely.

Commands

A command is a 16-bit unsigned integer value referring to a portion of code the game executes when it is called by passing an undefined or absent amount of arguments. The maximum number of available commands is 0x7FFF, since the last bit (0x8000) is set whenever they are used as negative conditions (those with the NOT clause).

Arguments

An argument is some data given as input to a command. Normally, commands have a defined amount of arguments up to 32. Those not, such as START_NEW_SCRIPT, can pass as many arguments as the available local variables are, except timers. This limitation is game specific.

Internal

Here is the list of all commands handled arbitrarily by a hypothetical compiler:

Acronym Description Acronym Description Acronym Description Acronym Description Acronym Description Acronym Description
IV INT value FV FLOAT value CV CONST value SV STRING value NV NAME value LO Label offset
GI Global INT GF Global FLOAT GC Global CONST GS Global STRING GN Global NAME MN Mission number
LI Local INT LF Local FLOAT LC Local CONST LS Local STRING LN Local NAME AN Stream number
Native or Operator Command Args Arguments
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
GTA III Vice City San Andreas Liberty City Stories Vice City Stories
MISSION_START[*] 0
GOTO[*] 0002 1 LO
GTA III Vice City San Andreas Liberty City Stories
INT Operator = 0004 2 GI IV
0006 LI IV
0084 GI GI
0085 LI LI
008A GI LI
008B LI GI
FLOAT Operator = 0005 2 GF FV
0007 LF FV
0086 GF GF
0087 LF LF
0088 GF LF
0089 LF GF
INT Operator += 0008 2 GI IV
000A LI IV
0058 GI GI
005A LI LI
005C LI GI
005E GI LI
FLOAT Operator += 0009 2 GF FV
000B LF FV
0059 GF GF
005B LF LF
005D LF GF
005F GF LF
INT Operator -= 000C 2 GI IV
000E LI IV
0060 GI GI
0062 LI LI
0064 LI GI
0066 GI LI
FLOAT Operator -= 000D 2 GF FV
000F LF FV
0061 GF GF
0063 LF LF
0065 LF GF
0067 GF LF
INT Operator *= 0010 2 GI IV
0012 LI IV
0068 GI GI
006A LI LI
006C GI LI
006E LI GI
FLOAT Operator *= 0011 2 GF FV
0013 LF FV
0069 GF GF
006B LF LF
006D GF LF
006F LF GF
INT Operator /= 0014 2 GI IV
0016 LI IV
0070 GI GI
0072 LI LI
0074 GI LI
0076 LI GI
FLOAT Operator /= 0015 2 GF FV
0017 LF FV
0071 GF GF
0073 LF LF
0075 GF LF
0077 LF GF
INT Operator =# 008C 2 GI GF
008E LI GF
0090 GI LF
0092 LI LF
FLOAT Operator =# 008D 2 GF GI
008F LF GI
0091 GF LI
0093 LF LI
INT Conditional Operator = 0038 2 GI IV
0039 LI IV
003A GI GI
003B LI LI
003C GI LI
FLOAT Conditional Operator = 0042 2 GF FV
0043 LF FV
0044 GF GF
0045 LF LF
0046 GF LF
INT Conditional Operator > 0018 2 GI IV
0019 LI IV
001C GI GI
001D LI LI
001E GI LI
001F LI GI
FLOAT Conditional Operator > 0020 2 GF FV
0021 LF FV
0024 GF GF
0025 LF LF
0026 GF LF
0027 LF GF
INT Conditional Operator < 002A 2 IV GI
002B IV LI
FLOAT Conditional Operator < 0032 2 FV GF
0033 FV LF
INT Conditional Operator >= 0028 2 GI IV
0029 LI IV
002C GI GI
002D LI LI
002E GI LI
002F LI GI
FLOAT Conditional Operator >= 0030 2 GF FV
0031 LF FV
0034 GF GF
0035 LF LF
0036 GF LF
0037 LF GF
INT Conditional Operator <= 001A 2 IV GI
001B IV LI
FLOAT Conditional Operator <= 0022 2 FV GF
0023 FV LF
TIMESTEP 0078 2 GF FV
0079 LF FV
007A GF GF
007B LF LF
007C LF GF
007D GF LF
TIMESTEPUNWARPED 007E 2 GF FV
007F LF FV
0080 GF GF
0081 LF LF
0082 LF GF
0083 GF LF
ABSI 0094 1 GI
0095 LI
ABSF 0096 1 GF
0097 LF
Jump If False[*] 004D 1 LO
TERMINATE_THIS_SCRIPT 004E 0
MISSION_END
START_NEW_SCRIPT[*] 004F 1 LO _ _ _
GOSUB 0050 1 LO
RETURN 0051 0
Comparing Rule[*] 00D6 1 IV
LAUNCH_SCRIPT[*] 00D7 1 LO
GTA III Vice City San Andreas
LAUNCH_MISSION 0417 1 MN
GTA III Vice City Liberty City Stories
Jump If True[*] 004C 1 LO
RETURN_TRUE[*] 00C5 0
RETURN_FALSE[*] 00C6 0
GTA III Vice City
LAUNCH_GOSUB[*] 02CD 2 LO LO
Vice City San Andreas
CONST Operator = 04AE 2 GC CV
04AF LC CV
CONST Conditional Operator = 04A3 2 GC CV
04A4 LC CV
CONST Conditional Operator > 04B0 2 GC CV
04B1 LC CV
CONST Conditional Operator < 04B6 2 CV GV
04B7 CV LC
CONST Conditional Operator >= 04B4 2 GC CV
04B5 LC CV
CONST Conditional Operator <= 04B2 2 CV GC
04B3 CV LC
San Andreas
STRING Operator = 05A9 2 GS SV
GS GS
GS LS
05AA LS SV
LS LS
LS GS
NAME Operator = 06D1 2 GN NV
GN GN
GN LN
06D2 LN NV
LN LN
LN GN
INT Conditional Operator = 07D6 2 LI GI
FLOAT Conditional Operator = 07D7 2 LF GF
STRING Conditional Operator =
COMPARE_STRING
05AD 2 GS SV
GS GS
GS LS
05AE LS SV
LS LS
LS GS
NAME Conditional Operator =
COMPARE_STRING
08F9 2 GN NV
GN GN
GN LN
08FA LN NV
LN LN
LN GN
SAVE_STRING_TO_DEBUG_FILE[*] 05B6 0
IS_STRING_NULL 0844 1 GS
0845 LS
0846 GN
0847 LN
Initialize Table Of Jumps 0871 18 GI IV IV LO IV LO IV LO IV LO IV LO IV LO IV LO IV LO
LI IV IV LO IV LO IV LO IV LO IV LO IV LO IV LO IV LO
Append Table Jumps 0872 18 IV LO IV LO IV LO IV LO IV LO IV LO IV LO IV LO IV LO
IS_BIT_SET 08B4 2 GI IV
08B5 GI GI
08B6 GI LI
08B7 LI IV
08B8 LI GI
08B9 LI LI
SET_BIT 08BA 2 GI IV
08BB GI GI
08BC GI LI
08BD LI IV
08BE LI GI
08BF LI LI
CLEAR_BIT 08C0 2 GI IV
08C1 GI GI
08C2 GI LI
08C3 LI IV
08C4 LI GI
08C5 LI LI
STRING_STRING 098B 3 GS GS GS
098C GN GN GN
Liberty City Stories
CONST Operator = 04B3 2 GC CV
04B4 LC CV
CONST Conditional Operator = 04A8 2 GC CV
04A9 LC CV
CONST Conditional Operator > 04B5 2 GC CV
04B6 LC CV
CONST Conditional Operator < 04BB 2 CV GV
04BC CV LC
CONST Conditional Operator >= 04B9 2 GC CV
04BA LC CV
CONST Conditional Operator <= 04B7 2 CV GC
04B8 CV LC
Comparing Rule[*] 00DB 1 IV
LAUNCH_SCRIPT[*] 00DC 1 LO
LAUNCH_GOSUB[*] 02D2 2 LO LO
LAUNCH_MISSION 041C 1 MN
CALL_FUNCTION[*] 05AE 4 IV IV IV LO _ _ _
05AF
Vice City Stories
INT Operator = 0004 2 GI IV
LI IV
0035 GI GI
GI LI
LI LI
LI GI
FLOAT Operator = 0005 2 GF FV
LF FV
0036 GF GF
GF LF
LF LF
LF GF
STRING Operator = 0006 2 GS SV
LS SV
0037 GS GS
GS LS
LS LS
LS GS
INT Operator += 0007 2 GI IV
LI IV
0029 GI GI
GI LI
LI LI
LI GI
FLOAT Operator += 0008 2 GF FV
LF FV
002A GF GF
GF LF
LF LF
LF GF
INT Operator -= 0009 2 GI IV
LI IV
002B GI GI
GI LI
LI LI
LI GI
FLOAT Operator -= 000A 2 GF FV
LF FV
002C GF GF
GF LF
LF LF
LF GF
INT Operator *= 000B 2 GI IV
LI IV
002D GI GI
GI LI
LI LI
LI GI
FLOAT Operator *= 000C 2 GF FV
LF FV
002E GF GF
GF LF
LF LF
LF GF
INT Operator /= 000D 2 GI IV
LI IV
002F GI GI
GI LI
LI LI
LI GI
FLOAT Operator /= 000E 2 GF FV
LF FV
0030 GF GF
GF LF
LF LF
LF GF
INT Operator =# 0038 2 GI GI
GI LI
LI LI
LI GI
FLOAT Operator =# 0039 2 GF GF
GF LF
LF LF
LF GF
INT Conditional Operator = 001B 2 GI IV
LI IV
001C GI GI
GI LI
LI LI
LI GI
FLOAT Conditional Operator = 001D 2 GF FV
LF FV
001E GF GF
GF LF
LF LF
LF GF
STRING Conditional Operator =
COMPARE_STRING
001F 2 GS SV
LS SV
0020 GS GS
GS LS
LS LS
LS GS
INT Conditional Operator > 000F 2 GI IV
LI IV
0011 GI GI
GI LI
LI LI
LI GI
FLOAT Conditional Operator > 0012 2 GF FV
LF FV
0014 GF GF
GF LF
LF LF
LF GF
INT Conditional Operator < 0016 2 IV GI
IV LI
FLOAT Conditional Operator < 0019 2 FV GF
FV LF
INT Conditional Operator >= 0015 2 GI IV
LI IV
0017 GI GI
GI LI
LI LI
LI GI
FLOAT Conditional Operator >= 0018 2 GF FV
LF FV
001A GF GF
GF LF
LF LF
LF GF
INT Conditional Operator <= 0010 2 IV GI
IV LI
FLOAT Conditional Operator <= 0013 2 IV GI
IV LI
TIMESTEP 0031 2 GF FV
LF FV
0032 GF GF
GF LF
LF LF
LF GF
TIMESTEPUNWARPED 0033 2 GF FV
LF FV
0034 GF GF
GF LF
LF LF
LF GF
ABSI 003A 1 GI
LI
ABSF 003B 1 GF
LF
CONST Operator = 02E2 2 GC CV
LC CV
CONST Conditional Operator = 02DB 2 GC CV
LC CV
CONST Conditional Operator > 02E3 2 GC CV
LC CV
CONST Conditional Operator < 02E6 2 CV GV
CV LC
CONST Conditional Operator >= 02E5 2 GC CV
LC CV
CONST Conditional Operator <= 02E4 2 CV GC
CV LC
Jump If True[*] 0021 1 LO
Jump If False[*] 0022 1 LO
TERMINATE_THIS_SCRIPT 0023 0
MISSION_END
START_NEW_SCRIPT[*] 0024 1 LO _ _ _
GOSUB 0025 1 LO
RETURN 0026 0
RETURN_TRUE[*] 005E 0
RETURN_FALSE[*] 005F 0
Comparing Rule[*] 0078 1 IV
LAUNCH_SCRIPT[*] 0079 1 LO
LAUNCH_GOSUB[*] 01BA 2 LO LO
LAUNCH_MISSION 0289 1 MN
CALL_FUNCTION[*] 037A 1 IV IV IV LO _ _ _
037B

^ A special mission directive which is never compiled.

^ It is hidden when used to build statements.

^ It is hidden in the source code.

^ It has an undefined amount of arguments.

^ It is a likely definition of the standard native.

^ The native still accepts an argument which can admit up to 127 characters plus the null-terminator. In the compiling process, the argument is skipped but its string is copied to a predefined 128-bytes buffer, compiled afterwards. These are the predetermined bytes that seem to do nothing:

00 00 41 00 09 2E 00 00 00 00 00 00 00 00 00 00 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
00 00 41 00 09 2E 00 00 00 00 00 00 01 00 00 00 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
00 00 41 00 09 2E 00 00 00 00 00 00 02 00 00 00 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00
00 00 41 00 09 2E 00 00 00 00 00 00 03 00 00 00 
09 2E 00 00 00 00 00 00 1C FB 12 00 D8 A8 41 00

Dynamic values within a block are marked in grey, while those which differs among each block are coloured in blue.

^ It gets transformed when compiling.

This section is incomplete. You can help by fixing and expanding it.

Analysis

As an overview of the compiled source, statements are literally nested meaning that the code is unoptimized. Furthermore, the jump of an embedded construct doesn't get merged with that of the construct itself, which consists of a benefit for the code parsing.

IF

The IF statement can handle up to 9 checks per construct. 006D (GTA III, Vice City, San Andreas), 00DB (Liberty City Stories) and 0078 (Vice City Stories indicate you are verifying a single check (0) or multiple checks with either AND clause (1 to 8) or OR clause (21 to 28). When the consequence is performed, the code jumps to the end of the construct:

Decompiled GTA III Vice City San Andreas Liberty City Stories Vice City Stories Compiled
IF [NOT] {condition0}
[AND|OR [NOT] {condition8}]
    {consequence}
[ELSE
    {alternative}]
ENDIF
{006D}
{....}
{....}
{004D}
{....}
{0002}
 ---- 
{....}
 ---- 
{00DB}
{....}
{....}
{004D}
{....}
{0002}
 ---- 
{....}
 ---- 
{0078}
{....}
{....}
{0022}
{....}
{0002}
 ---- 
{....}
 ---- 
{value}
    [NOT] {condition0}
    [[NOT] {condition8}]
ELSE
    {consequence}
    [GOTO ENDIF
ELSE:
    {alternative}]
ENDIF:

WHILE

The WHILE statement is pretty much similar to the previous, even though when the consequence is read the code is moved to the beginning of the construct:

Decompiled GTA III Vice City San Andreas Liberty City Stories Vice City Stories Compiled
WHILE [NOT] {condition0}
[AND|OR [NOT] {condition8}]
    {consequence}
ENDWHILE
 ---- 
{006D}
{....}
{....}
{004D}
{....}
{0002}
 ---- 
 ---- 
{00DB}
{....}
{....}
{004D}
{....}
{0002}
 ---- 
 ---- 
{0078}
{....}
{....}
{0022}
{....}
{0002}
 ---- 
WHILE:
{value}
    [NOT] {condition0}
    [[NOT] {condition8}]
ENDWHILE
    {consequence}
    GOTO WHILE
ENDWHILE:

DO-WHILE

This section is incomplete. You can help by fixing and expanding it.

FOR

Seemingly, the FOR statement is the first construct ever optimized as a result of a possible R* compiler fault. Moreover, it sounds ambiguous as it loops at least once. This was probably the intention of R*'s programmers, that is iterating at least twise else the construct is useless. However, there are some chance they decide to use such structure to avoid some conflict with the WHILE construct:

Decompiled G/L San Andreas Liberty City Stories G/L Vice City Stories Compiled
FOR {varname} FROM {value0} TO {valueN}
    {code}
ENDFOR
{0004}
 ---- 
{....}
{0008}
{0028}
{004D}
{0005}
 ---- 
{....}
{0009}
{0029}
{004D}
{0004}
 ---- 
{....}
{0007}
{0015}
{0022}
{varname} = {value0}
LOOP:
{code}
++ {varname}
    {varname} >= {valueN}
LOOP

CASE

In San Andreas, the CASE statement is more complex and efficient because the game uses internally a binary search algorithm to jump at the label that matches with the value of a particular case. This method requires a known amount of cases which is up to 75. When a case is true, a consequence is executed and the code jumps to the end of the construct, otherwise the alternative may be performed. As the code is unoptimized, the GOTO of the last case is still compiled even though its label points to the end of the jump itself:

Decompiled San Andreas Compiled
CASE {varname}
    WHEN {value0}
        {consequence}
    [WHEN {valueN}
        {consequence}]
    [ELSE
        {alternative}]
ENDCASE
{0871}
{0872}
 ---- 
{....}
{0002}
 ---- 
{....}
{0002}
 ---- 
{....}
{0002}
 ---- 
{varname} {cases} {iselse} ELSE {value0} WHEN0 {valueN} WHENN -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE
[-1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE -1 ENDCASE]
WHEN0:
    {consequence}
    GOTO ENDCASE
[WHENN:
    {consequence}
    GOTO ENDCASE]
[ELSE:
    {alternative}
    GOTO ENDCASE]
ENDCASE:

In Liberty City Stories and Vice City Stories, such statement is a set of nested IF constructs which causes a very slight loss of performance by considering that 00DB (Liberty City Stories) and 0078 (Vice City Stories aren't compiled:

Decompiled G/L Liberty City Stories G/L Vice City Stories Compiled
CASE {varname}
    WHEN {value0}
        {consequence}
    [WHEN {valueN}
        {consequence}]
    [ELSE
        {alternative}]
ENDCASE
 ---- 
{0038}
{004D}
{....}
{0002}
 ---- 
{0038}
{004D}
{....}
{0002}
 ---- 
{....}
 ---- 
 ---- 
 ---- 
{0039}
{004D}
{....}
{0002}
 ---- 
{0039}
{004D}
{....}
{0002}
 ---- 
{....}
 ---- 
 ---- 
 ---- 
{001B}
{0022}
{....}
{0002}
 ---- 
{001B}
{0022}
{....}
{0002}
 ---- 
{....}
 ---- 
 ---- 
WHEN0:
    {varname} = {value0}
WHENN
    {consequence}
    [GOTO ENDCASE0
WHENN:
        {varname} = {valueN}
    ELSE
        {consequence}
        [GOTO ENDCASEN
    ELSE:
        {alternative}]
    ENDCASEN:]
ENDCASE0:

Optimization

In Liberty City Stories and Vice City Stories, whenever a single condition is checked 00DB (Liberty City Stories) and 0078 (Vice City Stories) don't get compiled cause no logical operator (AND, OR) is used and so they become really useless. Its lack increase the script efficiency a lot. However, the jump of the ELSE clause of an IF statement which points to the end of the construct is still compiled after a GOTO. Furthermore, Stories Games come with an improved data type managing which causes a considerable decrease of the compiled file size.

External links