console support for iw6 & s1 (need fixes)

This commit is contained in:
xensik 2022-09-22 12:57:11 +02:00
parent a9e08a7171
commit 88b4539bbf
49 changed files with 41449 additions and 218 deletions

View File

@ -0,0 +1,9 @@
generate: iw6c
clean:
rm -rf ./parser.hpp
rm -rf ./parser.cpp
iw6c: parser.ypp
bison parser.ypp -Wcounterexamples
mv parser.hpp parser.cpp ../../../src/experimental/iw6c/xsk/

View File

@ -0,0 +1,888 @@
/* Copyright 2022 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 {IW6C}
%define api.namespace {xsk::gsc::iw6c}
%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 { xsk::gsc::iw6c::lexer& lexer }
%parse-param { xsk::gsc::iw6c::lexer& lexer }
%parse-param { xsk::gsc::ast::program::ptr& ast }
%code requires
{
#ifdef _MSC_VER
#pragma warning(disable:4065)
#pragma warning(disable:4127)
#endif
#include "iw6c.hpp"
namespace xsk::gsc::iw6c { class lexer; }
}
%code top
{
#include "stdafx.hpp"
#include "parser.hpp"
#include "lexer.hpp"
using namespace xsk::gsc;
xsk::gsc::iw6c::parser::symbol_type IW6Clex(xsk::gsc::iw6c::lexer& lexer);
}
%token SH_DEFINE "#define"
%token SH_UNDEF "#undef"
%token SH_IFDEF "#ifdef"
%token SH_IFNDEF "#ifndef"
%token SH_IF "#if"
%token SH_ELIF "#elif"
%token SH_ELSE "#else"
%token SH_ENDIF "#endif"
%token DEVBEGIN "/#"
%token DEVEND "#/"
%token INLINE "#inline"
%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 IF "if"
%token ELSE "else"
%token DO "do"
%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 BREAKPOINT "breakpoint"
%token PROFBEGIN "prof_begin"
%token PROFEND "prof_end"
%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_MUL "*="
%token ASSIGN_DIV "/="
%token ASSIGN_MOD "%="
%token ASSIGN_BW_OR "|="
%token ASSIGN_BW_AND "&="
%token ASSIGN_BW_EXOR "^="
%token ASSIGN_RSHIFT ">>="
%token ASSIGN_LSHIFT "<<="
%token BITWISE_OR "|"
%token BITWISE_AND "&"
%token BITWISE_EXOR "^"
%token ADD "+"
%token SUB "-"
%token MUL "*"
%token DIV "/"
%token MOD "%"
%token <std::string> PATH "path"
%token <std::string> IDENTIFIER "identifier"
%token <std::string> STRING "string literal"
%token <std::string> ISTRING "localized string"
%token <std::string> FLOAT "float"
%token <std::string> INTEGER "integer"
%type <ast::program::ptr> program
%type <ast::include::ptr> include
%type <ast::decl> declaration
%type <ast::decl_usingtree::ptr> decl_usingtree
%type <ast::decl_constant::ptr> decl_constant
%type <ast::decl_thread::ptr> decl_thread
%type <ast::stmt> stmt
%type <ast::stmt> stmt_or_dev
%type <ast::stmt_list::ptr> stmt_list
%type <ast::stmt_list::ptr> stmt_or_dev_list
%type <ast::stmt_dev::ptr> stmt_dev
%type <ast::stmt_list::ptr> stmt_block
%type <ast::stmt_expr::ptr> stmt_expr
%type <ast::stmt_call::ptr> stmt_call
%type <ast::stmt_assign::ptr> stmt_assign
%type <ast::stmt_endon::ptr> stmt_endon
%type <ast::stmt_notify::ptr> stmt_notify
%type <ast::stmt_wait::ptr> stmt_wait
%type <ast::stmt_waittill::ptr> stmt_waittill
%type <ast::stmt_waittillmatch::ptr> stmt_waittillmatch
%type <ast::stmt_waittillframeend::ptr> stmt_waittillframeend
%type <ast::stmt_if::ptr> stmt_if
%type <ast::stmt_ifelse::ptr> stmt_ifelse
%type <ast::stmt_while::ptr> stmt_while
%type <ast::stmt_dowhile::ptr> stmt_dowhile
%type <ast::stmt_for::ptr> stmt_for
%type <ast::stmt_foreach::ptr> stmt_foreach
%type <ast::stmt_switch::ptr> stmt_switch
%type <ast::stmt_case::ptr> stmt_case
%type <ast::stmt_default::ptr> stmt_default
%type <ast::stmt_break::ptr> stmt_break
%type <ast::stmt_continue::ptr> stmt_continue
%type <ast::stmt_return::ptr> stmt_return
%type <ast::stmt_breakpoint::ptr> stmt_breakpoint
%type <ast::stmt_prof_begin::ptr> stmt_prof_begin
%type <ast::stmt_prof_end::ptr> stmt_prof_end
%type <ast::expr> expr
%type <ast::expr> expr_or_empty
%type <ast::expr> expr_assign
%type <ast::expr> expr_increment
%type <ast::expr> expr_decrement
%type <ast::expr> expr_ternary
%type <ast::expr> expr_binary
%type <ast::expr> expr_primitive
%type <ast::expr_complement::ptr> expr_complement
%type <ast::expr_negate::ptr> expr_negate
%type <ast::expr_not::ptr> expr_not
%type <ast::expr_call::ptr> expr_call
%type <ast::expr_method::ptr> expr_method
%type <ast::call> expr_function
%type <ast::call> expr_pointer
%type <ast::expr_add_array::ptr> expr_add_array
%type <ast::expr_parameters::ptr> expr_parameters
%type <ast::expr_arguments::ptr> expr_arguments
%type <ast::expr_arguments::ptr> expr_arguments_no_empty
%type <ast::expr_reference::ptr> expr_reference
%type <ast::expr> expr_tuple
%type <ast::expr_tuple::ptr> expr_tuple_arguments
%type <ast::expr> expr_tuple_types
%type <ast::expr_array::ptr> expr_array
%type <ast::expr_field::ptr> expr_field
%type <ast::expr_size::ptr> expr_size
%type <ast::expr_paren::ptr> expr_paren
%type <ast::expr> expr_object
%type <ast::expr_thisthread::ptr> expr_thisthread
%type <ast::expr_empty_array::ptr> expr_empty_array
%type <ast::expr_undefined::ptr> expr_undefined
%type <ast::expr_game::ptr> expr_game
%type <ast::expr_self::ptr> expr_self
%type <ast::expr_anim::ptr> expr_anim
%type <ast::expr_level::ptr> expr_level
%type <ast::expr_animation::ptr> expr_animation
%type <ast::expr_animtree::ptr> expr_animtree
%type <ast::expr_identifier::ptr> expr_identifier_nosize
%type <ast::expr_identifier::ptr> expr_identifier
%type <ast::expr_path::ptr> expr_path
%type <ast::expr_istring::ptr> expr_istring
%type <ast::expr_string::ptr> expr_string
%type <ast::expr_vector::ptr> expr_vector
%type <ast::expr_float::ptr> expr_float
%type <ast::expr_integer::ptr> expr_integer
%type <ast::expr_false::ptr> expr_false
%type <ast::expr_true::ptr> expr_true
%nonassoc SIZEOF
%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_EXOR
%left BITWISE_AND
%left EQUALITY INEQUALITY
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%left LSHIFT RSHIFT
%left ADD SUB
%left MUL 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<ast::program>(@$); }
;
program
: program inline
{ $$ = std::move($1); }
| program include
{ $$ = std::move($1); $$->includes.push_back(std::move($2)); }
| program declaration
{ $$ = std::move($1); $$->declarations.push_back(std::move($2)); }
| inline
{ $$ = std::make_unique<ast::program>(@$); }
| include
{ $$ = std::make_unique<ast::program>(@$); $$->includes.push_back(std::move($1)); }
| declaration
{ $$ = std::make_unique<ast::program>(@$); $$->declarations.push_back(std::move($1)); }
;
inline
: INLINE expr_path SEMICOLON { lexer.push_header($2->value); }
;
include
: INCLUDE expr_path SEMICOLON
{ $$ = std::make_unique<ast::include>(@$, std::move($2)); }
;
declaration
: DEVBEGIN { $$.as_dev_begin = std::make_unique<ast::decl_dev_begin>(@$); }
| DEVEND { $$.as_dev_end = std::make_unique<ast::decl_dev_end>(@$); }
| decl_usingtree { $$.as_usingtree = std::move($1); }
| decl_constant { $$.as_constant = std::move($1); }
| decl_thread { $$.as_thread = std::move($1); }
;
decl_usingtree
: USINGTREE LPAREN expr_string RPAREN SEMICOLON
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_usingtree>(@$, std::move($3)); }
;
decl_constant
: expr_identifier ASSIGN expr SEMICOLON
{ $$ = std::make_unique<ast::decl_constant>(@$, std::move($1), std::move($3)); }
;
decl_thread
: expr_identifier LPAREN expr_parameters RPAREN stmt_block
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($1), std::move($3), std::move($5)); }
;
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_if { $$.as_if = std::move($1); }
| stmt_ifelse { $$.as_ifelse = std::move($1); }
| stmt_while { $$.as_while = std::move($1); }
| stmt_dowhile { $$.as_dowhile = 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_breakpoint { $$.as_breakpoint = std::move($1); }
| stmt_prof_begin { $$.as_prof_begin = std::move($1); }
| stmt_prof_end { $$.as_prof_end = std::move($1); }
;
stmt_or_dev
: stmt { $$ = std::move($1); }
| stmt_dev { $$.as_dev = std::move($1); }
;
stmt_list
: stmt_list stmt
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
;
stmt_or_dev_list
: stmt_or_dev_list stmt_or_dev
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt_or_dev
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
;
stmt_dev
: DEVBEGIN stmt_list DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::move($2)); }
| DEVBEGIN DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::make_unique<ast::stmt_list>(@$)); }
;
stmt_block
: LBRACE stmt_or_dev_list RBRACE { $$ = std::move($2); }
| LBRACE RBRACE { $$ = std::make_unique<ast::stmt_list>(@$); }
;
stmt_expr
: expr_assign
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
| expr_increment
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
| expr_decrement
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
|
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::make_unique<ast::node>(@$)); }
;
stmt_call
: expr_call SEMICOLON
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
| expr_method SEMICOLON
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
;
stmt_assign
: expr_assign SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
| expr_increment SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
| expr_decrement SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
;
stmt_endon
: expr_object ENDON LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_endon>(@$, std::move($1), std::move($4)); }
;
stmt_notify
: expr_object NOTIFY LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object NOTIFY LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_wait
: WAIT expr SEMICOLON
{ $$ = std::make_unique<ast::stmt_wait>(@$, std::move($2)); }
;
stmt_waittill
: expr_object WAITTILL LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object WAITTILL LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_waittillmatch
: expr_object WAITTILLMATCH LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object WAITTILLMATCH LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_waittillframeend
: WAITTILLFRAMEEND SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillframeend>(@$); }
;
stmt_if
: IF LPAREN expr RPAREN stmt %prec THEN
{ $$ = std::make_unique<ast::stmt_if>(@$, std::move($3), std::move($5)); }
;
stmt_ifelse
: IF LPAREN expr RPAREN stmt ELSE stmt
{ $$ = std::make_unique<ast::stmt_ifelse>(@$, std::move($3), std::move($5), std::move($7)); }
;
stmt_while
: WHILE LPAREN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_while>(@$, std::move($3), std::move($5)); }
;
stmt_dowhile
: DO stmt WHILE LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_dowhile>(@$, std::move($5), std::move($2)); }
;
stmt_for
: FOR LPAREN stmt_expr SEMICOLON expr_or_empty SEMICOLON stmt_expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_for>(@$, ast::stmt(std::move($3)), std::move($5), ast::stmt(std::move($7)), std::move($9)); }
;
stmt_foreach
: FOREACH LPAREN expr_identifier IN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), std::move($5), std::move($7)); }
| FOREACH LPAREN expr_identifier COMMA expr_identifier IN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), ast::expr(std::move($5)), std::move($7), std::move($9)); }
;
stmt_switch
: SWITCH LPAREN expr RPAREN stmt_block
{ $$ = std::make_unique<ast::stmt_switch>(@$, std::move($3), std::move($5)); }
;
stmt_case
: CASE expr_integer COLON
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
| CASE expr_string COLON
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
;
stmt_default
: DEFAULT COLON
{ $$ = std::make_unique<ast::stmt_default>(@$, std::make_unique<ast::stmt_list>(@$)); }
;
stmt_break
: BREAK SEMICOLON
{ $$ = std::make_unique<ast::stmt_break>(@$); }
;
stmt_continue
: CONTINUE SEMICOLON
{ $$ = std::make_unique<ast::stmt_continue>(@$); }
;
stmt_return
: RETURN expr SEMICOLON
{ $$ = std::make_unique<ast::stmt_return>(@$, std::move($2)); }
| RETURN SEMICOLON
{ $$ = std::make_unique<ast::stmt_return>(@$, std::make_unique<ast::node>(@$)); }
;
stmt_breakpoint
: BREAKPOINT SEMICOLON
{ $$ = std::make_unique<ast::stmt_breakpoint>(@$); }
;
stmt_prof_begin
: PROFBEGIN LPAREN expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_prof_begin>(@$, std::move($3)); }
;
stmt_prof_end
: PROFEND LPAREN expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_prof_end>(@$, std::move($3)); }
;
expr
: expr_ternary { $$ = std::move($1); }
| expr_binary { $$ = std::move($1); }
| expr_primitive { $$ = std::move($1); }
;
expr_or_empty
: expr { $$ = std::move($1); }
| { $$.as_node = std::make_unique<ast::node>(@$); }
;
expr_assign
: expr_tuple ASSIGN expr
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN expr
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_OR expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_or>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_AND expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_and>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_EXOR expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_exor>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_LSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_assign_shift_left>(@$, std::move($1),std::move( $3)); }
| expr_object ASSIGN_RSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_assign_shift_right>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_ADD expr
{ $$.as_node = std::make_unique<ast::expr_assign_add>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_SUB expr
{ $$.as_node = std::make_unique<ast::expr_assign_sub>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_MUL expr
{ $$.as_node = std::make_unique<ast::expr_assign_mul>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_DIV expr
{ $$.as_node = std::make_unique<ast::expr_assign_div>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_MOD expr
{ $$.as_node = std::make_unique<ast::expr_assign_mod>(@$, std::move($1), std::move($3)); }
;
expr_increment
: INCREMENT expr_object %prec PREINC
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($2), true); }
| expr_object INCREMENT %prec POSTINC
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($1), false); }
;
expr_decrement
: DECREMENT expr_object %prec PREDEC
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($2), true); }
| expr_object DECREMENT %prec POSTDEC
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($1), false); }
;
expr_ternary
: expr QMARK expr COLON expr %prec TERN
{ $$.as_node = std::make_unique<ast::expr_ternary>(@$, std::move($1), std::move($3), std::move($5)); }
;
expr_binary
: expr OR expr
{ $$.as_node = std::make_unique<ast::expr_or>(@$, std::move($1), std::move($3)); }
| expr AND expr
{ $$.as_node = std::make_unique<ast::expr_and>(@$, std::move($1), std::move($3)); }
| expr EQUALITY expr
{ $$.as_node = std::make_unique<ast::expr_equality>(@$, std::move($1), std::move($3)); }
| expr INEQUALITY expr
{ $$.as_node = std::make_unique<ast::expr_inequality>(@$, std::move($1), std::move($3)); }
| expr LESS_EQUAL expr
{ $$.as_node = std::make_unique<ast::expr_less_equal>(@$, std::move($1), std::move($3)); }
| expr GREATER_EQUAL expr
{ $$.as_node = std::make_unique<ast::expr_greater_equal>(@$, std::move($1), std::move($3)); }
| expr LESS expr
{ $$.as_node = std::make_unique<ast::expr_less>(@$, std::move($1), std::move($3)); }
| expr GREATER expr
{ $$.as_node = std::make_unique<ast::expr_greater>(@$, std::move($1), std::move($3)); }
| expr BITWISE_OR expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_or>(@$, std::move($1), std::move($3)); }
| expr BITWISE_AND expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_and>(@$, std::move($1), std::move($3)); }
| expr BITWISE_EXOR expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_exor>(@$, std::move($1), std::move($3)); }
| expr LSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_shift_left>(@$, std::move($1), std::move($3)); }
| expr RSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_shift_right>(@$, std::move($1), std::move($3)); }
| expr ADD expr
{ $$.as_node = std::make_unique<ast::expr_add>(@$, std::move($1), std::move($3)); }
| expr SUB expr
{ $$.as_node = std::make_unique<ast::expr_sub>(@$, std::move($1), std::move($3)); }
| expr MUL expr
{ $$.as_node = std::make_unique<ast::expr_mul>(@$, std::move($1), std::move($3)); }
| expr DIV expr
{ $$.as_node = std::make_unique<ast::expr_div>(@$, std::move($1), std::move($3)); }
| expr MOD expr
{ $$.as_node = std::make_unique<ast::expr_mod>(@$, std::move($1), std::move($3)); }
;
expr_primitive
: expr_complement { $$.as_node = std::move($1); }
| expr_negate { $$.as_node = std::move($1); }
| expr_not { $$.as_node = std::move($1); }
| expr_call { $$.as_node = std::move($1); }
| expr_method { $$.as_node = std::move($1); }
| expr_add_array { $$.as_node = std::move($1); }
| expr_reference { $$.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); }
| expr_paren { $$.as_node = std::move($1); }
| expr_thisthread { $$.as_node = std::move($1); }
| expr_empty_array { $$.as_node = std::move($1); }
| expr_undefined { $$.as_node = std::move($1); }
| expr_game { $$.as_node = std::move($1); }
| expr_self { $$.as_node = std::move($1); }
| expr_anim { $$.as_node = std::move($1); }
| expr_level { $$.as_node = std::move($1); }
| expr_animation { $$.as_node = std::move($1); }
| expr_animtree { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
| expr_istring { $$.as_node = std::move($1); }
| expr_string { $$.as_node = std::move($1); }
| expr_vector { $$.as_node = std::move($1); }
| expr_float { $$.as_node = std::move($1); }
| expr_integer { $$.as_node = std::move($1); }
| expr_false { $$.as_node = std::move($1); }
| expr_true { $$.as_node = std::move($1); }
;
expr_complement
: COMPLEMENT expr
{ $$ = std::make_unique<ast::expr_complement>(@$, std::move($2)); }
;
expr_negate
: SUB expr_identifier %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_paren %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_array %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_field %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
;
expr_not
: NOT expr
{ $$ = std::make_unique<ast::expr_not>(@$, std::move($2)); }
;
expr_call
: expr_function { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
| expr_pointer { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
;
expr_method
: expr_object expr_function { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
| expr_object expr_pointer { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
;
expr_function
: expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($1), std::move($3), ast::call::mode::normal); }
| expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($1), std::move($3), std::move($5), ast::call::mode::normal); }
| THREAD expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::thread); }
| THREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::thread); }
| CHILDTHREAD expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::childthread); }
| CHILDTHREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::childthread); }
;
expr_pointer
: LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($3), std::move($7), ast::call::mode::normal); }
| THREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::thread); }
| CHILDTHREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::childthread); }
| CALL LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::builtin); }
;
expr_add_array
: LBRACKET expr_arguments_no_empty RBRACKET
{ $$ = std::make_unique<ast::expr_add_array>(@$, std::move($2)); }
;
expr_parameters
: expr_parameters COMMA expr_identifier
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr_identifier
{ $$ = std::make_unique<ast::expr_parameters>(@$); $$->list.push_back(std::move($1)); }
|
{ $$ = std::make_unique<ast::expr_parameters>(@$); }
;
expr_arguments
: expr_arguments_no_empty
{ $$ = std::move($1); }
|
{ $$ = std::make_unique<ast::expr_arguments>(@$); }
;
expr_arguments_no_empty
: expr_arguments COMMA expr
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr %prec ADD_ARRAY
{ $$ = std::make_unique<ast::expr_arguments>(@$); $$->list.push_back(std::move($1)); }
;
expr_reference
: DOUBLECOLON expr_identifier
{ $$ = std::make_unique<ast::expr_reference>(@$, std::make_unique<ast::expr_path>(@$), std::move($2)); }
| expr_path DOUBLECOLON expr_identifier
{ $$ = std::make_unique<ast::expr_reference>(@$, std::move($1), std::move($3)); }
;
expr_tuple
: LBRACKET expr_tuple_arguments RBRACKET
{ $$.as_node = std::move($2); }
;
expr_tuple_arguments
: expr_tuple_arguments COMMA expr_tuple_types
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr_tuple_types
{ $$ = std::make_unique<ast::expr_tuple>(@$); $$->list.push_back(std::move($1)); }
;
expr_tuple_types
: expr_array { $$.as_node = std::move($1); }
| expr_field { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
;
expr_array
: expr_object LBRACKET expr RBRACKET
{ $$ = std::make_unique<ast::expr_array>(@$, std::move($1), std::move($3)); }
;
expr_field
: expr_object DOT expr_identifier_nosize
{ $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::move($3)); }
;
expr_size
: expr_object DOT SIZE %prec SIZEOF
{ $$ = std::make_unique<ast::expr_size>(@$, std::move($1)); }
;
expr_paren
: LPAREN expr RPAREN
{ $$ = std::make_unique<ast::expr_paren>(@$, std::move($2)); }
;
expr_object
: expr_call { $$.as_node = std::move($1); }
| expr_method { $$.as_node = std::move($1); }
| expr_array { $$.as_node = std::move($1); }
| expr_field { $$.as_node = std::move($1); }
| expr_game { $$.as_node = std::move($1); }
| expr_self { $$.as_node = std::move($1); }
| expr_anim { $$.as_node = std::move($1); }
| expr_level { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
;
expr_thisthread
: THISTHREAD
{ $$ = std::make_unique<ast::expr_thisthread>(@$); };
;
expr_empty_array
: LBRACKET RBRACKET
{ $$ = std::make_unique<ast::expr_empty_array>(@$); };
;
expr_undefined
: UNDEFINED
{ $$ = std::make_unique<ast::expr_undefined>(@$); };
;
expr_game
: GAME
{ $$ = std::make_unique<ast::expr_game>(@$); };
;
expr_self
: SELF
{ $$ = std::make_unique<ast::expr_self>(@$); };
;
expr_anim
: ANIM
{ $$ = std::make_unique<ast::expr_anim>(@$); };
;
expr_level
: LEVEL
{ $$ = std::make_unique<ast::expr_level>(@$); };
;
expr_animation
: MOD IDENTIFIER %prec ANIMREF
{ $$ = std::make_unique<ast::expr_animation>(@$, $2); };
;
expr_animtree
: ANIMTREE
{ $$ = std::make_unique<ast::expr_animtree>(@$); };
;
expr_identifier_nosize
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
;
expr_identifier
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
| SIZE
{ $$ = std::make_unique<ast::expr_identifier>(@$, "size"); };
;
expr_path
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
| PATH
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
;
expr_istring
: ISTRING
{ $$ = std::make_unique<ast::expr_istring>(@$, $1); };
;
expr_string
: STRING
{ $$ = std::make_unique<ast::expr_string>(@$, $1); };
;
expr_vector
: LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = std::make_unique<ast::expr_vector>(@$, std::move($2), std::move($4), std::move($6)); };
;
expr_float
: SUB FLOAT %prec NEG
{ $$ = std::make_unique<ast::expr_float>(@$, "-" + $2); };
| FLOAT
{ $$ = std::make_unique<ast::expr_float>(@$, $1); };
;
expr_integer
: SUB INTEGER %prec NEG
{ $$ = std::make_unique<ast::expr_integer>(@$, "-" + $2); };
| INTEGER
{ $$ = std::make_unique<ast::expr_integer>(@$, $1); };
;
expr_false
: FALSE
{ $$ = std::make_unique<ast::expr_false>(@$); };
;
expr_true
: TRUE
{ $$ = std::make_unique<ast::expr_true>(@$); };
;
%%
void xsk::gsc::iw6c::parser::error(const xsk::gsc::location& loc, const std::string& msg)
{
throw xsk::gsc::comp_error(loc, msg);
}

View File

@ -0,0 +1,9 @@
generate: s1c
clean:
rm -rf ./parser.hpp
rm -rf ./parser.cpp
s1c: parser.ypp
bison parser.ypp -Wcounterexamples
mv parser.hpp parser.cpp ../../../src/experimental/s1c/xsk/

View File

@ -0,0 +1,898 @@
/* Copyright 2022 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 {S1C}
%define api.namespace {xsk::gsc::s1c}
%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 { xsk::gsc::s1c::lexer& lexer }
%parse-param { xsk::gsc::s1c::lexer& lexer }
%parse-param { xsk::gsc::ast::program::ptr& ast }
%code requires
{
#ifdef _MSC_VER
#pragma warning(disable:4065)
#pragma warning(disable:4127)
#endif
#include "s1c.hpp"
namespace xsk::gsc::s1c { class lexer; }
}
%code top
{
#include "stdafx.hpp"
#include "parser.hpp"
#include "lexer.hpp"
using namespace xsk::gsc;
xsk::gsc::s1c::parser::symbol_type S1Clex(xsk::gsc::s1c::lexer& lexer);
}
%token SH_DEFINE "#define"
%token SH_UNDEF "#undef"
%token SH_IFDEF "#ifdef"
%token SH_IFNDEF "#ifndef"
%token SH_IF "#if"
%token SH_ELIF "#elif"
%token SH_ELSE "#else"
%token SH_ENDIF "#endif"
%token DEVBEGIN "/#"
%token DEVEND "#/"
%token INLINE "#inline"
%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 DO "do"
%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 BREAKPOINT "breakpoint"
%token PROFBEGIN "prof_begin"
%token PROFEND "prof_end"
%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_MUL "*="
%token ASSIGN_DIV "/="
%token ASSIGN_MOD "%="
%token ASSIGN_BW_OR "|="
%token ASSIGN_BW_AND "&="
%token ASSIGN_BW_EXOR "^="
%token ASSIGN_RSHIFT ">>="
%token ASSIGN_LSHIFT "<<="
%token BITWISE_OR "|"
%token BITWISE_AND "&"
%token BITWISE_EXOR "^"
%token ADD "+"
%token SUB "-"
%token MUL "*"
%token DIV "/"
%token MOD "%"
%token <std::string> PATH "path"
%token <std::string> IDENTIFIER "identifier"
%token <std::string> STRING "string literal"
%token <std::string> ISTRING "localized string"
%token <std::string> FLOAT "float"
%token <std::string> INTEGER "integer"
%type <ast::program::ptr> program
%type <ast::include::ptr> include
%type <ast::decl> declaration
%type <ast::decl_usingtree::ptr> decl_usingtree
%type <ast::decl_constant::ptr> decl_constant
%type <ast::decl_thread::ptr> decl_thread
%type <ast::stmt> stmt
%type <ast::stmt> stmt_or_dev
%type <ast::stmt_list::ptr> stmt_list
%type <ast::stmt_list::ptr> stmt_or_dev_list
%type <ast::stmt_dev::ptr> stmt_dev
%type <ast::stmt_list::ptr> stmt_block
%type <ast::stmt_expr::ptr> stmt_expr
%type <ast::stmt_call::ptr> stmt_call
%type <ast::stmt_assign::ptr> stmt_assign
%type <ast::stmt_endon::ptr> stmt_endon
%type <ast::stmt_notify::ptr> stmt_notify
%type <ast::stmt_wait::ptr> stmt_wait
%type <ast::stmt_waittill::ptr> stmt_waittill
%type <ast::stmt_waittillmatch::ptr> stmt_waittillmatch
%type <ast::stmt_waittillframeend::ptr> stmt_waittillframeend
%type <ast::stmt_waitframe::ptr> stmt_waitframe
%type <ast::stmt_if::ptr> stmt_if
%type <ast::stmt_ifelse::ptr> stmt_ifelse
%type <ast::stmt_while::ptr> stmt_while
%type <ast::stmt_dowhile::ptr> stmt_dowhile
%type <ast::stmt_for::ptr> stmt_for
%type <ast::stmt_foreach::ptr> stmt_foreach
%type <ast::stmt_switch::ptr> stmt_switch
%type <ast::stmt_case::ptr> stmt_case
%type <ast::stmt_default::ptr> stmt_default
%type <ast::stmt_break::ptr> stmt_break
%type <ast::stmt_continue::ptr> stmt_continue
%type <ast::stmt_return::ptr> stmt_return
%type <ast::stmt_breakpoint::ptr> stmt_breakpoint
%type <ast::stmt_prof_begin::ptr> stmt_prof_begin
%type <ast::stmt_prof_end::ptr> stmt_prof_end
%type <ast::expr> expr
%type <ast::expr> expr_or_empty
%type <ast::expr> expr_assign
%type <ast::expr> expr_increment
%type <ast::expr> expr_decrement
%type <ast::expr> expr_ternary
%type <ast::expr> expr_binary
%type <ast::expr> expr_primitive
%type <ast::expr_complement::ptr> expr_complement
%type <ast::expr_negate::ptr> expr_negate
%type <ast::expr_not::ptr> expr_not
%type <ast::expr_call::ptr> expr_call
%type <ast::expr_method::ptr> expr_method
%type <ast::call> expr_function
%type <ast::call> expr_pointer
%type <ast::expr_add_array::ptr> expr_add_array
%type <ast::expr_parameters::ptr> expr_parameters
%type <ast::expr_arguments::ptr> expr_arguments
%type <ast::expr_arguments::ptr> expr_arguments_no_empty
%type <ast::expr_reference::ptr> expr_reference
%type <ast::expr> expr_tuple
%type <ast::expr_tuple::ptr> expr_tuple_arguments
%type <ast::expr> expr_tuple_types
%type <ast::expr_array::ptr> expr_array
%type <ast::expr_field::ptr> expr_field
%type <ast::expr_size::ptr> expr_size
%type <ast::expr_paren::ptr> expr_paren
%type <ast::expr> expr_object
%type <ast::expr_thisthread::ptr> expr_thisthread
%type <ast::expr_empty_array::ptr> expr_empty_array
%type <ast::expr_undefined::ptr> expr_undefined
%type <ast::expr_game::ptr> expr_game
%type <ast::expr_self::ptr> expr_self
%type <ast::expr_anim::ptr> expr_anim
%type <ast::expr_level::ptr> expr_level
%type <ast::expr_animation::ptr> expr_animation
%type <ast::expr_animtree::ptr> expr_animtree
%type <ast::expr_identifier::ptr> expr_identifier_nosize
%type <ast::expr_identifier::ptr> expr_identifier
%type <ast::expr_path::ptr> expr_path
%type <ast::expr_istring::ptr> expr_istring
%type <ast::expr_string::ptr> expr_string
%type <ast::expr_vector::ptr> expr_vector
%type <ast::expr_float::ptr> expr_float
%type <ast::expr_integer::ptr> expr_integer
%type <ast::expr_false::ptr> expr_false
%type <ast::expr_true::ptr> expr_true
%nonassoc SIZEOF
%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_EXOR
%left BITWISE_AND
%left EQUALITY INEQUALITY
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%left LSHIFT RSHIFT
%left ADD SUB
%left MUL 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<ast::program>(@$); }
;
program
: program inline
{ $$ = std::move($1); }
| program include
{ $$ = std::move($1); $$->includes.push_back(std::move($2)); }
| program declaration
{ $$ = std::move($1); $$->declarations.push_back(std::move($2)); }
| inline
{ $$ = std::make_unique<ast::program>(@$); }
| include
{ $$ = std::make_unique<ast::program>(@$); $$->includes.push_back(std::move($1)); }
| declaration
{ $$ = std::make_unique<ast::program>(@$); $$->declarations.push_back(std::move($1)); }
;
inline
: INLINE expr_path SEMICOLON { lexer.push_header($2->value); }
;
include
: INCLUDE expr_path SEMICOLON
{ $$ = std::make_unique<ast::include>(@$, std::move($2)); }
;
declaration
: DEVBEGIN { $$.as_dev_begin = std::make_unique<ast::decl_dev_begin>(@$); }
| DEVEND { $$.as_dev_end = std::make_unique<ast::decl_dev_end>(@$); }
| decl_usingtree { $$.as_usingtree = std::move($1); }
| decl_constant { $$.as_constant = std::move($1); }
| decl_thread { $$.as_thread = std::move($1); }
;
decl_usingtree
: USINGTREE LPAREN expr_string RPAREN SEMICOLON
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_usingtree>(@$, std::move($3)); }
;
decl_constant
: expr_identifier ASSIGN expr SEMICOLON
{ $$ = std::make_unique<ast::decl_constant>(@$, std::move($1), std::move($3)); }
;
decl_thread
: expr_identifier LPAREN expr_parameters RPAREN stmt_block
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($1), std::move($3), std::move($5)); }
;
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_dowhile { $$.as_dowhile = 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_breakpoint { $$.as_breakpoint = std::move($1); }
| stmt_prof_begin { $$.as_prof_begin = std::move($1); }
| stmt_prof_end { $$.as_prof_end = std::move($1); }
;
stmt_or_dev
: stmt { $$ = std::move($1); }
| stmt_dev { $$.as_dev = std::move($1); }
;
stmt_list
: stmt_list stmt
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
;
stmt_or_dev_list
: stmt_or_dev_list stmt_or_dev
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt_or_dev
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
;
stmt_dev
: DEVBEGIN stmt_list DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::move($2)); }
| DEVBEGIN DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::make_unique<ast::stmt_list>(@$)); }
;
stmt_block
: LBRACE stmt_or_dev_list RBRACE { $$ = std::move($2); }
| LBRACE RBRACE { $$ = std::make_unique<ast::stmt_list>(@$); }
;
stmt_expr
: expr_assign
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
| expr_increment
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
| expr_decrement
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
|
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::make_unique<ast::node>(@$)); }
;
stmt_call
: expr_call SEMICOLON
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
| expr_method SEMICOLON
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
;
stmt_assign
: expr_assign SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
| expr_increment SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
| expr_decrement SEMICOLON
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
;
stmt_endon
: expr_object ENDON LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_endon>(@$, std::move($1), std::move($4)); }
;
stmt_notify
: expr_object NOTIFY LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object NOTIFY LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_wait
: WAIT expr SEMICOLON
{ $$ = std::make_unique<ast::stmt_wait>(@$, std::move($2)); }
;
stmt_waittill
: expr_object WAITTILL LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object WAITTILL LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_waittillmatch
: expr_object WAITTILLMATCH LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::move($6)); }
| expr_object WAITTILLMATCH LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
;
stmt_waittillframeend
: WAITTILLFRAMEEND SEMICOLON
{ $$ = std::make_unique<ast::stmt_waittillframeend>(@$); }
;
stmt_waitframe
: WAITFRAME SEMICOLON
{ $$ = std::make_unique<ast::stmt_waitframe>(@$); }
| WAITFRAME LPAREN RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_waitframe>(@$); }
;
stmt_if
: IF LPAREN expr RPAREN stmt %prec THEN
{ $$ = std::make_unique<ast::stmt_if>(@$, std::move($3), std::move($5)); }
;
stmt_ifelse
: IF LPAREN expr RPAREN stmt ELSE stmt
{ $$ = std::make_unique<ast::stmt_ifelse>(@$, std::move($3), std::move($5), std::move($7)); }
;
stmt_while
: WHILE LPAREN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_while>(@$, std::move($3), std::move($5)); }
;
stmt_dowhile
: DO stmt WHILE LPAREN expr RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_dowhile>(@$, std::move($5), std::move($2)); }
;
stmt_for
: FOR LPAREN stmt_expr SEMICOLON expr_or_empty SEMICOLON stmt_expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_for>(@$, ast::stmt(std::move($3)), std::move($5), ast::stmt(std::move($7)), std::move($9)); }
;
stmt_foreach
: FOREACH LPAREN expr_identifier IN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), std::move($5), std::move($7)); }
| FOREACH LPAREN expr_identifier COMMA expr_identifier IN expr RPAREN stmt
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), ast::expr(std::move($5)), std::move($7), std::move($9)); }
;
stmt_switch
: SWITCH LPAREN expr RPAREN stmt_block
{ $$ = std::make_unique<ast::stmt_switch>(@$, std::move($3), std::move($5)); }
;
stmt_case
: CASE expr_integer COLON
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
| CASE expr_string COLON
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
;
stmt_default
: DEFAULT COLON
{ $$ = std::make_unique<ast::stmt_default>(@$, std::make_unique<ast::stmt_list>(@$)); }
;
stmt_break
: BREAK SEMICOLON
{ $$ = std::make_unique<ast::stmt_break>(@$); }
;
stmt_continue
: CONTINUE SEMICOLON
{ $$ = std::make_unique<ast::stmt_continue>(@$); }
;
stmt_return
: RETURN expr SEMICOLON
{ $$ = std::make_unique<ast::stmt_return>(@$, std::move($2)); }
| RETURN SEMICOLON
{ $$ = std::make_unique<ast::stmt_return>(@$, std::make_unique<ast::node>(@$)); }
;
stmt_breakpoint
: BREAKPOINT SEMICOLON
{ $$ = std::make_unique<ast::stmt_breakpoint>(@$); }
;
stmt_prof_begin
: PROFBEGIN LPAREN expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_prof_begin>(@$, std::move($3)); }
;
stmt_prof_end
: PROFEND LPAREN expr_arguments RPAREN SEMICOLON
{ $$ = std::make_unique<ast::stmt_prof_end>(@$, std::move($3)); }
;
expr
: expr_ternary { $$ = std::move($1); }
| expr_binary { $$ = std::move($1); }
| expr_primitive { $$ = std::move($1); }
;
expr_or_empty
: expr { $$ = std::move($1); }
| { $$.as_node = std::make_unique<ast::node>(@$); }
;
expr_assign
: expr_tuple ASSIGN expr
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN expr
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_OR expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_or>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_AND expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_and>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_BW_EXOR expr
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_exor>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_LSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_assign_shift_left>(@$, std::move($1),std::move( $3)); }
| expr_object ASSIGN_RSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_assign_shift_right>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_ADD expr
{ $$.as_node = std::make_unique<ast::expr_assign_add>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_SUB expr
{ $$.as_node = std::make_unique<ast::expr_assign_sub>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_MUL expr
{ $$.as_node = std::make_unique<ast::expr_assign_mul>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_DIV expr
{ $$.as_node = std::make_unique<ast::expr_assign_div>(@$, std::move($1), std::move($3)); }
| expr_object ASSIGN_MOD expr
{ $$.as_node = std::make_unique<ast::expr_assign_mod>(@$, std::move($1), std::move($3)); }
;
expr_increment
: INCREMENT expr_object %prec PREINC
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($2), true); }
| expr_object INCREMENT %prec POSTINC
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($1), false); }
;
expr_decrement
: DECREMENT expr_object %prec PREDEC
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($2), true); }
| expr_object DECREMENT %prec POSTDEC
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($1), false); }
;
expr_ternary
: expr QMARK expr COLON expr %prec TERN
{ $$.as_node = std::make_unique<ast::expr_ternary>(@$, std::move($1), std::move($3), std::move($5)); }
;
expr_binary
: expr OR expr
{ $$.as_node = std::make_unique<ast::expr_or>(@$, std::move($1), std::move($3)); }
| expr AND expr
{ $$.as_node = std::make_unique<ast::expr_and>(@$, std::move($1), std::move($3)); }
| expr EQUALITY expr
{ $$.as_node = std::make_unique<ast::expr_equality>(@$, std::move($1), std::move($3)); }
| expr INEQUALITY expr
{ $$.as_node = std::make_unique<ast::expr_inequality>(@$, std::move($1), std::move($3)); }
| expr LESS_EQUAL expr
{ $$.as_node = std::make_unique<ast::expr_less_equal>(@$, std::move($1), std::move($3)); }
| expr GREATER_EQUAL expr
{ $$.as_node = std::make_unique<ast::expr_greater_equal>(@$, std::move($1), std::move($3)); }
| expr LESS expr
{ $$.as_node = std::make_unique<ast::expr_less>(@$, std::move($1), std::move($3)); }
| expr GREATER expr
{ $$.as_node = std::make_unique<ast::expr_greater>(@$, std::move($1), std::move($3)); }
| expr BITWISE_OR expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_or>(@$, std::move($1), std::move($3)); }
| expr BITWISE_AND expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_and>(@$, std::move($1), std::move($3)); }
| expr BITWISE_EXOR expr
{ $$.as_node = std::make_unique<ast::expr_bitwise_exor>(@$, std::move($1), std::move($3)); }
| expr LSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_shift_left>(@$, std::move($1), std::move($3)); }
| expr RSHIFT expr
{ $$.as_node = std::make_unique<ast::expr_shift_right>(@$, std::move($1), std::move($3)); }
| expr ADD expr
{ $$.as_node = std::make_unique<ast::expr_add>(@$, std::move($1), std::move($3)); }
| expr SUB expr
{ $$.as_node = std::make_unique<ast::expr_sub>(@$, std::move($1), std::move($3)); }
| expr MUL expr
{ $$.as_node = std::make_unique<ast::expr_mul>(@$, std::move($1), std::move($3)); }
| expr DIV expr
{ $$.as_node = std::make_unique<ast::expr_div>(@$, std::move($1), std::move($3)); }
| expr MOD expr
{ $$.as_node = std::make_unique<ast::expr_mod>(@$, std::move($1), std::move($3)); }
;
expr_primitive
: expr_complement { $$.as_node = std::move($1); }
| expr_negate { $$.as_node = std::move($1); }
| expr_not { $$.as_node = std::move($1); }
| expr_call { $$.as_node = std::move($1); }
| expr_method { $$.as_node = std::move($1); }
| expr_add_array { $$.as_node = std::move($1); }
| expr_reference { $$.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); }
| expr_paren { $$.as_node = std::move($1); }
| expr_thisthread { $$.as_node = std::move($1); }
| expr_empty_array { $$.as_node = std::move($1); }
| expr_undefined { $$.as_node = std::move($1); }
| expr_game { $$.as_node = std::move($1); }
| expr_self { $$.as_node = std::move($1); }
| expr_anim { $$.as_node = std::move($1); }
| expr_level { $$.as_node = std::move($1); }
| expr_animation { $$.as_node = std::move($1); }
| expr_animtree { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
| expr_istring { $$.as_node = std::move($1); }
| expr_string { $$.as_node = std::move($1); }
| expr_vector { $$.as_node = std::move($1); }
| expr_float { $$.as_node = std::move($1); }
| expr_integer { $$.as_node = std::move($1); }
| expr_false { $$.as_node = std::move($1); }
| expr_true { $$.as_node = std::move($1); }
;
expr_complement
: COMPLEMENT expr
{ $$ = std::make_unique<ast::expr_complement>(@$, std::move($2)); }
;
expr_negate
: SUB expr_identifier %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_paren %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_array %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
| SUB expr_field %prec NEG
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
;
expr_not
: NOT expr
{ $$ = std::make_unique<ast::expr_not>(@$, std::move($2)); }
;
expr_call
: expr_function { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
| expr_pointer { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
;
expr_method
: expr_object expr_function { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
| expr_object expr_pointer { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
;
expr_function
: expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($1), std::move($3), ast::call::mode::normal); }
| expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($1), std::move($3), std::move($5), ast::call::mode::normal); }
| THREAD expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::thread); }
| THREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::thread); }
| CHILDTHREAD expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::childthread); }
| CHILDTHREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::childthread); }
;
expr_pointer
: LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($3), std::move($7), ast::call::mode::normal); }
| THREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::thread); }
| CHILDTHREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::childthread); }
| CALL LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::builtin); }
;
expr_add_array
: LBRACKET expr_arguments_no_empty RBRACKET
{ $$ = std::make_unique<ast::expr_add_array>(@$, std::move($2)); }
;
expr_parameters
: expr_parameters COMMA expr_identifier
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr_identifier
{ $$ = std::make_unique<ast::expr_parameters>(@$); $$->list.push_back(std::move($1)); }
|
{ $$ = std::make_unique<ast::expr_parameters>(@$); }
;
expr_arguments
: expr_arguments_no_empty
{ $$ = std::move($1); }
|
{ $$ = std::make_unique<ast::expr_arguments>(@$); }
;
expr_arguments_no_empty
: expr_arguments COMMA expr
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr %prec ADD_ARRAY
{ $$ = std::make_unique<ast::expr_arguments>(@$); $$->list.push_back(std::move($1)); }
;
expr_reference
: DOUBLECOLON expr_identifier
{ $$ = std::make_unique<ast::expr_reference>(@$, std::make_unique<ast::expr_path>(@$), std::move($2)); }
| expr_path DOUBLECOLON expr_identifier
{ $$ = std::make_unique<ast::expr_reference>(@$, std::move($1), std::move($3)); }
;
expr_tuple
: LBRACKET expr_tuple_arguments RBRACKET
{ $$.as_node = std::move($2); }
;
expr_tuple_arguments
: expr_tuple_arguments COMMA expr_tuple_types
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
| expr_tuple_types
{ $$ = std::make_unique<ast::expr_tuple>(@$); $$->list.push_back(std::move($1)); }
;
expr_tuple_types
: expr_array { $$.as_node = std::move($1); }
| expr_field { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
;
expr_array
: expr_object LBRACKET expr RBRACKET
{ $$ = std::make_unique<ast::expr_array>(@$, std::move($1), std::move($3)); }
;
expr_field
: expr_object DOT expr_identifier_nosize
{ $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::move($3)); }
;
expr_size
: expr_object DOT SIZE %prec SIZEOF
{ $$ = std::make_unique<ast::expr_size>(@$, std::move($1)); }
;
expr_paren
: LPAREN expr RPAREN
{ $$ = std::make_unique<ast::expr_paren>(@$, std::move($2)); }
;
expr_object
: expr_call { $$.as_node = std::move($1); }
| expr_method { $$.as_node = std::move($1); }
| expr_array { $$.as_node = std::move($1); }
| expr_field { $$.as_node = std::move($1); }
| expr_game { $$.as_node = std::move($1); }
| expr_self { $$.as_node = std::move($1); }
| expr_anim { $$.as_node = std::move($1); }
| expr_level { $$.as_node = std::move($1); }
| expr_identifier { $$.as_node = std::move($1); }
;
expr_thisthread
: THISTHREAD
{ $$ = std::make_unique<ast::expr_thisthread>(@$); };
;
expr_empty_array
: LBRACKET RBRACKET
{ $$ = std::make_unique<ast::expr_empty_array>(@$); };
;
expr_undefined
: UNDEFINED
{ $$ = std::make_unique<ast::expr_undefined>(@$); };
;
expr_game
: GAME
{ $$ = std::make_unique<ast::expr_game>(@$); };
;
expr_self
: SELF
{ $$ = std::make_unique<ast::expr_self>(@$); };
;
expr_anim
: ANIM
{ $$ = std::make_unique<ast::expr_anim>(@$); };
;
expr_level
: LEVEL
{ $$ = std::make_unique<ast::expr_level>(@$); };
;
expr_animation
: MOD IDENTIFIER %prec ANIMREF
{ $$ = std::make_unique<ast::expr_animation>(@$, $2); };
;
expr_animtree
: ANIMTREE
{ $$ = std::make_unique<ast::expr_animtree>(@$); };
;
expr_identifier_nosize
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
;
expr_identifier
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
| SIZE
{ $$ = std::make_unique<ast::expr_identifier>(@$, "size"); };
;
expr_path
: IDENTIFIER
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
| PATH
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
;
expr_istring
: ISTRING
{ $$ = std::make_unique<ast::expr_istring>(@$, $1); };
;
expr_string
: STRING
{ $$ = std::make_unique<ast::expr_string>(@$, $1); };
;
expr_vector
: LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = std::make_unique<ast::expr_vector>(@$, std::move($2), std::move($4), std::move($6)); };
;
expr_float
: SUB FLOAT %prec NEG
{ $$ = std::make_unique<ast::expr_float>(@$, "-" + $2); };
| FLOAT
{ $$ = std::make_unique<ast::expr_float>(@$, $1); };
;
expr_integer
: SUB INTEGER %prec NEG
{ $$ = std::make_unique<ast::expr_integer>(@$, "-" + $2); };
| INTEGER
{ $$ = std::make_unique<ast::expr_integer>(@$, $1); };
;
expr_false
: FALSE
{ $$ = std::make_unique<ast::expr_false>(@$); };
;
expr_true
: TRUE
{ $$ = std::make_unique<ast::expr_true>(@$); };
;
%%
void xsk::gsc::s1c::parser::error(const xsk::gsc::location& loc, const std::string& msg)
{
throw xsk::gsc::comp_error(loc, msg);
}

View File

@ -62,6 +62,8 @@ project "xsk-gsc-tool"
dependson "xsk-gsc-s4"
dependson "xsk-arc-t6"
dependson "xsk-gsc-iw5c"
dependson "xsk-gsc-iw6c"
dependson "xsk-gsc-s1c"
pchheader "stdafx.hpp"
pchsource "src/tool/stdafx.cpp"
@ -84,7 +86,9 @@ project "xsk-gsc-tool"
"xsk-gsc-s2",
"xsk-gsc-s4",
"xsk-arc-t6",
"xsk-gsc-iw5c"
"xsk-gsc-iw5c",
"xsk-gsc-iw6c",
"xsk-gsc-s1c"
}
includedirs {
@ -312,5 +316,41 @@ project "xsk-gsc-iw5c"
"./src"
}
project "xsk-gsc-iw6c"
kind "StaticLib"
language "C++"
pchheader "stdafx.hpp"
pchsource "src/experimental/iw6c/stdafx.cpp"
files {
"./src/experimental/iw6c/**.h",
"./src/experimental/iw6c/**.hpp",
"./src/experimental/iw6c/**.cpp"
}
includedirs {
"./src/experimental/iw6c",
"./src"
}
project "xsk-gsc-s1c"
kind "StaticLib"
language "C++"
pchheader "stdafx.hpp"
pchsource "src/experimental/s1c/stdafx.cpp"
files {
"./src/experimental/s1c/**.h",
"./src/experimental/s1c/**.hpp",
"./src/experimental/s1c/**.cpp"
}
includedirs {
"./src/experimental/s1c",
"./src"
}
group "Dependencies"
zlib:project()

View File

@ -128,7 +128,7 @@ void assembler::assemble(const std::string& file, std::vector<function::ptr>& fu
filename_ = file;
functions_ = std::move(funcs);
script_->write<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
for (const auto& func : functions_)
{
@ -158,7 +158,7 @@ void assembler::assemble_function(const function::ptr& func)
void assembler::assemble_instruction(const instruction::ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
switch (static_cast<opcode>(inst->opcode))
{
@ -254,20 +254,20 @@ void assembler::assemble_instruction(const instruction::ptr& inst)
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->write<std::uint16_t>(0);
script_->write_endian<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_GetAnimation:
script_->write<std::uint32_t>(0);
script_->write_endian<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
stack_->write_c_string(inst->data[1]);
break;
case opcode::OP_GetAnimTree:
script_->write<std::uint8_t>(0);
script_->write_endian<std::uint8_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_waittillmatch:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
@ -283,7 +283,7 @@ void assembler::assemble_instruction(const instruction::ptr& inst)
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
@ -305,7 +305,7 @@ void assembler::assemble_instruction(const instruction::ptr& inst)
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
@ -382,7 +382,7 @@ void assembler::assemble_builtin_call(const instruction::ptr& inst, bool method,
{
if (args)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
const auto id = method ? resolver::method_id(inst->data[0]) : resolver::function_id(inst->data[0]);
@ -399,18 +399,18 @@ void assembler::assemble_local_call(const instruction::ptr& inst, bool thread)
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
}
void assembler::assemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->write<std::uint8_t>(0);
script_->write<std::uint16_t>(0);
script_->write_endian<std::uint8_t>(0);
script_->write_endian<std::uint16_t>(0);
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[2])));
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[2])));
}
const auto file_id = resolver::token_id(inst->data[0]);
@ -489,7 +489,7 @@ void assembler::assemble_field_variable(const instruction::ptr& inst)
if (id > max_string_id)
{
stack_->write<std::uint16_t>(0);
stack_->write_endian<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
}
}
@ -520,9 +520,9 @@ void assembler::assemble_offset(std::int32_t offset)
*reinterpret_cast<std::int32_t*>(bytes.data()) = offset;
script_->write<std::uint8_t>(bytes[2]);
script_->write<std::uint8_t>(bytes[1]);
script_->write<std::uint8_t>(bytes[0]);
script_->write_endian<std::uint8_t>(bytes[2]);
script_->write_endian<std::uint8_t>(bytes[1]);
script_->write_endian<std::uint8_t>(bytes[0]);
}
auto assembler::resolve_function(const std::string& name) -> std::int32_t

View File

@ -72,7 +72,7 @@ void disassembler::dissasemble_function(const function::ptr& func)
const auto& inst = func->instructions.back();
inst->index = static_cast<std::uint32_t>(script_->pos());
inst->opcode = script_->read<std::uint8_t>();
inst->opcode = script_->read_endian<std::uint8_t>();
inst->size = opcode_size(inst->opcode);
dissasemble_instruction(inst);
@ -157,7 +157,7 @@ void disassembler::dissasemble_instruction(const instruction::ptr& inst)
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
@ -190,7 +190,7 @@ void disassembler::dissasemble_instruction(const instruction::ptr& inst)
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_waittillmatch:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
break;
case opcode::OP_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
@ -206,7 +206,7 @@ void disassembler::dissasemble_instruction(const instruction::ptr& inst)
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
break;
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
@ -228,7 +228,7 @@ void disassembler::dissasemble_instruction(const instruction::ptr& inst)
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
break;
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall2:
@ -305,7 +305,7 @@ void disassembler::disassemble_builtin_call(const instruction::ptr& inst, bool m
{
if (args)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
}
const auto id = script_->read_endian<std::uint16_t>();
@ -321,7 +321,7 @@ void disassembler::disassemble_local_call(const instruction::ptr& inst, bool thr
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
}
}
@ -331,7 +331,7 @@ void disassembler::disassemble_far_call(const instruction::ptr& inst, bool threa
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
}
const auto file_id = stack_->read_endian<std::uint16_t>();
@ -444,7 +444,7 @@ auto disassembler::disassemble_offset() -> std::int32_t
for (auto i = 0; i < 3; i++)
{
bytes[2 - i] = script_->read<std::uint8_t>();
bytes[2 - i] = script_->read_endian<std::uint8_t>();
}
auto offset = *reinterpret_cast<std::int32_t*>(bytes.data());

View File

@ -1,179 +0,0 @@
// Copyright 2022 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 "iw6_console.hpp"
namespace xsk::gsc::iw6_console
{
auto opcode_size(std::uint8_t id) -> std::uint32_t
{
switch (opcode(id))
{
case opcode::OP_End:
case opcode::OP_Return:
case opcode::OP_GetUndefined:
case opcode::OP_GetZero:
case opcode::OP_waittillFrameEnd:
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_EvalArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_ClearArray:
case opcode::OP_EmptyArray:
case opcode::OP_AddArray:
case opcode::OP_PreScriptCall:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_GetLevelObject:
case opcode::OP_GetAnimObject:
case opcode::OP_GetSelf:
case opcode::OP_GetThisthread:
case opcode::OP_GetLevel:
case opcode::OP_GetGame:
case opcode::OP_GetAnim:
case opcode::OP_GetGameRef:
case opcode::OP_inc:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_bit_ex_or:
case opcode::OP_bit_and:
case opcode::OP_equality:
case opcode::OP_inequality:
case opcode::OP_less:
case opcode::OP_greater:
case opcode::OP_less_equal:
// case opcode::OP_waittillmatch2:
case opcode::OP_waittill:
case opcode::OP_notify:
case opcode::OP_endon:
case opcode::OP_voidCodepos:
case opcode::OP_vector:
case opcode::OP_greater_equal:
case opcode::OP_shift_left:
case opcode::OP_shift_right:
case opcode::OP_plus:
case opcode::OP_minus:
case opcode::OP_multiply:
case opcode::OP_divide:
case opcode::OP_mod:
case opcode::OP_size:
case opcode::OP_GetSelfObject:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_clearparams:
case opcode::OP_checkclearparams:
case opcode::OP_EvalLocalVariableRefCached0:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_SetVariableField:
// case opcode::OP_ClearVariableField:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_wait:
case opcode::OP_DecTop:
case opcode::OP_CastFieldObject:
case opcode::OP_CastBool:
case opcode::OP_BoolNot:
case opcode::OP_BoolComplement:
return 1;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
case opcode::OP_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_EvalLocalArrayCached:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_GetAnimTree:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
return 2;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
case opcode::OP_GetBuiltinFunction:
case opcode::OP_GetBuiltinMethod:
case opcode::OP_GetString:
case opcode::OP_GetIString:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_jumpback:
case opcode::OP_endswitch:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_waittillmatch:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalSelfFieldVariableRef:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_ClearFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
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_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_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_GetLocalFunction:
case opcode::OP_CallBuiltin:
case opcode::OP_CallBuiltinMethod:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
case opcode::OP_GetFarFunction:
return 4;
case opcode::OP_GetInteger:
case opcode::OP_GetFloat:
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
case opcode::OP_GetAnimation:
case opcode::OP_switch:
case opcode::OP_jump:
return 5;
case opcode::OP_GetVector:
return 13;
default:
throw std::runtime_error("Couldn't resolve instruction size for " + std::to_string(id));
}
}
} // namespace xsk::gsc::iw6_console

View File

@ -0,0 +1,555 @@
// Copyright 2022 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 "iw6c.hpp"
namespace xsk::gsc::iw6c
{
auto assembler::output_script() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> script;
if (script_ == nullptr) return script;
script.resize(script_->pos());
std::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());
std::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<function::ptr> functions;
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<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 opdata = utils::string::parse_code(line);
if (switchnum)
{
if (opdata[0] == "case" || opdata[0] == "default")
{
for (auto& entry : opdata)
{
func->instructions.back()->data.push_back(entry);
}
switchnum--;
continue;
}
throw asm_error("invalid instruction inside endswitch \""s + line + "\"!");
}
else
{
auto inst = std::make_unique<instruction>();
inst->index = index;
inst->opcode = resolver::opcode_id(opdata[0]);
inst->size = opcode_size(inst->opcode);
opdata.erase(opdata.begin());
inst->data = std::move(opdata);
switch (static_cast<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[0] = inst->data[0].substr(4);
break;
case opcode::OP_endswitch:
switchnum = static_cast<std::uint16_t>(std::stoi(inst->data[0]));
inst->size += 7 * switchnum;
break;
default:
break;
}
index += inst->size;
func->instructions.push_back(std::move(inst));
}
}
}
assemble(file, functions);
}
void assembler::assemble(const std::string& file, std::vector<function::ptr>& funcs)
{
script_ = std::make_unique<utils::byte_buffer>(0x100000);
stack_ = std::make_unique<utils::byte_buffer>(0x100000);
filename_ = file;
functions_ = std::move(funcs);
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
for (const auto& func : functions_)
{
assemble_function(func);
}
}
void assembler::assemble_function(const function::ptr& func)
{
labels_ = func->labels;
func->id = resolver::token_id(func->name);
stack_->write_endian<std::uint32_t>(func->size);
stack_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(func->id));
if (func->id == 0)
{
stack_->write_c_string(func->name);
}
for (const auto& inst : func->instructions)
{
assemble_instruction(inst);
}
}
void assembler::assemble_instruction(const instruction::ptr& inst)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_Return:
case opcode::OP_BoolNot:
case opcode::OP_CastBool:
case opcode::OP_inequality:
case opcode::OP_GetThisthread:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_checkclearparams:
case opcode::OP_CastFieldObject:
case opcode::OP_End:
case opcode::OP_size:
case opcode::OP_EmptyArray:
case opcode::OP_bit_and:
case opcode::OP_less_equal:
case opcode::OP_voidCodepos:
// case opcode::OP_ClearVariableField:
case opcode::OP_divide:
case opcode::OP_GetSelf:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_plus:
case opcode::OP_BoolComplement:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_inc:
case opcode::OP_clearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_endon:
case opcode::OP_greater_equal:
case opcode::OP_GetSelfObject:
case opcode::OP_SetVariableField:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_less:
case opcode::OP_GetGameRef:
case opcode::OP_waittillFrameEnd:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_notify:
case opcode::OP_DecTop:
case opcode::OP_shift_left:
case opcode::OP_greater:
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_GetZero:
case opcode::OP_wait:
case opcode::OP_minus:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_multiply:
case opcode::OP_mod:
case opcode::OP_GetGame:
case opcode::OP_waittill:
case opcode::OP_dec:
case opcode::OP_PreScriptCall:
case opcode::OP_GetAnim:
case opcode::OP_GetUndefined:
case opcode::OP_GetAnimObject:
case opcode::OP_GetLevelObject:
case opcode::OP_bit_ex_or:
case opcode::OP_equality:
case opcode::OP_ClearArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalArray:
case opcode::OP_vector:
case opcode::OP_bit_or:
case opcode::OP_AddArray:
/// case opcode::OP_waittillmatch2:
case opcode::OP_shift_right:
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
script_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetInteger:
script_->write_endian<std::int32_t>(std::stoi(inst->data[0]));
break;
case opcode::OP_GetFloat:
script_->write_endian<float>(std::stof(inst->data[0]));
break;
case opcode::OP_GetVector:
inst->size += script_->align(4);
script_->write_endian<float>(std::stof(inst->data[0]));
script_->write_endian<float>(std::stof(inst->data[1]));
script_->write_endian<float>(std::stof(inst->data[2]));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->write_endian<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_GetAnimation:
script_->write_endian<std::uint32_t>(0);
script_->write_endian<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
stack_->write_c_string(inst->data[1]);
break;
case opcode::OP_GetAnimTree:
script_->write_endian<std::uint8_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_waittillmatch:
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[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_endian<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:
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_endian<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:
assemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
assemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
assemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
assemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
assemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
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:
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:
assemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
assemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
assemble_jump(inst, false, true);
break;
case opcode::OP_jump:
assemble_jump(inst, false, false);
break;
case opcode::OP_switch:
assemble_switch(inst);
break;
case opcode::OP_endswitch:
assemble_end_switch(inst);
break;
default:
throw asm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void assembler::assemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
if (args)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
const auto id = method ? resolver::method_id(inst->data[0]) : resolver::function_id(inst->data[0]);
script_->write_endian<std::uint16_t>(id);
}
void assembler::assemble_local_call(const instruction::ptr& inst, bool thread)
{
const auto addr = resolve_function(inst->data[0]);
const auto offset = static_cast<std::int32_t>(addr - inst->index - 1);
assemble_offset(offset);
if (thread)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
}
void assembler::assemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->write_endian<std::uint8_t>(0);
script_->write_endian<std::uint16_t>(0);
if (thread)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[2])));
}
const auto file_id = resolver::token_id(inst->data[0]);
const auto func_id = resolver::token_id(inst->data[1]);
stack_->write_endian<std::uint16_t>(file_id);
if (file_id == 0) stack_->write_c_string(inst->data[0]);
stack_->write_endian<std::uint16_t>(func_id);
if (func_id == 0) stack_->write_c_string(inst->data[1]);
}
void assembler::assemble_switch(const instruction::ptr& inst)
{
const auto addr = resolve_label(inst->data[0]);
script_->write_endian<std::int32_t>(addr - inst->index - 4);
}
void assembler::assemble_end_switch(const instruction::ptr& inst)
{
const auto count = std::stoul(inst->data[0]);
script_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(count));
std::uint32_t index = inst->index + 3;
for (auto i = 0u; i < count; i++)
{
if (inst->data[1 + (3 * i)] == "case")
{
if (utils::string::is_number(inst->data[1 + (3 * i) + 1]))
{
script_->write_endian<uint32_t>((std::stoi(inst->data[1 + (3 * i) + 1]) & 0xFFFFFF) + 0x800000);
}
else
{
script_->write_endian<uint32_t>(i + 1);
stack_->write_c_string(inst->data[1 + (3 * i) + 1]);
}
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 2]);
assemble_offset(addr - index);
index += 3;
}
else if (inst->data[1 + (3 * i)] == "default")
{
script_->write_endian<uint32_t>(0);
stack_->write_c_string("\x01");
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 1]);
assemble_offset(addr - index);
index += 3;
}
else
{
throw asm_error("invalid switch case '" + inst->data[1 + (3 * i)] + "'!");
}
}
}
void assembler::assemble_field_variable(const instruction::ptr& inst)
{
auto id = resolver::token_id(inst->data[0]);
if (id == 0) id = 0xFFFF;
script_->write_endian<std::uint16_t>(id);
if (id > max_string_id)
{
stack_->write_endian<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
}
}
void assembler::assemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
const auto addr = resolve_label(inst->data[0]);
if (expr)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>(addr - inst->index - 3));
}
else if (back)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>((inst->index + 3) - addr));
}
else
{
script_->write_endian<std::int32_t>(static_cast<std::int32_t>(addr - inst->index - 5));
}
}
void assembler::assemble_offset(std::int32_t offset)
{
std::array<std::uint8_t, 4> bytes = {};
offset = (offset << 10) >> 8;
*reinterpret_cast<std::int32_t*>(bytes.data()) = offset;
script_->write_endian<std::uint8_t>(bytes[2]);
script_->write_endian<std::uint8_t>(bytes[1]);
script_->write_endian<std::uint8_t>(bytes[0]);
}
auto assembler::resolve_function(const std::string& name) -> std::int32_t
{
for (const auto& entry : functions_)
{
if (entry->name == name)
{
return entry->index;
}
}
throw asm_error("couldn't resolve local function address of '" + name + "'!");
}
auto assembler::resolve_label(const std::string& name) -> std::int32_t
{
for (const auto& entry : labels_)
{
if (entry.second == name)
{
return entry.first;
}
}
throw asm_error("couldn't resolve label address of '" + name + "'!");
}
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,40 @@
// Copyright 2022 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::iw6c
{
class assembler : public gsc::assembler
{
std::string filename_;
utils::byte_buffer::ptr script_;
utils::byte_buffer::ptr stack_;
std::vector<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<function::ptr>& funcs);
private:
void assemble_function(const function::ptr& func);
void assemble_instruction(const instruction::ptr& inst);
void assemble_builtin_call(const instruction::ptr& inst, bool method, bool args);
void assemble_local_call(const instruction::ptr& inst, bool thread);
void assemble_far_call(const instruction::ptr& inst, bool thread);
void assemble_switch(const instruction::ptr& inst);
void assemble_end_switch(const instruction::ptr& inst);
void assemble_field_variable(const instruction::ptr& inst);
void assemble_jump(const instruction::ptr& inst, bool expr, bool back);
void assemble_offset(std::int32_t offset);
auto resolve_function(const std::string& name) -> std::int32_t;
auto resolve_label(const std::string& name) -> std::int32_t;
};
} // namespace xsk::gsc::iw6c

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,159 @@
// Copyright 2022 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::iw6c
{
enum class opcode : std::uint8_t;
class compiler : public gsc::compiler
{
build mode_;
std::string filename_;
std::vector<function::ptr> assembly_;
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, ast::expr> constants_;
std::vector<block*> break_blks_;
std::vector<block*> continue_blks_;
bool can_break_;
bool can_continue_;
bool developer_thread_;
public:
auto output() -> std::vector<function::ptr>;
void compile(const std::string& file, std::vector<std::uint8_t>& data);
void mode(build mode);
private:
auto parse_buffer(const std::string& file, const char* data, size_t size) -> ast::program::ptr;
auto parse_file(const std::string& file) -> ast::program::ptr;
void compile_program(const ast::program::ptr& program);
void emit_include(const ast::include::ptr& include);
void emit_declaration(const ast::decl& decl);
void emit_decl_usingtree(const ast::decl_usingtree::ptr& animtree);
void emit_decl_constant(const ast::decl_constant::ptr& constant);
void emit_decl_thread(const ast::decl_thread::ptr& thread);
void emit_stmt(const ast::stmt& stmt, const block::ptr& blk, bool last);
void emit_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_dev(const ast::stmt_dev::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void emit_stmt_call(const ast::stmt_call::ptr& stmt, const block::ptr& blk);
void emit_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void emit_stmt_endon(const ast::stmt_endon::ptr& stmt, const block::ptr& blk);
void emit_stmt_notify(const ast::stmt_notify::ptr& stmt, const block::ptr& blk);
void emit_stmt_wait(const ast::stmt_wait::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittillframeend(const ast::stmt_waittillframeend::ptr& stmt, const block::ptr& blk);
void emit_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void emit_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void emit_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void emit_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void emit_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void emit_stmt_case(const ast::stmt_case::ptr& stmt, const block::ptr& blk);
void emit_stmt_default(const ast::stmt_default::ptr& stmt, const block::ptr& blk);
void emit_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void emit_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void emit_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void emit_stmt_breakpoint(const ast::stmt_breakpoint::ptr& stmt, const block::ptr& blk);
void emit_stmt_prof_begin(const ast::stmt_prof_begin::ptr& stmt, const block::ptr& blk);
void emit_stmt_prof_end(const ast::stmt_prof_end::ptr& stmt, const block::ptr& blk);
void emit_expr(const ast::expr& expr, const block::ptr& blk);
void emit_expr_assign(const ast::expr_assign::ptr& expr, const block::ptr& blk);
void emit_expr_clear(const ast::expr& expr, const block::ptr& blk);
void emit_expr_clear_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk);
void emit_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk);
void emit_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk);
void emit_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk);
void emit_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk);
void emit_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk);
void emit_expr_negate(const ast::expr_negate::ptr& expr, const block::ptr& blk);
void emit_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk);
void emit_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_method(const ast::expr_method::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_method_pointer(const ast::expr_pointer::ptr& expr, const ast::expr& obj, const block::ptr& blk, bool is_stmt);
void emit_expr_method_function(const ast::expr_function::ptr& expr, const ast::expr& obj, const block::ptr& blk, bool is_stmt);
void emit_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk);
void emit_expr_parameters(const ast::expr_parameters::ptr& expr, const block::ptr& blk);
void emit_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk);
void emit_expr_reference(const ast::expr_reference::ptr& expr, const block::ptr& blk);
void emit_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk);
void emit_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void emit_expr_variable_ref(const ast::expr& expr, const block::ptr& blk, bool set);
void emit_expr_array_ref(const ast::expr_array::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_field_ref(const ast::expr_field::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_local_ref(const ast::expr_identifier::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_variable(const ast::expr& expr, const block::ptr& blk);
void emit_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk);
void emit_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk);
void emit_expr_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk);
void emit_expr_object(const ast::expr& expr, const block::ptr& blk);
void emit_expr_vector(const ast::expr_vector::ptr& expr, const block::ptr& blk);
void emit_expr_animation(const ast::expr_animation::ptr& expr);
void emit_expr_animtree(const ast::expr_animtree::ptr& expr);
void emit_expr_istring(const ast::expr_istring::ptr& expr);
void emit_expr_string(const ast::expr_string::ptr& expr);
void emit_expr_float(const ast::expr_float::ptr& expr);
void emit_expr_integer(const ast::expr_integer::ptr& expr);
void emit_expr_false(const ast::expr_false::ptr& expr);
void emit_expr_true(const ast::expr_true::ptr& expr);
void emit_create_local_vars(const block::ptr& blk);
void emit_remove_local_vars(const block::ptr& blk);
void emit_opcode(opcode op);
void emit_opcode(opcode op, const std::string& data);
void emit_opcode(opcode op, const std::vector<std::string>& data);
void process_thread(const ast::decl_thread::ptr& decl, const block::ptr& blk);
void process_stmt(const ast::stmt& stmt, const block::ptr& blk);
void process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_dev(const ast::stmt_dev::ptr& stmt, const block::ptr& blk);
void process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk);
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk);
void process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void process_expr(const ast::expr& expr, const block::ptr& blk);
void process_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void process_expr_parameters(const ast::expr_parameters::ptr& decl, const block::ptr& blk);
void variable_register(const std::string& name, const block::ptr& blk);
void variable_initialize(const ast::expr_identifier::ptr& name, const block::ptr& blk);
void variable_create(const ast::expr_identifier::ptr& name, const block::ptr& blk);
auto variable_stack_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::uint8_t;
auto variable_create_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string;
auto variable_access_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string;
auto variable_initialized(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> bool;
auto resolve_function_type(const ast::expr_function::ptr& expr) -> ast::call::type;
auto resolve_reference_type(const ast::expr_reference::ptr& expr, bool& method) -> ast::call::type;
auto is_constant_condition(const ast::expr& 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::iw6c

View File

@ -0,0 +1,23 @@
// Copyright 2022 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 "iw6c.hpp"
namespace xsk::gsc::iw6c
{
void context::init(build mode, read_cb_type callback)
{
compiler_.mode(mode);
resolver::init(callback);
}
void context::cleanup()
{
resolver::cleanup();
}
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,28 @@
// Copyright 2022 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::iw6c
{
class context : public gsc::context
{
iw6c::assembler assembler_;
iw6c::disassembler disassembler_;
iw6c::compiler compiler_;
iw6c::decompiler decompiler_;
public:
void init(build mode, read_cb_type callback);
void cleanup();
auto assembler() -> gsc::assembler& { return assembler_; }
auto disassembler() -> gsc::disassembler& { return disassembler_; }
auto compiler() -> gsc::compiler& { return compiler_; }
auto decompiler() -> gsc::decompiler& { return decompiler_; }
};
} // namespace xsk::gsc::iw6c

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
// Copyright 2022 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::iw6c
{
class decompiler : public gsc::decompiler
{
std::string filename_;
ast::program::ptr program_;
ast::decl_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<ast::node::ptr> stack_;
std::vector<block> blocks_;
bool in_waittill_;
public:
auto output() -> std::vector<std::uint8_t>;
void decompile(const std::string& file, std::vector<function::ptr>& funcs);
private:
void decompile_function(const function::ptr& func);
void decompile_instruction(const instruction::ptr& inst);
void decompile_expressions(const instruction::ptr& inst);
void decompile_statements(const ast::stmt_list::ptr& stmt);
void decompile_loops(const ast::stmt_list::ptr& stmt);
void decompile_switches(const ast::stmt_list::ptr& stmt);
void decompile_ifelses(const ast::stmt_list::ptr& stmt);
void decompile_aborts(const ast::stmt_list::ptr& stmt);
void decompile_tuples(const ast::stmt_list::ptr& stmt);
void decompile_if(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_ifelse(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_ifelse_end(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_inf(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_loop(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_while(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_dowhile(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_for(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_foreach(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_switch(const ast::stmt_list::ptr& stmt, std::size_t begin);
auto find_location_reference(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end, const std::string& location) -> bool;
auto find_location_index(const ast::stmt_list::ptr& stmt, const std::string& location) -> std::size_t;
auto last_location_index(const ast::stmt_list::ptr& stmt, std::size_t index) -> bool;
void process_stack(const ast::decl_thread::ptr& thread);
void process_parameters(const ast::expr_parameters::ptr& params, const block::ptr& blk);
void process_stmt(const ast::stmt& stmt, const block::ptr& blk);
void process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void process_stmt_call(const ast::stmt_call::ptr& stmt, const block::ptr& blk);
void process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void process_stmt_endon(const ast::stmt_endon::ptr& stmt, const block::ptr& blk);
void process_stmt_notify(const ast::stmt_notify::ptr& stmt, const block::ptr& blk);
void process_stmt_wait(const ast::stmt_wait::ptr& stmt, const block::ptr& blk);
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void process_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt, const block::ptr& blk);
void process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk);
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk);
void process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void process_stmt_cases(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void process_expr(ast::expr& expr, const block::ptr& blk);
void process_expr_assign(ast::expr_assign::ptr& expr, const block::ptr& blk);
void process_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk);
void process_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk);
void process_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk);
void process_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk);
void process_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk);
void process_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk);
void process_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk);
void process_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk);
void process_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk);
void process_expr_method(const ast::expr_method::ptr& expr, const block::ptr& blk);
void process_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk);
void process_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk);
void process_expr_method_pointer(const ast::expr_pointer::ptr& expr, ast::expr& obj, const block::ptr& blk);
void process_expr_method_function(const ast::expr_function::ptr& expr, ast::expr& obj, const block::ptr& blk);
void process_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk);
void process_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk);
void process_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk);
void process_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void process_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk);
void process_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk);
void process_expr_vector(const ast::expr_vector::ptr& vec, const block::ptr& blk);
void process_var_create(ast::expr& expr, const block::ptr& blk, bool fromstmt = false);
void process_var_access(ast::expr& expr, const block::ptr& blk);
void process_var_remove(const ast::asm_remove::ptr& expr, const block::ptr& blk);
};
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,575 @@
// Copyright 2022 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 "iw6c.hpp"
namespace xsk::gsc::iw6c
{
auto disassembler::output() -> std::vector<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("// IW6 GSC ASSEMBLY\n");
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
for (const auto& func : functions_)
{
print_function(func);
}
std::vector<std::uint8_t> output;
output.resize(output_->pos());
std::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<function>());
const auto& func = functions_.back();
func->index = static_cast<std::uint32_t>(script_->pos());
func->size = stack_->read_endian<std::uint32_t>();
func->id = stack_->read_endian<std::uint16_t>();
func->name = func->id == 0 ? stack_->read_c_string() : resolver::token_name(static_cast<std::uint16_t>(func->id));
dissasemble_function(func);
func->labels = labels_;
labels_.clear();
}
resolve_local_functions();
}
void disassembler::dissasemble_function(const function::ptr& func)
{
auto size = func->size;
while (size > 0)
{
func->instructions.push_back(std::make_unique<instruction>());
const 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);
dissasemble_instruction(inst);
size -= inst->size;
}
}
void disassembler::dissasemble_instruction(const instruction::ptr& inst)
{
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_Return:
case opcode::OP_BoolNot:
case opcode::OP_CastBool:
case opcode::OP_inequality:
case opcode::OP_GetThisthread:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_checkclearparams:
case opcode::OP_CastFieldObject:
case opcode::OP_End:
case opcode::OP_size:
case opcode::OP_EmptyArray:
case opcode::OP_bit_and:
case opcode::OP_less_equal:
case opcode::OP_voidCodepos:
// case opcode::OP_ClearVariableField:
case opcode::OP_divide:
case opcode::OP_GetSelf:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_plus:
case opcode::OP_BoolComplement:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_inc:
case opcode::OP_clearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_endon:
case opcode::OP_greater_equal:
case opcode::OP_GetSelfObject:
case opcode::OP_SetVariableField:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_less:
case opcode::OP_GetGameRef:
case opcode::OP_waittillFrameEnd:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_notify:
case opcode::OP_DecTop:
case opcode::OP_shift_left:
case opcode::OP_greater:
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_GetZero:
case opcode::OP_wait:
case opcode::OP_minus:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_multiply:
case opcode::OP_mod:
case opcode::OP_GetGame:
case opcode::OP_waittill:
case opcode::OP_dec:
case opcode::OP_PreScriptCall:
case opcode::OP_GetAnim:
case opcode::OP_GetUndefined:
case opcode::OP_GetAnimObject:
case opcode::OP_GetLevelObject:
case opcode::OP_bit_ex_or:
case opcode::OP_equality:
case opcode::OP_ClearArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalArray:
case opcode::OP_vector:
case opcode::OP_bit_or:
case opcode::OP_AddArray:
// case opcode::OP_waittillmatch2:
case opcode::OP_shift_right:
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_endian<std::uint16_t>()));
break;
case opcode::OP_GetInteger:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::int32_t>()));
break;
case opcode::OP_GetFloat:
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
break;
case opcode::OP_GetVector:
inst->size += script_->align(4);
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<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(), false));
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_GetAnimTree:
script_->seek(1);
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_waittillmatch:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_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:
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:
disassemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
disassemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
disassemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
disassemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
disassemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
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:
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:
disassemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
disassemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
disassemble_jump(inst, false, true);
break;
case opcode::OP_jump:
disassemble_jump(inst, false, false);
break;
case opcode::OP_switch:
disassemble_switch(inst);
break;
case opcode::OP_endswitch:
disassemble_end_switch(inst);
break;
default:
throw disasm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void disassembler::disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
if (args)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
const auto id = script_->read_endian<std::uint16_t>();
const auto name = method ? resolver::method_name(id) : resolver::function_name(id);
inst->data.emplace(inst->data.begin(), name);
}
void disassembler::disassemble_local_call(const instruction::ptr& inst, bool thread)
{
const auto offset = 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 instruction::ptr& inst, bool thread)
{
script_->seek(3);
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
const auto file_id = stack_->read_endian<std::uint16_t>();
const auto file_name = file_id == 0 ? stack_->read_c_string() : resolver::token_name(file_id);
const auto func_id = stack_->read_endian<std::uint16_t>();
const auto func_name = func_id == 0 ? stack_->read_c_string() : resolver::token_name(func_id);
inst->data.emplace(inst->data.begin(), func_name);
inst->data.emplace(inst->data.begin(), file_name);
}
void disassembler::disassemble_switch(const instruction::ptr& inst)
{
const auto addr = inst->index + 4 + script_->read_endian<std::int32_t>();
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
void disassembler::disassemble_end_switch(const instruction::ptr& inst)
{
const auto count = script_->read_endian<std::uint16_t>();
inst->data.push_back(utils::string::va("%i", count));
std::uint32_t index = inst->index + 3;
if (count)
{
for (auto i = count; i > 0; i--)
{
const auto value = script_->read_endian<std::uint32_t>();
if (value < 0x40000)
{
const auto data = stack_->read_c_string();
if (data.data()[0] != 0x01)
{
inst->data.push_back("case");
inst->data.push_back(utils::string::quote(data, false));
}
else
inst->data.push_back("default");
}
else
{
inst->data.push_back("case");
inst->data.push_back(utils::string::va("%i", (value - 0x800000) & 0xFFFFFF));
}
index += 4;
const auto addr = disassemble_offset() + index;
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
index += 3;
inst->size += 7;
}
}
}
void disassembler::disassemble_field_variable(const instruction::ptr& inst)
{
const auto id = script_->read_endian<std::uint16_t>();
std::string name;
if (id > max_string_id)
{
auto temp = stack_->read_endian<std::uint16_t>();
name = temp == 0 ? stack_->read_c_string() : std::to_string(temp);
}
else
{
name = resolver::token_name(id);
}
inst->data.push_back(name);
}
void disassembler::disassemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
std::int32_t addr;
if (expr)
{
addr = inst->index + 3 + script_->read_endian<std::int16_t>();
}
else if (back)
{
addr = inst->index + 3 - script_->read_endian<std::uint16_t>();
}
else
{
addr = inst->index + 5 + script_->read_endian<std::int32_t>();
}
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
auto disassembler::disassemble_offset() -> std::int32_t
{
std::array<std::uint8_t, 4> bytes = {};
for (auto i = 0; i < 3; i++)
{
bytes[2 - i] = script_->read<std::uint8_t>();
}
auto offset = *reinterpret_cast<std::int32_t*>(bytes.data());
offset = (offset << 8) >> 10;
return offset;
}
void disassembler::resolve_local_functions()
{
for (const auto& func : functions_)
{
for (const auto& inst : func->instructions)
{
switch (static_cast<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[0] = 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 (const auto& func : functions_)
{
if (func->index == idx)
{
return func->name;
}
}
throw disasm_error(utils::string::va("couldn't resolve function name at index '0x%04X'!", idx));
}
throw disasm_error(utils::string::va("\"%s\" is not valid function address!", index.data()));
}
void disassembler::print_function(const function::ptr& func)
{
output_->write_string("\n");
output_->write_string(utils::string::va("sub_%s\n", func->name.data()));
for (const 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()));
}
print_instruction(inst);
}
output_->write_string(utils::string::va("end_%s\n", func->name.data()));
}
void disassembler::print_instruction(const instruction::ptr& inst)
{
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalMethodCall:
output_->write_string(utils::string::va(" sub_%s", inst->data[0].data()));
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
output_->write_string(utils::string::va(" sub_%s %s\n", inst->data[0].data(), inst->data[1].data()));
break;
case opcode::OP_endswitch:
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:
for (auto& data : inst->data)
{
output_->write_string(utils::string::va(" %s", data.data()));
}
break;
}
output_->write_string("\n");
}
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,42 @@
// Copyright 2022 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::iw6c
{
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<function::ptr> functions_;
std::unordered_map<std::uint32_t, std::string> labels_;
public:
auto output() -> std::vector<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 function::ptr& func);
void dissasemble_instruction(const instruction::ptr& inst);
void disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args);
void disassemble_local_call(const instruction::ptr& inst, bool thread);
void disassemble_far_call(const instruction::ptr& inst, bool thread);
void disassemble_switch(const instruction::ptr& inst);
void disassemble_end_switch(const instruction::ptr& inst);
void disassemble_field_variable(const instruction::ptr& inst);
void disassemble_jump(const instruction::ptr& inst, bool expr, bool back);
auto disassemble_offset() -> std::int32_t;
void resolve_local_functions();
auto resolve_function(const std::string& index) -> std::string;
void print_function(const function::ptr& func);
void print_instruction(const instruction::ptr& inst);
};
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,179 @@
// Copyright 2022 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 "iw6c.hpp"
namespace xsk::gsc::iw6c
{
auto opcode_size(std::uint8_t id) -> std::uint32_t
{
switch (static_cast<opcode>(id))
{
case opcode::OP_End:
case opcode::OP_Return:
case opcode::OP_GetUndefined:
case opcode::OP_GetZero:
case opcode::OP_waittillFrameEnd:
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_EvalArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_ClearArray:
case opcode::OP_EmptyArray:
case opcode::OP_AddArray:
case opcode::OP_PreScriptCall:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_GetLevelObject:
case opcode::OP_GetAnimObject:
case opcode::OP_GetSelf:
case opcode::OP_GetThisthread:
case opcode::OP_GetLevel:
case opcode::OP_GetGame:
case opcode::OP_GetAnim:
case opcode::OP_GetGameRef:
case opcode::OP_inc:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_bit_ex_or:
case opcode::OP_bit_and:
case opcode::OP_equality:
case opcode::OP_inequality:
case opcode::OP_less:
case opcode::OP_greater:
case opcode::OP_less_equal:
// case opcode::OP_waittillmatch2:
case opcode::OP_waittill:
case opcode::OP_notify:
case opcode::OP_endon:
case opcode::OP_voidCodepos:
case opcode::OP_vector:
case opcode::OP_greater_equal:
case opcode::OP_shift_left:
case opcode::OP_shift_right:
case opcode::OP_plus:
case opcode::OP_minus:
case opcode::OP_multiply:
case opcode::OP_divide:
case opcode::OP_mod:
case opcode::OP_size:
case opcode::OP_GetSelfObject:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_clearparams:
case opcode::OP_checkclearparams:
case opcode::OP_EvalLocalVariableRefCached0:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_SetVariableField:
// case opcode::OP_ClearVariableField:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_wait:
case opcode::OP_DecTop:
case opcode::OP_CastFieldObject:
case opcode::OP_CastBool:
case opcode::OP_BoolNot:
case opcode::OP_BoolComplement:
return 1;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
case opcode::OP_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_EvalLocalArrayCached:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_GetAnimTree:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
return 2;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
case opcode::OP_GetBuiltinFunction:
case opcode::OP_GetBuiltinMethod:
case opcode::OP_GetString:
case opcode::OP_GetIString:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_jumpback:
case opcode::OP_endswitch:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_waittillmatch:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalSelfFieldVariableRef:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_ClearFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
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_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_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_GetLocalFunction:
case opcode::OP_CallBuiltin:
case opcode::OP_CallBuiltinMethod:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
case opcode::OP_GetFarFunction:
return 4;
case opcode::OP_GetInteger:
case opcode::OP_GetFloat:
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
case opcode::OP_GetAnimation:
case opcode::OP_switch:
case opcode::OP_jump:
return 5;
case opcode::OP_GetVector:
return 13;
default:
throw error("couldn't resolve instruction size for " + std::to_string(id));
}
}
} // namespace xsk::gsc::iw6c

View File

@ -7,9 +7,18 @@
#include "utils/xsk/utils.hpp"
namespace xsk::gsc::iw6_console
#include "assembler.hpp"
#include "disassembler.hpp"
#include "compiler.hpp"
#include "decompiler.hpp"
#include "resolver.hpp"
#include "context.hpp"
namespace xsk::gsc::iw6c
{
constexpr std::uint16_t max_string_id = 0x0;
enum class opcode : std::uint8_t
{
OP_CastFieldObject = 0x1D,
@ -32,7 +41,7 @@ enum class opcode : std::uint8_t
OP_ClearLocalVariableFieldCached0 = 0x2E,
OP_notify = 0x2F,
OP_GetVector = 0x30,
OP_ScriptMethodChildThreadCallPointer = 0x31,
OP_ScriptChildThreadCallPointer = 0x31,
OP_voidCodepos = 0x32,
OP_GetByte = 0x33,
OP_ScriptFarMethodThreadCall = 0x34,
@ -169,4 +178,4 @@ enum class opcode : std::uint8_t
auto opcode_size(std::uint8_t id) -> std::uint32_t;
} // namespace xsk::gsc::iw6_console
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,848 @@
// Copyright 2022 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 "iw6c.hpp"
#include "parser.hpp"
#include "lexer.hpp"
xsk::gsc::iw6c::parser::symbol_type IW6Clex(xsk::gsc::iw6c::lexer& lexer)
{
return lexer.lex();
}
namespace xsk::gsc::iw6c
{
const std::unordered_map<std::string_view, parser::token::token_kind_type> keyword_map
{{
{ "#define", parser::token::SH_DEFINE },
{ "#undef", parser::token::SH_UNDEF },
{ "#ifdef", parser::token::SH_IFDEF },
{ "#ifndef", parser::token::SH_IFNDEF },
{ "#if", parser::token::SH_IF },
{ "#elif", parser::token::SH_ELIF },
{ "#else", parser::token::SH_ELSE },
{ "#endif", parser::token::SH_ENDIF },
{ "#inline", parser::token::INLINE },
{ "#include", parser::token::INCLUDE },
{ "#using_animtree", parser::token::USINGTREE },
{ "#animtree", parser::token::ANIMTREE },
{ "endon", parser::token::ENDON },
{ "notify", parser::token::NOTIFY },
{ "wait", parser::token::WAIT },
{ "waittill", parser::token::WAITTILL },
{ "waittillmatch", parser::token::WAITTILLMATCH },
{ "waittillframeend", parser::token::WAITTILLFRAMEEND },
{ "if", parser::token::IF },
{ "else", parser::token::ELSE },
{ "do", parser::token::DO },
{ "while", parser::token::WHILE },
{ "for", parser::token::FOR },
{ "foreach", parser::token::FOREACH },
{ "in", parser::token::IN },
{ "switch", parser::token::SWITCH },
{ "case", parser::token::CASE },
{ "default", parser::token::DEFAULT },
{ "break", parser::token::BREAK },
{ "continue", parser::token::CONTINUE },
{ "return", parser::token::RETURN },
{ "breakpoint", parser::token::BREAKPOINT },
{ "prof_begin", parser::token::PROFBEGIN },
{ "prof_end", parser::token::PROFEND },
{ "thread", parser::token::THREAD },
{ "childthread", parser::token::CHILDTHREAD },
{ "thisthread", parser::token::THISTHREAD },
{ "call", parser::token::CALL },
{ "true", parser::token::TRUE },
{ "false", parser::token::FALSE },
{ "undefined", parser::token::UNDEFINED },
{ "size", parser::token::SIZE },
{ "game", parser::token::GAME },
{ "self", parser::token::SELF },
{ "anim", parser::token::ANIM },
{ "level", parser::token::LEVEL },
}};
buffer::buffer() : length(0)
{
data = static_cast<char*>(std::malloc(max_buf_size));
}
buffer::~buffer()
{
if (data) std::free(data);
}
bool buffer::push(char c)
{
if (length >= max_buf_size)
return false;
data[length++] = c;
return true;
}
reader::reader() : buffer_pos(0), bytes_remaining(0), last_byte(0), current_byte(0), state(reader::end)
{
}
void reader::init(const char* data, size_t size)
{
if (data && size)
{
state = reader::ok;
buffer_pos = data;
bytes_remaining = static_cast<std::uint32_t>(size);
last_byte = 0;
current_byte = *data;
}
else
{
state = reader::end;
buffer_pos = 0;
bytes_remaining = 0;
last_byte = 0;
current_byte = 0;
}
}
void reader::advance()
{
++buffer_pos;
if (bytes_remaining-- == 1)
{
state = reader::end;
bytes_remaining = 0;
last_byte = current_byte;
current_byte = 0;
}
else
{
last_byte = current_byte;
current_byte = *buffer_pos;
}
}
lexer::lexer(build mode, const std::string& name, const char* data, size_t size) : loc_(location(&name)),
locs_(std::stack<location>()), readers_(std::stack<reader>()), header_top_(0), mode_(mode), indev_(false), clean_(true)
{
reader_.init(data, size);
}
void lexer::push_header(const std::string& file)
{
try
{
if (header_top_++ >= 10)
throw comp_error(loc_, "maximum gsh depth exceeded '10'");
auto data = resolver::file_data(file + ".gsh");
readers_.push(reader_);
locs_.push(loc_);
loc_.initialize(std::get<0>(data));
reader_.init(std::get<1>(data), std::get<2>(data));
clean_ = true;
}
catch (const std::exception& e)
{
throw error("parsing header file '" + file + "': " + e.what());
}
}
void lexer::pop_header()
{
header_top_--;
loc_ = locs_.top();
locs_.pop();
reader_ = readers_.top();
readers_.pop();
}
void lexer::ban_header(const location& loc)
{
if (header_top_ > 0)
{
throw comp_error(loc, "not allowed inside a gsh file");
}
}
auto lexer::lex() -> parser::symbol_type
{
buffer_.length = 0;
state_ = state::start;
while (true)
{
const auto& state = reader_.state;
auto& last = reader_.last_byte;
auto& curr = reader_.current_byte;
auto path = false;
loc_.step();
if (state == reader::end)
{
if (indev_)
throw comp_error(loc_, "unmatched devblock start ('/#')");
if (header_top_ > 0)
pop_header();
else
return parser::make_IW6CEOF(loc_);
}
if (clean_ && last != 0 && last != ' ' && last != '\t' && last != '\n')
clean_ = false;
advance();
switch (last)
{
case ' ':
case '\t':
case '\r':
loc_.step();
continue;
case '\n':
loc_.lines();
loc_.step();
clean_ = true;
continue;
case '\\':
throw comp_error(loc_, "invalid token ('\\')");
case '/':
if (curr != '=' && curr != '#' && curr != '@' && curr != '*' && curr != '/')
return parser::make_DIV(loc_);
advance();
if (last == '=')
return parser::make_ASSIGN_DIV(loc_);
if (last == '#')
{
if (indev_)
throw comp_error(loc_, "cannot recurse devblock ('/#')");
if (mode_ == build::dev)
{
indev_ = true;
return parser::make_DEVBEGIN(loc_);
}
else
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched devblock start ('/#')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '#' && curr == '/')
{
advance();
break;
}
advance();
}
}
}
else if (last == '@')
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched script doc comment start ('/@')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '@' && curr == '/')
{
advance();
break;
}
advance();
}
}
else if (last == '*')
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched multiline comment start ('/*')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '*' && curr == '/')
{
advance();
break;
}
advance();
}
}
else if (last == '/')
{
while (true)
{
if (state == reader::end)
break;
if (curr == '\n')
break;
advance();
}
}
continue;
case '#':
if (curr == '/')
{
if (!indev_)
throw comp_error(loc_, "unmatched devblock end ('#/')");
advance();
indev_ = false;
return parser::make_DEVEND(loc_);
}
buffer_.push(last);
advance();
while (state == reader::ok)
{
if (last != ' ' || last != '\t')
break;
advance();
}
if (state == reader::end || !((last > 64 && last < 91) || (last > 96 && last < 123)))
throw comp_error(loc_, "invalid preprocessor directive ('#')");
state_ = state::preprocessor;
goto lex_name;
case '*':
if (curr != '=' && curr != '/')
return parser::make_MUL(loc_);
advance();
if (last == '=')
return parser::make_ASSIGN_MUL(loc_);
throw comp_error(loc_, "unmatched multiline comment end ('*/')");
case '"':
state_ = state::string;
goto lex_string;
case '.':
if (curr < '0' || curr > '9')
return parser::make_DOT(loc_);
goto lex_number;
case '(':
return parser::make_LPAREN(loc_);
case ')':
return parser::make_RPAREN(loc_);
case '{':
return parser::make_LBRACE(loc_);
case '}':
return parser::make_RBRACE(loc_);
case '[':
return parser::make_LBRACKET(loc_);
case ']':
return parser::make_RBRACKET(loc_);
case ',':
return parser::make_COMMA(loc_);
case ';':
return parser::make_SEMICOLON(loc_);
case ':':
if (curr != ':')
return parser::make_COLON(loc_);
advance();
return parser::make_DOUBLECOLON(loc_);
case '?':
return parser::make_QMARK(loc_);
case '=':
if (curr != '=')
return parser::make_ASSIGN(loc_);
advance();
return parser::make_EQUALITY(loc_);
case '+':
if (curr != '+' && curr != '=')
return parser::make_ADD(loc_);
advance();
if (last == '+')
return parser::make_INCREMENT(loc_);
return parser::make_ASSIGN_ADD(loc_);
case '-':
if (curr != '-' && curr != '=')
return parser::make_SUB(loc_);
advance();
if (last == '-')
return parser::make_DECREMENT(loc_);
return parser::make_ASSIGN_SUB(loc_);
case '%':
if (curr != '=')
return parser::make_MOD(loc_);
advance();
return parser::make_ASSIGN_MOD(loc_);
case '|':
if (curr != '|' && curr != '=')
return parser::make_BITWISE_OR(loc_);
advance();
if (last == '|')
return parser::make_OR(loc_);
return parser::make_ASSIGN_BW_OR(loc_);
case '&':
if (curr != '&' && curr != '=' && curr != '"')
return parser::make_BITWISE_AND(loc_);
advance();
if (last == '&')
return parser::make_AND(loc_);
if (last == '=')
return parser::make_ASSIGN_BW_AND(loc_);
state_ = state::localize;
goto lex_string;
case '^':
if (curr != '=')
return parser::make_BITWISE_EXOR(loc_);
advance();
return parser::make_ASSIGN_BW_EXOR(loc_);
case '!':
if (curr != '=')
return parser::make_NOT(loc_);
advance();
return parser::make_INEQUALITY(loc_);
case '~':
return parser::make_COMPLEMENT(loc_);
case '<':
if (curr != '<' && curr != '=')
return parser::make_LESS(loc_);
advance();
if (last == '=')
return parser::make_LESS_EQUAL(loc_);
if (curr != '=')
return parser::make_LSHIFT(loc_);
advance();
return parser::make_ASSIGN_LSHIFT(loc_);
case '>':
if (curr != '>' && curr != '=')
return parser::make_GREATER(loc_);
advance();
if (last == '=')
return parser::make_GREATER_EQUAL(loc_);
if (curr != '=')
return parser::make_RSHIFT(loc_);
advance();
return parser::make_ASSIGN_RSHIFT(loc_);
default:
if (last >= '0' && last <= '9')
goto lex_number;
else if (last == '_' || (last >= 'A' && last <= 'Z') || (last >= 'a' && last <= 'z'))
goto lex_name;
throw comp_error(loc_, utils::string::va("bad token: \'%c\'", last));
}
lex_string:
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched string start ('\"')");
if (curr == '"')
{
advance();
break;
}
if (curr == '\n')
throw comp_error(loc_, "unterminated string literal");
if (curr == '\\')
{
advance();
if (state == reader::end)
throw comp_error(loc_, "invalid token ('\')");
char c = curr;
switch (curr)
{
case 't': c = '\t'; break;
case 'r': c = '\r'; break;
case 'n': c = '\n'; break;
case '"': c = '\"'; break;
case '\\': c = '\\'; break;
default: break;
}
if (!buffer_.push(c))
throw comp_error(loc_, "max string size exceeded");
}
else if (!buffer_.push(curr))
throw comp_error(loc_, "max string size exceeded");
advance();
}
if (state_ == state::localize)
return parser::make_ISTRING(std::string(buffer_.data, buffer_.length), loc_);
return parser::make_STRING(std::string(buffer_.data, buffer_.length), loc_);
lex_name:
buffer_.push(last);
while (true)
{
if (state == reader::end)
break;
if (!(curr == '\\' || curr == '_' || (curr > 64 && curr < 91) || (curr > 96 && curr < 123) || (curr > 47 && curr < 58)))
break;
if (curr == '\\')
{
if (last == '\\')
throw comp_error(loc_, "invalid path '\\\\'");
path = true;
if (!buffer_.push('/'))
throw comp_error(loc_, "max string size exceeded");
}
else if (!buffer_.push(curr))
throw comp_error(loc_, "max string size exceeded");
advance();
}
if (state_ == state::preprocessor)
{
auto token = parser::token::IW6CUNDEF;
if (buffer_.length < 16)
{
const auto itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
{
if (itr->second > parser::token::SH_ENDIF)
return parser::symbol_type(itr->second, loc_);
token = itr->second;
}
}
preprocessor_run(token);
state_ = state::start;
continue;
}
else
{
if (buffer_.length < 17)
{
const auto itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
return parser::symbol_type(itr->second, loc_);
}
if (path)
{
if (buffer_.data[buffer_.length - 1] == '/')
throw comp_error(loc_, "invalid path end '\\'");
return parser::make_PATH(resolver::make_token(std::string_view(buffer_.data, buffer_.length)), loc_);
}
return parser::make_IDENTIFIER(resolver::make_token(std::string_view(buffer_.data, buffer_.length)), loc_);
}
lex_number:
if (last == '.' || last != '0' || (last == '0' && (curr != 'o' && curr != 'b' && curr != 'x')))
{
buffer_.push(last);
auto dot = last == '.' ? 1 : 0;
auto flt = 0;
while (true)
{
if (state == reader::end)
break;
if (curr == '\'' && (last == '\'' || last == 'f' || last == '.'))
throw comp_error(loc_, "invalid number literal");
if ((curr == '.' || curr == 'f') && last == '\'')
throw comp_error(loc_, "invalid number literal");
if (curr == '\'')
{
advance();
continue;
}
if (curr == 'f')
flt++;
else if (curr == '.')
dot++;
else if (!(curr > 47 && curr < 58))
break;
if (!buffer_.push(curr))
throw comp_error(loc_, "number literal size exceeded");
advance();
}
if (last == '\'')
throw comp_error(loc_, "invalid number literal");
if (dot > 1 || flt > 1 || (flt && buffer_.data[buffer_.length - 1] != 'f'))
throw comp_error(loc_, "invalid number literal");
if (dot || flt)
return parser::make_FLOAT(std::string(buffer_.data, buffer_.length), loc_);
return parser::make_INTEGER(std::string(buffer_.data, buffer_.length), loc_);
}
else if (curr == 'o')
{
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'o')) || (curr == 'o' && last == '\''))
throw comp_error(loc_, "invalid octal literal");
if (curr == '\'')
{
advance();
continue;
}
if (!(curr > 47 && curr < 56))
break;
if (!buffer_.push(curr))
throw error("gsc lexer: out of memory!");
advance();
}
if (last == '\'' || buffer_.length <= 0)
throw comp_error(loc_, "invalid octal literal");
return parser::make_INTEGER(utils::string::oct_to_dec(buffer_.data), loc_);
}
else if (curr == 'b')
{
buffer_.push(last);
buffer_.push(curr);
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'b')) || (curr == 'b' && last == '\''))
throw comp_error(loc_, "invalid binary literal");
if (curr == '\'')
{
advance();
continue;
}
if (curr != '0' && curr != '1')
break;
if (!buffer_.push(curr))
throw comp_error(loc_, "number literal size exceeded");
advance();
}
if (last == '\'' || buffer_.length < 3)
throw comp_error(loc_, "invalid binary literal");
return parser::make_INTEGER(utils::string::bin_to_dec(buffer_.data), loc_);
}
else if (curr == 'x')
{
buffer_.push(last);
buffer_.push(curr);
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'x')) || (curr == 'x' && last == '\''))
throw comp_error(loc_, "invalid hexadecimal literal");
if (curr == '\'')
{
advance();
continue;
}
if (!((curr > 47 && curr < 58) || (curr > 64 && curr < 71) || (curr > 96 && curr < 103)))
break;
if (!buffer_.push(curr))
throw error("gsc lexer: out of memory!");
advance();
}
if (last == '\'' || buffer_.length < 3)
throw comp_error(loc_, "invalid hexadecimal literal");
return parser::make_INTEGER(utils::string::hex_to_dec(buffer_.data), loc_);
}
throw error("UNEXPECTED LEXER INTERNAL ERROR!");
}
}
void lexer::advance()
{
reader_.advance();
loc_.end.column++;
if (reader_.current_byte == '\\') [[unlikely]]
preprocessor_wrap();
}
void lexer::preprocessor_wrap()
{
while (reader_.current_byte == '\\')
{
if (reader_.bytes_remaining == 1)
throw comp_error(loc_, "invalid token ('\\')");
if (reader_.buffer_pos[1] != '\r' && reader_.buffer_pos[1] != '\n')
break;
if (reader_.buffer_pos[1] == '\r')
{
if (reader_.bytes_remaining <= 3 || reader_.buffer_pos[2] != '\n')
throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 3;
reader_.bytes_remaining -= 3;
}
if ((reader_.buffer_pos[1] == '\n'))
{
if (reader_.bytes_remaining == 2)
throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 2;
reader_.bytes_remaining -= 2;
}
if (reader_.bytes_remaining == 0)
{
reader_.state = reader::end;
reader_.current_byte = 0;
}
else
{
reader_.current_byte = *reader_.buffer_pos;
}
loc_.lines();
loc_.step();
}
}
void lexer::preprocessor_run(parser::token::token_kind_type token)
{
if (!clean_)
throw comp_error(loc_, "invalid token ('#')");
switch (token)
{
case parser::token::SH_DEFINE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_UNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELSE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ENDIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
default:
throw comp_error(loc_, "unknown preprocessor directive");
}
}
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,78 @@
// Copyright 2022 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::iw6c
{
constexpr size_t max_buf_size = 0x2000;
struct buffer
{
char* data;
size_t length;
buffer();
~buffer();
bool push(char c);
};
struct reader
{
enum state_type : std::uint8_t { end, ok };
const char* buffer_pos;
std::uint32_t bytes_remaining;
char last_byte;
char current_byte;
state_type state;
reader();
reader(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
}
reader& operator=(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
return *this;
}
void init(const char* data, size_t size);
void advance();
};
class lexer
{
enum class state : std::uint8_t { start, string, localize, preprocessor };
reader reader_;
buffer buffer_;
location loc_;
std::stack<location> locs_;
std::stack<reader> readers_;
std::uint32_t header_top_;
state state_;
build mode_;
bool indev_;
bool clean_;
public:
lexer(build mode, const std::string& name, const char* data, size_t size);
auto lex() -> parser::symbol_type;
void push_header(const std::string& file);
void pop_header();
void ban_header(const location& loc);
private:
void advance();
void preprocessor_wrap();
void preprocessor_run(parser::token::token_kind_type token);
};
} // namespace xsk::gsc::iw6c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,483 @@
// Copyright 2022 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 "iw6c.hpp"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
namespace xsk::gsc::iw6c
{
std::unordered_map<std::uint8_t, std::string_view> opcode_map;
std::unordered_map<std::uint16_t, std::string_view> function_map;
std::unordered_map<std::uint16_t, std::string_view> method_map;
std::unordered_map<std::uint16_t, std::string_view> token_map;
std::unordered_map<std::string_view, std::uint8_t> opcode_map_rev;
std::unordered_map<std::string_view, std::uint16_t> function_map_rev;
std::unordered_map<std::string_view, std::uint16_t> method_map_rev;
std::unordered_map<std::string_view, std::uint16_t> token_map_rev;
std::unordered_map<std::string, std::vector<std::uint8_t>> files;
read_cb_type read_callback = nullptr;
std::set<std::string> string_map;
void resolver::init(read_cb_type callback)
{
read_callback = callback;
}
void resolver::cleanup()
{
files.clear();
}
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 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 std::string(itr->second);
}
throw 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
{
if (name.starts_with("_func_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
}
const auto itr = function_map_rev.find(name);
if (itr != function_map_rev.end())
{
return itr->second;
}
throw 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 std::string(itr->second);
}
return utils::string::va("_func_%04X", id);
}
auto resolver::method_id(const std::string& name) -> std::uint16_t
{
if (name.starts_with("_meth_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
}
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return itr->second;
}
throw 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 std::string(itr->second);
}
return utils::string::va("_meth_%04X", id);
}
auto resolver::token_id(const std::string& name) -> std::uint16_t
{
if (name.starts_with("_id_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(4), nullptr, 16));
}
const auto itr = token_map_rev.find(name);
if (itr != token_map_rev.end())
{
return itr->second;
}
return 0;
}
auto resolver::token_name(std::uint16_t id) -> std::string
{
const auto itr = token_map.find(id);
if (itr != token_map.end())
{
return std::string(itr->second);
}
return utils::string::va("_id_%04X", id);
}
auto resolver::find_function(const std::string& name) -> bool
{
if (name.starts_with("_func_")) return true;
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
{
if (name.starts_with("_meth_")) return true;
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return true;
}
return false;
}
auto resolver::make_token(std::string_view str) -> std::string
{
if (str.starts_with("_id_") || str.starts_with("_func_") || str.starts_with("_meth_"))
{
return std::string(str);
}
auto data = std::string(str.begin(), str.end());
for (std::size_t i = 0; i < data.size(); i++)
{
data[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(str[i])));
if (data[i] == '\\') data[i] = '/';
}
return data;
}
auto resolver::file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>
{
const auto itr = files.find(name);
if (itr != files.end())
{
return { &itr->first ,reinterpret_cast<const char*>(itr->second.data()), itr->second.size() };
}
auto data = read_callback(name);
const auto res = files.insert({ name, std::move(data)});
if (res.second)
{
return { &res.first->first, reinterpret_cast<const char*>(res.first->second.data()), res.first->second.size() };
}
throw error("couldn't open gsc file '" + name + "'");
}
std::set<std::string_view> paths
{
"character"sv,
"codescripts"sv,
"common_scripts"sv,
"destructible_scripts"sv,
"maps"sv,
"vehicle_scripts"sv,
"xmodelalias"sv,
"maps/animated_models"sv,
"maps/createart"sv,
"maps/createfx"sv,
"maps/interactive_models"sv,
"maps/mp"sv,
"maps/mp/agents"sv,
"maps/mp/alien"sv,
"maps/mp/bots"sv,
"maps/mp/gametypes"sv,
"maps/mp/killstreaks"sv,
"maps/mp/perks"sv,
"maps/mp/agents/alien"sv,
"maps/mp/agents/dog"sv,
"maps/mp/agents/alien/alien_ancestor"sv,
"maps/mp/agents/alien/alien_kraken"sv,
"maps/mp/agents/alien/alien_spider"sv,
};
auto resolver::fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path
{
auto result = std::filesystem::path();
auto root = false;
for (auto& entry : file)
{
if (!root && paths.contains(entry.string()))
{
result = entry;
root = true;
}
else if (paths.contains(result.string()))
{
result /= entry;
}
}
return result.empty() ? file : result;
}
const std::array<std::pair<std::uint8_t, const char*>, 153> opcode_list
{{
{ 0x1D, "OP_CastFieldObject" },
{ 0x1E, "OP_SetLocalVariableFieldCached" },
{ 0x1F, "OP_plus" },
{ 0x20, "OP_RemoveLocalVariables" },
{ 0x21, "OP_EvalSelfFieldVariableRef" },
{ 0x22, "OP_ScriptFarMethodChildThreadCall" },
{ 0x23, "OP_GetGameRef" },
{ 0x24, "OP_EvalAnimFieldVariable" },
{ 0x25, "OP_EvalLevelFieldVariableRef" },
{ 0x26, "OP_GetThisthread" },
{ 0x27, "OP_greater" },
{ 0x28, "OP_waittillmatch" },
{ 0x29, "OP_shift_right" },
{ 0x2A, "OP_dec" },
{ 0x2B, "OP_JumpOnTrue" },
{ 0x2C, "OP_bit_or" },
{ 0x2D, "OP_equality" },
{ 0x2E, "OP_ClearLocalVariableFieldCached0" },
{ 0x2F, "OP_notify" },
{ 0x30, "OP_GetVector" },
{ 0x31, "OP_ScriptMethodChildThreadCallPointer" },
{ 0x32, "OP_voidCodepos" },
{ 0x33, "OP_GetByte" },
{ 0x34, "OP_ScriptFarMethodThreadCall" },
{ 0x35, "OP_SetSelfFieldVariableField" },
{ 0x36, "OP_JumpOnFalseExpr" },
{ 0x37, "OP_GetUndefined" },
{ 0x38, "OP_jumpback" },
{ 0x39, "OP_JumpOnTrueExpr" },
{ 0x3A, "OP_CallBuiltin0" },
{ 0x3B, "OP_CallBuiltin1" },
{ 0x3C, "OP_CallBuiltin2" },
{ 0x3D, "OP_CallBuiltin3" },
{ 0x3E, "OP_CallBuiltin4" },
{ 0x3F, "OP_CallBuiltin5" },
{ 0x40, "OP_CallBuiltin" },
{ 0x41, "OP_SetLocalVariableFieldCached0" },
{ 0x42, "OP_ClearFieldVariable" },
{ 0x43, "OP_GetLevel" },
{ 0x44, "OP_size" },
{ 0x45, "OP_SafeSetWaittillVariableFieldCached" },
{ 0x46, "OP_ScriptLocalThreadCall" },
{ 0x47, "OP_AddArray" },
{ 0x48, "OP_endon" },
{ 0x49, "OP_EvalFieldVariable" },
{ 0x4A, "OP_shift_left" },
{ 0x4B, "OP_EvalLocalArrayRefCached0" },
{ 0x4C, "OP_Return" },
{ 0x4D, "OP_CreateLocalVariable" },
{ 0x4E, "OP_SafeSetVariableFieldCached0" },
{ 0x4F, "OP_GetBuiltinFunction" },
{ 0x50, "OP_ScriptLocalMethodCall" },
{ 0x51, "OP_CallBuiltinMethodPointer" },
{ 0x52, "OP_ScriptLocalChildThreadCall" },
{ 0x53, "OP_GetSelfObject" },
{ 0x54, "OP_GetGame" },
{ 0x55, "OP_SetLevelFieldVariableField" },
{ 0x56, "OP_EvalArray" },
{ 0x57, "OP_GetSelf" },
{ 0x58, "OP_End" },
{ 0x59, "OP_EvalSelfFieldVariable" },
{ 0x5A, "OP_less_equal" },
{ 0x5B, "OP_EvalLocalVariableCached0" },
{ 0x5C, "OP_EvalLocalVariableCached1" },
{ 0x5D, "OP_EvalLocalVariableCached2" },
{ 0x5E, "OP_EvalLocalVariableCached3" },
{ 0x5F, "OP_EvalLocalVariableCached4" },
{ 0x60, "OP_EvalLocalVariableCached5" },
{ 0x61, "OP_EvalLocalVariableCached" },
{ 0x62, "OP_EvalNewLocalArrayRefCached0" },
{ 0x63, "OP_ScriptMethodChildThreadCallPointer" },
{ 0x64, "OP_EvalLocalVariableObjectCached" },
{ 0x65, "OP_ScriptLocalMethodThreadCall" },
{ 0x66, "OP_GetInteger" },
{ 0x67, "OP_ScriptMethodCallPointer" },
{ 0x68, "OP_checkclearparams" },
{ 0x69, "OP_SetAnimFieldVariableField" },
{ 0x6A, "OP_UNK_1" },
{ 0x6B, "OP_minus" },
{ 0x6C, "OP_ScriptLocalFunctionCall2" },
{ 0x6D, "OP_GetNegUnsignedShort" },
{ 0x6E, "OP_GetNegByte" },
{ 0x6F, "OP_SafeCreateVariableFieldCached" },
{ 0x70, "OP_greater_equal" },
{ 0x71, "OP_vector" },
{ 0x72, "OP_GetBuiltinMethod" },
{ 0x73, "OP_endswitch" },
{ 0x74, "OP_ClearArray" },
{ 0x75, "OP_DecTop" },
{ 0x76, "OP_CastBool" },
{ 0x77, "OP_EvalArrayRef" },
{ 0x78, "OP_SetNewLocalVariableFieldCached0" },
{ 0x79, "OP_GetZero" },
{ 0x7A, "OP_wait" },
{ 0x7B, "OP_waittill" },
{ 0x7C, "OP_GetIString" },
{ 0x7D, "OP_ScriptFarFunctionCall" },
{ 0x7E, "OP_GetAnimObject" },
{ 0x7F, "OP_GetAnimTree" },
{ 0x80, "OP_EvalLocalArrayCached" },
{ 0x81, "OP_mod" },
{ 0x82, "OP_ScriptFarThreadCall" },
{ 0x83, "OP_GetUnsignedShort" },
{ 0x84, "OP_clearparams" },
{ 0x85, "OP_ScriptMethodThreadCallPointer" },
{ 0x86, "OP_ScriptFunctionCallPointer" },
{ 0x87, "OP_EmptyArray" },
{ 0x88, "OP_SafeSetVariableFieldCached" },
{ 0x89, "OP_UNK_2" },
{ 0x8A, "OP_EvalFieldVariableRef" },
{ 0x8B, "OP_ScriptLocalMethodChildThreadCall" },
{ 0x8C, "OP_UNK_3" },
{ 0x8D, "OP_GetFloat" },
{ 0x8E, "OP_EvalLocalVariableRefCached" },
{ 0x8F, "OP_JumpOnFalse" },
{ 0x90, "OP_BoolComplement" },
{ 0x91, "OP_ScriptThreadCallPointer" },
{ 0x92, "OP_ScriptFarFunctionCall2" },
{ 0x93, "OP_less" },
{ 0x94, "OP_BoolNot" },
{ 0x95, "OP_waittillFrameEnd" },
{ 0x96, "OP_GetString" },
{ 0x97, "OP_EvalLevelFieldVariable" },
{ 0x98, "OP_GetLevelObject" },
{ 0x99, "OP_inc" },
{ 0x9A, "OP_CallBuiltinMethod0" },
{ 0x9B, "OP_CallBuiltinMethod1" },
{ 0x9C, "OP_CallBuiltinMethod2" },
{ 0x9D, "OP_CallBuiltinMethod3" },
{ 0x9E, "OP_CallBuiltinMethod4" },
{ 0x9F, "OP_CallBuiltinMethod5" },
{ 0xA0, "OP_CallBuiltinMethod" },
{ 0xA1, "OP_GetAnim" },
{ 0xA2, "OP_switch" },
{ 0xA3, "OP_SetVariableField" },
{ 0xA4, "OP_divide" },
{ 0xA5, "OP_GetLocalFunction" },
{ 0xA6, "OP_ScriptFarChildThreadCall" },
{ 0xA7, "OP_multiply" },
{ 0xA8, "OP_ClearLocalVariableFieldCached" },
{ 0xA9, "OP_EvalAnimFieldVariableRef" },
{ 0xAA, "OP_EvalLocalArrayRefCached" },
{ 0xAB, "OP_EvalLocalVariableRefCached0" },
{ 0xAC, "OP_bit_and" },
{ 0xAD, "OP_GetAnimation" },
{ 0xAE, "OP_GetFarFunction" },
{ 0xAF, "OP_CallBuiltinPointer" },
{ 0xB0, "OP_jump" },
{ 0xB1, "OP_PreScriptCall" },
{ 0xB2, "OP_ScriptFarMethodCall" },
{ 0xB3, "OP_inequality" },
{ 0xB4, "OP_ScriptLocalFunctionCall" },
{ 0xB5, "OP_bit_ex_or" },
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> function_list
{{
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> method_list
{{
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> token_list
{{
}};
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());
token_map.reserve(token_list.size());
token_map_rev.reserve(token_list.size());
for (const auto& entry : opcode_list)
{
opcode_map.insert({ entry.first, entry.second });
opcode_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : function_list)
{
function_map.insert({ entry.first, entry.second });
function_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : method_list)
{
method_map.insert({ entry.first, entry.second });
method_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : token_list)
{
token_map.insert({ entry.first, entry.second });
token_map_rev.insert({ entry.second, entry.first });
}
}
};
__init__ _;
} // namespace xsk::gsc::iw6c
#ifdef _MSC_VER
#pragma warning(pop)
#endif

View File

@ -0,0 +1,40 @@
// Copyright 2022 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::iw6c
{
class resolver
{
public:
static void init(read_cb_type callback);
static void cleanup();
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 token_id(const std::string& name) -> std::uint16_t;
static auto token_name(std::uint16_t id) -> std::string;
static auto find_function(const std::string& name) -> bool;
static auto find_method(const std::string& name) -> bool;
static void add_function(const std::string& name, std::uint16_t id);
static void add_method(const std::string& name, std::uint16_t id);
static auto make_token(std::string_view str) -> std::string;
static auto file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>;
static auto fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path;
};
} // namespace xsk::gsc::iw6c

View File

@ -0,0 +1,556 @@
// Copyright 2022 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 "s1c.hpp"
namespace xsk::gsc::s1c
{
auto assembler::output_script() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> script;
if (script_ == nullptr) return script;
script.resize(script_->pos());
std::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());
std::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<function::ptr> functions;
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<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 opdata = utils::string::parse_code(line);
if (switchnum)
{
if (opdata[0] == "case" || opdata[0] == "default")
{
for (auto& entry : opdata)
{
func->instructions.back()->data.push_back(entry);
}
switchnum--;
continue;
}
throw asm_error("invalid instruction inside endswitch \""s + line + "\"!");
}
else
{
auto inst = std::make_unique<instruction>();
inst->index = index;
inst->opcode = resolver::opcode_id(opdata[0]);
inst->size = opcode_size(inst->opcode);
opdata.erase(opdata.begin());
inst->data = std::move(opdata);
switch (static_cast<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[0] = inst->data[0].substr(4);
break;
case opcode::OP_endswitch:
switchnum = static_cast<std::uint16_t>(std::stoi(inst->data[0]));
inst->size += 7 * switchnum;
break;
default:
break;
}
index += inst->size;
func->instructions.push_back(std::move(inst));
}
}
}
assemble(file, functions);
}
void assembler::assemble(const std::string& file, std::vector<function::ptr>& funcs)
{
script_ = std::make_unique<utils::byte_buffer>(0x100000);
stack_ = std::make_unique<utils::byte_buffer>(0x100000);
filename_ = file;
functions_ = std::move(funcs);
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
for (const auto& func : functions_)
{
assemble_function(func);
}
}
void assembler::assemble_function(const function::ptr& func)
{
labels_ = func->labels;
func->id = resolver::token_id(func->name);
stack_->write_endian<std::uint32_t>(func->size);
stack_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(func->id));
if (func->id == 0)
{
stack_->write_c_string(func->name);
}
for (const auto& inst : func->instructions)
{
assemble_instruction(inst);
}
}
void assembler::assemble_instruction(const instruction::ptr& inst)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_Return:
case opcode::OP_BoolNot:
case opcode::OP_CastBool:
case opcode::OP_inequality:
case opcode::OP_GetThisthread:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_checkclearparams:
case opcode::OP_CastFieldObject:
case opcode::OP_End:
case opcode::OP_size:
case opcode::OP_EmptyArray:
case opcode::OP_bit_and:
case opcode::OP_less_equal:
case opcode::OP_voidCodepos:
// case opcode::OP_ClearVariableField:
case opcode::OP_divide:
case opcode::OP_GetSelf:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_plus:
case opcode::OP_BoolComplement:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_inc:
case opcode::OP_clearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_endon:
case opcode::OP_greater_equal:
case opcode::OP_GetSelfObject:
case opcode::OP_SetVariableField:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_less:
case opcode::OP_GetGameRef:
case opcode::OP_waittillFrameEnd:
case opcode::OP_waitFrame:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_notify:
case opcode::OP_DecTop:
case opcode::OP_shift_left:
case opcode::OP_greater:
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_GetZero:
case opcode::OP_wait:
case opcode::OP_minus:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_multiply:
case opcode::OP_mod:
case opcode::OP_GetGame:
case opcode::OP_waittill:
case opcode::OP_dec:
case opcode::OP_PreScriptCall:
case opcode::OP_GetAnim:
case opcode::OP_GetUndefined:
case opcode::OP_GetAnimObject:
case opcode::OP_GetLevelObject:
case opcode::OP_bit_ex_or:
case opcode::OP_equality:
case opcode::OP_ClearArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalArray:
case opcode::OP_vector:
case opcode::OP_bit_or:
case opcode::OP_AddArray:
// case opcode::OP_waittillmatch2:
case opcode::OP_shift_right:
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
script_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetInteger:
script_->write_endian<std::int32_t>(std::stoi(inst->data[0]));
break;
case opcode::OP_GetFloat:
script_->write_endian<float>(std::stof(inst->data[0]));
break;
case opcode::OP_GetVector:
inst->size += script_->align(4);
script_->write_endian<float>(std::stof(inst->data[0]));
script_->write_endian<float>(std::stof(inst->data[1]));
script_->write_endian<float>(std::stof(inst->data[2]));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->write_endian<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_GetAnimation:
script_->write_endian<std::uint32_t>(0);
script_->write_endian<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
stack_->write_c_string(inst->data[1]);
break;
case opcode::OP_GetAnimTree:
script_->write_endian<std::uint8_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_waittillmatch:
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[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_endian<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:
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_endian<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:
assemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
assemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
assemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
assemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
assemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
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:
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:
assemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
assemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
assemble_jump(inst, false, true);
break;
case opcode::OP_jump:
assemble_jump(inst, false, false);
break;
case opcode::OP_switch:
assemble_switch(inst);
break;
case opcode::OP_endswitch:
assemble_end_switch(inst);
break;
default:
throw asm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void assembler::assemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
if (args)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
const auto id = method ? resolver::method_id(inst->data[0]) : resolver::function_id(inst->data[0]);
script_->write_endian<std::uint16_t>(id);
}
void assembler::assemble_local_call(const instruction::ptr& inst, bool thread)
{
const auto addr = resolve_function(inst->data[0]);
const auto offset = static_cast<std::int32_t>(addr - inst->index - 1);
assemble_offset(offset);
if (thread)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
}
void assembler::assemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->write_endian<std::uint8_t>(0);
script_->write_endian<std::uint16_t>(0);
if (thread)
{
script_->write_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[2])));
}
const auto file_id = resolver::token_id(inst->data[0]);
const auto func_id = resolver::token_id(inst->data[1]);
stack_->write_endian<std::uint16_t>(file_id);
if (file_id == 0) stack_->write_c_string(inst->data[0]);
stack_->write_endian<std::uint16_t>(func_id);
if (func_id == 0) stack_->write_c_string(inst->data[1]);
}
void assembler::assemble_switch(const instruction::ptr& inst)
{
const auto addr = resolve_label(inst->data[0]);
script_->write_endian<std::int32_t>(addr - inst->index - 4);
}
void assembler::assemble_end_switch(const instruction::ptr& inst)
{
const auto count = std::stoul(inst->data[0]);
script_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(count));
std::uint32_t index = inst->index + 3;
for (auto i = 0u; i < count; i++)
{
if (inst->data[1 + (3 * i)] == "case")
{
if (utils::string::is_number(inst->data[1 + (3 * i) + 1]))
{
script_->write_endian<uint32_t>((std::stoi(inst->data[1 + (3 * i) + 1]) & 0xFFFFFF) + 0x800000);
}
else
{
script_->write_endian<uint32_t>(i + 1);
stack_->write_c_string(inst->data[1 + (3 * i) + 1]);
}
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 2]);
assemble_offset(addr - index);
index += 3;
}
else if (inst->data[1 + (3 * i)] == "default")
{
script_->write_endian<uint32_t>(0);
stack_->write_c_string("\x01");
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 1]);
assemble_offset(addr - index);
index += 3;
}
else
{
throw asm_error("invalid switch case '" + inst->data[1 + (3 * i)] + "'!");
}
}
}
void assembler::assemble_field_variable(const instruction::ptr& inst)
{
auto id = resolver::token_id(inst->data[0]);
if (id == 0) id = 0xFFFF;
script_->write_endian<std::uint16_t>(id);
if (id > max_string_id)
{
stack_->write_endian<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
}
}
void assembler::assemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
const auto addr = resolve_label(inst->data[0]);
if (expr)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>(addr - inst->index - 3));
}
else if (back)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>((inst->index + 3) - addr));
}
else
{
script_->write_endian<std::int32_t>(static_cast<std::int32_t>(addr - inst->index - 5));
}
}
void assembler::assemble_offset(std::int32_t offset)
{
std::array<std::uint8_t, 4> bytes = {};
offset = (offset << 10) >> 8;
*reinterpret_cast<std::int32_t*>(bytes.data()) = offset;
script_->write_endian<std::uint8_t>(bytes[2]);
script_->write_endian<std::uint8_t>(bytes[1]);
script_->write_endian<std::uint8_t>(bytes[0]);
}
auto assembler::resolve_function(const std::string& name) -> std::int32_t
{
for (const auto& entry : functions_)
{
if (entry->name == name)
{
return entry->index;
}
}
throw asm_error("couldn't resolve local function address of '" + name + "'!");
}
auto assembler::resolve_label(const std::string& name) -> std::int32_t
{
for (const auto& entry : labels_)
{
if (entry.second == name)
{
return entry.first;
}
}
throw asm_error("couldn't resolve label address of '" + name + "'!");
}
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,40 @@
// Copyright 2022 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::s1c
{
class assembler : public gsc::assembler
{
std::string filename_;
utils::byte_buffer::ptr script_;
utils::byte_buffer::ptr stack_;
std::vector<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<function::ptr>& funcs);
private:
void assemble_function(const function::ptr& func);
void assemble_instruction(const instruction::ptr& inst);
void assemble_builtin_call(const instruction::ptr& inst, bool method, bool args);
void assemble_local_call(const instruction::ptr& inst, bool thread);
void assemble_far_call(const instruction::ptr& inst, bool thread);
void assemble_switch(const instruction::ptr& inst);
void assemble_end_switch(const instruction::ptr& inst);
void assemble_field_variable(const instruction::ptr& inst);
void assemble_jump(const instruction::ptr& inst, bool expr, bool back);
void assemble_offset(std::int32_t offset);
auto resolve_function(const std::string& name) -> std::int32_t;
auto resolve_label(const std::string& name) -> std::int32_t;
};
} // namespace xsk::gsc::s1c

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
// Copyright 2022 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::s1c
{
enum class opcode : std::uint8_t;
class compiler : public gsc::compiler
{
build mode_;
std::string filename_;
std::vector<function::ptr> assembly_;
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, ast::expr> constants_;
std::vector<block*> break_blks_;
std::vector<block*> continue_blks_;
bool can_break_;
bool can_continue_;
bool developer_thread_;
public:
auto output() -> std::vector<function::ptr>;
void compile(const std::string& file, std::vector<std::uint8_t>& data);
void mode(build mode);
private:
auto parse_buffer(const std::string& file, const char* data, size_t size) -> ast::program::ptr;
auto parse_file(const std::string& file) -> ast::program::ptr;
void compile_program(const ast::program::ptr& program);
void emit_include(const ast::include::ptr& include);
void emit_declaration(const ast::decl& decl);
void emit_decl_usingtree(const ast::decl_usingtree::ptr& animtree);
void emit_decl_constant(const ast::decl_constant::ptr& constant);
void emit_decl_thread(const ast::decl_thread::ptr& thread);
void emit_stmt(const ast::stmt& stmt, const block::ptr& blk, bool last);
void emit_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_dev(const ast::stmt_dev::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void emit_stmt_call(const ast::stmt_call::ptr& stmt, const block::ptr& blk);
void emit_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void emit_stmt_endon(const ast::stmt_endon::ptr& stmt, const block::ptr& blk);
void emit_stmt_notify(const ast::stmt_notify::ptr& stmt, const block::ptr& blk);
void emit_stmt_wait(const ast::stmt_wait::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt, const block::ptr& blk);
void emit_stmt_waittillframeend(const ast::stmt_waittillframeend::ptr& stmt, const block::ptr& blk);
void emit_stmt_waitframe(const ast::stmt_waitframe::ptr& stmt, const block::ptr& blk);
void emit_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk, bool last);
void emit_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void emit_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void emit_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void emit_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void emit_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void emit_stmt_case(const ast::stmt_case::ptr& stmt, const block::ptr& blk);
void emit_stmt_default(const ast::stmt_default::ptr& stmt, const block::ptr& blk);
void emit_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void emit_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void emit_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void emit_stmt_breakpoint(const ast::stmt_breakpoint::ptr& stmt, const block::ptr& blk);
void emit_stmt_prof_begin(const ast::stmt_prof_begin::ptr& stmt, const block::ptr& blk);
void emit_stmt_prof_end(const ast::stmt_prof_end::ptr& stmt, const block::ptr& blk);
void emit_expr(const ast::expr& expr, const block::ptr& blk);
void emit_expr_assign(const ast::expr_assign::ptr& expr, const block::ptr& blk);
void emit_expr_clear(const ast::expr& expr, const block::ptr& blk);
void emit_expr_clear_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk);
void emit_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk);
void emit_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk);
void emit_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk);
void emit_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk);
void emit_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk);
void emit_expr_negate(const ast::expr_negate::ptr& expr, const block::ptr& blk);
void emit_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk);
void emit_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_method(const ast::expr_method::ptr& expr, const block::ptr& blk, bool is_stmt);
void emit_expr_method_pointer(const ast::expr_pointer::ptr& expr, const ast::expr& obj, const block::ptr& blk, bool is_stmt);
void emit_expr_method_function(const ast::expr_function::ptr& expr, const ast::expr& obj, const block::ptr& blk, bool is_stmt);
void emit_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk);
void emit_expr_parameters(const ast::expr_parameters::ptr& expr, const block::ptr& blk);
void emit_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk);
void emit_expr_reference(const ast::expr_reference::ptr& expr, const block::ptr& blk);
void emit_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk);
void emit_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void emit_expr_variable_ref(const ast::expr& expr, const block::ptr& blk, bool set);
void emit_expr_array_ref(const ast::expr_array::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_field_ref(const ast::expr_field::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_local_ref(const ast::expr_identifier::ptr& expr, const block::ptr& blk, bool set);
void emit_expr_variable(const ast::expr& expr, const block::ptr& blk);
void emit_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk);
void emit_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk);
void emit_expr_local(const ast::expr_identifier::ptr& expr, const block::ptr& blk);
void emit_expr_object(const ast::expr& expr, const block::ptr& blk);
void emit_expr_vector(const ast::expr_vector::ptr& expr, const block::ptr& blk);
void emit_expr_animation(const ast::expr_animation::ptr& expr);
void emit_expr_animtree(const ast::expr_animtree::ptr& expr);
void emit_expr_istring(const ast::expr_istring::ptr& expr);
void emit_expr_string(const ast::expr_string::ptr& expr);
void emit_expr_float(const ast::expr_float::ptr& expr);
void emit_expr_integer(const ast::expr_integer::ptr& expr);
void emit_expr_false(const ast::expr_false::ptr& expr);
void emit_expr_true(const ast::expr_true::ptr& expr);
void emit_create_local_vars(const block::ptr& blk);
void emit_remove_local_vars(const block::ptr& blk);
void emit_opcode(opcode op);
void emit_opcode(opcode op, const std::string& data);
void emit_opcode(opcode op, const std::vector<std::string>& data);
void process_thread(const ast::decl_thread::ptr& decl, const block::ptr& blk);
void process_stmt(const ast::stmt& stmt, const block::ptr& blk);
void process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_dev(const ast::stmt_dev::ptr& stmt, const block::ptr& blk);
void process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk);
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk);
void process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void process_expr(const ast::expr& expr, const block::ptr& blk);
void process_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void process_expr_parameters(const ast::expr_parameters::ptr& decl, const block::ptr& blk);
void variable_register(const std::string& name, const block::ptr& blk);
void variable_initialize(const ast::expr_identifier::ptr& name, const block::ptr& blk);
void variable_create(const ast::expr_identifier::ptr& name, const block::ptr& blk);
auto variable_stack_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::uint8_t;
auto variable_create_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string;
auto variable_access_index(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> std::string;
auto variable_initialized(const ast::expr_identifier::ptr& name, const block::ptr& blk) -> bool;
auto resolve_function_type(const ast::expr_function::ptr& expr) -> ast::call::type;
auto resolve_reference_type(const ast::expr_reference::ptr& expr, bool& method) -> ast::call::type;
auto is_constant_condition(const ast::expr& 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::s1c

View File

@ -0,0 +1,23 @@
// Copyright 2022 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 "s1c.hpp"
namespace xsk::gsc::s1c
{
void context::init(build mode, read_cb_type callback)
{
compiler_.mode(mode);
resolver::init(callback);
}
void context::cleanup()
{
resolver::cleanup();
}
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,28 @@
// Copyright 2022 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::s1c
{
class context : public gsc::context
{
s1c::assembler assembler_;
s1c::disassembler disassembler_;
s1c::compiler compiler_;
s1c::decompiler decompiler_;
public:
void init(build mode, read_cb_type callback);
void cleanup();
auto assembler() -> gsc::assembler& { return assembler_; }
auto disassembler() -> gsc::disassembler& { return disassembler_; }
auto compiler() -> gsc::compiler& { return compiler_; }
auto decompiler() -> gsc::decompiler& { return decompiler_; }
};
} // namespace xsk::gsc::s1c

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
// Copyright 2022 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::s1c
{
class decompiler : public gsc::decompiler
{
std::string filename_;
ast::program::ptr program_;
ast::decl_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<ast::node::ptr> stack_;
std::vector<block> blocks_;
bool in_waittill_;
public:
auto output() -> std::vector<std::uint8_t>;
void decompile(const std::string& file, std::vector<function::ptr>& funcs);
private:
void decompile_function(const function::ptr& func);
void decompile_instruction(const instruction::ptr& inst);
void decompile_expressions(const instruction::ptr& inst);
void decompile_statements(const ast::stmt_list::ptr& stmt);
void decompile_loops(const ast::stmt_list::ptr& stmt);
void decompile_switches(const ast::stmt_list::ptr& stmt);
void decompile_ifelses(const ast::stmt_list::ptr& stmt);
void decompile_aborts(const ast::stmt_list::ptr& stmt);
void decompile_tuples(const ast::stmt_list::ptr& stmt);
void decompile_if(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_ifelse(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_ifelse_end(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_inf(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_loop(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_while(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_dowhile(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_for(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_foreach(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end);
void decompile_switch(const ast::stmt_list::ptr& stmt, std::size_t begin);
auto find_location_reference(const ast::stmt_list::ptr& stmt, std::size_t begin, std::size_t end, const std::string& location) -> bool;
auto find_location_index(const ast::stmt_list::ptr& stmt, const std::string& location) -> std::size_t;
auto last_location_index(const ast::stmt_list::ptr& stmt, std::size_t index) -> bool;
void process_stack(const ast::decl_thread::ptr& thread);
void process_parameters(const ast::expr_parameters::ptr& params, const block::ptr& blk);
void process_stmt(const ast::stmt& stmt, const block::ptr& blk);
void process_stmt_list(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_expr(const ast::stmt_expr::ptr& stmt, const block::ptr& blk);
void process_stmt_call(const ast::stmt_call::ptr& stmt, const block::ptr& blk);
void process_stmt_assign(const ast::stmt_assign::ptr& stmt, const block::ptr& blk);
void process_stmt_endon(const ast::stmt_endon::ptr& stmt, const block::ptr& blk);
void process_stmt_notify(const ast::stmt_notify::ptr& stmt, const block::ptr& blk);
void process_stmt_wait(const ast::stmt_wait::ptr& stmt, const block::ptr& blk);
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt, const block::ptr& blk);
void process_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt, const block::ptr& blk);
void process_stmt_if(const ast::stmt_if::ptr& stmt, const block::ptr& blk);
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt, const block::ptr& blk);
void process_stmt_while(const ast::stmt_while::ptr& stmt, const block::ptr& blk);
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt, const block::ptr& blk);
void process_stmt_for(const ast::stmt_for::ptr& stmt, const block::ptr& blk);
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt, const block::ptr& blk);
void process_stmt_switch(const ast::stmt_switch::ptr& stmt, const block::ptr& blk);
void process_stmt_cases(const ast::stmt_list::ptr& stmt, const block::ptr& blk);
void process_stmt_break(const ast::stmt_break::ptr& stmt, const block::ptr& blk);
void process_stmt_continue(const ast::stmt_continue::ptr& stmt, const block::ptr& blk);
void process_stmt_return(const ast::stmt_return::ptr& stmt, const block::ptr& blk);
void process_expr(ast::expr& expr, const block::ptr& blk);
void process_expr_assign(ast::expr_assign::ptr& expr, const block::ptr& blk);
void process_expr_increment(const ast::expr_increment::ptr& expr, const block::ptr& blk);
void process_expr_decrement(const ast::expr_decrement::ptr& expr, const block::ptr& blk);
void process_expr_ternary(const ast::expr_ternary::ptr& expr, const block::ptr& blk);
void process_expr_binary(const ast::expr_binary::ptr& expr, const block::ptr& blk);
void process_expr_and(const ast::expr_and::ptr& expr, const block::ptr& blk);
void process_expr_or(const ast::expr_or::ptr& expr, const block::ptr& blk);
void process_expr_complement(const ast::expr_complement::ptr& expr, const block::ptr& blk);
void process_expr_not(const ast::expr_not::ptr& expr, const block::ptr& blk);
void process_expr_call(const ast::expr_call::ptr& expr, const block::ptr& blk);
void process_expr_method(const ast::expr_method::ptr& expr, const block::ptr& blk);
void process_expr_call_pointer(const ast::expr_pointer::ptr& expr, const block::ptr& blk);
void process_expr_call_function(const ast::expr_function::ptr& expr, const block::ptr& blk);
void process_expr_method_pointer(const ast::expr_pointer::ptr& expr, ast::expr& obj, const block::ptr& blk);
void process_expr_method_function(const ast::expr_function::ptr& expr, ast::expr& obj, const block::ptr& blk);
void process_expr_arguments(const ast::expr_arguments::ptr& expr, const block::ptr& blk);
void process_expr_add_array(const ast::expr_add_array::ptr& expr, const block::ptr& blk);
void process_expr_size(const ast::expr_size::ptr& expr, const block::ptr& blk);
void process_expr_tuple(const ast::expr_tuple::ptr& expr, const block::ptr& blk);
void process_expr_array(const ast::expr_array::ptr& expr, const block::ptr& blk);
void process_expr_field(const ast::expr_field::ptr& expr, const block::ptr& blk);
void process_expr_vector(const ast::expr_vector::ptr& vec, const block::ptr& blk);
void process_var_create(ast::expr& expr, const block::ptr& blk, bool fromstmt = false);
void process_var_access(ast::expr& expr, const block::ptr& blk);
void process_var_remove(const ast::asm_remove::ptr& expr, const block::ptr& blk);
};
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,576 @@
// Copyright 2022 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 "s1c.hpp"
namespace xsk::gsc::s1c
{
auto disassembler::output() -> std::vector<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("// S1 GSC ASSEMBLY\n");
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
for (const auto& func : functions_)
{
print_function(func);
}
std::vector<std::uint8_t> output;
output.resize(output_->pos());
std::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<function>());
const auto& func = functions_.back();
func->index = static_cast<std::uint32_t>(script_->pos());
func->size = stack_->read_endian<std::uint32_t>();
func->id = stack_->read_endian<std::uint16_t>();
func->name = func->id == 0 ? stack_->read_c_string() : resolver::token_name(static_cast<std::uint16_t>(func->id));
dissasemble_function(func);
func->labels = labels_;
labels_.clear();
}
resolve_local_functions();
}
void disassembler::dissasemble_function(const function::ptr& func)
{
auto size = func->size;
while (size > 0)
{
func->instructions.push_back(std::make_unique<instruction>());
const auto& inst = func->instructions.back();
inst->index = static_cast<std::uint32_t>(script_->pos());
inst->opcode = script_->read_endian<std::uint8_t>();
inst->size = opcode_size(inst->opcode);
dissasemble_instruction(inst);
size -= inst->size;
}
}
void disassembler::dissasemble_instruction(const instruction::ptr& inst)
{
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_Return:
case opcode::OP_BoolNot:
case opcode::OP_CastBool:
case opcode::OP_inequality:
case opcode::OP_GetThisthread:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_checkclearparams:
case opcode::OP_CastFieldObject:
case opcode::OP_End:
case opcode::OP_size:
case opcode::OP_EmptyArray:
case opcode::OP_bit_and:
case opcode::OP_less_equal:
case opcode::OP_voidCodepos:
// case opcode::OP_ClearVariableField:
case opcode::OP_divide:
case opcode::OP_GetSelf:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_plus:
case opcode::OP_BoolComplement:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_inc:
case opcode::OP_clearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_endon:
case opcode::OP_greater_equal:
case opcode::OP_GetSelfObject:
case opcode::OP_SetVariableField:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_less:
case opcode::OP_GetGameRef:
case opcode::OP_waittillFrameEnd:
case opcode::OP_waitFrame:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_GetLevel:
case opcode::OP_notify:
case opcode::OP_DecTop:
case opcode::OP_shift_left:
case opcode::OP_greater:
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_GetZero:
case opcode::OP_wait:
case opcode::OP_minus:
// case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_multiply:
case opcode::OP_mod:
case opcode::OP_GetGame:
case opcode::OP_waittill:
case opcode::OP_dec:
case opcode::OP_PreScriptCall:
case opcode::OP_GetAnim:
case opcode::OP_GetUndefined:
case opcode::OP_GetAnimObject:
case opcode::OP_GetLevelObject:
case opcode::OP_bit_ex_or:
case opcode::OP_equality:
case opcode::OP_ClearArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalArray:
case opcode::OP_vector:
case opcode::OP_bit_or:
case opcode::OP_AddArray:
// case opcode::OP_waittillmatch2:
case opcode::OP_shift_right:
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint16_t>()));
break;
case opcode::OP_GetInteger:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::int32_t>()));
break;
case opcode::OP_GetFloat:
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
break;
case opcode::OP_GetVector:
inst->size += script_->align(4);
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<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(), false));
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_GetAnimTree:
script_->seek(1);
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_waittillmatch:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_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_endian<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:
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_endian<std::uint8_t>()));
break;
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
disassemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
disassemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
disassemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
disassemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
disassemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
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:
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:
disassemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
disassemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
disassemble_jump(inst, false, true);
break;
case opcode::OP_jump:
disassemble_jump(inst, false, false);
break;
case opcode::OP_switch:
disassemble_switch(inst);
break;
case opcode::OP_endswitch:
disassemble_end_switch(inst);
break;
default:
throw disasm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void disassembler::disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
if (args)
{
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
}
const auto id = script_->read_endian<std::uint16_t>();
const auto name = method ? resolver::method_name(id) : resolver::function_name(id);
inst->data.emplace(inst->data.begin(), name);
}
void disassembler::disassemble_local_call(const instruction::ptr& inst, bool thread)
{
const auto offset = 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_endian<std::uint8_t>()));
}
}
void disassembler::disassemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->seek(3);
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint8_t>()));
}
const auto file_id = stack_->read_endian<std::uint16_t>();
const auto file_name = file_id == 0 ? stack_->read_c_string() : resolver::token_name(file_id);
const auto func_id = stack_->read_endian<std::uint16_t>();
const auto func_name = func_id == 0 ? stack_->read_c_string() : resolver::token_name(func_id);
inst->data.emplace(inst->data.begin(), func_name);
inst->data.emplace(inst->data.begin(), file_name);
}
void disassembler::disassemble_switch(const instruction::ptr& inst)
{
const auto addr = inst->index + 4 + script_->read_endian<std::int32_t>();
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
void disassembler::disassemble_end_switch(const instruction::ptr& inst)
{
const auto count = script_->read_endian<std::uint16_t>();
inst->data.push_back(utils::string::va("%i", count));
std::uint32_t index = inst->index + 3;
if (count)
{
for (auto i = count; i > 0; i--)
{
const auto value = script_->read_endian<std::uint32_t>();
if (value < 0x40000)
{
const auto data = stack_->read_c_string();
if (data.data()[0] != 0x01)
{
inst->data.push_back("case");
inst->data.push_back(utils::string::quote(data, false));
}
else
inst->data.push_back("default");
}
else
{
inst->data.push_back("case");
inst->data.push_back(utils::string::va("%i", (value - 0x800000) & 0xFFFFFF));
}
index += 4;
const auto addr = disassemble_offset() + index;
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
index += 3;
inst->size += 7;
}
}
}
void disassembler::disassemble_field_variable(const instruction::ptr& inst)
{
const auto id = script_->read_endian<std::uint16_t>();
std::string name;
if (id > max_string_id)
{
auto temp = stack_->read_endian<std::uint16_t>();
name = temp == 0 ? stack_->read_c_string() : std::to_string(temp);
}
else
{
name = resolver::token_name(id);
}
inst->data.push_back(name);
}
void disassembler::disassemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
std::int32_t addr;
if (expr)
{
addr = inst->index + 3 + script_->read_endian<std::int16_t>();
}
else if (back)
{
addr = inst->index + 3 - script_->read_endian<std::uint16_t>();
}
else
{
addr = inst->index + 5 + script_->read_endian<std::int32_t>();
}
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
auto disassembler::disassemble_offset() -> std::int32_t
{
std::array<std::uint8_t, 4> bytes = {};
for (auto i = 0; i < 3; i++)
{
bytes[2 - i] = script_->read_endian<std::uint8_t>();
}
auto offset = *reinterpret_cast<std::int32_t*>(bytes.data());
offset = (offset << 8) >> 10;
return offset;
}
void disassembler::resolve_local_functions()
{
for (const auto& func : functions_)
{
for (const auto& inst : func->instructions)
{
switch (static_cast<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[0] = 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 (const auto& func : functions_)
{
if (func->index == idx)
{
return func->name;
}
}
throw disasm_error(utils::string::va("couldn't resolve function name at index '0x%04X'!", idx));
}
throw disasm_error(utils::string::va("\"%s\" is not valid function address!", index.data()));
}
void disassembler::print_function(const function::ptr& func)
{
output_->write_string("\n");
output_->write_string(utils::string::va("sub_%s\n", func->name.data()));
for (const 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()));
}
print_instruction(inst);
}
output_->write_string(utils::string::va("end_%s\n", func->name.data()));
}
void disassembler::print_instruction(const instruction::ptr& inst)
{
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
switch (static_cast<opcode>(inst->opcode))
{
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalMethodCall:
output_->write_string(utils::string::va(" sub_%s", inst->data[0].data()));
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
output_->write_string(utils::string::va(" sub_%s %s\n", inst->data[0].data(), inst->data[1].data()));
break;
case opcode::OP_endswitch:
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:
for (auto& data : inst->data)
{
output_->write_string(utils::string::va(" %s", data.data()));
}
break;
}
output_->write_string("\n");
}
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,42 @@
// Copyright 2022 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::s1c
{
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<function::ptr> functions_;
std::unordered_map<std::uint32_t, std::string> labels_;
public:
auto output() -> std::vector<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 function::ptr& func);
void dissasemble_instruction(const instruction::ptr& inst);
void disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args);
void disassemble_local_call(const instruction::ptr& inst, bool thread);
void disassemble_far_call(const instruction::ptr& inst, bool thread);
void disassemble_switch(const instruction::ptr& inst);
void disassemble_end_switch(const instruction::ptr& inst);
void disassemble_field_variable(const instruction::ptr& inst);
void disassemble_jump(const instruction::ptr& inst, bool expr, bool back);
auto disassemble_offset() -> std::int32_t;
void resolve_local_functions();
auto resolve_function(const std::string& index) -> std::string;
void print_function(const function::ptr& func);
void print_instruction(const instruction::ptr& inst);
};
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,849 @@
// Copyright 2022 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 "s1c.hpp"
#include "parser.hpp"
#include "lexer.hpp"
xsk::gsc::s1c::parser::symbol_type S1Clex(xsk::gsc::s1c::lexer& lexer)
{
return lexer.lex();
}
namespace xsk::gsc::s1c
{
const std::unordered_map<std::string_view, parser::token::token_kind_type> keyword_map
{{
{ "#define", parser::token::SH_DEFINE },
{ "#undef", parser::token::SH_UNDEF },
{ "#ifdef", parser::token::SH_IFDEF },
{ "#ifndef", parser::token::SH_IFNDEF },
{ "#if", parser::token::SH_IF },
{ "#elif", parser::token::SH_ELIF },
{ "#else", parser::token::SH_ELSE },
{ "#endif", parser::token::SH_ENDIF },
{ "#inline", parser::token::INLINE },
{ "#include", parser::token::INCLUDE },
{ "#using_animtree", parser::token::USINGTREE },
{ "#animtree", parser::token::ANIMTREE },
{ "endon", parser::token::ENDON },
{ "notify", parser::token::NOTIFY },
{ "wait", parser::token::WAIT },
{ "waittill", parser::token::WAITTILL },
{ "waittillmatch", parser::token::WAITTILLMATCH },
{ "waittillframeend", parser::token::WAITTILLFRAMEEND },
{ "waitframe", parser::token::WAITFRAME },
{ "if", parser::token::IF },
{ "else", parser::token::ELSE },
{ "do", parser::token::DO },
{ "while", parser::token::WHILE },
{ "for", parser::token::FOR },
{ "foreach", parser::token::FOREACH },
{ "in", parser::token::IN },
{ "switch", parser::token::SWITCH },
{ "case", parser::token::CASE },
{ "default", parser::token::DEFAULT },
{ "break", parser::token::BREAK },
{ "continue", parser::token::CONTINUE },
{ "return", parser::token::RETURN },
{ "breakpoint", parser::token::BREAKPOINT },
{ "prof_begin", parser::token::PROFBEGIN },
{ "prof_end", parser::token::PROFEND },
{ "thread", parser::token::THREAD },
{ "childthread", parser::token::CHILDTHREAD },
{ "thisthread", parser::token::THISTHREAD },
{ "call", parser::token::CALL },
{ "true", parser::token::TRUE },
{ "false", parser::token::FALSE },
{ "undefined", parser::token::UNDEFINED },
{ "size", parser::token::SIZE },
{ "game", parser::token::GAME },
{ "self", parser::token::SELF },
{ "anim", parser::token::ANIM },
{ "level", parser::token::LEVEL },
}};
buffer::buffer() : length(0)
{
data = static_cast<char*>(std::malloc(max_buf_size));
}
buffer::~buffer()
{
if (data) std::free(data);
}
bool buffer::push(char c)
{
if (length >= max_buf_size)
return false;
data[length++] = c;
return true;
}
reader::reader() : buffer_pos(0), bytes_remaining(0), last_byte(0), current_byte(0), state(reader::end)
{
}
void reader::init(const char* data, size_t size)
{
if (data && size)
{
state = reader::ok;
buffer_pos = data;
bytes_remaining = static_cast<std::uint32_t>(size);
last_byte = 0;
current_byte = *data;
}
else
{
state = reader::end;
buffer_pos = 0;
bytes_remaining = 0;
last_byte = 0;
current_byte = 0;
}
}
void reader::advance()
{
++buffer_pos;
if (bytes_remaining-- == 1)
{
state = reader::end;
bytes_remaining = 0;
last_byte = current_byte;
current_byte = 0;
}
else
{
last_byte = current_byte;
current_byte = *buffer_pos;
}
}
lexer::lexer(build mode, const std::string& name, const char* data, size_t size) : loc_(location(&name)),
locs_(std::stack<location>()), readers_(std::stack<reader>()), header_top_(0), mode_(mode), indev_(false), clean_(true)
{
reader_.init(data, size);
}
void lexer::push_header(const std::string& file)
{
try
{
if (header_top_++ >= 10)
throw comp_error(loc_, "maximum gsh depth exceeded '10'");
auto data = resolver::file_data(file + ".gsh");
readers_.push(reader_);
locs_.push(loc_);
loc_.initialize(std::get<0>(data));
reader_.init(std::get<1>(data), std::get<2>(data));
clean_ = true;
}
catch (const std::exception& e)
{
throw error("parsing header file '" + file + "': " + e.what());
}
}
void lexer::pop_header()
{
header_top_--;
loc_ = locs_.top();
locs_.pop();
reader_ = readers_.top();
readers_.pop();
}
void lexer::ban_header(const location& loc)
{
if (header_top_ > 0)
{
throw comp_error(loc, "not allowed inside a gsh file");
}
}
auto lexer::lex() -> parser::symbol_type
{
buffer_.length = 0;
state_ = state::start;
while (true)
{
const auto& state = reader_.state;
auto& last = reader_.last_byte;
auto& curr = reader_.current_byte;
auto path = false;
loc_.step();
if (state == reader::end)
{
if (indev_)
throw comp_error(loc_, "unmatched devblock start ('/#')");
if (header_top_ > 0)
pop_header();
else
return parser::make_S1CEOF(loc_);
}
if (clean_ && last != 0 && last != ' ' && last != '\t' && last != '\n')
clean_ = false;
advance();
switch (last)
{
case ' ':
case '\t':
case '\r':
loc_.step();
continue;
case '\n':
loc_.lines();
loc_.step();
clean_ = true;
continue;
case '\\':
throw comp_error(loc_, "invalid token ('\\')");
case '/':
if (curr != '=' && curr != '#' && curr != '@' && curr != '*' && curr != '/')
return parser::make_DIV(loc_);
advance();
if (last == '=')
return parser::make_ASSIGN_DIV(loc_);
if (last == '#')
{
if (indev_)
throw comp_error(loc_, "cannot recurse devblock ('/#')");
if (mode_ == build::dev)
{
indev_ = true;
return parser::make_DEVBEGIN(loc_);
}
else
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched devblock start ('/#')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '#' && curr == '/')
{
advance();
break;
}
advance();
}
}
}
else if (last == '@')
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched script doc comment start ('/@')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '@' && curr == '/')
{
advance();
break;
}
advance();
}
}
else if (last == '*')
{
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched multiline comment start ('/*')");
if (curr == '\n')
{
loc_.lines();
loc_.step();
}
else if (last == '*' && curr == '/')
{
advance();
break;
}
advance();
}
}
else if (last == '/')
{
while (true)
{
if (state == reader::end)
break;
if (curr == '\n')
break;
advance();
}
}
continue;
case '#':
if (curr == '/')
{
if (!indev_)
throw comp_error(loc_, "unmatched devblock end ('#/')");
advance();
indev_ = false;
return parser::make_DEVEND(loc_);
}
buffer_.push(last);
advance();
while (state == reader::ok)
{
if (last != ' ' || last != '\t')
break;
advance();
}
if (state == reader::end || !((last > 64 && last < 91) || (last > 96 && last < 123)))
throw comp_error(loc_, "invalid preprocessor directive ('#')");
state_ = state::preprocessor;
goto lex_name;
case '*':
if (curr != '=' && curr != '/')
return parser::make_MUL(loc_);
advance();
if (last == '=')
return parser::make_ASSIGN_MUL(loc_);
throw comp_error(loc_, "unmatched multiline comment end ('*/')");
case '"':
state_ = state::string;
goto lex_string;
case '.':
if (curr < '0' || curr > '9')
return parser::make_DOT(loc_);
goto lex_number;
case '(':
return parser::make_LPAREN(loc_);
case ')':
return parser::make_RPAREN(loc_);
case '{':
return parser::make_LBRACE(loc_);
case '}':
return parser::make_RBRACE(loc_);
case '[':
return parser::make_LBRACKET(loc_);
case ']':
return parser::make_RBRACKET(loc_);
case ',':
return parser::make_COMMA(loc_);
case ';':
return parser::make_SEMICOLON(loc_);
case ':':
if (curr != ':')
return parser::make_COLON(loc_);
advance();
return parser::make_DOUBLECOLON(loc_);
case '?':
return parser::make_QMARK(loc_);
case '=':
if (curr != '=')
return parser::make_ASSIGN(loc_);
advance();
return parser::make_EQUALITY(loc_);
case '+':
if (curr != '+' && curr != '=')
return parser::make_ADD(loc_);
advance();
if (last == '+')
return parser::make_INCREMENT(loc_);
return parser::make_ASSIGN_ADD(loc_);
case '-':
if (curr != '-' && curr != '=')
return parser::make_SUB(loc_);
advance();
if (last == '-')
return parser::make_DECREMENT(loc_);
return parser::make_ASSIGN_SUB(loc_);
case '%':
if (curr != '=')
return parser::make_MOD(loc_);
advance();
return parser::make_ASSIGN_MOD(loc_);
case '|':
if (curr != '|' && curr != '=')
return parser::make_BITWISE_OR(loc_);
advance();
if (last == '|')
return parser::make_OR(loc_);
return parser::make_ASSIGN_BW_OR(loc_);
case '&':
if (curr != '&' && curr != '=' && curr != '"')
return parser::make_BITWISE_AND(loc_);
advance();
if (last == '&')
return parser::make_AND(loc_);
if (last == '=')
return parser::make_ASSIGN_BW_AND(loc_);
state_ = state::localize;
goto lex_string;
case '^':
if (curr != '=')
return parser::make_BITWISE_EXOR(loc_);
advance();
return parser::make_ASSIGN_BW_EXOR(loc_);
case '!':
if (curr != '=')
return parser::make_NOT(loc_);
advance();
return parser::make_INEQUALITY(loc_);
case '~':
return parser::make_COMPLEMENT(loc_);
case '<':
if (curr != '<' && curr != '=')
return parser::make_LESS(loc_);
advance();
if (last == '=')
return parser::make_LESS_EQUAL(loc_);
if (curr != '=')
return parser::make_LSHIFT(loc_);
advance();
return parser::make_ASSIGN_LSHIFT(loc_);
case '>':
if (curr != '>' && curr != '=')
return parser::make_GREATER(loc_);
advance();
if (last == '=')
return parser::make_GREATER_EQUAL(loc_);
if (curr != '=')
return parser::make_RSHIFT(loc_);
advance();
return parser::make_ASSIGN_RSHIFT(loc_);
default:
if (last >= '0' && last <= '9')
goto lex_number;
else if (last == '_' || (last >= 'A' && last <= 'Z') || (last >= 'a' && last <= 'z'))
goto lex_name;
throw comp_error(loc_, utils::string::va("bad token: \'%c\'", last));
}
lex_string:
while (true)
{
if (state == reader::end)
throw comp_error(loc_, "unmatched string start ('\"')");
if (curr == '"')
{
advance();
break;
}
if (curr == '\n')
throw comp_error(loc_, "unterminated string literal");
if (curr == '\\')
{
advance();
if (state == reader::end)
throw comp_error(loc_, "invalid token ('\')");
char c = curr;
switch (curr)
{
case 't': c = '\t'; break;
case 'r': c = '\r'; break;
case 'n': c = '\n'; break;
case '"': c = '\"'; break;
case '\\': c = '\\'; break;
default: break;
}
if (!buffer_.push(c))
throw comp_error(loc_, "max string size exceeded");
}
else if (!buffer_.push(curr))
throw comp_error(loc_, "max string size exceeded");
advance();
}
if (state_ == state::localize)
return parser::make_ISTRING(std::string(buffer_.data, buffer_.length), loc_);
return parser::make_STRING(std::string(buffer_.data, buffer_.length), loc_);
lex_name:
buffer_.push(last);
while (true)
{
if (state == reader::end)
break;
if (!(curr == '\\' || curr == '_' || (curr > 64 && curr < 91) || (curr > 96 && curr < 123) || (curr > 47 && curr < 58)))
break;
if (curr == '\\')
{
if (last == '\\')
throw comp_error(loc_, "invalid path '\\\\'");
path = true;
if (!buffer_.push('/'))
throw comp_error(loc_, "max string size exceeded");
}
else if (!buffer_.push(curr))
throw comp_error(loc_, "max string size exceeded");
advance();
}
if (state_ == state::preprocessor)
{
auto token = parser::token::S1CUNDEF;
if (buffer_.length < 16)
{
const auto itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
{
if (itr->second > parser::token::SH_ENDIF)
return parser::symbol_type(itr->second, loc_);
token = itr->second;
}
}
preprocessor_run(token);
state_ = state::start;
continue;
}
else
{
if (buffer_.length < 17)
{
const auto itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
return parser::symbol_type(itr->second, loc_);
}
if (path)
{
if (buffer_.data[buffer_.length - 1] == '/')
throw comp_error(loc_, "invalid path end '\\'");
return parser::make_PATH(resolver::make_token(std::string_view(buffer_.data, buffer_.length)), loc_);
}
return parser::make_IDENTIFIER(resolver::make_token(std::string_view(buffer_.data, buffer_.length)), loc_);
}
lex_number:
if (last == '.' || last != '0' || (last == '0' && (curr != 'o' && curr != 'b' && curr != 'x')))
{
buffer_.push(last);
auto dot = last == '.' ? 1 : 0;
auto flt = 0;
while (true)
{
if (state == reader::end)
break;
if (curr == '\'' && (last == '\'' || last == 'f' || last == '.'))
throw comp_error(loc_, "invalid number literal");
if ((curr == '.' || curr == 'f') && last == '\'')
throw comp_error(loc_, "invalid number literal");
if (curr == '\'')
{
advance();
continue;
}
if (curr == 'f')
flt++;
else if (curr == '.')
dot++;
else if (!(curr > 47 && curr < 58))
break;
if (!buffer_.push(curr))
throw comp_error(loc_, "number literal size exceeded");
advance();
}
if (last == '\'')
throw comp_error(loc_, "invalid number literal");
if (dot > 1 || flt > 1 || (flt && buffer_.data[buffer_.length - 1] != 'f'))
throw comp_error(loc_, "invalid number literal");
if (dot || flt)
return parser::make_FLOAT(std::string(buffer_.data, buffer_.length), loc_);
return parser::make_INTEGER(std::string(buffer_.data, buffer_.length), loc_);
}
else if (curr == 'o')
{
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'o')) || (curr == 'o' && last == '\''))
throw comp_error(loc_, "invalid octal literal");
if (curr == '\'')
{
advance();
continue;
}
if (!(curr > 47 && curr < 56))
break;
if (!buffer_.push(curr))
throw error("gsc lexer: out of memory!");
advance();
}
if (last == '\'' || buffer_.length <= 0)
throw comp_error(loc_, "invalid octal literal");
return parser::make_INTEGER(utils::string::oct_to_dec(buffer_.data), loc_);
}
else if (curr == 'b')
{
buffer_.push(last);
buffer_.push(curr);
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'b')) || (curr == 'b' && last == '\''))
throw comp_error(loc_, "invalid binary literal");
if (curr == '\'')
{
advance();
continue;
}
if (curr != '0' && curr != '1')
break;
if (!buffer_.push(curr))
throw comp_error(loc_, "number literal size exceeded");
advance();
}
if (last == '\'' || buffer_.length < 3)
throw comp_error(loc_, "invalid binary literal");
return parser::make_INTEGER(utils::string::bin_to_dec(buffer_.data), loc_);
}
else if (curr == 'x')
{
buffer_.push(last);
buffer_.push(curr);
advance();
while (true)
{
if (state == reader::end)
break;
if ((curr == '\'' && (last == '\'' || last == 'x')) || (curr == 'x' && last == '\''))
throw comp_error(loc_, "invalid hexadecimal literal");
if (curr == '\'')
{
advance();
continue;
}
if (!((curr > 47 && curr < 58) || (curr > 64 && curr < 71) || (curr > 96 && curr < 103)))
break;
if (!buffer_.push(curr))
throw error("gsc lexer: out of memory!");
advance();
}
if (last == '\'' || buffer_.length < 3)
throw comp_error(loc_, "invalid hexadecimal literal");
return parser::make_INTEGER(utils::string::hex_to_dec(buffer_.data), loc_);
}
throw error("UNEXPECTED LEXER INTERNAL ERROR!");
}
}
void lexer::advance()
{
reader_.advance();
loc_.end.column++;
if (reader_.current_byte == '\\') [[unlikely]]
preprocessor_wrap();
}
void lexer::preprocessor_wrap()
{
while (reader_.current_byte == '\\')
{
if (reader_.bytes_remaining == 1)
throw comp_error(loc_, "invalid token ('\\')");
if (reader_.buffer_pos[1] != '\r' && reader_.buffer_pos[1] != '\n')
break;
if (reader_.buffer_pos[1] == '\r')
{
if (reader_.bytes_remaining <= 3 || reader_.buffer_pos[2] != '\n')
throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 3;
reader_.bytes_remaining -= 3;
}
if ((reader_.buffer_pos[1] == '\n'))
{
if (reader_.bytes_remaining == 2)
throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 2;
reader_.bytes_remaining -= 2;
}
if (reader_.bytes_remaining == 0)
{
reader_.state = reader::end;
reader_.current_byte = 0;
}
else
{
reader_.current_byte = *reader_.buffer_pos;
}
loc_.lines();
loc_.step();
}
}
void lexer::preprocessor_run(parser::token::token_kind_type token)
{
if (!clean_)
throw comp_error(loc_, "invalid token ('#')");
switch (token)
{
case parser::token::SH_DEFINE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_UNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELSE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ENDIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
default:
throw comp_error(loc_, "unknown preprocessor directive");
}
}
} // namespace xsk::gsc::s1c

View File

@ -0,0 +1,78 @@
// Copyright 2022 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::s1c
{
constexpr size_t max_buf_size = 0x2000;
struct buffer
{
char* data;
size_t length;
buffer();
~buffer();
bool push(char c);
};
struct reader
{
enum state_type : std::uint8_t { end, ok };
const char* buffer_pos;
std::uint32_t bytes_remaining;
char last_byte;
char current_byte;
state_type state;
reader();
reader(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
}
reader& operator=(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
return *this;
}
void init(const char* data, size_t size);
void advance();
};
class lexer
{
enum class state : std::uint8_t { start, string, localize, preprocessor };
reader reader_;
buffer buffer_;
location loc_;
std::stack<location> locs_;
std::stack<reader> readers_;
std::uint32_t header_top_;
state state_;
build mode_;
bool indev_;
bool clean_;
public:
lexer(build mode, const std::string& name, const char* data, size_t size);
auto lex() -> parser::symbol_type;
void push_header(const std::string& file);
void pop_header();
void ban_header(const location& loc);
private:
void advance();
void preprocessor_wrap();
void preprocessor_run(parser::token::token_kind_type token);
};
} // namespace xsk::gsc::s1c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,517 @@
// Copyright 2022 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 "s1c.hpp"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
namespace xsk::gsc::s1c
{
std::unordered_map<std::uint8_t, std::string_view> opcode_map;
std::unordered_map<std::uint16_t, std::string_view> function_map;
std::unordered_map<std::uint16_t, std::string_view> method_map;
std::unordered_map<std::uint16_t, std::string_view> token_map;
std::unordered_map<std::string_view, std::uint8_t> opcode_map_rev;
std::unordered_map<std::string_view, std::uint16_t> function_map_rev;
std::unordered_map<std::string_view, std::uint16_t> method_map_rev;
std::unordered_map<std::string_view, std::uint16_t> token_map_rev;
std::unordered_map<std::string, std::vector<std::uint8_t>> files;
read_cb_type read_callback = nullptr;
std::set<std::string> string_map;
void resolver::init(read_cb_type callback)
{
read_callback = callback;
}
void resolver::cleanup()
{
files.clear();
}
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 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 std::string(itr->second);
}
throw 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
{
if (name.starts_with("_func_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
}
const auto itr = function_map_rev.find(name);
if (itr != function_map_rev.end())
{
return itr->second;
}
throw 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 std::string(itr->second);
}
return utils::string::va("_func_%04X", id);
}
auto resolver::method_id(const std::string& name) -> std::uint16_t
{
if (name.starts_with("_meth_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
}
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return itr->second;
}
throw 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 std::string(itr->second);
}
return utils::string::va("_meth_%04X", id);
}
auto resolver::token_id(const std::string& name) -> std::uint16_t
{
if (name.starts_with("_id_"))
{
return static_cast<std::uint16_t>(std::stoul(name.substr(4), nullptr, 16));
}
const auto itr = token_map_rev.find(name);
if (itr != token_map_rev.end())
{
return itr->second;
}
return 0;
}
auto resolver::token_name(std::uint16_t id) -> std::string
{
const auto itr = token_map.find(id);
if (itr != token_map.end())
{
return std::string(itr->second);
}
return utils::string::va("_id_%04X", id);
}
auto resolver::find_function(const std::string& name) -> bool
{
if (name.starts_with("_func_")) return true;
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
{
if (name.starts_with("_meth_")) return true;
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
return true;
}
return false;
}
void resolver::add_function(const std::string& name, std::uint16_t id)
{
const auto itr = function_map_rev.find(name);
if (itr != function_map_rev.end())
{
throw error(utils::string::va("builtin function '%s' already defined.", name.data()));
}
const auto str = string_map.find(name);
if (str != string_map.end())
{
function_map.insert({ id, *str });
function_map_rev.insert({ *str, id });
}
else
{
auto ins = string_map.insert(name);
if (ins.second)
{
function_map.insert({ id, *ins.first });
function_map_rev.insert({ *ins.first, id });
}
}
}
void resolver::add_method(const std::string& name, std::uint16_t id)
{
const auto itr = method_map_rev.find(name);
if (itr != method_map_rev.end())
{
throw error(utils::string::va("builtin method '%s' already defined.", name.data()));
}
const auto str = string_map.find(name);
if (str != string_map.end())
{
method_map.insert({ id, *str });
method_map_rev.insert({ *str, id });
}
else
{
auto ins = string_map.insert(name);
if (ins.second)
{
method_map.insert({ id, *ins.first });
method_map_rev.insert({ *ins.first, id });
}
}
}
auto resolver::make_token(std::string_view str) -> std::string
{
if (str.starts_with("_id_") || str.starts_with("_func_") || str.starts_with("_meth_"))
{
return std::string(str);
}
auto data = std::string(str.begin(), str.end());
for (std::size_t i = 0; i < data.size(); i++)
{
data[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(str[i])));
if (data[i] == '\\') data[i] = '/';
}
return data;
}
auto resolver::file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>
{
const auto itr = files.find(name);
if (itr != files.end())
{
return { &itr->first ,reinterpret_cast<const char*>(itr->second.data()), itr->second.size() };
}
auto data = read_callback(name);
const auto res = files.insert({ name, std::move(data)});
if (res.second)
{
return { &res.first->first, reinterpret_cast<const char*>(res.first->second.data()), res.first->second.size() };
}
throw error("couldn't open gsc file '" + name + "'");
}
std::set<std::string_view> paths
{
};
auto resolver::fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path
{
auto result = std::filesystem::path();
auto root = false;
for (auto& entry : file)
{
if (!root && paths.contains(entry.string()))
{
result = entry;
root = true;
}
else if (paths.contains(result.string()))
{
result /= entry;
}
}
return result.empty() ? file : result;
}
const std::array<std::pair<std::uint8_t, const char*>, 154> opcode_list
{{
{ 0x1D, "OP_CastFieldObject" },
{ 0x1E, "OP_SetLocalVariableFieldCached" },
{ 0x1F, "OP_plus" },
{ 0x20, "OP_RemoveLocalVariables" },
{ 0x21, "OP_EvalSelfFieldVariableRef" },
{ 0x22, "OP_ScriptFarMethodChildThreadCall" },
{ 0x23, "OP_GetGameRef" },
{ 0x24, "OP_EvalAnimFieldVariable" },
{ 0x25, "OP_EvalLevelFieldVariableRef" },
{ 0x26, "OP_GetThisthread" },
{ 0x27, "OP_greater" },
{ 0x28, "OP_waittillmatch" },
{ 0x29, "OP_shift_right" },
{ 0x2A, "OP_dec" },
{ 0x2B, "OP_JumpOnTrue" },
{ 0x2C, "OP_bit_or" },
{ 0x2D, "OP_equality" },
{ 0x2E, "OP_ClearLocalVariableFieldCached0" },
{ 0x2F, "OP_notify" },
{ 0x30, "OP_GetVector" },
{ 0x31, "OP_ScriptMethodChildThreadCallPointer" },
{ 0x32, "OP_PreScriptCall" },
{ 0x33, "OP_GetByte" },
{ 0x34, "OP_ScriptFarMethodThreadCall" },
{ 0x35, "OP_SetSelfFieldVariableField" },
{ 0x36, "OP_JumpOnFalseExpr" },
{ 0x37, "OP_GetUndefined" },
{ 0x38, "OP_jumpback" },
{ 0x39, "OP_JumpOnTrueExpr" },
{ 0x3A, "OP_CallBuiltin0" },
{ 0x3B, "OP_CallBuiltin1" },
{ 0x3C, "OP_CallBuiltin2" },
{ 0x3D, "OP_CallBuiltin3" },
{ 0x3E, "OP_CallBuiltin4" },
{ 0x3F, "OP_CallBuiltin5" },
{ 0x40, "OP_CallBuiltin" },
{ 0x41, "OP_SetLocalVariableFieldCached0" },
{ 0x42, "OP_ClearFieldVariable" },
{ 0x43, "OP_GetLevel" },
{ 0x44, "OP_size" },
{ 0x45, "OP_SafeSetWaittillVariableFieldCached" },
{ 0x46, "OP_ScriptLocalThreadCall" },
{ 0x47, "OP_AddArray" },
{ 0x48, "OP_endon" },
{ 0x49, "OP_EvalFieldVariable" },
{ 0x4A, "OP_shift_left" },
{ 0x4B, "OP_EvalLocalArrayRefCached0" },
{ 0x4C, "OP_Return" },
{ 0x4D, "OP_CreateLocalVariable" },
{ 0x4E, "OP_SafeSetVariableFieldCached0" },
{ 0x4F, "OP_GetBuiltinFunction" },
{ 0x50, "OP_ScriptLocalMethodCall" },
{ 0x51, "OP_CallBuiltinMethodPointer" },
{ 0x52, "OP_ScriptLocalChildThreadCall" },
{ 0x53, "OP_GetSelfObject" },
{ 0x54, "OP_GetGame" },
{ 0x55, "OP_SetLevelFieldVariableField" },
{ 0x56, "OP_EvalArray" },
{ 0x57, "OP_GetSelf" },
{ 0x58, "OP_End" },
{ 0x59, "OP_EvalSelfFieldVariable" },
{ 0x5A, "OP_less_equal" },
{ 0x5B, "OP_EvalLocalVariableCached0" },
{ 0x5C, "OP_EvalLocalVariableCached1" },
{ 0x5D, "OP_EvalLocalVariableCached2" },
{ 0x5E, "OP_EvalLocalVariableCached3" },
{ 0x5F, "OP_EvalLocalVariableCached4" },
{ 0x60, "OP_EvalLocalVariableCached5" },
{ 0x61, "OP_EvalLocalVariableCached" },
{ 0x62, "OP_EvalNewLocalArrayRefCached0" },
{ 0x63, "OP_ScriptMethodChildThreadCallPointer" },
{ 0x64, "OP_EvalLocalVariableObjectCached" },
{ 0x65, "OP_ScriptLocalMethodThreadCall" },
{ 0x66, "OP_GetInteger" },
{ 0x67, "OP_ScriptMethodCallPointer" },
{ 0x68, "OP_checkclearparams" },
{ 0x69, "OP_SetAnimFieldVariableField" },
{ 0x6A, "OP_UNK_1" },
{ 0x6B, "OP_minus " },
{ 0x6C, "OP_ScriptLocalFunctionCall2" },
{ 0x6D, "OP_GetNegUnsignedShort" },
{ 0x6E, "OP_GetNegByte" },
{ 0x6F, "OP_SafeCreateVariableFieldCached" },
{ 0x70, "OP_greater_equal" },
{ 0x71, "OP_vector" },
{ 0x72, "OP_GetBuiltinMethod" },
{ 0x73, "OP_endswitch" },
{ 0x74, "OP_ClearArray" },
{ 0x75, "OP_DecTop" },
{ 0x76, "OP_CastBool" },
{ 0x77, "OP_EvalArrayRef" },
{ 0x78, "OP_SetNewLocalVariableFieldCached0" },
{ 0x79, "OP_GetZero" },
{ 0x7A, "OP_wait" },
{ 0x7B, "OP_waittill" },
{ 0x7C, "OP_GetIString" },
{ 0x7D, "OP_ScriptFarFunctionCall" },
{ 0x7E, "OP_GetAnimObject" },
{ 0x7F, "OP_GetAnimTree" },
{ 0x80, "OP_EvalLocalArrayCached" },
{ 0x81, "OP_mod" },
{ 0x82, "OP_ScriptFarThreadCall" },
{ 0x83, "OP_GetUnsignedShort" },
{ 0x84, "OP_clearparams" },
{ 0x85, "OP_ScriptMethodThreadCallPointer" },
{ 0x86, "OP_ScriptFunctionCallPointer" },
{ 0x87, "OP_EmptyArray" },
{ 0x88, "OP_SafeSetVariableFieldCached" },
{ 0x89, "OP_UNK_2" },
{ 0x8A, "OP_EvalFieldVariableRef" },
{ 0x8B, "OP_ScriptLocalMethodChildThreadCall" },
{ 0x8C, "OP_UNK_3" },
{ 0x8D, "OP_GetFloat" },
{ 0x8E, "OP_EvalLocalVariableRefCached" },
{ 0x8F, "OP_JumpOnFalse" },
{ 0x90, "OP_BoolComplement" },
{ 0x91, "OP_ScriptThreadCallPointer" },
{ 0x92, "OP_ScriptFarFunctionCall2" },
{ 0x93, "OP_less" },
{ 0x94, "OP_BoolNot" },
{ 0x95, "OP_waittillFrameEnd" },
{ 0x96, "OP_waitFrame" },
{ 0x97, "OP_GetString" },
{ 0x98, "OP_EvalLevelFieldVariable" },
{ 0x99, "OP_GetLevelObject" },
{ 0x9A, "OP_inc" },
{ 0x9B, "OP_CallBuiltinMethod0" },
{ 0x9C, "OP_CallBuiltinMethod1" },
{ 0x9D, "OP_CallBuiltinMethod2" },
{ 0x9E, "OP_CallBuiltinMethod3" },
{ 0x9F, "OP_CallBuiltinMethod4" },
{ 0xA0, "OP_CallBuiltinMethod5" },
{ 0xA1, "OP_CallBuiltinMethod" },
{ 0xA2, "OP_GetAnim" },
{ 0xA3, "OP_switch" },
{ 0xA4, "OP_SetVariableField" },
{ 0xA5, "OP_divide" },
{ 0xA6, "OP_GetLocalFunction" },
{ 0xA7, "OP_ScriptFarChildThreadCall" },
{ 0xA8, "OP_multiply" },
{ 0xA9, "OP_ClearLocalVariableFieldCached" },
{ 0xAA, "OP_EvalAnimFieldVariableRef" },
{ 0xAB, "OP_EvalLocalArrayRefCached" },
{ 0xAC, "OP_EvalLocalVariableRefCached0" },
{ 0xAD, "OP_bit_and" },
{ 0xAE, "OP_GetAnimation" },
{ 0xAF, "OP_GetFarFunction" },
{ 0xB0, "OP_CallBuiltinPointer" },
{ 0xB1, "OP_jump" },
{ 0xB2, "OP_voidCodepos" },
{ 0xB3, "OP_ScriptFarMethodCall" },
{ 0xB4, "OP_inequality" },
{ 0xB5, "OP_ScriptLocalFunctionCall" },
{ 0xB6, "OP_bit_ex_or" },
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> function_list
{{
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> method_list
{{
}};
const std::array<std::pair<std::uint16_t, const char*>, 0> token_list
{{
}};
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());
token_map.reserve(token_list.size());
token_map_rev.reserve(token_list.size());
for (const auto& entry : opcode_list)
{
opcode_map.insert({ entry.first, entry.second });
opcode_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : function_list)
{
function_map.insert({ entry.first, entry.second });
function_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : method_list)
{
method_map.insert({ entry.first, entry.second });
method_map_rev.insert({ entry.second, entry.first });
}
for (const auto& entry : token_list)
{
token_map.insert({ entry.first, entry.second });
token_map_rev.insert({ entry.second, entry.first });
}
}
};
__init__ _;
} // namespace xsk::gsc::s1c
#ifdef _MSC_VER
#pragma warning(pop)
#endif

View File

@ -0,0 +1,40 @@
// Copyright 2022 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::s1c
{
class resolver
{
public:
static void init(read_cb_type callback);
static void cleanup();
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 token_id(const std::string& name) -> std::uint16_t;
static auto token_name(std::uint16_t id) -> std::string;
static auto find_function(const std::string& name) -> bool;
static auto find_method(const std::string& name) -> bool;
static void add_function(const std::string& name, std::uint16_t id);
static void add_method(const std::string& name, std::uint16_t id);
static auto make_token(std::string_view str) -> std::string;
static auto file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>;
static auto fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path;
};
} // namespace xsk::gsc::s1c

View File

@ -4,14 +4,14 @@
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "s1_console.hpp"
#include "s1c.hpp"
namespace xsk::gsc::s1_console
namespace xsk::gsc::s1c
{
auto opcode_size(std::uint8_t id) -> std::uint32_t
{
switch (opcode(id))
switch (static_cast<opcode>(id))
{
case opcode::OP_End:
case opcode::OP_Return:
@ -172,8 +172,8 @@ auto opcode_size(std::uint8_t id) -> std::uint32_t
case opcode::OP_GetVector:
return 13;
default:
throw std::runtime_error("Couldn't resolve instruction size for " + std::to_string(id));
throw error("couldn't resolve instruction size for " + std::to_string(id));
}
}
} // namespace xsk::gsc::s1_console
} // namespace xsk::gsc::s1c

View File

@ -7,9 +7,18 @@
#include "utils/xsk/utils.hpp"
namespace xsk::gsc::s1_console
#include "assembler.hpp"
#include "disassembler.hpp"
#include "compiler.hpp"
#include "decompiler.hpp"
#include "resolver.hpp"
#include "context.hpp"
namespace xsk::gsc::s1c
{
constexpr std::uint16_t max_string_id = 0x0;
enum class opcode : std::uint8_t
{
OP_CastFieldObject = 0x1D,
@ -32,7 +41,7 @@ enum class opcode : std::uint8_t
OP_ClearLocalVariableFieldCached0 = 0x2E,
OP_notify = 0x2F,
OP_GetVector = 0x30,
OP_ScriptMethodChildThreadCallPointer = 0x31,
OP_ScriptChildThreadCallPointer = 0x31,
OP_PreScriptCall = 0x32,
OP_GetByte = 0x33,
OP_ScriptFarMethodThreadCall = 0x34,
@ -170,4 +179,4 @@ enum class opcode : std::uint8_t
auto opcode_size(std::uint8_t id) -> std::uint32_t;
} // namespace xsk::gsc::s1_console
} // namespace xsk::gsc::s1c

View File

@ -6,6 +6,8 @@
#include "stdafx.hpp"
#include "utils/xsk/utils.hpp"
#include "experimental/iw5c/xsk/iw5c.hpp"
#include "experimental/iw6c/xsk/iw6c.hpp"
#include "experimental/s1c/xsk/s1c.hpp"
#include "iw5/xsk/iw5.hpp"
#include "iw6/xsk/iw6.hpp"
#include "iw7/xsk/iw7.hpp"
@ -22,7 +24,7 @@ namespace xsk
enum class encd { _, source, assembly, binary };
enum class mode { _, assemble, disassemble, compile, decompile };
enum class game { _, iw5c, iw5, iw6, iw7, iw8, s1, s2, s4, h1, h2, t6 };
enum class game { _, iw5c, iw6c, s1c, iw5, iw6, iw7, iw8, s1, s2, s4, h1, h2, t6 };
const std::map<std::string, encd> exts =
{
@ -43,6 +45,8 @@ const std::map<std::string, mode> modes =
const std::map<std::string, game> games =
{
{ "iw5c", game::iw5c },
{ "iw6c", game::iw6c },
{ "s1c", game::s1c },
{ "iw5", game::iw5 },
{ "iw6", game::iw6 },
{ "iw7", game::iw7 },
@ -102,6 +106,10 @@ auto choose_resolver_file_name(uint32_t id, game& game) -> std::string
{
case game::iw5c:
return iw5c::resolver::token_name(static_cast<std::uint16_t>(id));
case game::iw6c:
return iw6c::resolver::token_name(static_cast<std::uint16_t>(id));
case game::s1c:
return s1c::resolver::token_name(static_cast<std::uint16_t>(id));
case game::iw5:
return iw5::resolver::token_name(static_cast<std::uint16_t>(id));
case game::iw6:
@ -406,6 +414,10 @@ void init()
{
contexts[game::iw5c] = std::make_unique<iw5c::context>();
contexts[game::iw5c]->init(build::prod, utils::file::read);
contexts[game::iw6c] = std::make_unique<iw6c::context>();
contexts[game::iw6c]->init(build::prod, utils::file::read);
contexts[game::s1c] = std::make_unique<s1c::context>();
contexts[game::s1c]->init(build::prod, utils::file::read);
contexts[game::iw5] = std::make_unique<iw5::context>();
contexts[game::iw5]->init(build::prod, utils::file::read);
contexts[game::iw6] = std::make_unique<iw6::context>();