This commit is contained in:
xensik 2021-09-13 17:50:36 +02:00
parent 564c46cdb2
commit ee9552fa59
25 changed files with 21963 additions and 5 deletions

View File

@ -11,12 +11,13 @@ A utility to compile & decompile IW engine game scripts.
- **IW8** *(Call of Duty: Modern Warfare (2019))*
- **S1** *(Call of Duty: Advanced Warfare)*
- **S2** *(Call of Duty: WWII)*
- **S4** *(Call of Duty: Vanguard)*
- **H1** *(Call of Duty: Modern Warfare Remastered)*
- **H2** *(Call of Duty: Modern Warfare 2 Campaign Remastered)*
## Usage
``./gsc-tool.exe <game> <mode> <file>``
**game**: `-iw5`, `-iw6`, `-iw7`, `-iw8`, `-s1`, `-s2`, `-h1`, `-h2`
**game**: `-iw5`, `-iw6`, `-iw7`, `-iw8`, `-s1`, `-s2`, `-s4`, `-h1`, `-h2`
| Mode |Description | Output |
|:---------|:--------------------------|:------------|
|`-asm` |assemble a `file.gscasm` |`file.gscbin`|

View File

@ -1,4 +1,4 @@
generate: S2
generate: IW8
clean:
rm -rf ./parser.hpp
@ -6,6 +6,6 @@ clean:
rm -rf ./lexer.hpp
rm -rf ./lexer.cpp
S2: lexer.lpp parser.ypp
IW8: lexer.lpp parser.ypp
flex lexer.lpp
bison parser.ypp -Wcounterexamples

11
gen/s4/Makefile Normal file
View File

@ -0,0 +1,11 @@
generate: S4
clean:
rm -rf ./parser.hpp
rm -rf ./parser.cpp
rm -rf ./lexer.hpp
rm -rf ./lexer.cpp
S4: lexer.lpp parser.ypp
flex lexer.lpp
bison parser.ypp -Wcounterexamples

154
gen/s4/lexer.lpp Normal file
View File

@ -0,0 +1,154 @@
/* Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
*/
%option outfile="lexer.cpp"
%option header-file="lexer.hpp"
%option prefix="s4_"
%option reentrant
%option noyywrap batch nounput noinput
%option never-interactive
%option nounistd
%top{
#include "stdafx.hpp"
#include "s4.hpp"
#include "parser.hpp"
using namespace xsk::gsc;
}
%{
#define YY_USER_ACTION loc.columns(yyleng);
%}
RGX_FILE ([_A-Za-z0-9]+\\)+[_A-Za-z0-9]+
RGX_NAME [_A-Za-z][_A-Za-z0-9]*
RGX_STRING \"(?:\\.|[^\"])*?\"|\'(?:\\.|[^\'])*?\'
RGX_COLOR #([0-9a-fA-F]{6}|[0-9a-fA-F]{3})
RGX_FLT_DEC [0-9]+\.(?:[0-9]*)?f?|\.[0-9]+f?
RGX_INT_OCT 0[1-7][0-7]*
RGX_INT_BIN 0[bB][01]+
RGX_INT_HEX 0[xX][0-9a-fA-F]+
RGX_INT_DEC [0-9]+
RGX_DEFAULT (.|\n)
%x COMMENT_BLOCK_STATE
%x DEVELOPER_BLOCK_STATE
%%
%{
loc.step();
%}
[ \t\r] { loc.step(); }
\n { loc.lines(yyleng); loc.step(); }
"//".*
"/*" { BEGIN(COMMENT_BLOCK_STATE); }
<COMMENT_BLOCK_STATE>.
<COMMENT_BLOCK_STATE>\n { loc.lines(yyleng); loc.step(); }
<COMMENT_BLOCK_STATE>"*/" { BEGIN(INITIAL); }
"/#" { BEGIN(DEVELOPER_BLOCK_STATE); }
<DEVELOPER_BLOCK_STATE>.
<DEVELOPER_BLOCK_STATE>\n { loc.lines(yyleng); loc.step(); }
<DEVELOPER_BLOCK_STATE>"#/" { BEGIN(INITIAL); }
"#include" { return s4::parser::make_INCLUDE(loc); }
"#using_animtree" { return s4::parser::make_USINGTREE(loc); }
"#animtree" { return s4::parser::make_ANIMTREE(loc); }
"endon" { return s4::parser::make_ENDON(loc); }
"notify" { return s4::parser::make_NOTIFY(loc); }
"wait" { return s4::parser::make_WAIT(loc); }
"waittill" { return s4::parser::make_WAITTILL(loc); }
"waittillmatch" { return s4::parser::make_WAITTILLMATCH(loc); }
"waittillframeend" { return s4::parser::make_WAITTILLFRAMEEND(loc); }
"waitframe" { return s4::parser::make_WAITFRAME(loc); }
"if" { return s4::parser::make_IF(loc); }
"else" { return s4::parser::make_ELSE(loc); }
"while" { return s4::parser::make_WHILE(loc); }
"for" { return s4::parser::make_FOR(loc); }
"foreach" { return s4::parser::make_FOREACH(loc); }
"in" { return s4::parser::make_IN(loc); }
"switch" { return s4::parser::make_SWITCH(loc); }
"case" { return s4::parser::make_CASE(loc); }
"default" { return s4::parser::make_DEFAULT(loc); }
"break" { return s4::parser::make_BREAK(loc); }
"continue" { return s4::parser::make_CONTINUE(loc); }
"return" { return s4::parser::make_RETURN(loc); }
"thread" { return s4::parser::make_THREAD(loc); }
"childthread" { return s4::parser::make_CHILDTHREAD(loc); }
"thisthread" { return s4::parser::make_THISTHREAD(loc); }
"call" { return s4::parser::make_CALL(loc); }
"true" { return s4::parser::make_TRUE(loc); }
"false" { return s4::parser::make_FALSE(loc); }
"undefined" { return s4::parser::make_UNDEFINED(loc); }
"size" { return s4::parser::make_SIZE(loc); }
"game" { return s4::parser::make_GAME(loc); }
"self" { return s4::parser::make_SELF(loc); }
"anim" { return s4::parser::make_ANIM(loc); }
"level" { return s4::parser::make_LEVEL(loc); }
\( { return s4::parser::make_LPAREN(loc); }
\) { return s4::parser::make_RPAREN(loc); }
\{ { return s4::parser::make_LBRACE(loc); }
\} { return s4::parser::make_RBRACE(loc); }
\[ { return s4::parser::make_LBRACKET(loc); }
\] { return s4::parser::make_RBRACKET(loc); }
\, { return s4::parser::make_COMMA(loc); }
\. { return s4::parser::make_DOT(loc); }
\:\: { return s4::parser::make_DOUBLECOLON(loc); }
\: { return s4::parser::make_COLON(loc); }
\; { return s4::parser::make_SEMICOLON(loc); }
\? { return s4::parser::make_QMARK(loc); }
\+\+ { return s4::parser::make_INCREMENT(loc); }
\-\- { return s4::parser::make_DECREMENT(loc); }
\<\<\= { return s4::parser::make_ASSIGN_LSHIFT(loc); }
\>\>\= { return s4::parser::make_ASSIGN_RSHIFT(loc); }
\<\< { return s4::parser::make_LSHIFT(loc); }
\>\> { return s4::parser::make_RSHIFT(loc); }
\|\| { return s4::parser::make_OR(loc); }
\&\& { return s4::parser::make_AND(loc); }
\=\= { return s4::parser::make_EQUALITY(loc); }
\!\= { return s4::parser::make_INEQUALITY(loc); }
\<\= { return s4::parser::make_LESS_EQUAL(loc); }
\>\= { return s4::parser::make_GREATER_EQUAL(loc); }
\< { return s4::parser::make_LESS(loc); }
\> { return s4::parser::make_GREATER(loc); }
\+\= { return s4::parser::make_ASSIGN_ADD(loc); }
\-\= { return s4::parser::make_ASSIGN_SUB(loc); }
\*\= { return s4::parser::make_ASSIGN_MULT(loc); }
\/\= { return s4::parser::make_ASSIGN_DIV(loc); }
\%\= { return s4::parser::make_ASSIGN_MOD(loc); }
\|\= { return s4::parser::make_ASSIGN_BITWISE_OR(loc); }
\&\= { return s4::parser::make_ASSIGN_BITWISE_AND(loc); }
\^\= { return s4::parser::make_ASSIGN_BITWISE_EXOR(loc); }
\= { return s4::parser::make_ASSIGN(loc); }
\+ { return s4::parser::make_ADD(loc); }
\- { return s4::parser::make_SUB(loc); }
\* { return s4::parser::make_MULT(loc); }
\/ { return s4::parser::make_DIV(loc); }
\% { return s4::parser::make_MOD(loc); }
\! { return s4::parser::make_NOT(loc); }
\~ { return s4::parser::make_COMPLEMENT(loc); }
\| { return s4::parser::make_BITWISE_OR(loc); }
\& { return s4::parser::make_BITWISE_AND(loc); }
\^ { return s4::parser::make_BITWISE_EXOR(loc); }
{RGX_FILE} { return s4::parser::make_FILE(utils::string::fordslash(yytext), loc); }
{RGX_NAME} { return s4::parser::make_NAME((std::string(yytext, 3) == "_ID") ? std::string(yytext) : utils::string::to_lower(yytext), loc); }
\&{RGX_STRING} { return s4::parser::make_ISTRING(std::string(yytext).substr(1), loc); }
{RGX_STRING} { return s4::parser::make_STRING(std::string(yytext), loc); }
{RGX_COLOR} { return s4::parser::make_COLOR(std::string(yytext).substr(1), loc); }
{RGX_FLT_DEC} { return s4::parser::make_FLOAT(std::string(yytext), loc); }
{RGX_INT_OCT} { return s4::parser::make_INT_OCT(utils::string::oct_to_dec(yytext), loc); }
{RGX_INT_BIN} { return s4::parser::make_INT_BIN(utils::string::bin_to_dec(yytext), loc); }
{RGX_INT_HEX} { return s4::parser::make_INT_HEX(utils::string::hex_to_dec(yytext), loc); }
{RGX_INT_DEC} { return s4::parser::make_INT_DEC(std::string(yytext), loc); }
<<EOF>> { return s4::parser::make_S4EOF(loc); }
<*>{RGX_DEFAULT} { throw s4::parser::syntax_error(loc, "bad token: \'" + std::string(yytext) + "\'"); }
%%

663
gen/s4/parser.ypp Normal file
View File

@ -0,0 +1,663 @@
/* Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
*/
%require "3.7"
%skeleton "lalr1.cc"
%language "c++"
%output "parser.cpp"
%defines "parser.hpp"
%define api.prefix {S4}
%define api.namespace {xsk::gsc::s4}
%define api.location.type {xsk::gsc::location}
%define api.value.type variant
%define api.token.constructor
%define api.token.raw
%define parse.assert
%define parse.trace
%define parse.error detailed
%define parse.lac full
%locations
%lex-param { yyscan_t yyscanner }
%lex-param { xsk::gsc::location& loc }
%parse-param { yyscan_t yyscanner }
%parse-param { xsk::gsc::location& loc }
%parse-param { xsk::gsc::program_ptr& ast }
%code requires
{
#include "s4.hpp"
typedef void *yyscan_t;
#define YY_DECL xsk::gsc::s4::parser::symbol_type S4lex(yyscan_t yyscanner, xsk::gsc::location& loc)
}
%code top
{
#include "stdafx.hpp"
#include "parser.hpp"
#include "lexer.hpp"
using namespace xsk::gsc;
xsk::gsc::s4::parser::symbol_type S4lex(yyscan_t yyscanner, xsk::gsc::location& loc);
}
%token INCLUDE "#include"
%token USINGTREE "#using_animtree"
%token ANIMTREE "#animtree"
%token ENDON "endon"
%token NOTIFY "notify"
%token WAIT "wait"
%token WAITTILL "waittill"
%token WAITTILLMATCH "waittillmatch"
%token WAITTILLFRAMEEND "waittillframeend"
%token WAITFRAME "waitframe"
%token IF "if"
%token ELSE "else"
%token WHILE "while"
%token FOR "for"
%token FOREACH "foreach"
%token IN "in"
%token SWITCH "switch"
%token CASE "case"
%token DEFAULT "default"
%token BREAK "break"
%token CONTINUE "continue"
%token RETURN "return"
%token THREAD "thread"
%token CHILDTHREAD "childthread"
%token THISTHREAD "thisthread"
%token CALL "call"
%token TRUE "true"
%token FALSE "false"
%token UNDEFINED "undefined"
%token SIZE "size"
%token GAME "game"
%token SELF "self"
%token ANIM "anim"
%token LEVEL "level"
%token LPAREN "("
%token RPAREN ")"
%token LBRACE "{"
%token RBRACE "}"
%token LBRACKET "["
%token RBRACKET "]"
%token COMMA ","
%token DOT "."
%token DOUBLECOLON "::"
%token COLON ":"
%token SEMICOLON ";"
%token QMARK "?"
%token INCREMENT "++"
%token DECREMENT "--"
%token LSHIFT "<<"
%token RSHIFT ">>"
%token OR "||"
%token AND "&&"
%token EQUALITY "=="
%token INEQUALITY "!="
%token LESS_EQUAL "<="
%token GREATER_EQUAL ">="
%token LESS "<"
%token GREATER ">"
%token NOT "!"
%token COMPLEMENT "~"
%token ASSIGN "="
%token ASSIGN_ADD "+="
%token ASSIGN_SUB "-="
%token ASSIGN_MULT "*="
%token ASSIGN_DIV "/="
%token ASSIGN_MOD "%="
%token ASSIGN_BITWISE_OR "|="
%token ASSIGN_BITWISE_AND "&="
%token ASSIGN_BITWISE_EXOR "^="
%token ASSIGN_RSHIFT ">>="
%token ASSIGN_LSHIFT "<<="
%token BITWISE_OR "|"
%token BITWISE_AND "&"
%token BITWISE_EXOR "^"
%token ADD "+"
%token SUB "-"
%token MULT "*"
%token DIV "/"
%token MOD "%"
%token <std::string> FILE "file path"
%token <std::string> NAME "identifier"
%token <std::string> STRING "string literal"
%token <std::string> ISTRING "localized string"
%token <std::string> COLOR "color"
%token <std::string> FLOAT "float"
%token <std::string> INT_DEC "int"
%token <std::string> INT_OCT "octal int"
%token <std::string> INT_BIN "binary int"
%token <std::string> INT_HEX "hexadecimal int"
%type <program_ptr> program
%type <include_ptr> include
%type <define_ptr> define
%type <usingtree_ptr> usingtree
%type <constant_ptr> constant
%type <thread_ptr> thread
%type <parameters_ptr> parameters
%type <stmt_ptr> stmt
%type <stmt_list_ptr> stmt_block
%type <stmt_list_ptr> stmt_list
%type <stmt_call_ptr> stmt_call
%type <stmt_assign_ptr> stmt_assign
%type <stmt_endon_ptr> stmt_endon
%type <stmt_notify_ptr> stmt_notify
%type <stmt_wait_ptr> stmt_wait
%type <stmt_waittill_ptr> stmt_waittill
%type <stmt_waittillmatch_ptr> stmt_waittillmatch
%type <stmt_waittillframeend_ptr> stmt_waittillframeend
%type <stmt_waitframe_ptr> stmt_waitframe
%type <stmt_if_ptr> stmt_if
%type <stmt_ifelse_ptr> stmt_ifelse
%type <stmt_while_ptr> stmt_while
%type <stmt_for_ptr> stmt_for
%type <stmt_foreach_ptr> stmt_foreach
%type <stmt_switch_ptr> stmt_switch
%type <stmt_case_ptr> stmt_case
%type <stmt_default_ptr> stmt_default
%type <stmt_break_ptr> stmt_break
%type <stmt_continue_ptr> stmt_continue
%type <stmt_return_ptr> stmt_return
%type <stmt_ptr> for_stmt
%type <expr_ptr> for_expr
%type <expr_assign_ptr> expr_assign
%type <expr_ptr> expr
%type <expr_ptr> expr_compare
%type <expr_ptr> expr_ternary
%type <expr_ptr> expr_binary
%type <expr_ptr> expr_primitive
%type <expr_call_ptr> expr_call
%type <expr_call_ptr> expr_call_thread
%type <expr_call_ptr> expr_call_childthread
%type <expr_call_type_ptr> expr_call_function
%type <expr_call_type_ptr> expr_call_pointer
%type <expr_arguments_ptr> expr_arguments
%type <expr_arguments_ptr> expr_arguments_filled
%type <expr_arguments_ptr> expr_arguments_empty
%type <node_ptr> expr_function
%type <node_ptr> expr_add_array
%type <node_ptr> expr_array
%type <node_ptr> expr_field
%type <node_ptr> expr_size
%type <node_ptr> object
%type <thisthread_ptr> thisthread
%type <empty_array_ptr> empty_array
%type <undefined_ptr> undefined
%type <game_ptr> game
%type <self_ptr> self
%type <anim_ptr> anim
%type <level_ptr> level
%type <animation_ptr> animation
%type <animtree_ptr> animtree
%type <name_ptr> name
%type <file_ptr> file
%type <istring_ptr> istring
%type <string_ptr> string
%type <color_ptr> color
%type <vector_ptr> vector
%type <float_ptr> float
%type <integer_ptr> integer
%type <false_ptr> false
%type <true_ptr> true
%nonassoc ADD_ARRAY
%nonassoc RBRACKET
%nonassoc THEN
%nonassoc ELSE
%nonassoc INCREMENT DECREMENT
%precedence TERN
%right QMARK
%left OR
%left AND
%left BITWISE_OR
%left BITWISE_AND
%left BITWISE_EXOR
%left EQUALITY INEQUALITY
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%left LSHIFT RSHIFT
%left ADD SUB
%left MULT DIV MOD
%right NOT COMPLEMENT
%precedence NEG
%precedence ANIMREF
%precedence PREINC PREDEC
%precedence POSTINC POSTDEC
%start root
%%
root
: program { ast = std::move($1); }
| { ast = std::make_unique<node_program>(@$); }
;
program
: program include
{ $$ = std::move($1); $$->includes.push_back(std::move($2)); }
| program define
{ $$ = std::move($1); $$->definitions.push_back(std::move($2)); }
| include
{ $$ = std::make_unique<node_program>(@$); $$->includes.push_back(std::move($1)); }
| define
{ $$ = std::make_unique<node_program>(@$); $$->definitions.push_back(std::move($1)); }
;
include
: INCLUDE file SEMICOLON
{ $$ = std::make_unique<node_include>(@$, std::move($2)); }
;
define
: usingtree { $$.as_usingtree = std::move($1); }
| constant { $$.as_constant = std::move($1); }
| thread { $$.as_thread = std::move($1); }
;
usingtree
: USINGTREE LPAREN string RPAREN SEMICOLON
{ $$ = std::make_unique<node_usingtree>(@$, std::move($3)); }
;
constant
: name ASSIGN expr SEMICOLON
{ $$ = std::make_unique<node_constant>(@$, std::move($1), std::move($3)); }
;
thread
: name LPAREN parameters RPAREN stmt_block
{ $$ = std::make_unique<node_thread>(@$, std::move($1), std::move($3), std::move($5)); }
;
parameters
: parameters COMMA name
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| name
{ $$ = std::make_unique<node_parameters>(@$); $$->list.push_back(std::move($1)); }
|
{ $$ = std::make_unique<node_parameters>(@$); }
;
stmt
: stmt_block { $$.as_list = std::move($1); }
| stmt_call { $$.as_call = std::move($1); }
| stmt_assign { $$.as_assign = std::move($1); }
| stmt_endon { $$.as_endon = std::move($1); }
| stmt_notify { $$.as_notify = std::move($1); }
| stmt_wait { $$.as_wait = std::move($1); }
| stmt_waittill { $$.as_waittill = std::move($1); }
| stmt_waittillmatch { $$.as_waittillmatch = std::move($1); }
| stmt_waittillframeend { $$.as_waittillframeend = std::move($1); }
| stmt_waitframe { $$.as_waitframe = std::move($1); }
| stmt_if { $$.as_if = std::move($1); }
| stmt_ifelse { $$.as_ifelse = std::move($1); }
| stmt_while { $$.as_while = std::move($1); }
| stmt_for { $$.as_for = std::move($1); }
| stmt_foreach { $$.as_foreach = std::move($1); }
| stmt_switch { $$.as_switch = std::move($1); }
| stmt_case { $$.as_case = std::move($1); }
| stmt_default { $$.as_default = std::move($1); }
| stmt_break { $$.as_break = std::move($1); }
| stmt_continue { $$.as_continue = std::move($1); }
| stmt_return { $$.as_return = std::move($1); }
;
stmt_block
: LBRACE stmt_list RBRACE { $$ = std::move($2); }
| LBRACE RBRACE { $$ = std::make_unique<node_stmt_list>(@$); }
;
stmt_list
: stmt_list stmt
{ $$ = std::move($1); $$->stmts.push_back(std::move($2)); }
| stmt
{ $$ = std::make_unique<node_stmt_list>(@$); $$->stmts.push_back(std::move($1)); }
;
stmt_call
: expr_call SEMICOLON
{ $$ = std::make_unique<node_stmt_call>(@$, std::move($1)); }
| expr_call_thread SEMICOLON
{ $$ = std::make_unique<node_stmt_call>(@$, std::move($1)); }
;
stmt_assign
: expr_assign SEMICOLON
{ $$ = std::make_unique<node_stmt_assign>(@$, std::move($1)); }
;
stmt_endon
: object ENDON LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_endon>(@$, std::move($1), std::move($4)); }
;
stmt_notify
: object NOTIFY LPAREN expr COMMA expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_notify>(@$, std::move($1), std::move($4), std::move($6)); }
| object NOTIFY LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_notify>(@$, std::move($1), std::move($4), std::make_unique<node_expr_arguments>(@$)); }
;
stmt_wait
: WAIT expr SEMICOLON
{ $$ = std::make_unique<node_stmt_wait>(@$, std::move($2)); }
;
stmt_waittill
: object WAITTILL LPAREN expr COMMA expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_waittill>(@$, std::move($1), std::move($4), std::move($6)); }
| object WAITTILL LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_waittill>(@$, std::move($1), std::move($4), std::make_unique<node_expr_arguments>(@$)); }
;
stmt_waittillmatch
: object WAITTILLMATCH LPAREN expr COMMA expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_waittillmatch>(@$, std::move($1), std::move($4), std::move($6)); }
| object WAITTILLMATCH LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_waittillmatch>(@$, std::move($1), std::move($4), std::make_unique<node_expr_arguments>(@$)); }
;
stmt_waittillframeend
: WAITTILLFRAMEEND SEMICOLON
{ $$ = std::make_unique<node_stmt_waittillframeend>(@$); }
;
stmt_waitframe
: WAITFRAME SEMICOLON
{ $$ = std::make_unique<node_stmt_waitframe>(@$); }
| WAITFRAME LPAREN RPAREN SEMICOLON
{ $$ = std::make_unique<node_stmt_waitframe>(@$); }
;
stmt_if
: IF LPAREN expr RPAREN stmt %prec THEN
{ $$ = std::make_unique<node_stmt_if>(@$, std::move($3), std::move($5)); }
;
stmt_ifelse
: IF LPAREN expr RPAREN stmt ELSE stmt
{ $$ = std::make_unique<node_stmt_ifelse>(@$, std::move($3), std::move($5), std::move($7)); }
;
stmt_while
: WHILE LPAREN expr RPAREN stmt
{ $$ = std::make_unique<node_stmt_while>(@$, std::move($3), std::move($5)); }
;
stmt_for
: FOR LPAREN for_stmt SEMICOLON for_expr SEMICOLON for_stmt RPAREN stmt
{ $$ = std::make_unique<node_stmt_for>(@$, std::move($3), std::move($5), std::move($7), std::move($9)); }
;
stmt_foreach
: FOREACH LPAREN name IN expr RPAREN stmt
{ $$ = std::make_unique<node_stmt_foreach>(@$, expr_ptr(std::move($3)), std::move($5), std::move($7)); }
| FOREACH LPAREN name COMMA name IN expr RPAREN stmt
{ $$ = std::make_unique<node_stmt_foreach>(@$, expr_ptr(std::move($3)), expr_ptr(std::move($5)), std::move($7), std::move($9)); }
;
stmt_switch
: SWITCH LPAREN expr RPAREN stmt_block
{ $$ = std::make_unique<node_stmt_switch>(@$, std::move($3), std::move($5)); }
;
stmt_case
: CASE integer COLON
{ $$ = std::make_unique<node_stmt_case>(@$, expr_ptr(std::move($2)), std::make_unique<gsc::node_stmt_list>(@$)); }
| CASE string COLON
{ $$ = std::make_unique<node_stmt_case>(@$, expr_ptr(std::move($2)), std::make_unique<gsc::node_stmt_list>(@$)); }
;
stmt_default
: DEFAULT COLON
{ $$ = std::make_unique<node_stmt_default>(@$, std::make_unique<gsc::node_stmt_list>(@$)); }
;
stmt_break
: BREAK SEMICOLON
{ $$ = std::make_unique<node_stmt_break>(@$); }
;
stmt_continue
: CONTINUE SEMICOLON
{ $$ = std::make_unique<node_stmt_continue>(@$); }
;
stmt_return
: RETURN expr SEMICOLON
{ $$ = std::make_unique<node_stmt_return>(@$, std::move($2)); }
| RETURN SEMICOLON
{ $$ = std::make_unique<node_stmt_return>(@$, std::make_unique<node>(@$)); }
;
for_stmt
: expr_assign { $$.as_list = std::make_unique<node_stmt_list>(@$); $$.as_list->stmts.push_back(stmt_ptr(std::make_unique<node_stmt_assign>(@$, std::move($1)))); }
| { $$.as_node = std::make_unique<node>(@$); }
;
for_expr
: expr { $$ = std::move($1); }
| { $$.as_node = std::make_unique<node>(@$); }
;
expr
: expr_compare { $$ = std::move($1); }
| expr_ternary { $$ = std::move($1); }
| expr_binary { $$ = std::move($1); }
| expr_primitive { $$ = std::move($1); }
;
expr_assign
: INCREMENT object %prec PREINC { $$ = std::make_unique<node_expr_increment>(@$, std::move($2)); }
| DECREMENT object %prec PREDEC { $$ = std::make_unique<node_expr_decrement>(@$, std::move($2)); }
| object INCREMENT %prec POSTINC { $$ = std::make_unique<node_expr_increment>(@$, std::move($1)); }
| object DECREMENT %prec POSTDEC { $$ = std::make_unique<node_expr_decrement>(@$, std::move($1)); }
| object ASSIGN expr { $$ = std::make_unique<node_expr_assign_equal>(@$, std::move($1), std::move($3)); }
| object ASSIGN_BITWISE_OR expr { $$ = std::make_unique<node_expr_assign_bitwise_or>(@$, std::move($1), std::move($3)); }
| object ASSIGN_BITWISE_AND expr { $$ = std::make_unique<node_expr_assign_bitwise_and>(@$, std::move($1), std::move($3)); }
| object ASSIGN_BITWISE_EXOR expr { $$ = std::make_unique<node_expr_assign_bitwise_exor>(@$, std::move($1), std::move($3)); }
| object ASSIGN_LSHIFT expr { $$ = std::make_unique<node_expr_assign_shift_left>(@$, std::move($1),std::move( $3)); }
| object ASSIGN_RSHIFT expr { $$ = std::make_unique<node_expr_assign_shift_right>(@$, std::move($1), std::move($3)); }
| object ASSIGN_ADD expr { $$ = std::make_unique<node_expr_assign_add>(@$, std::move($1), std::move($3)); }
| object ASSIGN_SUB expr { $$ = std::make_unique<node_expr_assign_sub>(@$, std::move($1), std::move($3)); }
| object ASSIGN_MULT expr { $$ = std::make_unique<node_expr_assign_mult>(@$, std::move($1), std::move($3)); }
| object ASSIGN_DIV expr { $$ = std::make_unique<node_expr_assign_div>(@$, std::move($1), std::move($3)); }
| object ASSIGN_MOD expr { $$ = std::make_unique<node_expr_assign_mod>(@$, std::move($1), std::move($3)); }
;
expr_compare
: expr OR expr { $$.as_node = std::make_unique<node_expr_or>(@$, std::move($1), std::move($3)); }
| expr AND expr { $$.as_node = std::make_unique<node_expr_and>(@$, std::move($1), std::move($3)); }
| expr EQUALITY expr { $$.as_node = std::make_unique<node_expr_equality>(@$, std::move($1), std::move($3)); }
| expr INEQUALITY expr { $$.as_node = std::make_unique<node_expr_inequality>(@$, std::move($1), std::move($3)); }
| expr LESS_EQUAL expr { $$.as_node = std::make_unique<node_expr_less_equal>(@$, std::move($1), std::move($3)); }
| expr GREATER_EQUAL expr { $$.as_node = std::make_unique<node_expr_greater_equal>(@$, std::move($1), std::move($3)); }
| expr LESS expr { $$.as_node = std::make_unique<node_expr_less>(@$, std::move($1), std::move($3)); }
| expr GREATER expr { $$.as_node = std::make_unique<node_expr_greater>(@$, std::move($1), std::move($3)); }
;
expr_ternary
: expr QMARK expr COLON expr %prec TERN { $$.as_node = std::make_unique<node_expr_ternary>(@$, std::move($1), std::move($3), std::move($5)); }
;
expr_binary
: expr BITWISE_OR expr { $$.as_node = std::make_unique<node_expr_bitwise_or>(@$, std::move($1), std::move($3)); }
| expr BITWISE_AND expr { $$.as_node = std::make_unique<node_expr_bitwise_and>(@$, std::move($1), std::move($3)); }
| expr BITWISE_EXOR expr { $$.as_node = std::make_unique<node_expr_bitwise_exor>(@$, std::move($1), std::move($3)); }
| expr LSHIFT expr { $$.as_node = std::make_unique<node_expr_shift_left>(@$, std::move($1), std::move($3)); }
| expr RSHIFT expr { $$.as_node = std::make_unique<node_expr_shift_right>(@$, std::move($1), std::move($3)); }
| expr ADD expr { $$.as_node = std::make_unique<node_expr_add>(@$, std::move($1), std::move($3)); }
| expr SUB expr { $$.as_node = std::make_unique<node_expr_sub>(@$, std::move($1), std::move($3)); }
| expr MULT expr { $$.as_node = std::make_unique<node_expr_mult>(@$, std::move($1), std::move($3)); }
| expr DIV expr { $$.as_node = std::make_unique<node_expr_div>(@$, std::move($1), std::move($3)); }
| expr MOD expr { $$.as_node = std::make_unique<node_expr_mod>(@$, std::move($1), std::move($3)); }
;
expr_primitive
: LPAREN expr RPAREN { $$ = std::move($2); }
| COMPLEMENT expr { $$.as_node = std::make_unique<node_expr_complement>(@$, std::move($2)); }
| NOT expr { $$.as_node = std::make_unique<node_expr_not>(@$, std::move($2)); }
| expr_call { $$.as_node = std::move($1); }
| expr_call_thread { $$.as_node = std::move($1); }
| expr_call_childthread { $$.as_node = std::move($1); }
| expr_function { $$.as_node = std::move($1); }
| expr_add_array { $$.as_node = std::move($1); }
| expr_array { $$.as_node = std::move($1); }
| expr_field { $$.as_node = std::move($1); }
| expr_size { $$.as_node = std::move($1); }
| thisthread { $$.as_node = std::move($1); }
| empty_array { $$.as_node = std::move($1); }
| undefined { $$.as_node = std::move($1); }
| game { $$.as_node = std::move($1); }
| self { $$.as_node = std::move($1); }
| anim { $$.as_node = std::move($1); }
| level { $$.as_node = std::move($1); }
| animation { $$.as_node = std::move($1); }
| animtree { $$.as_node = std::move($1); }
| name { $$.as_node = std::move($1); }
| istring { $$.as_node = std::move($1); }
| string { $$.as_node = std::move($1); }
| color { $$.as_node = std::move($1); }
| vector { $$.as_node = std::move($1); }
| float { $$.as_node = std::move($1); }
| integer { $$.as_node = std::move($1); }
| false { $$.as_node = std::move($1); }
| true { $$.as_node = std::move($1); }
;
expr_call
: expr_call_function { $$ = std::make_unique<node_expr_call>(@$, false, false, std::make_unique<node>(@$), std::move($1)); }
| expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, false, false, std::make_unique<node>(@$), std::move($1)); }
| object expr_call_function { $$ = std::make_unique<node_expr_call>(@$, false, false, std::move($1), std::move($2)); }
| object expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, false, false, std::move($1), std::move($2)); }
;
expr_call_thread
: THREAD expr_call_function { $$ = std::make_unique<node_expr_call>(@$, true, false, std::make_unique<node>(@$), std::move($2)); }
| THREAD expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, true, false, std::make_unique<node>(@$), std::move($2)); }
| object THREAD expr_call_function { $$ = std::make_unique<node_expr_call>(@$, true, false, std::move($1), std::move($3)); }
| object THREAD expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, true, false, std::move($1), std::move($3)); }
;
expr_call_childthread
: CHILDTHREAD expr_call_function { $$ = std::make_unique<node_expr_call>(@$, false, true, std::make_unique<node>(@$), std::move($2)); }
| CHILDTHREAD expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, false, true, std::make_unique<node>(@$), std::move($2)); }
| object CHILDTHREAD expr_call_function { $$ = std::make_unique<node_expr_call>(@$, false, true, std::move($1), std::move($3)); }
| object CHILDTHREAD expr_call_pointer { $$ = std::make_unique<node_expr_call>(@$, false, true, std::move($1), std::move($3)); }
;
expr_call_function
: name LPAREN expr_arguments RPAREN
{$$.as_func = std::make_unique<node_expr_call_function>(@$, std::make_unique<node_file>(), std::move($1), std::move($3)); }
| file DOUBLECOLON name LPAREN expr_arguments RPAREN
{ $$.as_func = std::make_unique<node_expr_call_function>(@$, std::move($1), std::move($3), std::move($5)); }
;
expr_call_pointer
: LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<node_expr_call_pointer>(@$, false, std::move($3), std::move($7)); }
| CALL LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<node_expr_call_pointer>(@$, true, std::move($4), std::move($8)); }
;
expr_arguments
: expr_arguments_filled { $$ = std::move($1); }
| expr_arguments_empty { $$ = std::move($1); }
;
expr_arguments_filled
: expr_arguments COMMA expr
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr %prec ADD_ARRAY
{ $$ = std::make_unique<node_expr_arguments>(@$); $$->list.push_back(std::move($1)); }
;
expr_arguments_empty
:
{ $$ = std::make_unique<node_expr_arguments>(@$); }
;
expr_function
: DOUBLECOLON name
{ $$ = std::make_unique<node_expr_function>(@$, std::make_unique<node_file>(@$), std::move($2)); }
| file DOUBLECOLON name
{ $$ = std::make_unique<node_expr_function>(@$, std::move($1), std::move($3)); }
;
expr_add_array
: LBRACKET expr_arguments_filled RBRACKET
{ $$ = std::make_unique<node_expr_add_array>(@$, std::move($2)); }
;
expr_array
: object LBRACKET expr RBRACKET
{ $$ = std::make_unique<node_expr_array>(@$, std::move($1), std::move($3)); }
;
expr_field
: object DOT name
{ $$ = std::make_unique<node_expr_field>(@$, std::move($1), std::move($3)); }
;
expr_size
: object DOT SIZE
{ $$ = std::make_unique<node_expr_size>(@$, std::move($1)); }
;
object
: expr_call { $$ = std::move($1); }
| expr_array { $$ = std::move($1); }
| expr_field { $$ = std::move($1); }
| game { $$ = std::move($1); }
| self { $$ = std::move($1); }
| anim { $$ = std::move($1); }
| level { $$ = std::move($1); }
| name { $$ = std::move($1); }
;
float
: SUB FLOAT %prec NEG { $$ = std::make_unique<node_float>(@$, "-" + $2); };
| FLOAT { $$ = std::make_unique<node_float>(@$, $1); };
;
integer
: SUB INT_DEC %prec NEG { $$ = std::make_unique<node_integer>(@$, "-" + $2); };
| INT_DEC { $$ = std::make_unique<node_integer>(@$, $1); };
| INT_OCT { $$ = std::make_unique<node_integer>(@$, $1); };
| INT_BIN { $$ = std::make_unique<node_integer>(@$, $1); };
| INT_HEX { $$ = std::make_unique<node_integer>(@$, $1); };
;
thisthread : THISTHREAD { $$ = std::make_unique<node_thisthread>(@$); };
empty_array : LBRACKET RBRACKET { $$ = std::make_unique<node_empty_array>(@$); };
undefined : UNDEFINED { $$ = std::make_unique<node_undefined>(@$); };
game : GAME { $$ = std::make_unique<node_game>(@$); };
self : SELF { $$ = std::make_unique<node_self>(@$); };
anim : ANIM { $$ = std::make_unique<node_anim>(@$); };
level : LEVEL { $$ = std::make_unique<node_level>(@$); };
animation : MOD NAME %prec ANIMREF { $$ = std::make_unique<node_animation>(@$, $2); };
animtree : ANIMTREE { $$ = std::make_unique<node_animtree>(@$); };
name : NAME { $$ = std::make_unique<node_name>(@$, $1); };
file : FILE { $$ = std::make_unique<node_file>(@$, $1); };
istring : ISTRING { $$ = std::make_unique<node_istring>(@$, $1); };
string : STRING { $$ = std::make_unique<node_string>(@$, $1); };
color : COLOR { $$ = std::make_unique<node_color>(@$, $1); };
vector : LPAREN expr COMMA expr COMMA expr RPAREN { $$ = std::make_unique<node_vector>(@$, std::move($2), std::move($4), std::move($6)); };
false : FALSE { $$ = std::make_unique<node_false>(@$); };
true : TRUE { $$ = std::make_unique<node_true>(@$); };
%%
void xsk::gsc::s4::parser::error(const xsk::gsc::location& loc, const std::string& msg)
{
throw xsk::gsc::comp_error(loc, msg);
}

View File

@ -55,6 +55,7 @@ project "xsk-gsc-tool"
dependson "xsk-gsc-iw8"
dependson "xsk-gsc-s1"
dependson "xsk-gsc-s2"
dependson "xsk-gsc-s4"
dependson "xsk-gsc-h1"
dependson "xsk-gsc-h2"
@ -75,6 +76,7 @@ project "xsk-gsc-tool"
"xsk-gsc-iw8",
"xsk-gsc-s1",
"xsk-gsc-s2",
"xsk-gsc-s4",
"xsk-gsc-h1",
"xsk-gsc-h2"
}
@ -214,6 +216,24 @@ project "xsk-gsc-s2"
"./src"
}
project "xsk-gsc-s4"
kind "StaticLib"
language "C++"
pchheader "stdafx.hpp"
pchsource "src/s4/stdafx.cpp"
files {
"./src/s4/**.h",
"./src/s4/**.hpp",
"./src/s4/**.cpp"
}
includedirs {
"./src/s4",
"./src"
}
project "xsk-gsc-h1"
kind "StaticLib"
language "C++"

6
src/s4/stdafx.cpp Normal file
View File

@ -0,0 +1,6 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"

38
src/s4/stdafx.hpp Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
// Warnings
#ifdef _WIN32
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4005)
#pragma warning(disable:4065)
#define _CRT_SECURE_NO_WARNINGS
#endif
// C/C++
#include <regex>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <filesystem>
#include <functional>
#include <stdexcept>
#include <map>
#include <stack>
#include <array>
#include <iostream>
#include <sstream>
#include <fstream>
#include <unordered_map>
#include <stdio.h>
// Ext
using namespace std::literals;
#include "xsk/s4.hpp"

682
src/s4/xsk/assembler.cpp Normal file
View File

@ -0,0 +1,682 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "s4.hpp"
namespace xsk::gsc::s4
{
auto assembler::output_script() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> script;
if(script_ == nullptr) return script;
script.resize(script_->pos());
memcpy(script.data(), script_->buffer().data(), script.size());
return script;
}
auto assembler::output_stack() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> stack;
if(stack_ == nullptr) return stack;
stack.resize(stack_->pos());
memcpy(stack.data(), stack_->buffer().data(), stack.size());
return stack;
}
void assembler::assemble(const std::string& file, std::vector<std::uint8_t>& data)
{
std::vector<std::string> assembly = utils::string::clean_buffer_lines(data);
std::vector<gsc::function_ptr> functions;
gsc::function_ptr func = nullptr;
std::uint32_t index = 1;
std::uint16_t switchnum = 0;
for (auto& line : assembly)
{
if (line == "" || line.substr(0, 2) == "//")
{
continue;
}
else if (line.substr(0, 4) == "sub_")
{
func = std::make_unique<gsc::function>();
func->index = index;
func->name = line.substr(4);
}
else if (line.substr(0, 4) == "end_")
{
if (func != nullptr)
{
func->size = index - func->index;
functions.push_back(std::move(func));
}
}
else if (line.substr(0, 4) == "loc_")
{
func->labels[index] = line;
}
else
{
auto data = utils::string::parse_code(line);
if (switchnum)
{
if (data[0] == "case" || data[0] == "default")
{
for (auto& entry : data)
{
func->instructions.back()->data.push_back(entry);
}
switchnum--;
continue;
}
throw gsc::asm_error("invalid instruction inside endswitch \""s + line + "\"!");
}
else
{
auto inst = std::make_unique<gsc::instruction>();
inst->index = index;
inst->opcode = static_cast<std::uint8_t>(resolver::opcode_id(data[0]));
inst->size = opcode_size(inst->opcode);
data.erase(data.begin());
inst->data = std::move(data);
if (opcode(inst->opcode) == opcode::OP_endswitch)
{
switchnum = static_cast<std::uint16_t>(std::stoi(inst->data[0]));
inst->size += 7 * switchnum;
}
index += inst->size;
func->instructions.push_back(std::move(inst));
}
}
}
this->assemble(file, functions);
}
void assembler::assemble(const std::string& file, std::vector<gsc::function_ptr>& functions)
{
script_ = std::make_unique<utils::byte_buffer>(0x100000);
stack_ = std::make_unique<utils::byte_buffer>(0x100000);
filename_ = file;
functions_ = std::move(functions);
script_->write<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
for (const auto& func : functions_)
{
this->assemble_function(func);
}
}
void assembler::assemble_function(const gsc::function_ptr& func)
{
labels_ = func->labels;
stack_->write<std::uint32_t>(func->size);
func->id = func->name.substr(0, 3) == "_ID" ? std::stoi(func->name.substr(3)) : resolver::token_id(func->name);
stack_->write<std::uint32_t>(func->id);
if (func->id == 0)
{
stack_->write_c_string(func->name);
}
for (const auto& inst : func->instructions)
{
this->assemble_instruction(inst);
}
}
void assembler::assemble_instruction(const gsc::instruction_ptr& inst)
{
switch (opcode(inst->opcode))
{
case opcode::OP_CastFieldObject:
case opcode::OP_plus:
case opcode::OP_GetGameRef:
case opcode::OP_GetThisthread:
case opcode::OP_greater:
case opcode::OP_shift_right:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_equality:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_notify:
case opcode::OP_PreScriptCall:
case opcode::OP_GetUndefined:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_size:
case opcode::OP_AddArray:
case opcode::OP_endon:
case opcode::OP_shift_left:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_Return:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetSelfObject:
case opcode::OP_GetGame:
case opcode::OP_EvalArray:
case opcode::OP_GetSelf:
case opcode::OP_End:
case opcode::OP_less_equal:
case opcode::OP_EvalLocalVariableCached0:
case opcode::OP_EvalLocalVariableCached1:
case opcode::OP_EvalLocalVariableCached2:
case opcode::OP_EvalLocalVariableCached3:
case opcode::OP_EvalLocalVariableCached4:
case opcode::OP_EvalLocalVariableCached5:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_checkclearparams:
case opcode::OP_waittillmatch2:
case opcode::OP_minus:
case opcode::OP_greater_equal:
case opcode::OP_vector:
case opcode::OP_ClearArray:
case opcode::OP_DecTop:
case opcode::OP_CastBool:
case opcode::OP_EvalArrayRef:
case opcode::OP_GetZero:
case opcode::OP_wait:
case opcode::OP_waittill:
case opcode::OP_GetAnimObject:
case opcode::OP_mod:
case opcode::OP_clearparams:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_EmptyArray:
case opcode::OP_ClearVariableField:
case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_BoolComplement:
case opcode::OP_less:
case opcode::OP_BoolNot:
case opcode::OP_waittillFrameEnd:
case opcode::OP_waitframe:
case opcode::OP_GetLevelObject:
case opcode::OP_inc:
case opcode::OP_GetAnim:
case opcode::OP_SetVariableField:
case opcode::OP_divide:
case opcode::OP_multiply:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_bit_and:
case opcode::OP_voidCodepos:
case opcode::OP_inequality:
case opcode::OP_bit_ex_or:
/*
case opcode::OP_NOP:
case opcode::OP_abort:
case opcode::OP_object:
case opcode::OP_thread_object:
case opcode::OP_EvalLocalVariable:
case opcode::OP_EvalLocalVariableRef:
case opcode::OP_breakpoint:
case opcode::OP_assignmentBreakpoint:
case opcode::OP_manualAndAssignmentBreakpoint:
*/
case opcode::OP_BoolNotAfterAnd:
case opcode::OP_IsDefined:
case opcode::OP_IsTrue:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
break;
case opcode::OP_GetByte:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetNegByte:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::int8_t>(static_cast<std::int8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetUnsignedShort:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint16_t>(static_cast<std::uint16_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetNegUnsignedShort:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::int16_t>(static_cast<std::int16_t>(std::stoi(inst->data[0])));
case opcode::OP_GetInteger:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::int32_t>(std::stoi(inst->data[0]));
break;
case opcode::OP_GetFloat:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<float>(std::stof(inst->data[0]));
break;
case opcode::OP_GetVector:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<float>(std::stof(inst->data[0]));
script_->write<float>(std::stof(inst->data[1]));
script_->write<float>(std::stof(inst->data[2]));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint32_t>(0);
stack_->write_c_string(utils::string::to_code(inst->data[0]));
break;
case opcode::OP_GetAnimation:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint32_t>(0);
script_->write<std::uint32_t>(0);
stack_->write_c_string(utils::string::unquote(inst->data[0]));
stack_->write_c_string(utils::string::unquote(inst->data[1]));
break;
case opcode::OP_GetAnimTree:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(0);
stack_->write_c_string(utils::string::unquote(inst->data[0]));
break;
case opcode::OP_waittillmatch:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint16_t>(0);
break;
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_CreateLocalVariable:
case opcode::OP_EvalLocalVariableObjectCached:
case opcode::OP_EvalLocalArrayCached:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_ClearFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariableRef:
this->assemble_field_variable(inst);
break;
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptChildThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
this->assemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
this->assemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
this->assemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
this->assemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
this->assemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
this->assemble_builtin_call(inst, true, true);
break;
case opcode::OP_GetBuiltinFunction:
case opcode::OP_CallBuiltin0:
case opcode::OP_CallBuiltin1:
case opcode::OP_CallBuiltin2:
case opcode::OP_CallBuiltin3:
case opcode::OP_CallBuiltin4:
case opcode::OP_CallBuiltin5:
this->assemble_builtin_call(inst, false, false);
break;
case opcode::OP_GetBuiltinMethod:
case opcode::OP_CallBuiltinMethod0:
case opcode::OP_CallBuiltinMethod1:
case opcode::OP_CallBuiltinMethod2:
case opcode::OP_CallBuiltinMethod3:
case opcode::OP_CallBuiltinMethod4:
case opcode::OP_CallBuiltinMethod5:
this->assemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
this->assemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
this->assemble_jump(inst, false, true);
break;
case opcode::OP_jump:
this->assemble_jump(inst, false, false);
break;
case opcode::OP_switch:
this->assemble_switch(inst);
break;
case opcode::OP_endswitch:
this->assemble_end_switch(inst);
break;
/*
case opcode::OP_prof_begin:
script_->write<std::uint32_t>(0); // TODO: skipped data
script_->write<std::uint8_t>(0);
break;
case opcode::OP_prof_end:
script_->write<std::uint8_t>(0); // TODO: skipped data
break;
case opcode::OP_EvalNewLocalArrayRefCached0_Precompiled:
case opcode::OP_SetNewLocalVariableFieldCached0_Precompiled:
case opcode::OP_CreateLocalVariable_Precompiled:
case opcode::OP_SafeCreateVariableFieldCached_Precompiled:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
*/
case opcode::OP_FormalParams:
// case opcode::OP_FormalParams_Precompiled:
this->assemble_formal_params(inst);
break;
/*
case opcode::OP_NativeGetLocalFunction:
case opcode::OP_NativeLocalFunctionCall:
case opcode::OP_NativeLocalFunctionCall2:
case opcode::OP_NativeLocalMethodCall:
this->assemble_local_call(inst, false);
break;
case opcode::OP_NativeGetFarFunction:
case opcode::OP_NativeFarFunctionCall:
case opcode::OP_NativeFarFunctionCall2:
case opcode::OP_NativeFarMethodCall:
this->assemble_far_call(inst, false);
break;
case opcode::OP_NativeLocalFunctionThreadCall:
case opcode::OP_NativeLocalMethodThreadCall:
case opcode::OP_NativeLocalFunctionChildThreadCall:
case opcode::OP_NativeLocalMethodChildThreadCall:
this->assemble_local_call(inst, true);
break;
case opcode::OP_NativeFarFunctionThreadCall:
case opcode::OP_NativeFarMethodThreadCall:
case opcode::OP_NativeFarFunctionChildThreadCall:
case opcode::OP_NativeFarMethodChildThreadCall:
this->assemble_far_call(inst, true);
break;
*/
default:
throw gsc::asm_error(utils::string::va("Unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void assembler::assemble_builtin_call(const gsc::instruction_ptr& inst, bool method, bool arg_num)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::uint16_t id = 0;
if (arg_num)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
if (method)
id = inst->data[1].substr(0, 3) == "_ID" ? std::stoi(inst->data[1].substr(3)) : resolver::method_id(inst->data[1]);
else
id = inst->data[1].substr(0, 3) == "_ID" ? std::stoi(inst->data[1].substr(3)) : resolver::function_id(inst->data[1]);
}
else
{
if (method)
id = inst->data[0].substr(0, 3) == "_ID" ? std::stoi(inst->data[0].substr(3)) : resolver::method_id(inst->data[0]);
else
id = inst->data[0].substr(0, 3) == "_ID" ? std::stoi(inst->data[0].substr(3)) : resolver::function_id(inst->data[0]);
}
script_->write<std::uint16_t>(id);
}
void assembler::assemble_local_call(const gsc::instruction_ptr& inst, bool thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::int32_t addr = this->resolve_function(inst->data[0]);
std::int32_t offset = addr - inst->index - 1;
this->assemble_offset(offset);
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
}
void assembler::assemble_far_call(const gsc::instruction_ptr& inst, bool thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(0);
script_->write<std::uint16_t>(0);
std::uint32_t file_id = 0;
std::uint32_t func_id = 0;
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
file_id = inst->data[1].substr(0, 3) == "_ID" ? std::stoi(inst->data[1].substr(3)) : resolver::file_id(inst->data[1]);
func_id = inst->data[2].substr(0, 3) == "_ID" ? std::stoi(inst->data[2].substr(3)) : resolver::token_id(inst->data[2]);
}
else
{
file_id = inst->data[0].substr(0, 3) == "_ID" ? std::stoi(inst->data[0].substr(3)) : resolver::file_id(inst->data[0]);
func_id = inst->data[1].substr(0, 3) == "_ID" ? std::stoi(inst->data[1].substr(3)) : resolver::token_id(inst->data[1]);
}
stack_->write<std::uint32_t>(file_id);
if (file_id == 0) stack_->write_c_string(thread ? inst->data[1] : inst->data[0]);
stack_->write<std::uint32_t>(func_id);
if (func_id == 0) stack_->write_c_string(thread ? inst->data[2] : inst->data[1]);
}
void assembler::assemble_jump(const gsc::instruction_ptr& inst, bool expr, bool back)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::int32_t addr = this->resolve_label(inst->data[0]);
if (expr)
{
script_->write<std::int16_t>(addr - inst->index - 3);
}
else if (back)
{
script_->write<std::int16_t>((inst->index + 3) - addr);
}
else
{
script_->write<std::int32_t>(addr - inst->index - 5);
}
}
void assembler::assemble_field_variable(const gsc::instruction_ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::uint32_t field_id = 0;
if (inst->data[0].substr(0, 3) == "_ID")
{
field_id = std::stoi(inst->data[0].substr(3));
}
else
{
field_id = resolver::token_id(inst->data[0]);
if (field_id == 0)
{
field_id = 0xFFFF;
}
}
script_->write<std::uint32_t>(field_id);
if (field_id > 0x1C000)
{
stack_->write<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
}
}
void assembler::assemble_formal_params(const gsc::instruction_ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
auto size = std::stoi(inst->data[0]);
script_->write<std::uint8_t>(static_cast<std::uint8_t>(size));
for(auto i = 1; i <= size; i++)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[i])));
}
}
void assembler::assemble_switch(const gsc::instruction_ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::int32_t addr = this->resolve_label(inst->data[0]);
script_->write<std::int32_t>(addr - inst->index - 4);
}
void assembler::assemble_end_switch(const gsc::instruction_ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
std::uint16_t casenum = 0;
if (utils::string::is_number(inst->data[0]))
{
casenum = std::stoi(inst->data[0]);
}
else
{
throw gsc::asm_error("invalid endswitch number!");
}
script_->write<std::uint16_t>(casenum);
std::uint32_t internal_index = inst->index + 3;
for (std::uint16_t i = 0; i < casenum; i++)
{
if (inst->data[1 + (3 * i)] == "case")
{
if (utils::string::is_number(inst->data[1 + (3 * i) + 1]))
{
script_->write<uint32_t>((std::stoi(inst->data[1 + (3 * i) + 1]) & 0xFFFFFF) + 0x800000);
}
else
{
script_->write<uint32_t>(i + 1);
stack_->write_c_string(utils::string::unquote(inst->data[1 + (3 * i) + 1]));
}
internal_index += 4;
std::int32_t addr = this->resolve_label(inst->data[1 + (3 * i) + 2]);
this->assemble_offset(addr - internal_index);
internal_index += 3;
}
else if (inst->data[1 + (3 * i)] == "default")
{
script_->write<uint32_t>(0);
stack_->write_c_string("\x01");
internal_index += 4;
std::int32_t addr = this->resolve_label(inst->data[1 + (3 * i) + 1]);
this->assemble_offset(addr - internal_index);
internal_index += 3;
}
}
}
void assembler::assemble_offset(std::int32_t offset)
{
std::array<std::uint8_t, 4> bytes = {};
offset = (offset << 8) >> 8;
*reinterpret_cast<std::int32_t*>(bytes.data()) = offset;
script_->write<std::uint8_t>(bytes[0]);
script_->write<std::uint8_t>(bytes[1]);
script_->write<std::uint8_t>(bytes[2]);
}
auto assembler::resolve_function(const std::string& name) -> std::uint32_t
{
auto temp = name.substr(0, 4) == "sub_" ? name.substr(4) : name;
for (const auto& func : functions_)
{
if (func->name == temp)
{
return func->index;
}
}
throw gsc::asm_error("Couldn't resolve local function address of '" + temp + "'!");
}
auto assembler::resolve_label(const std::string& name) -> std::uint32_t
{
for (auto& func : labels_)
{
if (func.second == name)
{
return func.first;
}
}
throw gsc::asm_error("Couldn't resolve label address of '" + name + "'!");
}
} // namespace xsk::gsc::s4

41
src/s4/xsk/assembler.hpp Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::s4
{
class assembler : public gsc::assembler
{
std::string filename_;
utils::byte_buffer_ptr script_;
utils::byte_buffer_ptr stack_;
std::vector<gsc::function_ptr> functions_;
std::unordered_map<std::uint32_t, std::string> labels_;
public:
auto output_script() -> std::vector<std::uint8_t>;
auto output_stack() -> std::vector<std::uint8_t>;
void assemble(const std::string& file, std::vector<std::uint8_t>& data);
void assemble(const std::string& file, std::vector<gsc::function_ptr>& functions);
private:
void assemble_function(const gsc::function_ptr& func);
void assemble_instruction(const gsc::instruction_ptr& inst);
void assemble_builtin_call(const gsc::instruction_ptr& inst, bool method, bool arg_num);
void assemble_local_call(const gsc::instruction_ptr& inst, bool thread);
void assemble_far_call(const gsc::instruction_ptr& inst, bool thread);
void assemble_jump(const gsc::instruction_ptr& inst, bool expr, bool back);
void assemble_field_variable(const gsc::instruction_ptr& inst);
void assemble_formal_params(const gsc::instruction_ptr& inst);
void assemble_switch(const gsc::instruction_ptr& inst);
void assemble_end_switch(const gsc::instruction_ptr& inst);
void assemble_offset(std::int32_t offset);
auto resolve_function(const std::string& name) -> std::uint32_t;
auto resolve_label(const std::string& name) -> std::uint32_t;
};
} // namespace xsk::gsc::s4

2453
src/s4/xsk/compiler.cpp Normal file

File diff suppressed because it is too large Load Diff

152
src/s4/xsk/compiler.hpp Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::s4
{
enum class opcode : std::uint8_t;
class compiler : public gsc::compiler
{
std::string filename_;
std::vector<gsc::function_ptr> assembly_;
gsc::function_ptr function_;
std::uint32_t index_;
std::uint32_t label_idx_;
std::uint8_t stack_idx_;
std::vector<std::string> local_stack_;
std::vector<std::string> local_functions_;
std::vector<include_t> includes_;
std::vector<animtree_t> animtrees_;
std::unordered_map<std::string, gsc::expr_ptr> constants_;
std::function<std::vector<std::uint8_t>(const std::string&)> callback_readf_;
std::vector<gsc::context*> break_ctxs_;
std::vector<gsc::context*> continue_ctxs_;
bool can_break_;
bool can_continue_;
public:
auto output() -> std::vector<gsc::function_ptr>;
void compile(const std::string& file, std::vector<std::uint8_t>& data);
void set_readf_callback(std::function<std::vector<std::uint8_t>(const std::string&)> func);
private:
auto parse_buffer(const std::string& file, std::vector<std::uint8_t>& data) -> gsc::program_ptr;
auto parse_file(const std::string& file) -> gsc::program_ptr;
void compile_program(const gsc::program_ptr& program);
void emit_include(const gsc::include_ptr& include);
void emit_define(const gsc::define_ptr& define);
void emit_usingtree(const gsc::usingtree_ptr& animtree);
void emit_constant(const gsc::constant_ptr& constant);
void emit_thread(const gsc::thread_ptr& thread);
void emit_parameters(const gsc::context_ptr& ctx, const gsc::parameters_ptr& params);
void emit_stmt(const gsc::context_ptr& ctx, const gsc::stmt_ptr& stmt, bool last);
void emit_stmt_list(const gsc::context_ptr& ctx, const gsc::stmt_list_ptr& stmt, bool last);
void emit_stmt_call(const gsc::context_ptr& ctx, const gsc::stmt_call_ptr& stmt);
void emit_stmt_assign(const gsc::context_ptr& ctx, const gsc::stmt_assign_ptr& stmt);
void emit_stmt_endon(const gsc::context_ptr& ctx, const gsc::stmt_endon_ptr& stmt);
void emit_stmt_notify(const gsc::context_ptr& ctx, const gsc::stmt_notify_ptr& stmt);
void emit_stmt_wait(const gsc::context_ptr& ctx, const gsc::stmt_wait_ptr& stmt);
void emit_stmt_waittill(const gsc::context_ptr& ctx, const gsc::stmt_waittill_ptr& stmt);
void emit_stmt_waittillmatch(const gsc::context_ptr& ctx, const gsc::stmt_waittillmatch_ptr& stmt);
void emit_stmt_waittillframeend(const gsc::context_ptr& ctx, const gsc::stmt_waittillframeend_ptr& stmt);
void emit_stmt_waitframe(const gsc::context_ptr& ctx, const gsc::stmt_waitframe_ptr& stmt);
void emit_stmt_if(const gsc::context_ptr& ctx, const gsc::stmt_if_ptr& stmt, bool last);
void emit_stmt_ifelse(const gsc::context_ptr& ctx, const gsc::stmt_ifelse_ptr& stmt, bool last);
void emit_stmt_while(const gsc::context_ptr& ctx, const gsc::stmt_while_ptr& stmt);
void emit_stmt_for(const gsc::context_ptr& ctx, const gsc::stmt_for_ptr& stmt);
void emit_stmt_foreach(const gsc::context_ptr& ctx, const gsc::stmt_foreach_ptr& stmt);
void emit_stmt_switch(const gsc::context_ptr& ctx, const gsc::stmt_switch_ptr& stmt);
void emit_stmt_case(const gsc::context_ptr& ctx, const gsc::stmt_case_ptr& stmt);
void emit_stmt_default(const gsc::context_ptr& ctx, const gsc::stmt_default_ptr& stmt);
void emit_stmt_break(const gsc::context_ptr& ctx, const gsc::stmt_break_ptr& stmt);
void emit_stmt_continue(const gsc::context_ptr& ctx, const gsc::stmt_continue_ptr& stmt);
void emit_stmt_return(const gsc::context_ptr& ctx, const gsc::stmt_return_ptr& stmt);
void emit_expr(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr);
void emit_expr_assign(const gsc::context_ptr& ctx, const gsc::expr_assign_ptr& expr);
void emit_expr_ternary(const gsc::context_ptr& ctx, const gsc::expr_ternary_ptr& expr);
void emit_expr_binary(const gsc::context_ptr& ctx, const gsc::expr_binary_ptr& expr);
void emit_expr_isdefined(const gsc::context_ptr& ctx, const gsc::expr_arguments_ptr& expr);
void emit_expr_istrue(const gsc::context_ptr& ctx, const gsc::expr_arguments_ptr& expr);
void emit_expr_bool_not_after_and(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr);
void emit_expr_and(const gsc::context_ptr& ctx, const gsc::expr_and_ptr& expr);
void emit_expr_or(const gsc::context_ptr& ctx, const gsc::expr_or_ptr& expr);
void emit_expr_complement(const gsc::context_ptr& ctx, const gsc::expr_complement_ptr& expr);
void emit_expr_not(const gsc::context_ptr& ctx, const gsc::expr_not_ptr& expr);
void emit_expr_call(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void emit_expr_call_pointer(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void emit_expr_call_pointer_type(const gsc::context_ptr& ctx, int args, bool builtin, bool method, bool thread, bool child);
void emit_expr_call_function(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void emit_expr_call_function_builtin(const gsc::context_ptr& ctx, const std::string& func, int args, bool method);
void emit_expr_call_function_local(const gsc::context_ptr& ctx, const std::string& func, int args, bool method, bool thread, bool child);
void emit_expr_call_function_far(const gsc::context_ptr& ctx, const std::string& file, const std::string& func, int args, bool method, bool thread, bool child);
void emit_expr_arguments(const gsc::context_ptr& ctx, const gsc::expr_arguments_ptr& arg_list);
void emit_expr_function(const gsc::context_ptr& ctx, const gsc::expr_function_ptr& node);
void emit_expr_clear_variable(const gsc::context_ptr& ctx, const gsc::expr_ptr& lvalue);
void emit_expr_add_array(const gsc::context_ptr& ctx, const gsc::expr_add_array_ptr& expr);
void emit_expr_size(const gsc::context_ptr& ctx, const gsc::expr_size_ptr& expr);
void emit_variable_ref(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr, bool set);
void emit_array_variable_ref(const gsc::context_ptr& ctx, const gsc::expr_array_ptr& expr, bool set);
void emit_field_variable_ref(const gsc::context_ptr& ctx, const gsc::expr_field_ptr& expr, bool set);
void emit_local_variable_ref(const gsc::context_ptr& ctx, const gsc::name_ptr& expr, bool set);
void emit_variable(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr);
void emit_array_variable(const gsc::context_ptr& ctx, const gsc::expr_array_ptr& expr);
void emit_field_variable(const gsc::context_ptr& ctx, const gsc::expr_field_ptr& expr);
void emit_local_variable(const gsc::context_ptr& ctx, const gsc::name_ptr& expr);
void emit_clear_local_variable(const gsc::context_ptr& ctx, const gsc::name_ptr& expr);
void emit_create_local_vars(const gsc::context_ptr& ctx);
void emit_remove_local_vars(const gsc::context_ptr& ctx);
void emit_object(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr);
void emit_animtree(const gsc::context_ptr& ctx, const gsc::animtree_ptr& animtree);
void emit_animation(const gsc::context_ptr& ctx, const gsc::animation_ptr& animation);
void emit_istring(const gsc::context_ptr& ctx, const gsc::istring_ptr& str);
void emit_string(const gsc::context_ptr& ctx, const gsc::string_ptr& str);
void emit_color(const gsc::context_ptr& ctx, const gsc::color_ptr& color);
void emit_vector(const gsc::context_ptr& ctx, const gsc::vector_ptr& vec);
void emit_float(const gsc::context_ptr& ctx, const gsc::float_ptr& num);
void emit_integer(const gsc::context_ptr& ctx, const gsc::integer_ptr& num);
void emit_false(const gsc::context_ptr& ctx, const gsc::false_ptr& expr);
void emit_true(const gsc::context_ptr& ctx, const gsc::true_ptr& expr);
void emit_opcode(const gsc::context_ptr& ctx, opcode op);
void emit_opcode(const gsc::context_ptr& ctx, opcode op, const std::string& data);
void emit_opcode(const gsc::context_ptr& ctx, opcode op, const std::vector<std::string>& data);
void process_thread(const gsc::context_ptr& ctx, const gsc::thread_ptr& thread);
void process_parameters(const gsc::context_ptr& ctx, const gsc::parameters_ptr& params);
void process_stmt(const gsc::context_ptr& ctx, const gsc::stmt_ptr& stmt);
void process_stmt_list(const gsc::context_ptr& ctx, const gsc::stmt_list_ptr& stmt);
void process_expr(const gsc::context_ptr& ctx, const gsc::expr_ptr& expr);
void process_stmt_waittill(const gsc::context_ptr& ctx, const gsc::stmt_waittill_ptr& stmt);
void process_stmt_if(const gsc::context_ptr& ctx, const gsc::stmt_if_ptr& stmt);
void process_stmt_ifelse(const gsc::context_ptr& ctx, const gsc::stmt_ifelse_ptr& stmt);
void process_stmt_while(const gsc::context_ptr& ctx, const gsc::stmt_while_ptr& stmt);
void process_stmt_for(const gsc::context_ptr& ctx, const gsc::stmt_for_ptr& stmt);
void process_stmt_foreach(const gsc::context_ptr& ctx, const gsc::stmt_foreach_ptr& stmt);
void process_stmt_switch(const gsc::context_ptr& ctx, const gsc::stmt_switch_ptr& stmt);
void process_stmt_break(const gsc::context_ptr& ctx, const gsc::stmt_break_ptr& stmt);
void process_stmt_continue(const gsc::context_ptr& ctx, const gsc::stmt_continue_ptr& stmt);
void process_stmt_return(const gsc::context_ptr& ctx, const gsc::stmt_return_ptr& stmt);
void register_variable(const gsc::context_ptr& ctx, const std::string& name);
void initialize_variable(const gsc::context_ptr& ctx, const gsc::name_ptr& name);
void create_variable(const gsc::context_ptr& ctx, const gsc::name_ptr& name);
auto variable_stack_index(const gsc::context_ptr& ctx, const gsc::name_ptr& name) -> std::uint8_t;
auto variable_create_index(const gsc::context_ptr& ctx, const gsc::name_ptr& name) -> std::string;
auto variable_access_index(const gsc::context_ptr& ctx, const gsc::name_ptr& name) -> std::string;
auto variable_initialized(const gsc::context_ptr& ctx, const gsc::name_ptr& name) -> bool;
auto is_include_call(const std::string& name, std::string& file) -> bool;
auto is_local_call(const std::string& name) -> bool;
auto is_builtin_call(const std::string& name) -> bool;
auto is_builtin_func(const std::string& name) -> bool;
auto is_builtin_method(const std::string& name) -> bool;
auto is_constant_condition(const gsc::expr_ptr& expr) -> bool;
auto create_label() -> std::string;
auto insert_label() -> std::string;
void insert_label(const std::string& label);
auto map_known_includes(const std::string& include) -> bool;
};
} // namespace xsk::gsc::s4

3386
src/s4/xsk/decompiler.cpp Normal file

File diff suppressed because it is too large Load Diff

95
src/s4/xsk/decompiler.hpp Normal file
View File

@ -0,0 +1,95 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::s4
{
class decompiler : public gsc::decompiler
{
std::string filename_;
std::unique_ptr<utils::byte_buffer> output_;
gsc::program_ptr program_;
gsc::thread_ptr func_;
std::unordered_map<std::uint32_t, std::string> labels_;
std::vector<std::string> expr_labels_;
std::vector<std::string> tern_labels_;
std::stack<gsc::node_ptr> stack_;
std::vector<gsc::context> blocks_;
public:
auto output() -> std::vector<std::uint8_t>;
void decompile(const std::string& file, std::vector<gsc::function_ptr>& functions);
private:
void decompile_function(const gsc::function_ptr& func);
void decompile_statements(const gsc::function_ptr& func);
void decompile_expr();
void decompile_ternary();
void decompile_block(const gsc::stmt_list_ptr& block);
void decompile_search_infinite(const gsc::stmt_list_ptr& block);
void decompile_search_loop(const gsc::stmt_list_ptr& block);
void decompile_search_switch(const gsc::stmt_list_ptr& block);
void decompile_search_ifelse(const gsc::stmt_list_ptr& block);
void decompile_break_continue(const gsc::stmt_list_ptr& block);
void decompile_if(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_ifelse(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_last_ifelse(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_infinite(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_loop(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_while(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_for(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_foreach(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end);
void decompile_switch(const gsc::stmt_list_ptr& block, std::uint32_t start);
auto find_location_reference(const gsc::stmt_list_ptr& block, std::uint32_t start, std::uint32_t end, const std::string& location) -> bool;
auto find_location_index(const gsc::stmt_list_ptr& block, const std::string& location) -> std::uint32_t;
auto last_location_index(const gsc::stmt_list_ptr& block, std::uint32_t index) -> bool;
void process_stack(const gsc::thread_ptr& thread);
void process_parameters(const gsc::context_ptr& ctx, const gsc::parameters_ptr& params);
void process_stmt(const gsc::context_ptr& ctx, const gsc::stmt_ptr& stmt);
void process_stmt_list(const gsc::context_ptr& ctx, const gsc::stmt_list_ptr& stmt);
void process_stmt_call(const gsc::context_ptr& ctx, const gsc::stmt_call_ptr& stmt);
void process_stmt_assign(const gsc::context_ptr& ctx, const gsc::stmt_assign_ptr& stmt);
void process_stmt_endon(const gsc::context_ptr& ctx, const gsc::stmt_endon_ptr& stmt);
void process_stmt_notify(const gsc::context_ptr& ctx, const gsc::stmt_notify_ptr& stmt);
void process_stmt_wait(const gsc::context_ptr& ctx, const gsc::stmt_wait_ptr& stmt);
void process_stmt_waittill(const gsc::context_ptr& ctx, const gsc::stmt_waittill_ptr& stmt);
void process_stmt_waittillmatch(const gsc::context_ptr& ctx, const gsc::stmt_waittillmatch_ptr& stmt);
void process_stmt_waittillframeend(const gsc::context_ptr& ctx, const gsc::stmt_waittillframeend_ptr& stmt);
void process_stmt_if(const gsc::context_ptr& ctx, const gsc::stmt_if_ptr& stmt);
void process_stmt_ifelse(const gsc::context_ptr& ctx, const gsc::stmt_ifelse_ptr& stmt);
void process_stmt_while(const gsc::context_ptr& ctx, const gsc::stmt_while_ptr& stmt);
void process_stmt_for(const gsc::context_ptr& ctx, const gsc::stmt_for_ptr& stmt);
void process_stmt_foreach(const gsc::context_ptr& ctx, const gsc::stmt_foreach_ptr& stmt);
void process_stmt_switch(const gsc::context_ptr& ctx, const gsc::stmt_switch_ptr& stmt);
void process_stmt_cases(const gsc::context_ptr& ctx, const gsc::stmt_list_ptr& stmt);
void process_stmt_break(const gsc::context_ptr& ctx, const gsc::stmt_break_ptr& stmt);
void process_stmt_return(const gsc::context_ptr& ctx, const gsc::stmt_return_ptr& stmt);
void process_expr(const gsc::context_ptr& ctx, gsc::expr_ptr& expr);
void process_expr_assign(const gsc::context_ptr& ctx, gsc::expr_assign_ptr& expr);
void process_expr_ternary(const gsc::context_ptr& ctx, const gsc::expr_ternary_ptr& expr);
void process_expr_binary(const gsc::context_ptr& ctx, const gsc::expr_binary_ptr& expr);
void process_expr_and(const gsc::context_ptr& ctx, const gsc::expr_and_ptr& expr);
void process_expr_or(const gsc::context_ptr& ctx, const gsc::expr_or_ptr& expr);
void process_expr_complement(const gsc::context_ptr& ctx, const gsc::expr_complement_ptr& expr);
void process_expr_not(const gsc::context_ptr& ctx, const gsc::expr_not_ptr& expr);
void process_expr_call(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void process_expr_call_pointer(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void process_expr_call_function(const gsc::context_ptr& ctx, const gsc::expr_call_ptr& expr);
void process_expr_arguments(const gsc::context_ptr& ctx, const gsc::expr_arguments_ptr& arg_list);
void process_expr_function(const gsc::context_ptr& ctx, const gsc::expr_function_ptr& node);
void process_expr_add_array(const gsc::context_ptr& ctx, const gsc::expr_add_array_ptr& expr);
void process_expr_size(const gsc::context_ptr& ctx, const gsc::expr_size_ptr& expr);
void process_array_variable(const gsc::context_ptr& ctx, const gsc::expr_array_ptr& expr);
void process_field_variable(const gsc::context_ptr& ctx, const gsc::expr_field_ptr& expr);
void process_local_variable(const gsc::context_ptr& ctx, const gsc::name_ptr& expr);
void process_vector(const gsc::context_ptr& ctx, const gsc::vector_ptr& vec);
void process_var_create(const gsc::context_ptr& ctx, gsc::expr_ptr& expr, bool fromstmt = false);
void process_var_access(const gsc::context_ptr& ctx, gsc::expr_ptr& expr);
void process_var_remove(const gsc::context_ptr& ctx, const gsc::asm_remove_ptr& expr);
};
} // namespace xsk::gsc::s4

644
src/s4/xsk/disassembler.cpp Normal file
View File

@ -0,0 +1,644 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "s4.hpp"
namespace xsk::gsc::s4
{
auto disassembler::output() -> std::vector<gsc::function_ptr>
{
return std::move(functions_);
}
auto disassembler::output_data() -> std::vector<std::uint8_t>
{
output_ = std::make_unique<utils::byte_buffer>(0x100000);
output_->write_string("// S4 PC GSCASM\n");
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
for (auto& func : functions_)
{
this->print_function(func);
}
std::vector<std::uint8_t> output;
output.resize(output_->pos());
memcpy(output.data(), output_->buffer().data(), output.size());
return output;
}
void disassembler::disassemble(const std::string& file, std::vector<std::uint8_t>& script, std::vector<std::uint8_t>& stack)
{
filename_ = file;
script_ = std::make_unique<utils::byte_buffer>(script);
stack_ = std::make_unique<utils::byte_buffer>(stack);
functions_.clear();
script_->seek(1);
while (stack_->is_avail() && script_->is_avail())
{
functions_.push_back(std::make_unique<gsc::function>());
auto& func = functions_.back();
func->index = static_cast<std::uint32_t>(script_->pos());
func->size = stack_->read<std::uint32_t>();
func->id = stack_->read<std::uint32_t>();
func->name = "sub_"s + (func->id == 0 ? stack_->read_c_string() : resolver::token_name(func->id));
this->dissasemble_function(func);
func->labels = labels_;
labels_.clear();
}
this->resolve_local_functions();
}
void disassembler::dissasemble_function(const gsc::function_ptr& func)
{
auto size = func->size;
while (size > 0)
{
func->instructions.push_back(std::make_unique<gsc::instruction>());
auto& inst = func->instructions.back();
inst->index = static_cast<std::uint32_t>(script_->pos());
inst->opcode = script_->read<std::uint8_t>();
inst->size = opcode_size(inst->opcode);
this->dissasemble_instruction(inst);
size -= inst->size;
}
}
void disassembler::dissasemble_instruction(const gsc::instruction_ptr& inst)
{
switch (opcode(inst->opcode))
{
case opcode::OP_CastFieldObject:
case opcode::OP_plus:
case opcode::OP_GetGameRef:
case opcode::OP_GetThisthread:
case opcode::OP_greater:
case opcode::OP_shift_right:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_equality:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_notify:
case opcode::OP_PreScriptCall:
case opcode::OP_GetUndefined:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_size:
case opcode::OP_AddArray:
case opcode::OP_endon:
case opcode::OP_shift_left:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_Return:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetSelfObject:
case opcode::OP_GetGame:
case opcode::OP_EvalArray:
case opcode::OP_GetSelf:
case opcode::OP_End:
case opcode::OP_less_equal:
case opcode::OP_EvalLocalVariableCached0:
case opcode::OP_EvalLocalVariableCached1:
case opcode::OP_EvalLocalVariableCached2:
case opcode::OP_EvalLocalVariableCached3:
case opcode::OP_EvalLocalVariableCached4:
case opcode::OP_EvalLocalVariableCached5:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_checkclearparams:
case opcode::OP_waittillmatch2:
case opcode::OP_minus:
case opcode::OP_greater_equal:
case opcode::OP_vector:
case opcode::OP_ClearArray:
case opcode::OP_DecTop:
case opcode::OP_CastBool:
case opcode::OP_EvalArrayRef:
case opcode::OP_GetZero:
case opcode::OP_wait:
case opcode::OP_waittill:
case opcode::OP_GetAnimObject:
case opcode::OP_mod:
case opcode::OP_clearparams:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_EmptyArray:
case opcode::OP_ClearVariableField:
case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_BoolComplement:
case opcode::OP_less:
case opcode::OP_BoolNot:
case opcode::OP_waittillFrameEnd:
case opcode::OP_waitframe:
case opcode::OP_GetLevelObject:
case opcode::OP_inc:
case opcode::OP_GetAnim:
case opcode::OP_SetVariableField:
case opcode::OP_divide:
case opcode::OP_multiply:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_bit_and:
case opcode::OP_voidCodepos:
case opcode::OP_inequality:
case opcode::OP_bit_ex_or:
// case opcode::OP_NOP:
// case opcode::OP_abort:
// case opcode::OP_object:
// case opcode::OP_thread_object:
case opcode::OP_EvalLocalVariable:
case opcode::OP_EvalLocalVariableRef:
// case opcode::OP_breakpoint:
// case opcode::OP_assignmentBreakpoint:
// case opcode::OP_manualAndAssignmentBreakpoint:
case opcode::OP_BoolNotAfterAnd:
case opcode::OP_IsDefined:
case opcode::OP_IsTrue:
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint16_t>()));
break;
case opcode::OP_GetInteger:
inst->data.push_back(utils::string::va("%i", script_->read<std::int32_t>()));
break;
case opcode::OP_GetFloat:
{
auto val = script_->read<float>();
inst->data.push_back(utils::string::va("%g%s", val, val == int(val) ? ".0" : ""));
}
break;
case opcode::OP_GetVector:
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->seek(4);
inst->data.push_back(utils::string::to_literal(stack_->read_c_string()));
break;
case opcode::OP_GetAnimation:
script_->seek(8);
inst->data.push_back(utils::string::quote(stack_->read_c_string().data(), false));
inst->data.push_back(utils::string::quote(stack_->read_c_string().data(), false));
break;
case opcode::OP_GetAnimTree:
script_->seek(1);
inst->data.push_back(utils::string::quote(stack_->read_c_string().data(), false));
break;
case opcode::OP_waittillmatch:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint16_t>()));
break;
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_CreateLocalVariable:
case opcode::OP_EvalLocalVariableObjectCached:
case opcode::OP_EvalLocalArrayCached:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_ClearFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariableRef:
this->disassemble_field_variable(inst);
break;
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptChildThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
this->disassemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
this->disassemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
this->disassemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
this->disassemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
this->disassemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
this->disassemble_builtin_call(inst, true, true);
break;
case opcode::OP_GetBuiltinFunction:
case opcode::OP_CallBuiltin0:
case opcode::OP_CallBuiltin1:
case opcode::OP_CallBuiltin2:
case opcode::OP_CallBuiltin3:
case opcode::OP_CallBuiltin4:
case opcode::OP_CallBuiltin5:
this->disassemble_builtin_call(inst, false, false);
break;
case opcode::OP_GetBuiltinMethod:
case opcode::OP_CallBuiltinMethod0:
case opcode::OP_CallBuiltinMethod1:
case opcode::OP_CallBuiltinMethod2:
case opcode::OP_CallBuiltinMethod3:
case opcode::OP_CallBuiltinMethod4:
case opcode::OP_CallBuiltinMethod5:
this->disassemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
this->disassemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
this->disassemble_jump(inst, false, true);
break;
case opcode::OP_jump:
this->disassemble_jump(inst, false, false);
break;
case opcode::OP_switch:
this->disassemble_switch(inst);
break;
case opcode::OP_endswitch:
this->disassemble_end_switch(inst);
break;
/*
case opcode::OP_prof_begin:
script_->seek(5); // TODO: skipped data
break;
case opcode::OP_prof_end:
script_->seek(1); // TODO: skipped data
break;
case opcode::OP_EvalNewLocalArrayRefCached0_Precompiled:
case opcode::OP_SetNewLocalVariableFieldCached0_Precompiled:
case opcode::OP_CreateLocalVariable_Precompiled:
case opcode::OP_SafeCreateVariableFieldCached_Precompiled:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
*/
case opcode::OP_FormalParams:
case opcode::OP_FormalParams_Precompiled:
this->disassemble_formal_params(inst);
break;
/* case opcode::OP_NativeGetLocalFunction:
case opcode::OP_NativeLocalFunctionCall:
case opcode::OP_NativeLocalFunctionCall2:
case opcode::OP_NativeLocalMethodCall:
this->disassemble_local_call(inst, false);
break;
case opcode::OP_NativeGetFarFunction:
case opcode::OP_NativeFarFunctionCall:
case opcode::OP_NativeFarFunctionCall2:
case opcode::OP_NativeFarMethodCall:
this->disassemble_far_call(inst, false);
break;
case opcode::OP_NativeLocalFunctionThreadCall:
case opcode::OP_NativeLocalMethodThreadCall:
case opcode::OP_NativeLocalFunctionChildThreadCall:
case opcode::OP_NativeLocalMethodChildThreadCall:
this->disassemble_local_call(inst, true);
break;
case opcode::OP_NativeFarFunctionThreadCall:
case opcode::OP_NativeFarMethodThreadCall:
case opcode::OP_NativeFarFunctionChildThreadCall:
case opcode::OP_NativeFarMethodChildThreadCall:
this->disassemble_far_call(inst, true);
break;
*/
default:
throw gsc::disasm_error(utils::string::va("Unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void disassembler::disassemble_builtin_call(const gsc::instruction_ptr& inst, bool method, bool arg_num)
{
if (arg_num)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
if (method)
{
inst->data.push_back(resolver::method_name(script_->read<std::uint16_t>()));
}
else
{
inst->data.push_back(resolver::function_name(script_->read<std::uint16_t>()));
}
}
void disassembler::disassemble_local_call(const gsc::instruction_ptr& inst, bool thread)
{
std::int32_t offset = this->disassemble_offset();
inst->data.push_back(utils::string::va("%X", offset + inst->index + 1));
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
}
void disassembler::disassemble_far_call(const gsc::instruction_ptr& inst, bool thread)
{
script_->seek(3);
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
auto file_id = stack_->read<std::uint32_t>();
auto file_name = file_id == 0 ? stack_->read_c_string() : resolver::file_name(file_id);
auto func_id = stack_->read<std::uint32_t>();
auto func_name = func_id == 0 ? stack_->read_c_string() : resolver::token_name(func_id);
inst->data.push_back(file_name != "" ? file_name : utils::string::va("_ID%i", file_id));
inst->data.push_back(func_name != "" ? func_name : utils::string::va("_ID%i", func_id));
}
void disassembler::disassemble_jump(const gsc::instruction_ptr& inst, bool expr, bool back)
{
std::int32_t addr;
std::string label;
if (expr)
{
addr = inst->index + 3 + script_->read<std::int16_t>();
label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
}
else if (back)
{
addr = inst->index + 3 - script_->read<std::uint16_t>();
label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
}
else
{
addr = inst->index + 5 + script_->read<std::int32_t>();
label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
}
labels_.insert({addr, label});
}
void disassembler::disassemble_field_variable(const gsc::instruction_ptr& inst)
{
std::uint32_t field_id = script_->read<std::uint32_t>();
std::string field_name;
if(field_id > 0x1C000)
{
auto temp = stack_->read<std::uint32_t>();
field_name = temp == 0 ? stack_->read_c_string() : std::to_string(temp);
}
else
{
field_name = resolver::token_name(field_id);
}
inst->data.push_back(field_name != "" ? field_name : utils::string::va("_ID%i", field_id));
}
void disassembler::disassemble_formal_params(const gsc::instruction_ptr& inst)
{
auto size = script_->read<std::uint8_t>();
inst->size += size;
inst->data.push_back(utils::string::va("%i", size));
while(size > 0)
{
inst->data.push_back(utils::string::va("%d", script_->read<std::uint8_t>()));
size--;
}
}
void disassembler::disassemble_switch(const gsc::instruction_ptr& inst)
{
std::int32_t addr = inst->index + 4 + script_->read<std::int32_t>();
std::string label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({addr, label});
}
void disassembler::disassemble_end_switch(const gsc::instruction_ptr& inst)
{
std::uint16_t case_num = script_->read<std::uint16_t>();
inst->data.push_back(utils::string::va("%i", case_num));
std::uint32_t internal_index = inst->index + 3;
if (case_num)
{
for (auto i = case_num; i > 0; i--)
{
std::uint32_t case_label = script_->read<std::uint32_t>();
if (case_label < 0x80000 && case_label > 0)
{
inst->data.push_back("case");
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
}
else if (case_label == 0)
{
inst->data.push_back("default");
stack_->read<std::uint16_t>();
}
else
{
inst->data.push_back("case");
inst->data.push_back(utils::string::va("%i", (case_label - 0x800000) & 0xFFFFFF));
}
inst->size += 4;
internal_index += 4;
auto addr = this->disassemble_offset() + internal_index;
std::string label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({addr, label});
inst->size += 3;
internal_index += 3;
}
}
}
auto disassembler::disassemble_offset() -> std::int32_t
{
std::array<std::uint8_t, 4> bytes = {};
for (auto i = 0; i < 3; i++)
{
bytes[i] = script_->read<std::uint8_t>();
}
std::int32_t offset = *reinterpret_cast<std::int32_t*>(bytes.data());
offset = (offset << 8) >> 8;
return offset;
}
void disassembler::resolve_local_functions()
{
for (auto& func : functions_)
{
for (auto& inst : func->instructions)
{
switch (opcode(inst->opcode))
{
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
inst->data.at(0) = this->resolve_function(inst->data[0]);
break;
default:
break;
}
}
}
}
auto disassembler::resolve_function(const std::string& index) -> std::string
{
if (utils::string::is_hex_number(index))
{
std::uint32_t idx = std::stoul(index, nullptr, 16);
for (auto& func : functions_)
{
if (func->index == idx)
{
return func->name;
}
}
throw gsc::disasm_error(utils::string::va("Couldn't resolve function name at index '0x%04X'!", idx));
}
throw gsc::disasm_error(utils::string::va("\"%s\" is not valid function address!", index.data()));
}
void disassembler::print_function(const gsc::function_ptr& func)
{
output_->write_string("\n");
output_->write_string(utils::string::va("%s\n", func->name.data()));
for (auto& inst : func->instructions)
{
const auto itr = func->labels.find(inst->index);
if (itr != func->labels.end())
{
output_->write_string(utils::string::va("\t%s\n", itr->second.data()));
}
this->print_instruction(inst);
}
output_->write_string("\n");
output_->write_string(utils::string::va("end_%s\n", func->name.substr(4).data()));
}
void disassembler::print_instruction(const gsc::instruction_ptr& inst)
{
switch (opcode(inst->opcode))
{
case opcode::OP_endswitch:
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
output_->write_string(utils::string::va(" %s\n", inst->data[0].data()));
{
std::uint32_t totalcase = std::stoul(inst->data[0]);
auto index = 0;
for (auto casenum = 0u; casenum < totalcase; casenum++)
{
if (inst->data[1 + index] == "case")
{
output_->write_string(utils::string::va("\t\t\t%s %s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data(), inst->data[1 + index + 2].data()));
index += 3;
}
else if (inst->data[1 + index] == "default")
{
output_->write_string(utils::string::va("\t\t\t%s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data()));
index += 2;
}
if (casenum != totalcase - 1)
{
output_->write_string("\n");
}
}
}
break;
default:
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
for (auto& d : inst->data)
{
output_->write_string(utils::string::va(" %s", d.data()));
}
break;
}
output_->write_string("\n");
}
} // namespace xsk::gsc::s4

View File

@ -0,0 +1,43 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::s4
{
class disassembler : public gsc::disassembler
{
std::string filename_;
utils::byte_buffer_ptr script_;
utils::byte_buffer_ptr stack_;
utils::byte_buffer_ptr output_;
std::vector<gsc::function_ptr> functions_;
std::unordered_map<std::uint32_t, std::string> labels_;
public:
auto output() -> std::vector<gsc::function_ptr>;
auto output_data() -> std::vector<std::uint8_t>;
void disassemble(const std::string& file, std::vector<std::uint8_t>& script, std::vector<std::uint8_t>& stack);
private:
void dissasemble_function(const gsc::function_ptr& func);
void dissasemble_instruction(const gsc::instruction_ptr& inst);
void disassemble_builtin_call(const gsc::instruction_ptr& inst, bool method, bool arg_num);
void disassemble_local_call(const gsc::instruction_ptr& inst, bool thread);
void disassemble_far_call(const gsc::instruction_ptr& inst, bool thread);
void disassemble_jump(const gsc::instruction_ptr& inst, bool expr, bool back);
void disassemble_field_variable(const gsc::instruction_ptr& inst);
void disassemble_formal_params(const gsc::instruction_ptr& inst);
void disassemble_switch(const gsc::instruction_ptr& inst);
void disassemble_end_switch(const gsc::instruction_ptr& inst);
auto disassemble_offset() -> std::int32_t;
void resolve_local_functions();
auto resolve_function(const std::string& index) -> std::string;
void print_function(const gsc::function_ptr& func);
void print_instruction(const gsc::instruction_ptr& inst);
};
} // namespace xsk::gsc::s4

2882
src/s4/xsk/lexer.cpp Normal file

File diff suppressed because it is too large Load Diff

708
src/s4/xsk/lexer.hpp Normal file
View File

@ -0,0 +1,708 @@
#ifndef s4_HEADER_H
#define s4_HEADER_H 1
#define s4_IN_HEADER 1
#line 5 "lexer.hpp"
#include "stdafx.hpp"
#include "s4.hpp"
#include "parser.hpp"
using namespace xsk::gsc;
#line 11 "lexer.hpp"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 6
#define YY_FLEX_SUBMINOR_VERSION 4
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
#ifdef yy_create_buffer
#define s4__create_buffer_ALREADY_DEFINED
#else
#define yy_create_buffer s4__create_buffer
#endif
#ifdef yy_delete_buffer
#define s4__delete_buffer_ALREADY_DEFINED
#else
#define yy_delete_buffer s4__delete_buffer
#endif
#ifdef yy_scan_buffer
#define s4__scan_buffer_ALREADY_DEFINED
#else
#define yy_scan_buffer s4__scan_buffer
#endif
#ifdef yy_scan_string
#define s4__scan_string_ALREADY_DEFINED
#else
#define yy_scan_string s4__scan_string
#endif
#ifdef yy_scan_bytes
#define s4__scan_bytes_ALREADY_DEFINED
#else
#define yy_scan_bytes s4__scan_bytes
#endif
#ifdef yy_init_buffer
#define s4__init_buffer_ALREADY_DEFINED
#else
#define yy_init_buffer s4__init_buffer
#endif
#ifdef yy_flush_buffer
#define s4__flush_buffer_ALREADY_DEFINED
#else
#define yy_flush_buffer s4__flush_buffer
#endif
#ifdef yy_load_buffer_state
#define s4__load_buffer_state_ALREADY_DEFINED
#else
#define yy_load_buffer_state s4__load_buffer_state
#endif
#ifdef yy_switch_to_buffer
#define s4__switch_to_buffer_ALREADY_DEFINED
#else
#define yy_switch_to_buffer s4__switch_to_buffer
#endif
#ifdef yypush_buffer_state
#define s4_push_buffer_state_ALREADY_DEFINED
#else
#define yypush_buffer_state s4_push_buffer_state
#endif
#ifdef yypop_buffer_state
#define s4_pop_buffer_state_ALREADY_DEFINED
#else
#define yypop_buffer_state s4_pop_buffer_state
#endif
#ifdef yyensure_buffer_stack
#define s4_ensure_buffer_stack_ALREADY_DEFINED
#else
#define yyensure_buffer_stack s4_ensure_buffer_stack
#endif
#ifdef yylex
#define s4_lex_ALREADY_DEFINED
#else
#define yylex s4_lex
#endif
#ifdef yyrestart
#define s4_restart_ALREADY_DEFINED
#else
#define yyrestart s4_restart
#endif
#ifdef yylex_init
#define s4_lex_init_ALREADY_DEFINED
#else
#define yylex_init s4_lex_init
#endif
#ifdef yylex_init_extra
#define s4_lex_init_extra_ALREADY_DEFINED
#else
#define yylex_init_extra s4_lex_init_extra
#endif
#ifdef yylex_destroy
#define s4_lex_destroy_ALREADY_DEFINED
#else
#define yylex_destroy s4_lex_destroy
#endif
#ifdef yyget_debug
#define s4_get_debug_ALREADY_DEFINED
#else
#define yyget_debug s4_get_debug
#endif
#ifdef yyset_debug
#define s4_set_debug_ALREADY_DEFINED
#else
#define yyset_debug s4_set_debug
#endif
#ifdef yyget_extra
#define s4_get_extra_ALREADY_DEFINED
#else
#define yyget_extra s4_get_extra
#endif
#ifdef yyset_extra
#define s4_set_extra_ALREADY_DEFINED
#else
#define yyset_extra s4_set_extra
#endif
#ifdef yyget_in
#define s4_get_in_ALREADY_DEFINED
#else
#define yyget_in s4_get_in
#endif
#ifdef yyset_in
#define s4_set_in_ALREADY_DEFINED
#else
#define yyset_in s4_set_in
#endif
#ifdef yyget_out
#define s4_get_out_ALREADY_DEFINED
#else
#define yyget_out s4_get_out
#endif
#ifdef yyset_out
#define s4_set_out_ALREADY_DEFINED
#else
#define yyset_out s4_set_out
#endif
#ifdef yyget_leng
#define s4_get_leng_ALREADY_DEFINED
#else
#define yyget_leng s4_get_leng
#endif
#ifdef yyget_text
#define s4_get_text_ALREADY_DEFINED
#else
#define yyget_text s4_get_text
#endif
#ifdef yyget_lineno
#define s4_get_lineno_ALREADY_DEFINED
#else
#define yyget_lineno s4_get_lineno
#endif
#ifdef yyset_lineno
#define s4_set_lineno_ALREADY_DEFINED
#else
#define yyset_lineno s4_set_lineno
#endif
#ifdef yyget_column
#define s4_get_column_ALREADY_DEFINED
#else
#define yyget_column s4_get_column
#endif
#ifdef yyset_column
#define s4_set_column_ALREADY_DEFINED
#else
#define yyset_column s4_set_column
#endif
#ifdef yywrap
#define s4_wrap_ALREADY_DEFINED
#else
#define yywrap s4_wrap
#endif
#ifdef yyalloc
#define s4_alloc_ALREADY_DEFINED
#else
#define yyalloc s4_alloc
#endif
#ifdef yyrealloc
#define s4_realloc_ALREADY_DEFINED
#else
#define yyrealloc s4_realloc
#endif
#ifdef yyfree
#define s4_free_ALREADY_DEFINED
#else
#define yyfree s4_free
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
/* begin standard C++ headers. */
/* TODO: this is always defined, so inline it */
#define yyconst const
#if defined(__GNUC__) && __GNUC__ >= 3
#define yynoreturn __attribute__((__noreturn__))
#else
#define yynoreturn
#endif
/* An opaque pointer. */
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
/* For convenience, these vars (plus the bison vars far below)
are macros in the reentrant scanner. */
#define yyin yyg->yyin_r
#define yyout yyg->yyout_r
#define yyextra yyg->yyextra_r
#define yyleng yyg->yyleng_r
#define yytext yyg->yytext_r
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
#define yy_flex_debug yyg->yy_flex_debug_r
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k.
* Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
* Ditto for the __ia64__ case accordingly.
*/
#define YY_BUF_SIZE 32768
#else
#define YY_BUF_SIZE 16384
#endif /* __ia64__ */
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
int yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void yyrestart ( FILE *input_file , yyscan_t yyscanner );
void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
void yypop_buffer_state ( yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
void *yyalloc ( yy_size_t , yyscan_t yyscanner );
void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
void yyfree ( void * , yyscan_t yyscanner );
/* Begin user sect3 */
#define s4_wrap(yyscanner) (/*CONSTCOND*/1)
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#define COMMENT_BLOCK_STATE 1
#define DEVELOPER_BLOCK_STATE 2
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include <unistd.h>
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
int yylex_init (yyscan_t* scanner);
int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int yylex_destroy ( yyscan_t yyscanner );
int yyget_debug ( yyscan_t yyscanner );
void yyset_debug ( int debug_flag , yyscan_t yyscanner );
YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
FILE *yyget_in ( yyscan_t yyscanner );
void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
FILE *yyget_out ( yyscan_t yyscanner );
void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
int yyget_leng ( yyscan_t yyscanner );
char *yyget_text ( yyscan_t yyscanner );
int yyget_lineno ( yyscan_t yyscanner );
void yyset_lineno ( int _line_number , yyscan_t yyscanner );
int yyget_column ( yyscan_t yyscanner );
void yyset_column ( int _column_no , yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int yywrap ( yyscan_t yyscanner );
#else
extern int yywrap ( yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k */
#define YY_READ_BUF_SIZE 16384
#else
#define YY_READ_BUF_SIZE 8192
#endif /* __ia64__ */
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int yylex (yyscan_t yyscanner);
#define YY_DECL int yylex (yyscan_t yyscanner)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#ifndef s4__create_buffer_ALREADY_DEFINED
#undef yy_create_buffer
#endif
#ifndef s4__delete_buffer_ALREADY_DEFINED
#undef yy_delete_buffer
#endif
#ifndef s4__scan_buffer_ALREADY_DEFINED
#undef yy_scan_buffer
#endif
#ifndef s4__scan_string_ALREADY_DEFINED
#undef yy_scan_string
#endif
#ifndef s4__scan_bytes_ALREADY_DEFINED
#undef yy_scan_bytes
#endif
#ifndef s4__init_buffer_ALREADY_DEFINED
#undef yy_init_buffer
#endif
#ifndef s4__flush_buffer_ALREADY_DEFINED
#undef yy_flush_buffer
#endif
#ifndef s4__load_buffer_state_ALREADY_DEFINED
#undef yy_load_buffer_state
#endif
#ifndef s4__switch_to_buffer_ALREADY_DEFINED
#undef yy_switch_to_buffer
#endif
#ifndef s4_push_buffer_state_ALREADY_DEFINED
#undef yypush_buffer_state
#endif
#ifndef s4_pop_buffer_state_ALREADY_DEFINED
#undef yypop_buffer_state
#endif
#ifndef s4_ensure_buffer_stack_ALREADY_DEFINED
#undef yyensure_buffer_stack
#endif
#ifndef s4_lex_ALREADY_DEFINED
#undef yylex
#endif
#ifndef s4_restart_ALREADY_DEFINED
#undef yyrestart
#endif
#ifndef s4_lex_init_ALREADY_DEFINED
#undef yylex_init
#endif
#ifndef s4_lex_init_extra_ALREADY_DEFINED
#undef yylex_init_extra
#endif
#ifndef s4_lex_destroy_ALREADY_DEFINED
#undef yylex_destroy
#endif
#ifndef s4_get_debug_ALREADY_DEFINED
#undef yyget_debug
#endif
#ifndef s4_set_debug_ALREADY_DEFINED
#undef yyset_debug
#endif
#ifndef s4_get_extra_ALREADY_DEFINED
#undef yyget_extra
#endif
#ifndef s4_set_extra_ALREADY_DEFINED
#undef yyset_extra
#endif
#ifndef s4_get_in_ALREADY_DEFINED
#undef yyget_in
#endif
#ifndef s4_set_in_ALREADY_DEFINED
#undef yyset_in
#endif
#ifndef s4_get_out_ALREADY_DEFINED
#undef yyget_out
#endif
#ifndef s4_set_out_ALREADY_DEFINED
#undef yyset_out
#endif
#ifndef s4_get_leng_ALREADY_DEFINED
#undef yyget_leng
#endif
#ifndef s4_get_text_ALREADY_DEFINED
#undef yyget_text
#endif
#ifndef s4_get_lineno_ALREADY_DEFINED
#undef yyget_lineno
#endif
#ifndef s4_set_lineno_ALREADY_DEFINED
#undef yyset_lineno
#endif
#ifndef s4_get_column_ALREADY_DEFINED
#undef yyget_column
#endif
#ifndef s4_set_column_ALREADY_DEFINED
#undef yyset_column
#endif
#ifndef s4_wrap_ALREADY_DEFINED
#undef yywrap
#endif
#ifndef s4_get_lval_ALREADY_DEFINED
#undef yyget_lval
#endif
#ifndef s4_set_lval_ALREADY_DEFINED
#undef yyset_lval
#endif
#ifndef s4_get_lloc_ALREADY_DEFINED
#undef yyget_lloc
#endif
#ifndef s4_set_lloc_ALREADY_DEFINED
#undef yyset_lloc
#endif
#ifndef s4_alloc_ALREADY_DEFINED
#undef yyalloc
#endif
#ifndef s4_realloc_ALREADY_DEFINED
#undef yyrealloc
#endif
#ifndef s4_free_ALREADY_DEFINED
#undef yyfree
#endif
#ifndef s4_text_ALREADY_DEFINED
#undef yytext
#endif
#ifndef s4_leng_ALREADY_DEFINED
#undef yyleng
#endif
#ifndef s4_in_ALREADY_DEFINED
#undef yyin
#endif
#ifndef s4_out_ALREADY_DEFINED
#undef yyout
#endif
#ifndef s4__flex_debug_ALREADY_DEFINED
#undef yy_flex_debug
#endif
#ifndef s4_lineno_ALREADY_DEFINED
#undef yylineno
#endif
#ifndef s4_tables_fload_ALREADY_DEFINED
#undef yytables_fload
#endif
#ifndef s4_tables_destroy_ALREADY_DEFINED
#undef yytables_destroy
#endif
#ifndef s4_TABLES_NAME_ALREADY_DEFINED
#undef yyTABLES_NAME
#endif
#line 155 "lexer.lpp"
#line 706 "lexer.hpp"
#undef s4_IN_HEADER
#endif /* s4_HEADER_H */

4116
src/s4/xsk/parser.cpp Normal file

File diff suppressed because it is too large Load Diff

4918
src/s4/xsk/parser.hpp Normal file

File diff suppressed because it is too large Load Diff

442
src/s4/xsk/resolver.cpp Normal file
View File

@ -0,0 +1,442 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "s4.hpp"
namespace xsk::gsc::s4
{
std::unordered_map<std::uint8_t, std::string> opcode_map;
std::unordered_map<std::uint16_t, std::string> function_map;
std::unordered_map<std::uint16_t, std::string> method_map;
std::unordered_map<std::uint32_t, std::string> file_map;
std::unordered_map<std::uint32_t, std::string> token_map;
std::unordered_map<std::string, std::uint8_t> opcode_map_rev;
std::unordered_map<std::string, std::uint16_t> function_map_rev;
std::unordered_map<std::string, std::uint16_t> method_map_rev;
std::unordered_map<std::string, std::uint32_t> file_map_rev;
std::unordered_map<std::string, std::uint32_t> token_map_rev;
auto resolver::opcode_id(const std::string& name) -> std::uint8_t
{
const auto itr = opcode_map_rev.find(name);
if (itr != opcode_map_rev.end())
{
return itr->second;
}
throw gsc::error(utils::string::va("Couldn't resolve opcode id for name '%s'!", name.data()));
}
auto resolver::opcode_name(std::uint8_t id) -> std::string
{
const auto itr = opcode_map.find(id);
if (itr != opcode_map.end())
{
return itr->second;
}
throw gsc::error(utils::string::va("Couldn't resolve opcode name for id '0x%hhX'!", id));
}
auto resolver::function_id(const std::string& name) -> std::uint16_t
{
const auto itr = function_map_rev.find(name);
if (itr != function_map_rev.end())
{
return itr->second;
}
throw gsc::error(utils::string::va("Couldn't resolve builtin function id for name '%s'!", name.data()));
}
auto resolver::function_name(std::uint16_t id) -> std::string
{
const auto itr = function_map.find(id);
if (itr != function_map.end())
{
return itr->second;
}
return utils::string::va("_func_%04X", id);
// throw gsc::error(utils::string::va("Couldn't resolve builtin function name for id '%i'!", id));
}
auto resolver::method_id(const std::string& name) -> std::uint16_t
{
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return itr->second;
}
throw gsc::error(utils::string::va("Couldn't resolve builtin method id for name '%s'!", name.data()));
}
auto resolver::method_name(std::uint16_t id) -> std::string
{
const auto itr = method_map.find(id);
if (itr != method_map.end())
{
return itr->second;
}
return utils::string::va("_meth_%04X", id);
// throw gsc::error(utils::string::va("Couldn't resolve builtin method name for id '%i'!", id));
}
auto resolver::file_id(const std::string& name) -> std::uint32_t
{
const auto itr = file_map_rev.find(name);
if (itr != file_map_rev.end())
{
return itr->second;
}
return 0;
}
auto resolver::file_name(std::uint32_t id) -> std::string
{
const auto itr = file_map.find(id);
if (itr != file_map.end())
{
return itr->second;
}
return utils::string::va("_ID%i", id);
}
auto resolver::token_id(const std::string& name) -> std::uint32_t
{
const auto itr = token_map_rev.find(name);
if (itr != token_map_rev.end())
{
return itr->second;
}
return 0;
}
auto resolver::token_name(std::uint32_t id) -> std::string
{
const auto itr = token_map.find(id);
if (itr != token_map.end())
{
return itr->second;
}
return utils::string::va("_ID%i", id);
}
auto resolver::find_function(const std::string& name) -> bool
{
const auto itr = function_map_rev.find(name);
if (itr != function_map_rev.end())
{
return true;
}
return false;
}
auto resolver::find_method(const std::string& name) -> bool
{
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return true;
}
return false;
}
const std::array<gsc::pair_8C, 190> opcode_list
{{
{ std::uint8_t(opcode::OP_CastFieldObject), "CAST_FIELD_OBJECT" },
{ std::uint8_t(opcode::OP_SetLocalVariableFieldCached), "SET_LOCAL_VARIABLE_FIELD_CACHED" },
{ std::uint8_t(opcode::OP_plus), "PLUS" },
{ std::uint8_t(opcode::OP_RemoveLocalVariables), "REMOVE_LOCAL_VARIABLES" },
{ std::uint8_t(opcode::OP_EvalSelfFieldVariableRef), "EVAL_SELF_FIELD_VARIABLE_REF" },
{ std::uint8_t(opcode::OP_ScriptFarMethodChildThreadCall), "SCRIPT_FAR_METHOD_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_GetGameRef), "GET_GAME_REF" },
{ std::uint8_t(opcode::OP_EvalAnimFieldVariable), "EVAL_ANIM_FIELD_VARIABLE" },
{ std::uint8_t(opcode::OP_EvalLevelFieldVariableRef), "EVAL_LEVEL_FIELD_VARIABLE_REF" },
{ std::uint8_t(opcode::OP_GetThisthread), "GET_THISTHREAD" },
{ std::uint8_t(opcode::OP_greater), "GREATER" },
{ std::uint8_t(opcode::OP_waittillmatch), "WAITTILLMATCH" },
{ std::uint8_t(opcode::OP_shift_right), "SHIFT_RIGHT" },
{ std::uint8_t(opcode::OP_dec), "DECREMENT" },
{ std::uint8_t(opcode::OP_JumpOnTrue), "JUMP_ON_TRUE" },
{ std::uint8_t(opcode::OP_bit_or), "BIT_OR" },
{ std::uint8_t(opcode::OP_equality), "EQUALITY" },
{ std::uint8_t(opcode::OP_ClearLocalVariableFieldCached0), "CLEAR_LOCAL_VARIABLE_FIELD_CACHED_0" },
{ std::uint8_t(opcode::OP_notify), "NOTIFY" },
{ std::uint8_t(opcode::OP_GetVector), "GET_VECTOR" },
{ std::uint8_t(opcode::OP_ScriptMethodChildThreadCallPointer), "SCRIPT_METHOD_CHILD_THREAD_CALL_POINTER" },
{ std::uint8_t(opcode::OP_PreScriptCall), "PRE_SCRIPT_CALL" },
{ std::uint8_t(opcode::OP_GetByte), "GET_BYTE" },
{ std::uint8_t(opcode::OP_ScriptFarThreadCall), "SCRIPT_FAR_THREAD_CALL" },
{ std::uint8_t(opcode::OP_SetSelfFieldVariableField), "SET_SELF_FIELD_VARIABLE_FIELD" },
{ std::uint8_t(opcode::OP_JumpOnFalseExpr), "JUMP_ON_FALSE_EXPR" },
{ std::uint8_t(opcode::OP_GetUndefined), "GET_UNDEFINED" },
{ std::uint8_t(opcode::OP_jumpback), "JUMP_BACK" },
{ std::uint8_t(opcode::OP_JumpOnTrueExpr), "JUMP_ON_TRUE_EXPR" },
{ std::uint8_t(opcode::OP_CallBuiltin0),"CALL_BUILTIN_FUNC_0" },
{ std::uint8_t(opcode::OP_CallBuiltin1),"CALL_BUILTIN_FUNC_1" },
{ std::uint8_t(opcode::OP_CallBuiltin2),"CALL_BUILTIN_FUNC_2" },
{ std::uint8_t(opcode::OP_CallBuiltin3),"CALL_BUILTIN_FUNC_3" },
{ std::uint8_t(opcode::OP_CallBuiltin4),"CALL_BUILTIN_FUNC_4" },
{ std::uint8_t(opcode::OP_CallBuiltin5),"CALL_BUILTIN_FUNC_5" },
{ std::uint8_t(opcode::OP_CallBuiltin),"CALL_BUILTIN_FUNC" },
{ std::uint8_t(opcode::OP_SetLocalVariableFieldCached0), "SET_LOCAL_VARIABLE_FIELD_CACHED_0" },
{ std::uint8_t(opcode::OP_ClearFieldVariable), "CLEAR_FIELD_VARIABLE" },
{ std::uint8_t(opcode::OP_GetLevel), "GET_LEVEL" },
{ std::uint8_t(opcode::OP_size), "SIZE" },
{ std::uint8_t(opcode::OP_SafeSetWaittillVariableFieldCached), "SAFE_SET_WAITTILL_VARIABLE_FIELD_CACHED" },
{ std::uint8_t(opcode::OP_ScriptLocalMethodThreadCall), "SCRIPT_LOCAL_METHOD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_AddArray), "ADD_ARRAY" },
{ std::uint8_t(opcode::OP_endon), "ENDON" },
{ std::uint8_t(opcode::OP_EvalFieldVariable), "EVAL_FIELD_VARIABLE" },
{ std::uint8_t(opcode::OP_shift_left), "SHIFT_LEFT" },
{ std::uint8_t(opcode::OP_EvalLocalArrayRefCached0), "EVAL_LOCAL_ARRAY_REF_CACHED_0" },
{ std::uint8_t(opcode::OP_Return), "RETURN" },
{ std::uint8_t(opcode::OP_CreateLocalVariable), "CREATE_LOCAL_VARIABLE" },
{ std::uint8_t(opcode::OP_SafeSetVariableFieldCached0), "SAFE_SET_VARIABLE_FIELD_CACHED_0" },
{ std::uint8_t(opcode::OP_GetBuiltinFunction), "GET_BUILTIN_FUNCTION" },
{ std::uint8_t(opcode::OP_ScriptLocalMethodCall), "SCRIPT_LOCAL_METHOD_CALL" },
{ std::uint8_t(opcode::OP_CallBuiltinMethodPointer), "CALL_BUILTIN_METHOD_POINTER" },
{ std::uint8_t(opcode::OP_ScriptLocalChildThreadCall), "SCRIPT_LOCAL_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_GetSelfObject), "GET_SELF_OBJECT" },
{ std::uint8_t(opcode::OP_GetGame), "GET_GAME" },
{ std::uint8_t(opcode::OP_SetLevelFieldVariableField), "SET_LEVEL_FIELD_VARIABLE_FIELD" },
{ std::uint8_t(opcode::OP_EvalArray), "EVAL_ARRAY" },
{ std::uint8_t(opcode::OP_GetSelf), "GET_SELF" },
{ std::uint8_t(opcode::OP_End), "END" },
{ std::uint8_t(opcode::OP_EvalSelfFieldVariable), "EVAL_SELF_FIELD_VARIABLE" },
{ std::uint8_t(opcode::OP_less_equal), "LESS_EQUAL" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached0), "EVAL_LOCAL_VARIABLE_CACHED_0" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached1), "EVAL_LOCAL_VARIABLE_CACHED_1" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached2), "EVAL_LOCAL_VARIABLE_CACHED_2" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached3), "EVAL_LOCAL_VARIABLE_CACHED_3" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached4), "EVAL_LOCAL_VARIABLE_CACHED_4" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached5), "EVAL_LOCAL_VARIABLE_CACHED_5" },
{ std::uint8_t(opcode::OP_EvalLocalVariableCached), "EVAL_LOCAL_VARIABLE_CACHED" },
{ std::uint8_t(opcode::OP_EvalNewLocalArrayRefCached0), "EVAL_NEW_LOCAL_ARRAY_REF_CACHED_0" },
{ std::uint8_t(opcode::OP_ScriptChildThreadCallPointer), "SCRIPT_CHILD_THREAD_CALL_POINTER" },
{ std::uint8_t(opcode::OP_EvalLocalVariableObjectCached), "EVAL_LOCAL_VARIABLE_OBJECT_CACHED" },
{ std::uint8_t(opcode::OP_ScriptLocalThreadCall), "SCRIPT_LOCAL_THREAD_CALL" },
{ std::uint8_t(opcode::OP_GetInteger), "GET_INTEGER" },
{ std::uint8_t(opcode::OP_ScriptMethodCallPointer), "SCRIPT_METHOD_CALL_POINTER" },
{ std::uint8_t(opcode::OP_checkclearparams), "CHECK_CLEAR_PARAMS" },
{ std::uint8_t(opcode::OP_SetAnimFieldVariableField), "SET_ANIM_FIELD_VARIABLE_FIELD" },
{ std::uint8_t(opcode::OP_waittillmatch2), "WAITTILLMATCH2" },
{ std::uint8_t(opcode::OP_minus), "MINUS" },
{ std::uint8_t(opcode::OP_ScriptLocalFunctionCall2), "SCRIPT_LOCAL_FUNCTION_CALL2" },
{ std::uint8_t(opcode::OP_GetNegUnsignedShort), "GET_NEG_USHORT" },
{ std::uint8_t(opcode::OP_GetNegByte), "GET_NEG_BYTE" },
{ std::uint8_t(opcode::OP_SafeCreateVariableFieldCached), "SAFE_CREATE_VARIABLE_FIELD_CACHED" },
{ std::uint8_t(opcode::OP_greater_equal), "GREATER_EQUAL" },
{ std::uint8_t(opcode::OP_vector), "VECTOR" },
{ std::uint8_t(opcode::OP_GetBuiltinMethod), "GET_BUILTIN_METHOD" },
{ std::uint8_t(opcode::OP_endswitch), "END_SWITCH" },
{ std::uint8_t(opcode::OP_ClearArray), "CLEAR_ARRAY" },
{ std::uint8_t(opcode::OP_DecTop), "DECREMENT_TOP" },
{ std::uint8_t(opcode::OP_CastBool), "CAST_BOOL" },
{ std::uint8_t(opcode::OP_EvalArrayRef), "EVAL_ARRAY_REF" },
{ std::uint8_t(opcode::OP_SetNewLocalVariableFieldCached0), "SET_NEW_LOCAL_VARIABLE_FIELD_CACHED_0" },
{ std::uint8_t(opcode::OP_GetZero), "GET_ZERO" },
{ std::uint8_t(opcode::OP_wait), "WAIT" },
{ std::uint8_t(opcode::OP_waittill), "WAITTILL" },
{ std::uint8_t(opcode::OP_GetIString), "GET_ISTRING" },
{ std::uint8_t(opcode::OP_ScriptFarFunctionCall), "SCRIPT_FAR_FUNCTION_CALL" },
{ std::uint8_t(opcode::OP_GetAnimObject), "GET_ANIM_OBJECT" },
{ std::uint8_t(opcode::OP_GetAnimTree), "GET_ANIMTREE" },
{ std::uint8_t(opcode::OP_EvalLocalArrayCached), "EVAL_LOCAL_ARRAY_CACHED" },
{ std::uint8_t(opcode::OP_mod), "MOD" },
{ std::uint8_t(opcode::OP_ScriptFarMethodThreadCall), "SCRIPT_FAR_METHOD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_GetUnsignedShort), "GET_USHORT" },
{ std::uint8_t(opcode::OP_clearparams), "CLEAR_PARAMS" },
{ std::uint8_t(opcode::OP_ScriptMethodThreadCallPointer), "SCRIPT_METHOD_THREAD_CALL_POINTER" },
{ std::uint8_t(opcode::OP_ScriptFunctionCallPointer), "SCRIPT_FUNCTION_CALL_POINTER" },
{ std::uint8_t(opcode::OP_EmptyArray), "EMPTY_ARRAY" },
{ std::uint8_t(opcode::OP_SafeSetVariableFieldCached), "SAFE_SET_VARIABLE_FIELD_CACHED" },
{ std::uint8_t(opcode::OP_ClearVariableField), "CLEAR_VARIABLE_FIELD" },
{ std::uint8_t(opcode::OP_EvalFieldVariableRef), "EVAL_FIELD_VARIABLE_REF" },
{ std::uint8_t(opcode::OP_ScriptLocalMethodChildThreadCall), "SCRIPT_LOCAL_METHOD_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_EvalNewLocalVariableRefCached0), "EVAL_NEW_LOCAL_VARIABLE_REF_CACHED_0" },
{ std::uint8_t(opcode::OP_GetFloat), "GET_FLOAT" },
{ std::uint8_t(opcode::OP_EvalLocalVariableRefCached), "EVAL_LOCAL_VARIABLE_REF_CACHED" },
{ std::uint8_t(opcode::OP_JumpOnFalse), "JUMP_ON_FALSE" },
{ std::uint8_t(opcode::OP_BoolComplement), "BOOL_COMPLEMENT" },
{ std::uint8_t(opcode::OP_ScriptThreadCallPointer), "SCRIPT_THREAD_CALL_POINTER" },
{ std::uint8_t(opcode::OP_ScriptFarFunctionCall2), "SCRIPT_FAR_FUNCTION_CALL2" },
{ std::uint8_t(opcode::OP_less), "LESS" },
{ std::uint8_t(opcode::OP_BoolNot), "BOOL_NOT" },
{ std::uint8_t(opcode::OP_waittillFrameEnd), "WAITTILLFRAMEEND" },
{ std::uint8_t(opcode::OP_waitframe), "WAITFRAME" },
{ std::uint8_t(opcode::OP_GetString), "GET_STRING" },
{ std::uint8_t(opcode::OP_EvalLevelFieldVariable), "EVAL_LEVEL_FIELD_VARIABLE" },
{ std::uint8_t(opcode::OP_GetLevelObject), "GET_LEVEL_OBJECT" },
{ std::uint8_t(opcode::OP_inc), "INCREMENT" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod0),"CALL_BUILTIN_METHOD_0" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod1),"CALL_BUILTIN_METHOD_1" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod2),"CALL_BUILTIN_METHOD_2" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod3),"CALL_BUILTIN_METHOD_3" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod4),"CALL_BUILTIN_METHOD_4" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod5),"CALL_BUILTIN_METHOD_5" },
{ std::uint8_t(opcode::OP_CallBuiltinMethod),"CALL_BUILTIN_METHOD" },
{ std::uint8_t(opcode::OP_GetAnim), "GET_ANIM" },
{ std::uint8_t(opcode::OP_switch), "SWITCH" },
{ std::uint8_t(opcode::OP_SetVariableField), "SET_VARIABLE_FIELD" },
{ std::uint8_t(opcode::OP_divide), "DIV" },
{ std::uint8_t(opcode::OP_GetLocalFunction), "GET_LOCAL_FUNCTION" },
{ std::uint8_t(opcode::OP_ScriptFarChildThreadCall), "SCRIPT_FAR_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_multiply), "MUL" },
{ std::uint8_t(opcode::OP_ClearLocalVariableFieldCached), "CLEAR_LOCAL_VARIABLE_FIELD_CACHED" },
{ std::uint8_t(opcode::OP_EvalAnimFieldVariableRef), "EVAL_ANIM_FIELD_VARIABLE_REF" },
{ std::uint8_t(opcode::OP_EvalLocalArrayRefCached), "EVAL_LOCAL_ARRAY_REF_CACHED" },
{ std::uint8_t(opcode::OP_EvalLocalVariableRefCached0), "EVAL_LOCAL_VARIABLE_REF_CACHED_0" },
{ std::uint8_t(opcode::OP_bit_and), "BIT_AND" },
{ std::uint8_t(opcode::OP_GetAnimation), "GET_ANIMATION" },
{ std::uint8_t(opcode::OP_GetFarFunction), "GET_FAR_FUNCTION" },
{ std::uint8_t(opcode::OP_CallBuiltinPointer), "CALL_BUILTIN_POINTER" },
{ std::uint8_t(opcode::OP_jump), "JUMP" },
{ std::uint8_t(opcode::OP_voidCodepos), "VOIDCODEPOS" },
{ std::uint8_t(opcode::OP_ScriptFarMethodCall), "SCRIPT_FAR_METHOD_CALL" },
{ std::uint8_t(opcode::OP_inequality), "INEQUALITY" },
{ std::uint8_t(opcode::OP_ScriptLocalFunctionCall), "SCRIPT_LOCAL_FUNCTION_CALL" },
{ std::uint8_t(opcode::OP_bit_ex_or), "BIT_EXOR" },
{ std::uint8_t(opcode::OP_NOP), "NOP" },
{ std::uint8_t(opcode::OP_abort), "ABORT" },
{ std::uint8_t(opcode::OP_object), "OBJECT" },
{ std::uint8_t(opcode::OP_thread_object), "THREAD_OBJECT" },
{ std::uint8_t(opcode::OP_EvalLocalVariable), "EVAL_LOCAL_VARIABLE" },
{ std::uint8_t(opcode::OP_EvalLocalVariableRef), "EVAL_LOCAL_VARIABLE_REF" },
{ std::uint8_t(opcode::OP_prof_begin), "PROF_BEGIN" },
{ std::uint8_t(opcode::OP_prof_end), "PROF_END" },
{ std::uint8_t(opcode::OP_breakpoint), "BREAKPOINT" },
{ std::uint8_t(opcode::OP_assignmentBreakpoint), "ASSIGN_BREAKPOINT" },
{ std::uint8_t(opcode::OP_manualAndAssignmentBreakpoint), "MANUAL_AND_ASSIGN_BREAKPOINT" },
{ std::uint8_t(opcode::OP_BoolNotAfterAnd), "BOOL_NOT_AFTER_AND" },
{ std::uint8_t(opcode::OP_FormalParams), "FORMAL_PARAMS" },
{ std::uint8_t(opcode::OP_IsDefined), "IS_DEFINED" },
{ std::uint8_t(opcode::OP_IsTrue), "IS_TRUE" },
{ std::uint8_t(opcode::OP_NativeGetLocalFunction), "NATIVE_GET_LOCAL_FUNCTION" },
{ std::uint8_t(opcode::OP_NativeLocalFunctionCall), "NATIVE_LOCAL_FUNCTION_CALL" },
{ std::uint8_t(opcode::OP_NativeLocalFunctionCall2), "NATIVE_LOCAL_FUNCTION_CALL2" },
{ std::uint8_t(opcode::OP_NativeLocalMethodCall), "NATIVE_LOCAL_METHOD_CALL" },
{ std::uint8_t(opcode::OP_NativeLocalFunctionThreadCall), "NATIVE_LOCAL_FUNCTION_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeLocalMethodThreadCall), "NATIVE_LOCAL_METHOD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeLocalFunctionChildThreadCall), "NATIVE_LOCAL_FUNCTION_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeLocalMethodChildThreadCall), "NATIVE_LOCAL_METHOD_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeGetFarFunction), "NATIVE_GET_FAR_FUNCTION" },
{ std::uint8_t(opcode::OP_NativeFarFunctionCall), "NATIVE_FAR_FUNCTION_CALL" },
{ std::uint8_t(opcode::OP_NativeFarFunctionCall2), "NATIVE_FAR_FUNCTION_CALL2" },
{ std::uint8_t(opcode::OP_NativeFarMethodCall), "NATIVE_FAR_METHOD_CALL" },
{ std::uint8_t(opcode::OP_NativeFarFunctionThreadCall), "NATIVE_FAR_FUNCTION_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeFarMethodThreadCall), "NATIVE_FAR_METHOD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeFarFunctionChildThreadCall), "NATIVE_FAR_FUNCTION_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_NativeFarMethodChildThreadCall), "NATIVE_FAR_METHOD_CHILD_THREAD_CALL" },
{ std::uint8_t(opcode::OP_EvalNewLocalArrayRefCached0_Precompiled), "EVAL_NEW_LOCAL_ARRAY_REF_CACHED_0_PRECOMPILED" },
{ std::uint8_t(opcode::OP_SetNewLocalVariableFieldCached0_Precompiled), "SET_NEW_LOCAL_VARIABLE_FIELD_CACHED_0_PRECOMPILED" },
{ std::uint8_t(opcode::OP_CreateLocalVariable_Precompiled), "CREATE_LOCAL_VARIABLE_PRECOMPILED" },
{ std::uint8_t(opcode::OP_SafeCreateVariableFieldCached_Precompiled), "SAFE_CREATE_VARIABLE_FIELD_CACHED_PRECOMPILED" },
{ std::uint8_t(opcode::OP_FormalParams_Precompiled), "FORMAL_PARAMS_PRECOMPILED" },
}};
const std::array<gsc::pair_16C, 4> function_list
{{
{ 0x08F, "getdvar" },
{ 0x09B, "getfirstarraykey" },
{ 0x0B1, "getnextarraykey" },
{ 0x126, "isusingmatchrulesdata" },
}};
const std::array<gsc::pair_16C, 1> method_list
{{
{ 0x0, "null" },
}};
const std::array<gsc::pair_32C, 1> file_list
{{
{ 0x0, "null" },
}};
const std::array<gsc::pair_32C, 4> token_list
{{
{ 0x00, "" }, // VOID
{ 0x01, "pl#" }, // PL
{ 0x02, "-" }, // MINUS
// { 0x03, "" }, // RADIUS_TYPO
{ 0x04, ":" }, // NOTE_COLON
}};
struct __init__
{
__init__()
{
static bool init = false;
if(init) return;
init = true;
opcode_map.reserve(opcode_list.size());
opcode_map_rev.reserve(opcode_list.size());
function_map.reserve(function_list.size());
function_map_rev.reserve(function_list.size());
method_map.reserve(method_list.size());
method_map_rev.reserve(method_list.size());
file_map.reserve(file_list.size());
file_map_rev.reserve(file_list.size());
token_map.reserve(token_list.size());
token_map_rev.reserve(token_list.size());
for(const auto& entry : opcode_list)
{
opcode_map.insert({ entry.key, entry.value });
opcode_map_rev.insert({ entry.value, entry.key });
}
for(const auto& entry : function_list)
{
function_map.insert({ entry.key, entry.value });
function_map_rev.insert({ entry.value, entry.key });
}
for(const auto& entry : method_list)
{
method_map.insert({ entry.key, entry.value });
method_map_rev.insert({ entry.value, entry.key });
}
for(const auto& entry : file_list)
{
file_map.insert({ entry.key, entry.value });
file_map_rev.insert({ entry.value, entry.key });
}
for(const auto& entry : token_list)
{
token_map.insert({ entry.key, entry.value });
token_map_rev.insert({ entry.value, entry.key });
}
}
};
__init__ _;
} // namespace xsk::gsc::s4

33
src/s4/xsk/resolver.hpp Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::s4
{
class resolver
{
public:
static auto opcode_id(const std::string& name) -> std::uint8_t;
static auto opcode_name(std::uint8_t id) -> std::string;
static auto function_id(const std::string& name) -> std::uint16_t;
static auto function_name(std::uint16_t id) -> std::string;
static auto method_id(const std::string& name) -> std::uint16_t;
static auto method_name(std::uint16_t id) -> std::string;
static auto file_id(const std::string& name) -> std::uint32_t;
static auto file_name(std::uint32_t id) -> std::string;
static auto token_id(const std::string& name) -> std::uint32_t;
static auto token_name(std::uint32_t id) -> std::string;
static auto find_function(const std::string& name) -> bool;
static auto find_method(const std::string& name) -> bool;
};
} // namespace xsk::gsc::s4

229
src/s4/xsk/s4.cpp Normal file
View File

@ -0,0 +1,229 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "s4.hpp"
namespace xsk::gsc::s4
{
auto opcode_size(std::uint8_t id) -> std::uint32_t
{
switch (opcode(id))
{
case opcode::OP_CastFieldObject:
case opcode::OP_plus:
case opcode::OP_GetGameRef:
case opcode::OP_GetThisthread:
case opcode::OP_greater:
case opcode::OP_shift_right:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_equality:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_notify:
case opcode::OP_PreScriptCall:
case opcode::OP_GetUndefined:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_size:
case opcode::OP_AddArray:
case opcode::OP_endon:
case opcode::OP_shift_left:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_Return:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetSelfObject:
case opcode::OP_GetGame:
case opcode::OP_EvalArray:
case opcode::OP_GetSelf:
case opcode::OP_End:
case opcode::OP_less_equal:
case opcode::OP_EvalLocalVariableCached0:
case opcode::OP_EvalLocalVariableCached1:
case opcode::OP_EvalLocalVariableCached2:
case opcode::OP_EvalLocalVariableCached3:
case opcode::OP_EvalLocalVariableCached4:
case opcode::OP_EvalLocalVariableCached5:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_checkclearparams:
case opcode::OP_waittillmatch2:
case opcode::OP_minus:
case opcode::OP_greater_equal:
case opcode::OP_vector:
case opcode::OP_ClearArray:
case opcode::OP_DecTop:
case opcode::OP_CastBool:
case opcode::OP_EvalArrayRef:
case opcode::OP_GetZero:
case opcode::OP_wait:
case opcode::OP_waittill:
case opcode::OP_GetAnimObject:
case opcode::OP_mod:
case opcode::OP_clearparams:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_EmptyArray:
case opcode::OP_ClearVariableField:
case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_BoolComplement:
case opcode::OP_less:
case opcode::OP_BoolNot:
case opcode::OP_waittillFrameEnd:
case opcode::OP_waitframe:
case opcode::OP_GetLevelObject:
case opcode::OP_inc:
case opcode::OP_GetAnim:
case opcode::OP_SetVariableField:
case opcode::OP_divide:
case opcode::OP_multiply:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_bit_and:
case opcode::OP_voidCodepos:
case opcode::OP_inequality:
case opcode::OP_bit_ex_or:
/*
case opcode::OP_NOP:
case opcode::OP_abort:
case opcode::OP_object:
case opcode::OP_thread_object:
case opcode::OP_EvalLocalVariable:
case opcode::OP_EvalLocalVariableRef:
case opcode::OP_breakpoint:
case opcode::OP_assignmentBreakpoint:
case opcode::OP_manualAndAssignmentBreakpoint:
*/
case opcode::OP_BoolNotAfterAnd:
case opcode::OP_IsDefined:
case opcode::OP_IsTrue:
return 1;
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_GetByte:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_CreateLocalVariable:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_ScriptChildThreadCallPointer:
case opcode::OP_EvalLocalVariableObjectCached:
case opcode::OP_GetNegByte:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_GetAnimTree:
case opcode::OP_EvalLocalArrayCached:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_CallBuiltinPointer:
// case opcode::OP_prof_end:
case opcode::OP_FormalParams:
/*
case opcode::OP_EvalNewLocalArrayRefCached0_Precompiled:
case opcode::OP_SetNewLocalVariableFieldCached0_Precompiled:
case opcode::OP_CreateLocalVariable_Precompiled:
case opcode::OP_SafeCreateVariableFieldCached_Precompiled:
case opcode::OP_FormalParams_Precompiled:
*/
return 2;
case opcode::OP_waittillmatch:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_jumpback:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_CallBuiltin0:
case opcode::OP_CallBuiltin1:
case opcode::OP_CallBuiltin2:
case opcode::OP_CallBuiltin3:
case opcode::OP_CallBuiltin4:
case opcode::OP_CallBuiltin5:
case opcode::OP_GetBuiltinFunction:
case opcode::OP_GetNegUnsignedShort:
case opcode::OP_GetBuiltinMethod:
case opcode::OP_endswitch:
case opcode::OP_GetUnsignedShort:
case opcode::OP_JumpOnFalse:
case opcode::OP_CallBuiltinMethod0:
case opcode::OP_CallBuiltinMethod1:
case opcode::OP_CallBuiltinMethod2:
case opcode::OP_CallBuiltinMethod3:
case opcode::OP_CallBuiltinMethod4:
case opcode::OP_CallBuiltinMethod5:
return 3;
case opcode::OP_CallBuiltin:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_CallBuiltinMethod:
case opcode::OP_GetLocalFunction:
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarMethodCall:
case opcode::OP_ScriptLocalFunctionCall:
/*
case opcode::OP_NativeGetLocalFunction:
case opcode::OP_NativeLocalFunctionCall:
case opcode::OP_NativeLocalFunctionCall2:
case opcode::OP_NativeLocalMethodCall:
case opcode::OP_NativeGetFarFunction:
case opcode::OP_NativeFarFunctionCall:
case opcode::OP_NativeFarFunctionCall2:
case opcode::OP_NativeFarMethodCall:
*/
return 4;
case opcode::OP_EvalSelfFieldVariableRef:
case opcode::OP_ScriptFarMethodChildThreadCall:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_SetSelfFieldVariableField:
case opcode::OP_ClearFieldVariable:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_EvalFieldVariable:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_GetInteger:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_GetIString:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_ScriptLocalMethodChildThreadCall:
case opcode::OP_GetFloat:
case opcode::OP_GetString:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_switch:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_jump:
/*
case opcode::OP_NativeLocalFunctionThreadCall:
case opcode::OP_NativeLocalMethodThreadCall:
case opcode::OP_NativeLocalFunctionChildThreadCall:
case opcode::OP_NativeLocalMethodChildThreadCall:
case opcode::OP_NativeFarFunctionThreadCall:
case opcode::OP_NativeFarMethodThreadCall:
case opcode::OP_NativeFarFunctionChildThreadCall:
case opcode::OP_NativeFarMethodChildThreadCall:
*/
return 5;
/*
case opcode::OP_prof_begin:
return 6;
*/
case opcode::OP_GetAnimation:
return 9;
case opcode::OP_GetVector:
return 13;
default:
throw std::runtime_error("Couldn't resolve instruction size for " + std::to_string(id));
}
}
} // namespace xsk::gsc::s4

216
src/s4/xsk/s4.hpp Normal file
View File

@ -0,0 +1,216 @@
// Copyright 2021 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
#include "utils/xsk/utils.hpp"
#include "assembler.hpp"
#include "disassembler.hpp"
#include "compiler.hpp"
#include "decompiler.hpp"
#include "resolver.hpp"
namespace xsk::gsc::s4
{
enum class opcode : std::uint8_t
{
OP_CastFieldObject = 0x0,
OP_SetLocalVariableFieldCached = 0x1,
OP_plus = 0x2,
OP_RemoveLocalVariables = 0x3,
OP_EvalSelfFieldVariableRef = 0x4,
OP_ScriptFarMethodChildThreadCall = 0x5,
OP_GetGameRef = 0x6,
OP_EvalAnimFieldVariable = 0x7,
OP_EvalLevelFieldVariableRef = 0x8,
OP_GetThisthread = 0x9,
OP_greater = 0xA,
OP_waittillmatch = 0xB,
OP_shift_right = 0xC,
OP_dec = 0xD,
OP_JumpOnTrue = 0xE,
OP_bit_or = 0xF,
OP_equality = 0x10,
OP_ClearLocalVariableFieldCached0 = 0x11,
OP_notify = 0x12,
OP_GetVector = 0x13,
OP_ScriptMethodChildThreadCallPointer = 0x14,
OP_PreScriptCall = 0x15,
OP_GetByte = 0x16,
OP_ScriptFarThreadCall = 0x17,
OP_SetSelfFieldVariableField = 0x18,
OP_JumpOnFalseExpr = 0x19,
OP_GetUndefined = 0x1A,
OP_jumpback = 0x1B,
OP_JumpOnTrueExpr = 0x1C,
OP_CallBuiltin0 = 0x1D,
OP_CallBuiltin1 = 0x1E,
OP_CallBuiltin2 = 0x1F,
OP_CallBuiltin3 = 0x20,
OP_CallBuiltin4 = 0x21,
OP_CallBuiltin5 = 0x22,
OP_CallBuiltin = 0x23,
OP_SetLocalVariableFieldCached0 = 0x24,
OP_ClearFieldVariable = 0x25,
OP_GetLevel = 0x26,
OP_size = 0x27,
OP_SafeSetWaittillVariableFieldCached = 0x28,
OP_ScriptLocalMethodThreadCall = 0x29,
OP_AddArray = 0x2A,
OP_endon = 0x2B,
OP_EvalFieldVariable = 0x2C,
OP_shift_left = 0x2D,
OP_EvalLocalArrayRefCached0 = 0x2E,
OP_Return = 0x2F,
OP_CreateLocalVariable = 0x30,
OP_SafeSetVariableFieldCached0 = 0x31,
OP_GetBuiltinFunction = 0x32,
OP_ScriptLocalMethodCall = 0x33,
OP_CallBuiltinMethodPointer = 0x34,
OP_ScriptLocalChildThreadCall = 0x35,
OP_GetSelfObject = 0x36,
OP_GetGame = 0x37,
OP_SetLevelFieldVariableField = 0x38,
OP_EvalArray = 0x39,
OP_GetSelf = 0x3A,
OP_End = 0x3B,
OP_EvalSelfFieldVariable = 0x3C,
OP_less_equal = 0x3D,
OP_EvalLocalVariableCached0 = 0x3E,
OP_EvalLocalVariableCached1 = 0x3F,
OP_EvalLocalVariableCached2 = 0x40,
OP_EvalLocalVariableCached3 = 0x41,
OP_EvalLocalVariableCached4 = 0x42,
OP_EvalLocalVariableCached5 = 0x43,
OP_EvalLocalVariableCached = 0x44,
OP_EvalNewLocalArrayRefCached0 = 0x45,
OP_ScriptChildThreadCallPointer = 0x46,
OP_EvalLocalVariableObjectCached = 0x47,
OP_ScriptLocalThreadCall = 0x48,
OP_GetInteger = 0x49,
OP_ScriptMethodCallPointer = 0x4A,
OP_checkclearparams = 0x4B,
OP_SetAnimFieldVariableField = 0x4C,
OP_waittillmatch2 = 0x4D,
OP_minus = 0x4E,
OP_ScriptLocalFunctionCall2 = 0x4F,
OP_GetNegUnsignedShort = 0x50,
OP_GetNegByte = 0x51,
OP_SafeCreateVariableFieldCached = 0x52,
OP_greater_equal = 0x53,
OP_vector = 0x54,
OP_GetBuiltinMethod = 0x55,
OP_endswitch = 0x56,
OP_ClearArray = 0x57,
OP_DecTop = 0x58,
OP_CastBool = 0x59,
OP_EvalArrayRef = 0x5A,
OP_SetNewLocalVariableFieldCached0 = 0x5B,
OP_GetZero = 0x5C,
OP_wait = 0x5D,
OP_waittill = 0x5E,
OP_GetIString = 0x5F,
OP_ScriptFarFunctionCall = 0x60,
OP_GetAnimObject = 0x61,
OP_GetAnimTree = 0x62,
OP_EvalLocalArrayCached = 0x63,
OP_mod = 0x64,
OP_ScriptFarMethodThreadCall = 0x65,
OP_GetUnsignedShort = 0x66,
OP_clearparams = 0x67,
OP_ScriptMethodThreadCallPointer = 0x68,
OP_ScriptFunctionCallPointer = 0x69,
OP_EmptyArray = 0x6A,
OP_SafeSetVariableFieldCached = 0x6B,
OP_ClearVariableField = 0x6C,
OP_EvalFieldVariableRef = 0x6D,
OP_ScriptLocalMethodChildThreadCall = 0x6E,
OP_EvalNewLocalVariableRefCached0 = 0x6F,
OP_GetFloat = 0x70,
OP_EvalLocalVariableRefCached = 0x71,
OP_JumpOnFalse = 0x72,
OP_BoolComplement = 0x73,
OP_ScriptThreadCallPointer = 0x74,
OP_ScriptFarFunctionCall2 = 0x75,
OP_less = 0x76,
OP_BoolNot = 0x77,
OP_waittillFrameEnd = 0x78,
OP_waitframe = 0x79,
OP_GetString = 0x7A,
OP_EvalLevelFieldVariable = 0x7B,
OP_GetLevelObject = 0x7C,
OP_inc = 0x7D,
OP_CallBuiltinMethod0 = 0x7E,
OP_CallBuiltinMethod1 = 0x7F,
OP_CallBuiltinMethod2 = 0x80,
OP_CallBuiltinMethod3 = 0x81,
OP_CallBuiltinMethod4 = 0x82,
OP_CallBuiltinMethod5 = 0x83,
OP_CallBuiltinMethod = 0x84,
OP_GetAnim = 0x85,
OP_switch = 0x86,
OP_SetVariableField = 0x87,
OP_divide = 0x88,
OP_GetLocalFunction = 0x89,
OP_ScriptFarChildThreadCall = 0x8A,
OP_multiply = 0x8B,
OP_ClearLocalVariableFieldCached = 0x8C,
OP_EvalAnimFieldVariableRef = 0x8D,
OP_EvalLocalArrayRefCached = 0x8E,
OP_EvalLocalVariableRefCached0 = 0x8F,
OP_bit_and = 0x90,
OP_GetAnimation = 0x91,
OP_GetFarFunction = 0x92,
OP_CallBuiltinPointer = 0x93,
OP_jump = 0x94,
OP_voidCodepos = 0x95,
OP_ScriptFarMethodCall = 0x96,
OP_inequality = 0x97,
OP_ScriptLocalFunctionCall = 0x98,
OP_bit_ex_or = 0x99,
OP_NOP = 0x9A,
OP_abort = 0x9B,
OP_object = 0x9C,
OP_thread_object = 0x9D,
OP_EvalLocalVariable = 0x9E,
OP_EvalLocalVariableRef = 0x9F,
OP_prof_begin = 0xA0,
OP_prof_end = 0xA1,
OP_breakpoint = 0xA2,
OP_assignmentBreakpoint = 0xA3,
OP_manualAndAssignmentBreakpoint = 0xA4,
OP_BoolNotAfterAnd = 0xA5,
OP_FormalParams = 0xA6,
OP_IsDefined = 0xA7,
OP_IsTrue = 0xA8,
OP_NativeGetLocalFunction = 0xA9,
OP_NativeLocalFunctionCall = 0xAA,
OP_NativeLocalFunctionCall2 = 0xAB,
OP_NativeLocalMethodCall = 0xAC,
OP_NativeLocalFunctionThreadCall = 0xAD,
OP_NativeLocalMethodThreadCall = 0xAE,
OP_NativeLocalFunctionChildThreadCall = 0xAF,
OP_NativeLocalMethodChildThreadCall = 0xB0,
OP_NativeGetFarFunction = 0xB1,
OP_NativeFarFunctionCall = 0xB2,
OP_NativeFarFunctionCall2 = 0xB3,
OP_NativeFarMethodCall = 0xB4,
OP_NativeFarFunctionThreadCall = 0xB5,
OP_NativeFarMethodThreadCall = 0xB6,
OP_NativeFarFunctionChildThreadCall = 0xB7,
OP_NativeFarMethodChildThreadCall = 0xB8,
OP_EvalNewLocalArrayRefCached0_Precompiled = 0xB9,
OP_SetNewLocalVariableFieldCached0_Precompiled = 0xBA,
OP_CreateLocalVariable_Precompiled = 0xBB,
OP_SafeCreateVariableFieldCached_Precompiled = 0xBC,
OP_FormalParams_Precompiled = 0xBD,
OP_count = 0xBE,
};
auto opcode_size(std::uint8_t op) -> std::uint32_t;
} // namespace xsk::gsc::s4

View File

@ -11,6 +11,7 @@
#include "iw8/xsk/iw8.hpp"
#include "s1/xsk/s1.hpp"
#include "s2/xsk/s2.hpp"
#include "s4/xsk/s4.hpp"
#include "h1/xsk/h1.hpp"
#include "h2/xsk/h2.hpp"
@ -18,7 +19,7 @@ namespace xsk::gsc
{
enum class mode { __, ASM, DISASM, COMP, DECOMP };
enum class game { __, IW5, IW6, IW7, IW8, S1, S2, H1, H2 };
enum class game { __, IW5, IW6, IW7, IW8, S1, S2, S4, H1, H2 };
std::map<std::string, mode> modes =
{
@ -36,6 +37,7 @@ std::map<std::string, game> games =
{ "-iw8", game::IW8 },
{ "-s1", game::S1 },
{ "-s2", game::S2 },
{ "-s4", game::S4 },
{ "-h1", game::H1 },
{ "-h2", game::H2 },
};
@ -76,6 +78,7 @@ auto choose_resolver_file_name(uint32_t id, game& game) -> std::string
case game::IW8: return iw8::resolver::file_name(id);
case game::S1: return s1::resolver::file_name(static_cast<std::uint16_t>(id));
case game::S2: return s2::resolver::file_name(static_cast<std::uint16_t>(id));
case game::S4: return s4::resolver::file_name(id);
case game::H1: return h1::resolver::file_name(static_cast<std::uint16_t>(id));
case game::H2: return h2::resolver::file_name(static_cast<std::uint16_t>(id));
default: return "";
@ -386,7 +389,7 @@ int parse_flags(int argc, char** argv, game& game, mode& mode, bool& zonetool)
void print_usage()
{
std::cout << "usage: gsc-tool.exe <game> <mode> <file>\n";
std::cout << " * games: -iw5, -iw6, -iw7, -s1, -s2, -h1, -h2\n";
std::cout << " * games: -iw5, -iw6, -iw7, -iw8, -s1, -s2, -s4, -h1, -h2\n";
std::cout << " * modes: -asm, -disasm, -comp, -decomp\n";
}
@ -435,6 +438,11 @@ std::uint32_t main(std::uint32_t argc, char** argv)
s2::assembler assembler;
assemble_file(assembler, file, zonetool);
}
else if (game == game::S4)
{
s4::assembler assembler;
assemble_file(assembler, file, zonetool);
}
else if (game == game::H1)
{
h1::assembler assembler;
@ -478,6 +486,11 @@ std::uint32_t main(std::uint32_t argc, char** argv)
s2::disassembler disassembler;
disassemble_file(disassembler, file, game, zonetool);
}
else if (game == game::S4)
{
s4::disassembler disassembler;
disassemble_file(disassembler, file, game, zonetool);
}
else if (game == game::H1)
{
h1::disassembler disassembler;
@ -527,6 +540,12 @@ std::uint32_t main(std::uint32_t argc, char** argv)
s2::compiler compiler;
compile_file(assembler, compiler, file ,zonetool);
}
else if (game == game::S4)
{
s4::assembler assembler;
s4::compiler compiler;
compile_file(assembler, compiler, file ,zonetool);
}
else if (game == game::H1)
{
h1::assembler assembler;
@ -578,6 +597,12 @@ std::uint32_t main(std::uint32_t argc, char** argv)
s2::decompiler decompiler;
decompile_file(disassembler, decompiler, file, game, zonetool);
}
else if (game == game::S4)
{
s4::disassembler disassembler;
s4::decompiler decompiler;
decompile_file(disassembler, decompiler, file, game, zonetool);
}
else if (game == game::H1)
{
h1::disassembler disassembler;