blackops2 implementation
This commit is contained in:
parent
c9c5e7c259
commit
2f402a10cc
1
.gitignore
vendored
1
.gitignore
vendored
@ -158,3 +158,4 @@ data/h2/
|
|||||||
data/s1/
|
data/s1/
|
||||||
data/s2/
|
data/s2/
|
||||||
data/s4/
|
data/s4/
|
||||||
|
data/t6/
|
||||||
|
@ -16,13 +16,13 @@ A utility to compile & decompile IW engine game scripts.
|
|||||||
- **H2** *(Call of Duty: Modern Warfare 2 Campaign Remastered)*
|
- **H2** *(Call of Duty: Modern Warfare 2 Campaign Remastered)*
|
||||||
- **T4** *(Call of Duty: World at War)* ***\*WIP\****
|
- **T4** *(Call of Duty: World at War)* ***\*WIP\****
|
||||||
- **T5** *(Call of Duty: Black Ops)* ***\*WIP\****
|
- **T5** *(Call of Duty: Black Ops)* ***\*WIP\****
|
||||||
- **T6** *(Call of Duty: Black Ops II)* ***\*WIP\****
|
- **T6** *(Call of Duty: Black Ops II)*
|
||||||
## Usage
|
## Usage
|
||||||
``./gsc-tool.exe <mode> <game> <path>``
|
``./gsc-tool.exe <mode> <game> <path>``
|
||||||
|
|
||||||
**modes**: `asm`, `disasm`, `comp`, `decomp`
|
**modes**: `asm`, `disasm`, `comp`, `decomp`
|
||||||
|
|
||||||
**games**: `iw5`, `iw6`, `iw7`, `iw8`, `s1`, `s2`, `s4`, `h1`, `h2`
|
**games**: `iw5`, `iw6`, `iw7`, `iw8`, `s1`, `s2`, `s4`, `h1`, `h2`, `t6`
|
||||||
|
|
||||||
**paths**: `file`, `directory` (recursive process all files with mode extension)
|
**paths**: `file`, `directory` (recursive process all files with mode extension)
|
||||||
|
|
||||||
|
9
gen/t6/Makefile
Normal file
9
gen/t6/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
generate: T6
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf ./parser.hpp
|
||||||
|
rm -rf ./parser.cpp
|
||||||
|
|
||||||
|
T6: parser.ypp
|
||||||
|
bison parser.ypp -Wcounterexamples
|
||||||
|
mv parser.hpp parser.cpp ../../src/t6/xsk/
|
968
gen/t6/parser.ypp
Normal file
968
gen/t6/parser.ypp
Normal file
@ -0,0 +1,968 @@
|
|||||||
|
/* 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 {T6}
|
||||||
|
%define api.namespace {xsk::arc::t6}
|
||||||
|
%define api.location.type {xsk::arc::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::arc::t6::lexer& lexer }
|
||||||
|
%parse-param { xsk::arc::t6::lexer& lexer }
|
||||||
|
%parse-param { xsk::arc::ast::program::ptr& ast }
|
||||||
|
|
||||||
|
%code requires
|
||||||
|
{
|
||||||
|
#include "t6.hpp"
|
||||||
|
namespace xsk::arc::t6 { class lexer; }
|
||||||
|
}
|
||||||
|
|
||||||
|
%code top
|
||||||
|
{
|
||||||
|
#include "stdafx.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "lexer.hpp"
|
||||||
|
using namespace xsk::arc;
|
||||||
|
xsk::arc::t6::parser::symbol_type T6lex(xsk::arc::t6::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 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 TRUE "true"
|
||||||
|
%token FALSE "false"
|
||||||
|
%token UNDEFINED "undefined"
|
||||||
|
%token SIZE ".size"
|
||||||
|
%token GAME "game"
|
||||||
|
%token SELF "self"
|
||||||
|
%token ANIM "anim"
|
||||||
|
%token LEVEL "level"
|
||||||
|
%token GETNEXTARRAYKEY "getnextarraykey"
|
||||||
|
%token GETFIRSTARRAYKEY "getfirstarraykey"
|
||||||
|
%token GETDVARCOLORALPHA "getdvarcoloralpha"
|
||||||
|
%token GETDVARCOLORBLUE "getdvarcolorblue"
|
||||||
|
%token GETDVARCOLORGREEN "getdvarcolorgreen"
|
||||||
|
%token GETDVARCOLORRED "getdvarcolorred"
|
||||||
|
%token GETDVARVECTOR "getdvarvector"
|
||||||
|
%token GETDVARFLOAT "getdvarfloat"
|
||||||
|
%token GETDVARINT "getdvarint"
|
||||||
|
%token GETDVAR "getdvar"
|
||||||
|
%token GETTIME "gettime"
|
||||||
|
%token ABS "abs"
|
||||||
|
%token VECTORTOANGLES "vectortoangles"
|
||||||
|
%token ANGLECLAMP180 "angleclamp180"
|
||||||
|
%token ANGLESTOFORWARD "anglestoforward"
|
||||||
|
%token ANGLESTORIGHT "anglestoright"
|
||||||
|
%token ANGLESTOUP "anglestoup"
|
||||||
|
%token VECTORSCALE "vectorscale"
|
||||||
|
%token ISDEFINED "isdefined"
|
||||||
|
%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> FIELD "field"
|
||||||
|
%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> HASH "hash"
|
||||||
|
%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_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_parameters::ptr> expr_parameters
|
||||||
|
%type <ast::expr_arguments::ptr> expr_arguments
|
||||||
|
%type <ast::expr_arguments::ptr> expr_arguments_no_empty
|
||||||
|
%type <ast::expr_getnextarraykey::ptr> expr_getnextarraykey
|
||||||
|
%type <ast::expr_getfirstarraykey::ptr> expr_getfirstarraykey
|
||||||
|
%type <ast::expr_getdvarcoloralpha::ptr> expr_getdvarcoloralpha
|
||||||
|
%type <ast::expr_getdvarcolorblue::ptr> expr_getdvarcolorblue
|
||||||
|
%type <ast::expr_getdvarcolorgreen::ptr> expr_getdvarcolorgreen
|
||||||
|
%type <ast::expr_getdvarcolorred::ptr> expr_getdvarcolorred
|
||||||
|
%type <ast::expr_getdvarvector::ptr> expr_getdvarvector
|
||||||
|
%type <ast::expr_getdvarfloat::ptr> expr_getdvarfloat
|
||||||
|
%type <ast::expr_getdvarint::ptr> expr_getdvarint
|
||||||
|
%type <ast::expr_getdvar::ptr> expr_getdvar
|
||||||
|
%type <ast::expr_gettime::ptr> expr_gettime
|
||||||
|
%type <ast::expr_abs::ptr> expr_abs
|
||||||
|
%type <ast::expr_vectortoangles::ptr> expr_vectortoangles
|
||||||
|
%type <ast::expr_angleclamp180::ptr> expr_angleclamp180
|
||||||
|
%type <ast::expr_anglestoforward::ptr> expr_anglestoforward
|
||||||
|
%type <ast::expr_anglestoright::ptr> expr_anglestoright
|
||||||
|
%type <ast::expr_anglestoup::ptr> expr_anglestoup
|
||||||
|
%type <ast::expr_vectorscale::ptr> expr_vectorscale
|
||||||
|
%type <ast::expr_isdefined::ptr> expr_isdefined
|
||||||
|
%type <ast::expr_reference::ptr> expr_reference
|
||||||
|
%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_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_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_hash::ptr> expr_hash
|
||||||
|
%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 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_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_not { $$.as_node = std::move($1); }
|
||||||
|
| expr_call { $$.as_node = std::move($1); }
|
||||||
|
| expr_method { $$.as_node = std::move($1); }
|
||||||
|
| expr_getnextarraykey { $$.as_node = std::move($1); }
|
||||||
|
| expr_getfirstarraykey { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarcoloralpha { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarcolorblue { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarcolorgreen { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarcolorred { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarvector { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarfloat { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvarint { $$.as_node = std::move($1); }
|
||||||
|
| expr_getdvar { $$.as_node = std::move($1); }
|
||||||
|
| expr_gettime { $$.as_node = std::move($1); }
|
||||||
|
| expr_abs { $$.as_node = std::move($1); }
|
||||||
|
| expr_vectortoangles { $$.as_node = std::move($1); }
|
||||||
|
| expr_angleclamp180 { $$.as_node = std::move($1); }
|
||||||
|
| expr_anglestoforward { $$.as_node = std::move($1); }
|
||||||
|
| expr_anglestoright { $$.as_node = std::move($1); }
|
||||||
|
| expr_anglestoup { $$.as_node = std::move($1); }
|
||||||
|
| expr_vectorscale { $$.as_node = std::move($1); }
|
||||||
|
| expr_isdefined { $$.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_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_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_hash { $$.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_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); }
|
||||||
|
;
|
||||||
|
|
||||||
|
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); }
|
||||||
|
;
|
||||||
|
|
||||||
|
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
|
||||||
|
{ $$ = std::make_unique<ast::expr_arguments>(@$); $$->list.push_back(std::move($1)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getnextarraykey
|
||||||
|
: GETNEXTARRAYKEY LPAREN expr COMMA expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getnextarraykey>(@$, std::move($3), std::move($5)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getfirstarraykey
|
||||||
|
: GETFIRSTARRAYKEY LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getfirstarraykey>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarcoloralpha
|
||||||
|
: GETDVARCOLORALPHA LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarcoloralpha>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarcolorblue
|
||||||
|
: GETDVARCOLORBLUE LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarcolorblue>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarcolorgreen
|
||||||
|
: GETDVARCOLORGREEN LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarcolorgreen>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarcolorred
|
||||||
|
: GETDVARCOLORRED LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarcolorred>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarvector
|
||||||
|
: GETDVARVECTOR LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarvector>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarfloat
|
||||||
|
: GETDVARFLOAT LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarfloat>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvarint
|
||||||
|
: GETDVARINT LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvarint>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_getdvar
|
||||||
|
: GETDVAR LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_getdvar>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_gettime
|
||||||
|
: GETTIME LPAREN RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_gettime>(@$); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_abs
|
||||||
|
: ABS LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_abs>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_vectortoangles
|
||||||
|
: VECTORTOANGLES LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_vectortoangles>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_angleclamp180
|
||||||
|
: ANGLECLAMP180 LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_angleclamp180>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_anglestoforward
|
||||||
|
: ANGLESTOFORWARD LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_anglestoforward>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_anglestoright
|
||||||
|
: ANGLESTORIGHT LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_anglestoright>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_anglestoup
|
||||||
|
: ANGLESTOUP LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_anglestoup>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_vectorscale
|
||||||
|
: VECTORSCALE LPAREN expr COMMA expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_vectorscale>(@$, std::move($3), std::move($5)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_isdefined
|
||||||
|
: ISDEFINED LPAREN expr RPAREN
|
||||||
|
{ $$ = std::make_unique<ast::expr_isdefined>(@$, std::move($3)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
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_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
|
||||||
|
{ $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::move($3)); }
|
||||||
|
| expr_object FIELD
|
||||||
|
{ $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::make_unique<ast::expr_identifier>(@$, $2)); }
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_size
|
||||||
|
: expr_object SIZE
|
||||||
|
{ $$ = 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_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_identifier
|
||||||
|
: IDENTIFIER
|
||||||
|
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
|
||||||
|
;
|
||||||
|
|
||||||
|
expr_path
|
||||||
|
: PATH
|
||||||
|
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
|
||||||
|
| expr_identifier
|
||||||
|
{ $$ = std::make_unique<ast::expr_path>(@$, $1->value); };
|
||||||
|
;
|
||||||
|
|
||||||
|
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_hash
|
||||||
|
: HASH
|
||||||
|
{ $$ = std::make_unique<ast::expr_hash>(@$, $1); };
|
||||||
|
;
|
||||||
|
|
||||||
|
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::arc::t6::parser::error(const xsk::arc::location& loc, const std::string& msg)
|
||||||
|
{
|
||||||
|
throw xsk::arc::comp_error(loc, msg);
|
||||||
|
}
|
22
premake5.lua
22
premake5.lua
@ -58,6 +58,7 @@ project "xsk-gsc-tool"
|
|||||||
dependson "xsk-gsc-s4"
|
dependson "xsk-gsc-s4"
|
||||||
dependson "xsk-gsc-h1"
|
dependson "xsk-gsc-h1"
|
||||||
dependson "xsk-gsc-h2"
|
dependson "xsk-gsc-h2"
|
||||||
|
dependson "xsk-arc-t6"
|
||||||
|
|
||||||
pchheader "stdafx.hpp"
|
pchheader "stdafx.hpp"
|
||||||
pchsource "src/tool/stdafx.cpp"
|
pchsource "src/tool/stdafx.cpp"
|
||||||
@ -78,7 +79,8 @@ project "xsk-gsc-tool"
|
|||||||
"xsk-gsc-s2",
|
"xsk-gsc-s2",
|
||||||
"xsk-gsc-s4",
|
"xsk-gsc-s4",
|
||||||
"xsk-gsc-h1",
|
"xsk-gsc-h1",
|
||||||
"xsk-gsc-h2"
|
"xsk-gsc-h2",
|
||||||
|
"xsk-arc-t6"
|
||||||
}
|
}
|
||||||
|
|
||||||
includedirs {
|
includedirs {
|
||||||
@ -270,5 +272,23 @@ project "xsk-gsc-h2"
|
|||||||
"./src"
|
"./src"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project "xsk-arc-t6"
|
||||||
|
kind "StaticLib"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
pchheader "stdafx.hpp"
|
||||||
|
pchsource "src/t6/stdafx.cpp"
|
||||||
|
|
||||||
|
files {
|
||||||
|
"./src/t6/**.h",
|
||||||
|
"./src/t6/**.hpp",
|
||||||
|
"./src/t6/**.cpp"
|
||||||
|
}
|
||||||
|
|
||||||
|
includedirs {
|
||||||
|
"./src/t6",
|
||||||
|
"./src"
|
||||||
|
}
|
||||||
|
|
||||||
group "Dependencies"
|
group "Dependencies"
|
||||||
zlib:project()
|
zlib:project()
|
||||||
|
6
src/t6/stdafx.cpp
Normal file
6
src/t6/stdafx.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// 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"
|
39
src/t6/stdafx.hpp
Normal file
39
src/t6/stdafx.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
// Warnings
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma warning(disable:4244)
|
||||||
|
#pragma warning(disable:4267)
|
||||||
|
#pragma warning(disable:4005)
|
||||||
|
#pragma warning(disable:4065)
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C/C++
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <stack>
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// Ext
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
#include "xsk/t6.hpp"
|
886
src/t6/xsk/assembler.cpp
Normal file
886
src/t6/xsk/assembler.cpp
Normal file
@ -0,0 +1,886 @@
|
|||||||
|
// 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 "t6.hpp"
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
auto assembler::output() -> std::vector<std::uint8_t>
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> output;
|
||||||
|
|
||||||
|
if (script_ == nullptr) return output;
|
||||||
|
|
||||||
|
output.resize(script_->pos());
|
||||||
|
std::memcpy(output.data(), script_->buffer().data(), output.size());
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble(const std::string& file, std::vector<std::uint8_t>& data)
|
||||||
|
{
|
||||||
|
throw error("assemble from source unimplemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble(const std::string& file, assembly::ptr& data)
|
||||||
|
{
|
||||||
|
script_ = std::make_unique<utils::byte_buffer>(0x100000);
|
||||||
|
filename_ = file;
|
||||||
|
assembly_ = std::move(data);
|
||||||
|
stringlist_.clear();
|
||||||
|
|
||||||
|
std::memset(&header_, 0 ,sizeof(header_));
|
||||||
|
|
||||||
|
// skip header
|
||||||
|
script_->pos(64);
|
||||||
|
|
||||||
|
// assemble strings
|
||||||
|
process_string(filename_);
|
||||||
|
|
||||||
|
for (const auto& func : assembly_->functions)
|
||||||
|
{
|
||||||
|
process_function(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& entry : assembly_->includes)
|
||||||
|
{
|
||||||
|
process_string(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble includes
|
||||||
|
header_.include_offset = script_->pos();
|
||||||
|
header_.include_count = assembly_->includes.size();
|
||||||
|
|
||||||
|
for (const auto& entry : assembly_->includes)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(string_offset(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble functions
|
||||||
|
header_.cseg_offset = script_->pos();
|
||||||
|
|
||||||
|
for (const auto& func : assembly_->functions)
|
||||||
|
{
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_t>(0);
|
||||||
|
assemble_function(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
header_.cseg_size = script_->pos() - header_.cseg_offset;
|
||||||
|
|
||||||
|
header_.source_crc = 0; // calcule_crc();
|
||||||
|
|
||||||
|
// assemble exports
|
||||||
|
header_.exports_offset = script_->pos();
|
||||||
|
header_.exports_count = exports_.size();
|
||||||
|
|
||||||
|
for (const auto& entry : exports_)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(entry.checksum);
|
||||||
|
script_->write<std::uint32_t>(entry.offset);
|
||||||
|
script_->write<std::uint16_t>(string_offset(entry.name));
|
||||||
|
script_->write<std::uint8_t>(entry.params);
|
||||||
|
script_->write<std::uint8_t>(entry.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble imports
|
||||||
|
header_.imports_offset = script_->pos();
|
||||||
|
header_.imports_count = imports_.size();
|
||||||
|
|
||||||
|
for (const auto& entry : imports_)
|
||||||
|
{
|
||||||
|
script_->write<std::uint16_t>(string_offset(entry.name));
|
||||||
|
script_->write<std::uint16_t>(string_offset(entry.space));
|
||||||
|
script_->write<std::uint16_t>(entry.refs.size());
|
||||||
|
script_->write<std::uint8_t>(entry.params);
|
||||||
|
script_->write<std::uint8_t>(entry.flags);
|
||||||
|
|
||||||
|
for (const auto& ref : entry.refs)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble animtrees
|
||||||
|
header_.animtree_offset = script_->pos();
|
||||||
|
header_.animtree_count = animtrees_.size();
|
||||||
|
|
||||||
|
for (const auto& entry : animtrees_)
|
||||||
|
{
|
||||||
|
script_->write<std::uint16_t>(string_offset(entry.name));
|
||||||
|
script_->write<std::uint16_t>(entry.refs.size());
|
||||||
|
script_->write<std::uint16_t>(entry.anims.size());
|
||||||
|
script_->seek(2);
|
||||||
|
|
||||||
|
for (const auto& ref : entry.refs)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& anim : entry.anims)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(string_offset(anim.name));
|
||||||
|
script_->write<std::uint32_t>(anim.ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble stringtable
|
||||||
|
header_.stringtablefixup_offset = script_->pos();
|
||||||
|
header_.stringtablefixup_count = stringtables_.size();
|
||||||
|
|
||||||
|
for (const auto& entry : stringtables_)
|
||||||
|
{
|
||||||
|
script_->write<std::uint16_t>(string_offset(entry.name));
|
||||||
|
script_->write<std::uint8_t>(entry.refs.size());
|
||||||
|
script_->write<std::uint8_t>(entry.type);
|
||||||
|
|
||||||
|
for (const auto& ref : entry.refs)
|
||||||
|
{
|
||||||
|
script_->write<std::uint32_t>(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble fixup
|
||||||
|
header_.fixup_offset = script_->pos();
|
||||||
|
header_.fixup_count = 0;
|
||||||
|
|
||||||
|
// assemble profile
|
||||||
|
header_.profile_offset = script_->pos();
|
||||||
|
header_.profile_count = 0;
|
||||||
|
|
||||||
|
header_.flags = 0;
|
||||||
|
|
||||||
|
auto endpos = script_->pos();
|
||||||
|
|
||||||
|
// assemble header
|
||||||
|
script_->pos(0);
|
||||||
|
script_->write<std::uint64_t>(magic);
|
||||||
|
script_->write<std::uint32_t>(header_.source_crc);
|
||||||
|
script_->write<std::uint32_t>(header_.include_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.animtree_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.cseg_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.stringtablefixup_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.exports_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.imports_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.fixup_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.profile_offset);
|
||||||
|
script_->write<std::uint32_t>(header_.cseg_size);
|
||||||
|
script_->write<std::uint16_t>(header_.name);
|
||||||
|
script_->write<std::uint16_t>(header_.stringtablefixup_count);
|
||||||
|
script_->write<std::uint16_t>(header_.exports_count);
|
||||||
|
script_->write<std::uint16_t>(header_.imports_count);
|
||||||
|
script_->write<std::uint16_t>(header_.fixup_count);
|
||||||
|
script_->write<std::uint16_t>(header_.profile_count);
|
||||||
|
script_->write<std::uint8_t>(header_.include_count);
|
||||||
|
script_->write<std::uint8_t>(header_.animtree_count);
|
||||||
|
script_->write<std::uint8_t>(header_.flags);
|
||||||
|
script_->pos(endpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_function(const function::ptr& func)
|
||||||
|
{
|
||||||
|
func->index = script_->pos();
|
||||||
|
func->size = 0;
|
||||||
|
|
||||||
|
for (const auto& inst : func->instructions)
|
||||||
|
{
|
||||||
|
auto old_idx = inst->index;
|
||||||
|
inst->index = func->index + func->size;
|
||||||
|
|
||||||
|
align_instruction(inst);
|
||||||
|
|
||||||
|
func->size += inst->size;
|
||||||
|
|
||||||
|
const auto& itr = func->labels.find(old_idx);
|
||||||
|
|
||||||
|
if (itr != labels_.end())
|
||||||
|
{
|
||||||
|
labels_.insert({ inst->index, itr->second });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_->pos(func->index);
|
||||||
|
|
||||||
|
for (const auto& inst : func->instructions)
|
||||||
|
{
|
||||||
|
assemble_instruction(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
export_ref obj;
|
||||||
|
obj.checksum = 0; // calculate_checksum();
|
||||||
|
obj.offset = func->index;
|
||||||
|
obj.name = func->name;
|
||||||
|
obj.params = func->params;
|
||||||
|
obj.flags = func->flags;
|
||||||
|
exports_.push_back(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_instruction(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
switch (opcode(inst->opcode))
|
||||||
|
{
|
||||||
|
case opcode::OP_End:
|
||||||
|
case opcode::OP_Return:
|
||||||
|
case opcode::OP_GetUndefined:
|
||||||
|
case opcode::OP_GetZero:
|
||||||
|
case opcode::OP_GetLevelObject:
|
||||||
|
case opcode::OP_GetAnimObject:
|
||||||
|
case opcode::OP_GetSelf:
|
||||||
|
case opcode::OP_GetLevel:
|
||||||
|
case opcode::OP_GetGame:
|
||||||
|
case opcode::OP_GetAnim:
|
||||||
|
case opcode::OP_GetGameRef:
|
||||||
|
case opcode::OP_CreateLocalVariable:
|
||||||
|
case opcode::OP_RemoveLocalVariables:
|
||||||
|
case opcode::OP_EvalArray:
|
||||||
|
case opcode::OP_EvalLocalArrayRefCached:
|
||||||
|
case opcode::OP_EvalArrayRef:
|
||||||
|
case opcode::OP_ClearArray:
|
||||||
|
case opcode::OP_EmptyArray:
|
||||||
|
case opcode::OP_GetSelfObject:
|
||||||
|
case opcode::OP_SafeSetVariableFieldCached:
|
||||||
|
case opcode::OP_ClearParams:
|
||||||
|
case opcode::OP_CheckClearParams:
|
||||||
|
case opcode::OP_SetVariableField:
|
||||||
|
case opcode::OP_Wait:
|
||||||
|
case opcode::OP_WaitTillFrameEnd:
|
||||||
|
case opcode::OP_PreScriptCall:
|
||||||
|
case opcode::OP_DecTop:
|
||||||
|
case opcode::OP_CastFieldObject:
|
||||||
|
case opcode::OP_CastBool:
|
||||||
|
case opcode::OP_BoolNot:
|
||||||
|
case opcode::OP_BoolComplement:
|
||||||
|
case opcode::OP_Inc:
|
||||||
|
case opcode::OP_Dec:
|
||||||
|
case opcode::OP_Bit_Or:
|
||||||
|
case opcode::OP_Bit_Xor:
|
||||||
|
case opcode::OP_Bit_And:
|
||||||
|
case opcode::OP_Equal:
|
||||||
|
case opcode::OP_NotEqual:
|
||||||
|
case opcode::OP_LessThan:
|
||||||
|
case opcode::OP_GreaterThan:
|
||||||
|
case opcode::OP_LessThanOrEqualTo:
|
||||||
|
case opcode::OP_GreaterThanOrEqualTo:
|
||||||
|
case opcode::OP_ShiftLeft:
|
||||||
|
case opcode::OP_ShiftRight:
|
||||||
|
case opcode::OP_Plus:
|
||||||
|
case opcode::OP_Minus:
|
||||||
|
case opcode::OP_Multiply:
|
||||||
|
case opcode::OP_Divide:
|
||||||
|
case opcode::OP_Modulus:
|
||||||
|
case opcode::OP_SizeOf:
|
||||||
|
case opcode::OP_WaitTill:
|
||||||
|
case opcode::OP_Notify:
|
||||||
|
case opcode::OP_EndOn:
|
||||||
|
case opcode::OP_VoidCodePos:
|
||||||
|
case opcode::OP_Vector:
|
||||||
|
case opcode::OP_RealWait:
|
||||||
|
case opcode::OP_IsDefined:
|
||||||
|
case opcode::OP_VectorScale:
|
||||||
|
case opcode::OP_AnglesToUp:
|
||||||
|
case opcode::OP_AnglesToRight:
|
||||||
|
case opcode::OP_AnglesToForward:
|
||||||
|
case opcode::OP_AngleClamp180:
|
||||||
|
case opcode::OP_VectorToAngles:
|
||||||
|
case opcode::OP_Abs:
|
||||||
|
case opcode::OP_GetTime:
|
||||||
|
case opcode::OP_GetDvar:
|
||||||
|
case opcode::OP_GetDvarInt:
|
||||||
|
case opcode::OP_GetDvarFloat:
|
||||||
|
case opcode::OP_GetDvarVector:
|
||||||
|
case opcode::OP_GetDvarColorRed:
|
||||||
|
case opcode::OP_GetDvarColorGreen:
|
||||||
|
case opcode::OP_GetDvarColorBlue:
|
||||||
|
case opcode::OP_GetDvarColorAlpha:
|
||||||
|
case opcode::OP_FirstArrayKey:
|
||||||
|
case opcode::OP_NextArrayKey:
|
||||||
|
case opcode::OP_ProfileStart:
|
||||||
|
case opcode::OP_ProfileStop:
|
||||||
|
case opcode::OP_SafeDecTop:
|
||||||
|
case opcode::OP_Nop:
|
||||||
|
case opcode::OP_Abort:
|
||||||
|
case opcode::OP_Object:
|
||||||
|
case opcode::OP_ThreadObject:
|
||||||
|
case opcode::OP_EvalLocalVariable:
|
||||||
|
case opcode::OP_EvalLocalVariableRef:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetByte:
|
||||||
|
case opcode::OP_GetNegByte:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetUnsignedShort:
|
||||||
|
case opcode::OP_GetNegUnsignedShort:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::uint16_t>(static_cast<std::uint16_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetInteger:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::int32_t>(std::stoi(inst->data[0]));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFloat:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<float>(std::stof(inst->data[0]));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetVector:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<float>(std::stof(inst->data[0]));
|
||||||
|
script_->write<float>(std::stof(inst->data[1]));
|
||||||
|
script_->write<float>(std::stof(inst->data[2]));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetString:
|
||||||
|
case opcode::OP_GetIString:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::uint16_t>(0);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetAnimation:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_t>(0);
|
||||||
|
break;
|
||||||
|
case opcode::OP_WaitTillMatch:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_VectorConstant:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetHash:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_t>(static_cast<std::uint32_t>(std::stoul(inst->data[0], 0, 16)));
|
||||||
|
break;
|
||||||
|
case opcode::OP_SafeCreateLocalVariables:
|
||||||
|
assemble_localvars(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalLocalVariableCached:
|
||||||
|
case opcode::OP_SafeSetWaittillVariableFieldCached:
|
||||||
|
case opcode::OP_EvalLocalVariableRefCached:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalFieldVariable:
|
||||||
|
case opcode::OP_EvalFieldVariableRef:
|
||||||
|
case opcode::OP_ClearFieldVariable:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::uint16_t>(0);
|
||||||
|
break;
|
||||||
|
case opcode::OP_ScriptFunctionCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodCallPointer:
|
||||||
|
case opcode::OP_ScriptThreadCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodThreadCallPointer:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFunction:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_t>(0);
|
||||||
|
break;
|
||||||
|
case opcode::OP_CallBuiltin:
|
||||||
|
case opcode::OP_CallBuiltinMethod:
|
||||||
|
case opcode::OP_ScriptFunctionCall:
|
||||||
|
case opcode::OP_ScriptMethodCall:
|
||||||
|
case opcode::OP_ScriptThreadCall:
|
||||||
|
case opcode::OP_ScriptMethodThreadCall:
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(0);
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_t>(0);
|
||||||
|
break;
|
||||||
|
case opcode::OP_JumpOnFalse:
|
||||||
|
case opcode::OP_JumpOnTrue:
|
||||||
|
case opcode::OP_JumpOnFalseExpr:
|
||||||
|
case opcode::OP_JumpOnTrueExpr:
|
||||||
|
case opcode::OP_Jump:
|
||||||
|
case opcode::OP_JumpBack:
|
||||||
|
assemble_jump(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_Switch:
|
||||||
|
assemble_switch(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EndSwitch:
|
||||||
|
assemble_end_switch(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_DevblockBegin:
|
||||||
|
case opcode::OP_DevblockEnd:
|
||||||
|
assemble_devblock(inst);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw asm_error(utils::string::va("Unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_localvars(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->data.size()));
|
||||||
|
|
||||||
|
for (const auto& entry : inst->data)
|
||||||
|
{
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::uint16_t>(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_jump(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
|
||||||
|
const std::int16_t addr = resolve_label(inst->data[0]) - inst->index - inst->size;
|
||||||
|
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::int16_t>(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_switch(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
|
||||||
|
const std::int32_t addr = ((resolve_label(inst->data[0]) + 4) & 0xFFFFFFFC) - inst->index - inst->size;
|
||||||
|
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::int32_t>(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_end_switch(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
|
||||||
|
const auto count = std::stoul(inst->data[0]);
|
||||||
|
|
||||||
|
script_->align(4);
|
||||||
|
script_->write<std::uint32_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<uint32_t>((std::stoi(inst->data[1 + (3 * i) + 1]) & 0xFFFFFF) + 0x800000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
script_->write<uint32_t>(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::int32_t addr = resolve_label(inst->data[1 + (3 * i) + 2]) - script_->pos() - 4;
|
||||||
|
|
||||||
|
script_->write<int32_t>(addr);
|
||||||
|
}
|
||||||
|
else if (inst->data[1 + (3 * i)] == "default")
|
||||||
|
{
|
||||||
|
script_->write<uint32_t>(0);
|
||||||
|
|
||||||
|
const std::int32_t addr = resolve_label(inst->data[1 + (3 * i) + 1]) - script_->pos() - 4;
|
||||||
|
|
||||||
|
script_->write<int32_t>(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::assemble_devblock(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
|
||||||
|
|
||||||
|
const std::int16_t addr = resolve_label(inst->data[0]) - inst->index - inst->size;
|
||||||
|
|
||||||
|
script_->align(2);
|
||||||
|
script_->write<std::int16_t>(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::align_instruction(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size = opcode_size(inst->opcode);
|
||||||
|
script_->seek(1);
|
||||||
|
|
||||||
|
switch (opcode(inst->opcode))
|
||||||
|
{
|
||||||
|
case opcode::OP_End:
|
||||||
|
case opcode::OP_Return:
|
||||||
|
case opcode::OP_GetUndefined:
|
||||||
|
case opcode::OP_GetZero:
|
||||||
|
case opcode::OP_GetLevelObject:
|
||||||
|
case opcode::OP_GetAnimObject:
|
||||||
|
case opcode::OP_GetSelf:
|
||||||
|
case opcode::OP_GetLevel:
|
||||||
|
case opcode::OP_GetGame:
|
||||||
|
case opcode::OP_GetAnim:
|
||||||
|
case opcode::OP_GetGameRef:
|
||||||
|
case opcode::OP_CreateLocalVariable:
|
||||||
|
case opcode::OP_RemoveLocalVariables:
|
||||||
|
case opcode::OP_EvalArray:
|
||||||
|
case opcode::OP_EvalLocalArrayRefCached:
|
||||||
|
case opcode::OP_EvalArrayRef:
|
||||||
|
case opcode::OP_ClearArray:
|
||||||
|
case opcode::OP_EmptyArray:
|
||||||
|
case opcode::OP_GetSelfObject:
|
||||||
|
case opcode::OP_SafeSetVariableFieldCached:
|
||||||
|
case opcode::OP_ClearParams:
|
||||||
|
case opcode::OP_CheckClearParams:
|
||||||
|
case opcode::OP_SetVariableField:
|
||||||
|
case opcode::OP_Wait:
|
||||||
|
case opcode::OP_WaitTillFrameEnd:
|
||||||
|
case opcode::OP_PreScriptCall:
|
||||||
|
case opcode::OP_DecTop:
|
||||||
|
case opcode::OP_CastFieldObject:
|
||||||
|
case opcode::OP_CastBool:
|
||||||
|
case opcode::OP_BoolNot:
|
||||||
|
case opcode::OP_BoolComplement:
|
||||||
|
case opcode::OP_Inc:
|
||||||
|
case opcode::OP_Dec:
|
||||||
|
case opcode::OP_Bit_Or:
|
||||||
|
case opcode::OP_Bit_Xor:
|
||||||
|
case opcode::OP_Bit_And:
|
||||||
|
case opcode::OP_Equal:
|
||||||
|
case opcode::OP_NotEqual:
|
||||||
|
case opcode::OP_LessThan:
|
||||||
|
case opcode::OP_GreaterThan:
|
||||||
|
case opcode::OP_LessThanOrEqualTo:
|
||||||
|
case opcode::OP_GreaterThanOrEqualTo:
|
||||||
|
case opcode::OP_ShiftLeft:
|
||||||
|
case opcode::OP_ShiftRight:
|
||||||
|
case opcode::OP_Plus:
|
||||||
|
case opcode::OP_Minus:
|
||||||
|
case opcode::OP_Multiply:
|
||||||
|
case opcode::OP_Divide:
|
||||||
|
case opcode::OP_Modulus:
|
||||||
|
case opcode::OP_SizeOf:
|
||||||
|
case opcode::OP_WaitTill:
|
||||||
|
case opcode::OP_Notify:
|
||||||
|
case opcode::OP_EndOn:
|
||||||
|
case opcode::OP_VoidCodePos:
|
||||||
|
case opcode::OP_Vector:
|
||||||
|
case opcode::OP_RealWait:
|
||||||
|
case opcode::OP_IsDefined:
|
||||||
|
case opcode::OP_VectorScale:
|
||||||
|
case opcode::OP_AnglesToUp:
|
||||||
|
case opcode::OP_AnglesToRight:
|
||||||
|
case opcode::OP_AnglesToForward:
|
||||||
|
case opcode::OP_AngleClamp180:
|
||||||
|
case opcode::OP_VectorToAngles:
|
||||||
|
case opcode::OP_Abs:
|
||||||
|
case opcode::OP_GetTime:
|
||||||
|
case opcode::OP_GetDvar:
|
||||||
|
case opcode::OP_GetDvarInt:
|
||||||
|
case opcode::OP_GetDvarFloat:
|
||||||
|
case opcode::OP_GetDvarVector:
|
||||||
|
case opcode::OP_GetDvarColorRed:
|
||||||
|
case opcode::OP_GetDvarColorGreen:
|
||||||
|
case opcode::OP_GetDvarColorBlue:
|
||||||
|
case opcode::OP_GetDvarColorAlpha:
|
||||||
|
case opcode::OP_FirstArrayKey:
|
||||||
|
case opcode::OP_NextArrayKey:
|
||||||
|
case opcode::OP_ProfileStart:
|
||||||
|
case opcode::OP_ProfileStop:
|
||||||
|
case opcode::OP_SafeDecTop:
|
||||||
|
case opcode::OP_Nop:
|
||||||
|
case opcode::OP_Abort:
|
||||||
|
case opcode::OP_Object:
|
||||||
|
case opcode::OP_ThreadObject:
|
||||||
|
case opcode::OP_EvalLocalVariable:
|
||||||
|
case opcode::OP_EvalLocalVariableRef:
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetByte:
|
||||||
|
case opcode::OP_GetNegByte:
|
||||||
|
script_->seek(1);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetUnsignedShort:
|
||||||
|
case opcode::OP_GetNegUnsignedShort:
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
script_->seek(2);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetInteger:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFloat:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetVector:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(12);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetString:
|
||||||
|
case opcode::OP_GetIString:
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
add_string_reference(inst->data[0], string_type::literal, script_->pos());
|
||||||
|
script_->seek(2);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetAnimation:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
add_anim_reference(inst->data, script_->pos());
|
||||||
|
script_->seek(4);
|
||||||
|
break;
|
||||||
|
case opcode::OP_WaitTillMatch:
|
||||||
|
script_->seek(1);
|
||||||
|
break;
|
||||||
|
case opcode::OP_VectorConstant:
|
||||||
|
script_->seek(1);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetHash:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
break;
|
||||||
|
case opcode::OP_SafeCreateLocalVariables:
|
||||||
|
script_->seek(1);
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < inst->data.size(); i++)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(2) + 2;
|
||||||
|
add_string_reference(inst->data[i], string_type::canonical, script_->pos());
|
||||||
|
script_->seek(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalLocalVariableCached:
|
||||||
|
case opcode::OP_SafeSetWaittillVariableFieldCached:
|
||||||
|
case opcode::OP_EvalLocalVariableRefCached:
|
||||||
|
script_->seek(1);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalFieldVariable:
|
||||||
|
case opcode::OP_EvalFieldVariableRef:
|
||||||
|
case opcode::OP_ClearFieldVariable:
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
add_string_reference(inst->data[0], string_type::canonical, script_->pos());
|
||||||
|
script_->seek(2);
|
||||||
|
break;
|
||||||
|
case opcode::OP_ScriptFunctionCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodCallPointer:
|
||||||
|
case opcode::OP_ScriptThreadCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodThreadCallPointer:
|
||||||
|
script_->seek(1);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFunction:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
add_import_reference(inst->data, inst->index);
|
||||||
|
break;
|
||||||
|
case opcode::OP_CallBuiltin:
|
||||||
|
case opcode::OP_CallBuiltinMethod:
|
||||||
|
case opcode::OP_ScriptFunctionCall:
|
||||||
|
case opcode::OP_ScriptMethodCall:
|
||||||
|
case opcode::OP_ScriptThreadCall:
|
||||||
|
case opcode::OP_ScriptMethodThreadCall:
|
||||||
|
script_->seek(1);
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
add_import_reference(inst->data, inst->index);
|
||||||
|
break;
|
||||||
|
case opcode::OP_JumpOnFalse:
|
||||||
|
case opcode::OP_JumpOnTrue:
|
||||||
|
case opcode::OP_JumpOnFalseExpr:
|
||||||
|
case opcode::OP_JumpOnTrueExpr:
|
||||||
|
case opcode::OP_Jump:
|
||||||
|
case opcode::OP_JumpBack:
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
script_->seek(2);
|
||||||
|
break;
|
||||||
|
case opcode::OP_Switch:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EndSwitch:
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
|
||||||
|
const auto count = std::stoul(inst->data[0]);
|
||||||
|
|
||||||
|
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]))
|
||||||
|
{
|
||||||
|
add_string_reference(inst->data[1 + (3 * i) + 1], string_type::literal, script_->pos() + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->size += 8;
|
||||||
|
script_->seek(8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case opcode::OP_DevblockBegin:
|
||||||
|
case opcode::OP_DevblockEnd:
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
script_->seek(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw asm_error(utils::string::va("Unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::process_string(const std::string& data)
|
||||||
|
{
|
||||||
|
if (!stringlist_.contains(data))
|
||||||
|
{
|
||||||
|
auto pos = script_->pos();
|
||||||
|
script_->write_c_string(data);
|
||||||
|
stringlist_.insert({ data, pos });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::process_function(const function::ptr& func)
|
||||||
|
{
|
||||||
|
process_string(func->name);
|
||||||
|
|
||||||
|
for (const auto& inst : func->instructions)
|
||||||
|
{
|
||||||
|
process_instruction(inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::process_instruction(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
switch (opcode(inst->opcode))
|
||||||
|
{
|
||||||
|
case opcode::OP_GetString:
|
||||||
|
case opcode::OP_GetIString:
|
||||||
|
process_string(inst->data[0]);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetAnimation:
|
||||||
|
process_string(inst->data[0]);
|
||||||
|
process_string(inst->data[1]);
|
||||||
|
break;
|
||||||
|
case opcode::OP_SafeCreateLocalVariables:
|
||||||
|
{
|
||||||
|
for (const auto& entry : inst->data)
|
||||||
|
{
|
||||||
|
process_string(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalFieldVariable:
|
||||||
|
case opcode::OP_EvalFieldVariableRef:
|
||||||
|
case opcode::OP_ClearFieldVariable:
|
||||||
|
process_string(inst->data[0]);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFunction:
|
||||||
|
process_string(inst->data[0]);
|
||||||
|
process_string(inst->data[1]);
|
||||||
|
break;
|
||||||
|
case opcode::OP_CallBuiltin:
|
||||||
|
case opcode::OP_CallBuiltinMethod:
|
||||||
|
case opcode::OP_ScriptFunctionCall:
|
||||||
|
case opcode::OP_ScriptMethodCall:
|
||||||
|
case opcode::OP_ScriptThreadCall:
|
||||||
|
case opcode::OP_ScriptMethodThreadCall:
|
||||||
|
process_string(inst->data[0]);
|
||||||
|
process_string(inst->data[1]);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EndSwitch:
|
||||||
|
{
|
||||||
|
const auto count = std::stoul(inst->data[0]);
|
||||||
|
|
||||||
|
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]))
|
||||||
|
{
|
||||||
|
process_string(utils::string::unquote(inst->data[1 + (3 * i) + 1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto assembler::resolve_label(const std::string& name) -> std::int32_t
|
||||||
|
{
|
||||||
|
for (const auto& func : labels_)
|
||||||
|
{
|
||||||
|
if (func.second == name)
|
||||||
|
{
|
||||||
|
return func.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw asm_error("Couldn't resolve label address of '" + name + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto assembler::string_offset(const std::string& name) -> std::uint32_t
|
||||||
|
{
|
||||||
|
const auto& itr = stringlist_.find(name);
|
||||||
|
|
||||||
|
if (itr != stringlist_.end())
|
||||||
|
{
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw asm_error("couldn't resolve string assembly address of '" + name + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::add_string_reference(const std::string& str, string_type type, std::uint32_t ref)
|
||||||
|
{
|
||||||
|
for (auto& entry : stringtables_)
|
||||||
|
{
|
||||||
|
if (entry.name == str && entry.type == std::uint8_t(type))
|
||||||
|
{
|
||||||
|
entry.refs.push_back(ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stringtables_.push_back({ str, std::uint8_t(type), { ref } });
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::add_import_reference(const std::vector<std::string>& data, std::uint32_t ref)
|
||||||
|
{
|
||||||
|
for (auto& entry : imports_)
|
||||||
|
{
|
||||||
|
if (entry.space == data[0] && entry.name == data[1] && entry.params == std::stoi(data[2]) && entry.params == std::stoi(data[3]))
|
||||||
|
{
|
||||||
|
entry.refs.push_back(ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import_ref n;
|
||||||
|
n.space = data[0];
|
||||||
|
n.name = data[1];
|
||||||
|
n.params = std::stoi(data[2]);
|
||||||
|
n.flags = std::stoi(data[3]);
|
||||||
|
n.refs.push_back(ref);
|
||||||
|
imports_.push_back(std::move(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
void assembler::add_anim_reference(const std::vector<std::string>& data, std::uint32_t ref)
|
||||||
|
{
|
||||||
|
for (auto& entry : animtrees_)
|
||||||
|
{
|
||||||
|
if (entry.name == data[0])
|
||||||
|
{
|
||||||
|
entry.anims.push_back({ data[1], ref });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animtree_ref n;
|
||||||
|
n.name = data[0];
|
||||||
|
n.anims.push_back({ data[1], ref });
|
||||||
|
animtrees_.push_back(std::move(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
48
src/t6/xsk/assembler.hpp
Normal file
48
src/t6/xsk/assembler.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
class assembler : public arc::assembler
|
||||||
|
{
|
||||||
|
std::string filename_;
|
||||||
|
utils::byte_buffer::ptr script_;
|
||||||
|
assembly::ptr assembly_;
|
||||||
|
std::unordered_map<std::uint32_t, std::string> labels_;
|
||||||
|
std::unordered_map<std::string, std::uint16_t> stringlist_;
|
||||||
|
std::vector<export_ref> exports_;
|
||||||
|
std::vector<import_ref> imports_;
|
||||||
|
std::vector<animtree_ref> animtrees_;
|
||||||
|
std::vector<string_ref> stringtables_;
|
||||||
|
header header_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto output() -> std::vector<std::uint8_t>;
|
||||||
|
void assemble(const std::string& file, std::vector<std::uint8_t>& data);
|
||||||
|
void assemble(const std::string& file, assembly::ptr& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void assemble_function(const function::ptr& func);
|
||||||
|
void assemble_instruction(const instruction::ptr& inst);
|
||||||
|
void assemble_localvars(const instruction::ptr& inst);
|
||||||
|
void assemble_jump(const instruction::ptr& inst);
|
||||||
|
void assemble_switch(const instruction::ptr& inst);
|
||||||
|
void assemble_end_switch(const instruction::ptr& inst);
|
||||||
|
void assemble_devblock(const instruction::ptr& inst);
|
||||||
|
void process_string(const std::string& data);
|
||||||
|
void process_function(const function::ptr& func);
|
||||||
|
void process_instruction(const instruction::ptr& inst);
|
||||||
|
void align_instruction(const instruction::ptr& inst);
|
||||||
|
auto resolve_label(const std::string& name) -> std::int32_t;
|
||||||
|
auto string_offset(const std::string& data) -> std::uint32_t;
|
||||||
|
void add_string_reference(const std::string& str, string_type type, std::uint32_t ref);
|
||||||
|
void add_import_reference(const std::vector<std::string>& data, std::uint32_t ref);
|
||||||
|
void add_anim_reference(const std::vector<std::string>& data, std::uint32_t ref);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
2473
src/t6/xsk/compiler.cpp
Normal file
2473
src/t6/xsk/compiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
172
src/t6/xsk/compiler.hpp
Normal file
172
src/t6/xsk/compiler.hpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
// 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::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class opcode : std::uint8_t;
|
||||||
|
|
||||||
|
class compiler : public arc::compiler
|
||||||
|
{
|
||||||
|
build mode_;
|
||||||
|
std::string filename_;
|
||||||
|
assembly::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::function<std::vector<std::uint8_t>(const std::string&)> read_callback_;
|
||||||
|
abort_t abort_;
|
||||||
|
std::string break_loc_;
|
||||||
|
std::string continue_loc_;
|
||||||
|
bool can_break_;
|
||||||
|
bool can_continue_;
|
||||||
|
bool developer_thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
compiler(build mode) : mode_(mode) {}
|
||||||
|
auto output() -> assembly::ptr;
|
||||||
|
auto output_data() -> std::vector<std::uint8_t>;
|
||||||
|
void compile(const std::string& file, std::vector<std::uint8_t>& data);
|
||||||
|
void read_callback(std::function<std::vector<std::uint8_t>(const std::string&)> func);
|
||||||
|
|
||||||
|
private:
|
||||||
|
auto parse_buffer(const std::string& file, std::vector<std::uint8_t>& data) -> 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);
|
||||||
|
void emit_stmt_list(const ast::stmt_list::ptr& stmt);
|
||||||
|
void emit_stmt_dev(const ast::stmt_dev::ptr& stmt);
|
||||||
|
void emit_stmt_expr(const ast::stmt_expr::ptr& stmt);
|
||||||
|
void emit_stmt_call(const ast::stmt_call::ptr& stmt);
|
||||||
|
void emit_stmt_assign(const ast::stmt_assign::ptr& stmt);
|
||||||
|
void emit_stmt_endon(const ast::stmt_endon::ptr& stmt);
|
||||||
|
void emit_stmt_notify(const ast::stmt_notify::ptr& stmt);
|
||||||
|
void emit_stmt_wait(const ast::stmt_wait::ptr& stmt);
|
||||||
|
void emit_stmt_waittill(const ast::stmt_waittill::ptr& stmt);
|
||||||
|
void emit_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt);
|
||||||
|
void emit_stmt_waittillframeend(const ast::stmt_waittillframeend::ptr& stmt);
|
||||||
|
void emit_stmt_if(const ast::stmt_if::ptr& stmt);
|
||||||
|
void emit_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt);
|
||||||
|
void emit_stmt_while(const ast::stmt_while::ptr& stmt);
|
||||||
|
void emit_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt);
|
||||||
|
void emit_stmt_for(const ast::stmt_for::ptr& stmt);
|
||||||
|
void emit_stmt_foreach(const ast::stmt_foreach::ptr& stmt);
|
||||||
|
void emit_stmt_switch(const ast::stmt_switch::ptr& stmt);
|
||||||
|
void emit_stmt_case(const ast::stmt_case::ptr& stmt);
|
||||||
|
void emit_stmt_default(const ast::stmt_default::ptr& stmt);
|
||||||
|
void emit_stmt_break(const ast::stmt_break::ptr& stmt);
|
||||||
|
void emit_stmt_continue(const ast::stmt_continue::ptr& stmt);
|
||||||
|
void emit_stmt_return(const ast::stmt_return::ptr& stmt);
|
||||||
|
void emit_stmt_breakpoint(const ast::stmt_breakpoint::ptr& stmt);
|
||||||
|
void emit_stmt_prof_begin(const ast::stmt_prof_begin::ptr& stmt);
|
||||||
|
void emit_stmt_prof_end(const ast::stmt_prof_end::ptr& stmt);
|
||||||
|
void emit_expr(const ast::expr& expr);
|
||||||
|
void emit_expr_assign(const ast::expr_assign::ptr& expr);
|
||||||
|
void emit_expr_clear(const ast::expr& expr);
|
||||||
|
void emit_expr_increment(const ast::expr_increment::ptr& expr, bool stmt);
|
||||||
|
void emit_expr_decrement(const ast::expr_decrement::ptr& expr, bool stmt);
|
||||||
|
void emit_expr_ternary(const ast::expr_ternary::ptr& expr);
|
||||||
|
void emit_expr_binary(const ast::expr_binary::ptr& expr);
|
||||||
|
void emit_expr_and(const ast::expr_and::ptr& expr);
|
||||||
|
void emit_expr_or(const ast::expr_or::ptr& expr);
|
||||||
|
void emit_expr_complement(const ast::expr_complement::ptr& expr);
|
||||||
|
void emit_expr_not(const ast::expr_not::ptr& expr);
|
||||||
|
void emit_expr_call(const ast::expr_call::ptr& expr);
|
||||||
|
void emit_expr_call_pointer(const ast::expr_pointer::ptr& expr);
|
||||||
|
void emit_expr_call_function(const ast::expr_function::ptr& expr);
|
||||||
|
void emit_expr_method(const ast::expr_method::ptr& expr);
|
||||||
|
void emit_expr_method_pointer(const ast::expr_pointer::ptr& expr, const ast::expr& obj);
|
||||||
|
void emit_expr_method_function(const ast::expr_function::ptr& expr, const ast::expr& obj);
|
||||||
|
void emit_expr_parameters(const ast::expr_parameters::ptr& expr);
|
||||||
|
void emit_expr_arguments(const ast::expr_arguments::ptr& expr);
|
||||||
|
void emit_expr_isdefined(const ast::expr_isdefined::ptr& expr);
|
||||||
|
void emit_expr_vectorscale(const ast::expr_vectorscale::ptr& expr);
|
||||||
|
void emit_expr_anglestoup(const ast::expr_anglestoup::ptr& expr);
|
||||||
|
void emit_expr_anglestoright(const ast::expr_anglestoright::ptr& expr);
|
||||||
|
void emit_expr_anglestoforward(const ast::expr_anglestoforward::ptr& expr);
|
||||||
|
void emit_expr_angleclamp180(const ast::expr_angleclamp180::ptr& expr);
|
||||||
|
void emit_expr_vectortoangles(const ast::expr_vectortoangles::ptr& expr);
|
||||||
|
void emit_expr_abs(const ast::expr_abs::ptr& expr);
|
||||||
|
void emit_expr_gettime(const ast::expr_gettime::ptr& expr);
|
||||||
|
void emit_expr_getdvar(const ast::expr_getdvar::ptr& expr);
|
||||||
|
void emit_expr_getdvarint(const ast::expr_getdvarint::ptr& expr);
|
||||||
|
void emit_expr_getdvarfloat(const ast::expr_getdvarfloat::ptr& expr);
|
||||||
|
void emit_expr_getdvarvector(const ast::expr_getdvarvector::ptr& expr);
|
||||||
|
void emit_expr_getdvarcolorred(const ast::expr_getdvarcolorred::ptr& expr);
|
||||||
|
void emit_expr_getdvarcolorgreen(const ast::expr_getdvarcolorgreen::ptr& expr);
|
||||||
|
void emit_expr_getdvarcolorblue(const ast::expr_getdvarcolorblue::ptr& expr);
|
||||||
|
void emit_expr_getdvarcoloralpha(const ast::expr_getdvarcoloralpha::ptr& expr);
|
||||||
|
void emit_expr_getfirstarraykey(const ast::expr_getfirstarraykey::ptr& expr);
|
||||||
|
void emit_expr_getnextarraykey(const ast::expr_getnextarraykey::ptr& expr);
|
||||||
|
void emit_expr_reference(const ast::expr_reference::ptr& expr);
|
||||||
|
void emit_expr_size(const ast::expr_size::ptr& expr);
|
||||||
|
void emit_expr_variable_ref(const ast::expr& expr, bool set);
|
||||||
|
void emit_expr_array_ref(const ast::expr_array::ptr& expr, bool set);
|
||||||
|
void emit_expr_field_ref(const ast::expr_field::ptr& expr, bool set);
|
||||||
|
void emit_expr_local_ref(const ast::expr_identifier::ptr& expr, bool set);
|
||||||
|
void emit_expr_variable(const ast::expr& expr);
|
||||||
|
void emit_expr_array(const ast::expr_array::ptr& expr);
|
||||||
|
void emit_expr_field(const ast::expr_field::ptr& expr);
|
||||||
|
void emit_expr_local(const ast::expr_identifier::ptr& expr);
|
||||||
|
void emit_expr_object(const ast::expr& expr);
|
||||||
|
void emit_expr_vector(const ast::expr_vector::ptr& expr);
|
||||||
|
void emit_expr_animation(const ast::expr_animation::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_hash(const ast::expr_hash::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_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);
|
||||||
|
void process_stmt(const ast::stmt& stmt);
|
||||||
|
void process_stmt_list(const ast::stmt_list::ptr& stmt);
|
||||||
|
void process_stmt_dev(const ast::stmt_dev::ptr& stmt);
|
||||||
|
void process_stmt_expr(const ast::stmt_expr::ptr& stmt);
|
||||||
|
void process_stmt_assign(const ast::stmt_assign::ptr& stmt);
|
||||||
|
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt);
|
||||||
|
void process_stmt_if(const ast::stmt_if::ptr& stmt);
|
||||||
|
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt);
|
||||||
|
void process_stmt_while(const ast::stmt_while::ptr& stmt);
|
||||||
|
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt);
|
||||||
|
void process_stmt_for(const ast::stmt_for::ptr& stmt);
|
||||||
|
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt);
|
||||||
|
void process_stmt_switch(const ast::stmt_switch::ptr& stmt);
|
||||||
|
void process_expr(const ast::expr& expr);
|
||||||
|
void process_expr_parameters(const ast::expr_parameters::ptr& expr);
|
||||||
|
void variable_register(const std::string& name);
|
||||||
|
auto variable_access(const ast::expr_identifier::ptr& name) -> std::string;
|
||||||
|
//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;
|
||||||
|
|
||||||
|
|
||||||
|
utils::byte_buffer::ptr output_;
|
||||||
|
void print_function(const function::ptr& func);
|
||||||
|
void print_instruction(const instruction::ptr& inst);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
2657
src/t6/xsk/decompiler.cpp
Normal file
2657
src/t6/xsk/decompiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
98
src/t6/xsk/decompiler.hpp
Normal file
98
src/t6/xsk/decompiler.hpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// 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::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
class decompiler : public arc::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<std::string> locals_;
|
||||||
|
std::vector<block> blocks_;
|
||||||
|
bool in_waittill_;
|
||||||
|
bool retbool_;
|
||||||
|
int retnum_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto output() -> std::vector<std::uint8_t>;
|
||||||
|
void decompile(const std::string& file, const assembly::ptr& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void decompile_function(const function::ptr& func);
|
||||||
|
void decompile_instruction(const instruction::ptr& inst, bool last);
|
||||||
|
void decompile_expressions(const instruction::ptr& inst);
|
||||||
|
void decompile_statements(const ast::stmt_list::ptr& stmt);
|
||||||
|
void decompile_infinites(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_devblocks(const ast::stmt_list::ptr& stmt);
|
||||||
|
void decompile_if(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_ifelse(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_infinite(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_loop(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_while(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_dowhile(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_for(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_foreach(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end);
|
||||||
|
void decompile_switch(const ast::stmt_list::ptr& stmt, std::uint32_t begin);
|
||||||
|
auto find_location_reference(const ast::stmt_list::ptr& stmt, std::uint32_t begin, std::uint32_t end, const std::string& location) -> bool;
|
||||||
|
auto find_location_index(const ast::stmt_list::ptr& stmt, const std::string& location) -> std::uint32_t;
|
||||||
|
auto last_location_index(const ast::stmt_list::ptr& stmt, std::uint32_t index) -> bool;
|
||||||
|
auto lvalues_match(const ast::stmt_assign::ptr& stmt1, const ast::stmt_assign::ptr& stmt2) -> bool;
|
||||||
|
void process_thread(const ast::decl_thread::ptr& thread);
|
||||||
|
void process_stmt(const ast::stmt& stmt);
|
||||||
|
void process_stmt_list(const ast::stmt_list::ptr& stmt);
|
||||||
|
void process_stmt_dev(const ast::stmt_dev::ptr& stmt);
|
||||||
|
void process_stmt_expr(const ast::stmt_expr::ptr& stmt);
|
||||||
|
void process_stmt_call(const ast::stmt_call::ptr& stmt);
|
||||||
|
void process_stmt_assign(const ast::stmt_assign::ptr& stmt);
|
||||||
|
void process_stmt_endon(const ast::stmt_endon::ptr& stmt);
|
||||||
|
void process_stmt_notify(const ast::stmt_notify::ptr& stmt);
|
||||||
|
void process_stmt_wait(const ast::stmt_wait::ptr& stmt);
|
||||||
|
void process_stmt_waittill(const ast::stmt_waittill::ptr& stmt);
|
||||||
|
void process_stmt_waittillmatch(const ast::stmt_waittillmatch::ptr& stmt);
|
||||||
|
void process_stmt_if(const ast::stmt_if::ptr& stmt);
|
||||||
|
void process_stmt_ifelse(const ast::stmt_ifelse::ptr& stmt);
|
||||||
|
void process_stmt_while(const ast::stmt_while::ptr& stmt);
|
||||||
|
void process_stmt_dowhile(const ast::stmt_dowhile::ptr& stmt);
|
||||||
|
void process_stmt_for(const ast::stmt_for::ptr& stmt);
|
||||||
|
void process_stmt_foreach(const ast::stmt_foreach::ptr& stmt);
|
||||||
|
void process_stmt_switch(const ast::stmt_switch::ptr& stmt);
|
||||||
|
void process_stmt_cases(const ast::stmt_list::ptr& stmt);
|
||||||
|
void process_stmt_return(const ast::stmt_return::ptr& stmt);
|
||||||
|
void process_expr(const ast::expr& expr);
|
||||||
|
void process_expr_assign(ast::expr_assign::ptr& expr);
|
||||||
|
void process_expr_increment(const ast::expr_increment::ptr& expr);
|
||||||
|
void process_expr_decrement(const ast::expr_decrement::ptr& expr);
|
||||||
|
void process_expr_ternary(const ast::expr_ternary::ptr& expr);
|
||||||
|
void process_expr_binary(const ast::expr_binary::ptr& expr);
|
||||||
|
void process_expr_and(const ast::expr_and::ptr& expr);
|
||||||
|
void process_expr_or(const ast::expr_or::ptr& expr);
|
||||||
|
void process_expr_complement(const ast::expr_complement::ptr& expr);
|
||||||
|
void process_expr_not(const ast::expr_not::ptr& expr);
|
||||||
|
void process_expr_call(const ast::expr_call::ptr& expr);
|
||||||
|
void process_expr_method(const ast::expr_method::ptr& expr);
|
||||||
|
void process_expr_call_pointer(const ast::expr_pointer::ptr& expr);
|
||||||
|
void process_expr_call_function(const ast::expr_function::ptr& expr);
|
||||||
|
void process_expr_method_pointer(const ast::expr_pointer::ptr& expr, ast::expr& obj);
|
||||||
|
void process_expr_method_function(const ast::expr_function::ptr& expr, ast::expr& obj);
|
||||||
|
void process_expr_arguments(const ast::expr_arguments::ptr& expr);
|
||||||
|
void process_expr_size(const ast::expr_size::ptr& expr);
|
||||||
|
void process_array_variable(const ast::expr_array::ptr& expr);
|
||||||
|
void process_field_variable(const ast::expr_field::ptr& expr);
|
||||||
|
void process_expr_vector(const ast::expr_vector::ptr& vec);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
644
src/t6/xsk/disassembler.cpp
Normal file
644
src/t6/xsk/disassembler.cpp
Normal file
@ -0,0 +1,644 @@
|
|||||||
|
// 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 "t6.hpp"
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
auto disassembler::output() -> assembly::ptr
|
||||||
|
{
|
||||||
|
return std::move(assembly_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto disassembler::output_data() -> std::vector<std::uint8_t>
|
||||||
|
{
|
||||||
|
output_ = std::make_unique<utils::byte_buffer>(0x100000);
|
||||||
|
|
||||||
|
output_->write_string("// T6 GSC ASSEMBLY\n");
|
||||||
|
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
|
||||||
|
|
||||||
|
for (const auto& func : assembly_->functions)
|
||||||
|
{
|
||||||
|
this->print_function(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> output;
|
||||||
|
|
||||||
|
output.resize(output_->pos());
|
||||||
|
memcpy(output.data(), output_->buffer().data(), output.size());
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble(const std::string& file, std::vector<std::uint8_t>& data)
|
||||||
|
{
|
||||||
|
filename_ = file;
|
||||||
|
script_ = std::make_unique<utils::byte_buffer>(data);
|
||||||
|
assembly_ = std::make_unique<assembly>();
|
||||||
|
|
||||||
|
std::memset(&header_, 0 ,sizeof(header_));
|
||||||
|
exports_.clear();
|
||||||
|
imports_.clear();
|
||||||
|
strings_.clear();
|
||||||
|
animtrees_.clear();
|
||||||
|
stringlist_.clear();
|
||||||
|
string_refs_.clear();
|
||||||
|
anim_refs_.clear();
|
||||||
|
import_refs_.clear();
|
||||||
|
labels_.clear();
|
||||||
|
|
||||||
|
// header
|
||||||
|
header_.magic = script_->read<std::uint64_t>();
|
||||||
|
|
||||||
|
if (header_.magic != magic)
|
||||||
|
{
|
||||||
|
throw error("invalid binary gsc file '" + filename_ + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
header_.source_crc = script_->read<std::uint32_t>();
|
||||||
|
header_.include_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.animtree_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.cseg_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.stringtablefixup_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.exports_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.imports_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.fixup_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.profile_offset = script_->read<std::uint32_t>();
|
||||||
|
header_.cseg_size = script_->read<std::uint32_t>();
|
||||||
|
header_.name = script_->read<std::uint16_t>();
|
||||||
|
header_.stringtablefixup_count = script_->read<std::uint16_t>();
|
||||||
|
header_.exports_count = script_->read<std::uint16_t>();
|
||||||
|
header_.imports_count = script_->read<std::uint16_t>();
|
||||||
|
header_.fixup_count = script_->read<std::uint16_t>();
|
||||||
|
header_.profile_count = script_->read<std::uint16_t>();
|
||||||
|
header_.include_count = script_->read<std::uint8_t>();
|
||||||
|
header_.animtree_count = script_->read<std::uint8_t>();
|
||||||
|
header_.flags = script_->read<std::uint8_t>();
|
||||||
|
|
||||||
|
// string list
|
||||||
|
script_->pos(64);
|
||||||
|
|
||||||
|
while (script_->pos() < header_.include_offset)
|
||||||
|
{
|
||||||
|
std::uint16_t pos = script_->pos();
|
||||||
|
stringlist_.insert({ pos, script_->read_c_string() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// include list
|
||||||
|
script_->pos(header_.include_offset);
|
||||||
|
|
||||||
|
for (auto i = 0; i < header_.include_count; i++)
|
||||||
|
{
|
||||||
|
assembly_->includes.push_back(stringlist_.at(script_->read<std::uint32_t>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// animtree list
|
||||||
|
script_->pos(header_.animtree_offset);
|
||||||
|
|
||||||
|
for (auto i = 0; i < header_.animtree_count; i++)
|
||||||
|
{
|
||||||
|
auto entry = std::make_shared<animtree_ref>();
|
||||||
|
entry->name = stringlist_.at(script_->read<std::uint16_t>());
|
||||||
|
auto ref_count = script_->read<std::uint16_t>();
|
||||||
|
auto anim_count = script_->read<std::uint16_t>();
|
||||||
|
script_->seek(2);
|
||||||
|
|
||||||
|
for (auto j = 0; j < ref_count; j++)
|
||||||
|
{
|
||||||
|
entry->refs.push_back(script_->read<std::uint32_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto k = 0; k < anim_count; k++)
|
||||||
|
{
|
||||||
|
auto name = stringlist_.at(script_->read<std::uint32_t>());
|
||||||
|
auto ref = script_->read<std::uint32_t>();
|
||||||
|
entry->anims.push_back({ name, ref });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& anim : entry->anims)
|
||||||
|
{
|
||||||
|
anim_refs_.insert({ anim.ref, entry });
|
||||||
|
}
|
||||||
|
|
||||||
|
animtrees_.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringtable list
|
||||||
|
script_->pos(header_.stringtablefixup_offset);
|
||||||
|
|
||||||
|
for (auto i = 0; i < header_.stringtablefixup_count; i++)
|
||||||
|
{
|
||||||
|
auto entry = std::make_shared<string_ref>();
|
||||||
|
entry->name = stringlist_.at(script_->read<std::uint16_t>());
|
||||||
|
auto ref_count = script_->read<std::uint8_t>();
|
||||||
|
entry->type = script_->read<std::uint8_t>();
|
||||||
|
|
||||||
|
for (auto j = 0; j < ref_count; j++)
|
||||||
|
{
|
||||||
|
auto ref = script_->read<std::uint32_t>();
|
||||||
|
entry->refs.push_back(ref);
|
||||||
|
string_refs_.insert({ ref, entry });
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// import list
|
||||||
|
script_->pos(header_.imports_offset);
|
||||||
|
|
||||||
|
for (auto i = 0; i < header_.imports_count; i++)
|
||||||
|
{
|
||||||
|
auto entry = std::make_shared<import_ref>();
|
||||||
|
entry->name = stringlist_.at(script_->read<std::uint16_t>());
|
||||||
|
entry->space = stringlist_.at(script_->read<std::uint16_t>());
|
||||||
|
auto ref_count = script_->read<std::uint16_t>();
|
||||||
|
entry->params = script_->read<std::uint8_t>();
|
||||||
|
entry->flags = script_->read<std::uint8_t>();
|
||||||
|
|
||||||
|
for (auto j = 0; j < ref_count; j++)
|
||||||
|
{
|
||||||
|
auto ref = script_->read<std::uint32_t>();
|
||||||
|
entry->refs.push_back(ref);
|
||||||
|
import_refs_.insert({ ref, entry });
|
||||||
|
}
|
||||||
|
|
||||||
|
imports_.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// export list
|
||||||
|
script_->pos(header_.exports_offset);
|
||||||
|
|
||||||
|
for (auto i = 0; i < header_.exports_count; i++)
|
||||||
|
{
|
||||||
|
auto entry = std::make_shared<export_ref>();
|
||||||
|
entry->checksum = script_->read<std::uint32_t>();
|
||||||
|
entry->offset = script_->read<std::uint32_t>();
|
||||||
|
entry->name = stringlist_.at(script_->read<std::uint16_t>());
|
||||||
|
entry->space = "";
|
||||||
|
entry->params = script_->read<std::uint8_t>();
|
||||||
|
entry->flags = script_->read<std::uint8_t>();
|
||||||
|
exports_.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < exports_.size(); i++)
|
||||||
|
{
|
||||||
|
auto& entry = exports_[i];
|
||||||
|
auto size = 0;
|
||||||
|
|
||||||
|
if (i < exports_.size() - 1)
|
||||||
|
{
|
||||||
|
entry->size = (exports_[i+1]->offset - entry->offset) - 4;
|
||||||
|
|
||||||
|
auto end_pos = entry->offset + entry->size;
|
||||||
|
|
||||||
|
for (auto j = 1; j < 4; j++)
|
||||||
|
{
|
||||||
|
script_->pos(end_pos - j);
|
||||||
|
auto op = script_->read<std::uint8_t>();
|
||||||
|
|
||||||
|
if (op == '\x00' || op == '\x01')
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
entry->size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry->size = (header_.cseg_offset + header_.cseg_size) - entry->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_->pos(entry->offset);
|
||||||
|
|
||||||
|
assembly_->functions.push_back(std::make_unique<function>());
|
||||||
|
auto& func = assembly_->functions.back();
|
||||||
|
func->index = static_cast<std::uint32_t>(script_->pos());
|
||||||
|
func->size = entry->size;
|
||||||
|
func->params = entry->params;
|
||||||
|
func->flags = entry->flags;
|
||||||
|
func->name = entry->name;
|
||||||
|
|
||||||
|
this->disassemble_function(func);
|
||||||
|
|
||||||
|
func->labels = labels_;
|
||||||
|
labels_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixup list ...
|
||||||
|
// profile list ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_function(const function::ptr& func)
|
||||||
|
{
|
||||||
|
auto size = func->size;
|
||||||
|
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
func->instructions.push_back(std::make_unique<instruction>());
|
||||||
|
auto& inst = func->instructions.back();
|
||||||
|
inst->index = static_cast<std::uint32_t>(script_->pos());
|
||||||
|
inst->opcode = script_->read<std::uint8_t>();
|
||||||
|
inst->size = opcode_size(inst->opcode);
|
||||||
|
|
||||||
|
this->disassemble_instruction(inst);
|
||||||
|
size -= inst->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_instruction(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
switch (opcode(inst->opcode))
|
||||||
|
{
|
||||||
|
case opcode::OP_End:
|
||||||
|
case opcode::OP_Return:
|
||||||
|
case opcode::OP_GetUndefined:
|
||||||
|
case opcode::OP_GetZero:
|
||||||
|
case opcode::OP_GetLevelObject:
|
||||||
|
case opcode::OP_GetAnimObject:
|
||||||
|
case opcode::OP_GetSelf:
|
||||||
|
case opcode::OP_GetLevel:
|
||||||
|
case opcode::OP_GetGame:
|
||||||
|
case opcode::OP_GetAnim:
|
||||||
|
case opcode::OP_GetGameRef:
|
||||||
|
case opcode::OP_CreateLocalVariable:
|
||||||
|
case opcode::OP_RemoveLocalVariables:
|
||||||
|
case opcode::OP_EvalArray:
|
||||||
|
case opcode::OP_EvalLocalArrayRefCached:
|
||||||
|
case opcode::OP_EvalArrayRef:
|
||||||
|
case opcode::OP_ClearArray:
|
||||||
|
case opcode::OP_EmptyArray:
|
||||||
|
case opcode::OP_GetSelfObject:
|
||||||
|
case opcode::OP_SafeSetVariableFieldCached:
|
||||||
|
case opcode::OP_ClearParams:
|
||||||
|
case opcode::OP_CheckClearParams:
|
||||||
|
case opcode::OP_SetVariableField:
|
||||||
|
case opcode::OP_Wait:
|
||||||
|
case opcode::OP_WaitTillFrameEnd:
|
||||||
|
case opcode::OP_PreScriptCall:
|
||||||
|
case opcode::OP_DecTop:
|
||||||
|
case opcode::OP_CastFieldObject:
|
||||||
|
case opcode::OP_CastBool:
|
||||||
|
case opcode::OP_BoolNot:
|
||||||
|
case opcode::OP_BoolComplement:
|
||||||
|
case opcode::OP_Inc:
|
||||||
|
case opcode::OP_Dec:
|
||||||
|
case opcode::OP_Bit_Or:
|
||||||
|
case opcode::OP_Bit_Xor:
|
||||||
|
case opcode::OP_Bit_And:
|
||||||
|
case opcode::OP_Equal:
|
||||||
|
case opcode::OP_NotEqual:
|
||||||
|
case opcode::OP_LessThan:
|
||||||
|
case opcode::OP_GreaterThan:
|
||||||
|
case opcode::OP_LessThanOrEqualTo:
|
||||||
|
case opcode::OP_GreaterThanOrEqualTo:
|
||||||
|
case opcode::OP_ShiftLeft:
|
||||||
|
case opcode::OP_ShiftRight:
|
||||||
|
case opcode::OP_Plus:
|
||||||
|
case opcode::OP_Minus:
|
||||||
|
case opcode::OP_Multiply:
|
||||||
|
case opcode::OP_Divide:
|
||||||
|
case opcode::OP_Modulus:
|
||||||
|
case opcode::OP_SizeOf:
|
||||||
|
case opcode::OP_WaitTill:
|
||||||
|
case opcode::OP_Notify:
|
||||||
|
case opcode::OP_EndOn:
|
||||||
|
case opcode::OP_VoidCodePos:
|
||||||
|
case opcode::OP_Vector:
|
||||||
|
case opcode::OP_RealWait:
|
||||||
|
case opcode::OP_IsDefined:
|
||||||
|
case opcode::OP_VectorScale:
|
||||||
|
case opcode::OP_AnglesToUp:
|
||||||
|
case opcode::OP_AnglesToRight:
|
||||||
|
case opcode::OP_AnglesToForward:
|
||||||
|
case opcode::OP_AngleClamp180:
|
||||||
|
case opcode::OP_VectorToAngles:
|
||||||
|
case opcode::OP_Abs:
|
||||||
|
case opcode::OP_GetTime:
|
||||||
|
case opcode::OP_GetDvar:
|
||||||
|
case opcode::OP_GetDvarInt:
|
||||||
|
case opcode::OP_GetDvarFloat:
|
||||||
|
case opcode::OP_GetDvarVector:
|
||||||
|
case opcode::OP_GetDvarColorRed:
|
||||||
|
case opcode::OP_GetDvarColorGreen:
|
||||||
|
case opcode::OP_GetDvarColorBlue:
|
||||||
|
case opcode::OP_GetDvarColorAlpha:
|
||||||
|
case opcode::OP_FirstArrayKey:
|
||||||
|
case opcode::OP_NextArrayKey:
|
||||||
|
case opcode::OP_ProfileStart:
|
||||||
|
case opcode::OP_ProfileStop:
|
||||||
|
case opcode::OP_SafeDecTop:
|
||||||
|
case opcode::OP_Nop:
|
||||||
|
case opcode::OP_Abort:
|
||||||
|
case opcode::OP_Object:
|
||||||
|
case opcode::OP_ThreadObject:
|
||||||
|
case opcode::OP_EvalLocalVariable:
|
||||||
|
case opcode::OP_EvalLocalVariableRef:
|
||||||
|
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->size += script_->align(2);
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::uint16_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetInteger:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::int32_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFloat:
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
auto val = script_->read<float>();
|
||||||
|
inst->data.push_back(utils::string::va("%g%s", val, val == int(val) ? ".0" : ""));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetVector:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
|
||||||
|
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
|
||||||
|
inst->data.push_back(utils::string::va("%g", script_->read<float>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetString:
|
||||||
|
case opcode::OP_GetIString:
|
||||||
|
disassemble_string(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetAnimation:
|
||||||
|
disassemble_animation(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_WaitTillMatch:
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_VectorConstant:
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetHash:
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
inst->data.push_back(resolver::dvar_name(script_->read<std::uint32_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_SafeCreateLocalVariables:
|
||||||
|
disassemble_localvars(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalLocalVariableCached:
|
||||||
|
case opcode::OP_SafeSetWaittillVariableFieldCached:
|
||||||
|
case opcode::OP_EvalLocalVariableRefCached:
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_EvalFieldVariable:
|
||||||
|
case opcode::OP_EvalFieldVariableRef:
|
||||||
|
case opcode::OP_ClearFieldVariable:
|
||||||
|
disassemble_string(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_ScriptFunctionCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodCallPointer:
|
||||||
|
case opcode::OP_ScriptThreadCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodThreadCallPointer:
|
||||||
|
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||||
|
break;
|
||||||
|
case opcode::OP_GetFunction:
|
||||||
|
disassemble_import(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_CallBuiltin:
|
||||||
|
case opcode::OP_CallBuiltinMethod:
|
||||||
|
case opcode::OP_ScriptFunctionCall:
|
||||||
|
case opcode::OP_ScriptMethodCall:
|
||||||
|
case opcode::OP_ScriptThreadCall:
|
||||||
|
case opcode::OP_ScriptMethodThreadCall:
|
||||||
|
script_->seek(1);
|
||||||
|
disassemble_import(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_JumpOnFalse:
|
||||||
|
case opcode::OP_JumpOnTrue:
|
||||||
|
case opcode::OP_JumpOnFalseExpr:
|
||||||
|
case opcode::OP_JumpOnTrueExpr:
|
||||||
|
case opcode::OP_Jump:
|
||||||
|
case opcode::OP_JumpBack:
|
||||||
|
disassemble_jump(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_Switch:
|
||||||
|
disassemble_switch(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_EndSwitch:
|
||||||
|
disassemble_end_switch(inst);
|
||||||
|
break;
|
||||||
|
case opcode::OP_DevblockBegin:
|
||||||
|
disassemble_devblock(inst);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw disasm_error(utils::string::va("Unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_string(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
|
||||||
|
const auto& entry = string_refs_.at(script_->pos());
|
||||||
|
|
||||||
|
inst->data.push_back(entry->type ? entry->name : utils::string::to_literal(entry->name));
|
||||||
|
script_->seek(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_animation(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
|
||||||
|
const auto ref = script_->pos();
|
||||||
|
const auto& entry = anim_refs_.at(ref);
|
||||||
|
|
||||||
|
inst->data.push_back(entry->name);
|
||||||
|
|
||||||
|
for (const auto& anim : entry->anims)
|
||||||
|
{
|
||||||
|
if (anim.ref == ref)
|
||||||
|
{
|
||||||
|
inst->data.push_back(anim.name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script_->seek(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_localvars(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
const auto count = script_->read<std::uint8_t>();
|
||||||
|
|
||||||
|
for (auto i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
disassemble_string(inst);
|
||||||
|
inst->size += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_import(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
script_->seek(4);
|
||||||
|
|
||||||
|
const auto& entry = import_refs_.at(inst->index);
|
||||||
|
|
||||||
|
inst->data.push_back(entry->space);
|
||||||
|
inst->data.push_back(entry->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_jump(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
|
||||||
|
const auto addr = inst->index + inst->size + script_->read<std::int16_t>();
|
||||||
|
const auto label = utils::string::va("loc_%X", addr);
|
||||||
|
|
||||||
|
inst->data.push_back(label);
|
||||||
|
labels_.insert({ addr, label });
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_switch(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
|
||||||
|
const auto addr = script_->read<std::int32_t>() + script_->pos();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(4);
|
||||||
|
|
||||||
|
const auto& itr = labels_.find(script_->pos());
|
||||||
|
|
||||||
|
if (itr != labels_.end())
|
||||||
|
{
|
||||||
|
for (const auto& entry : assembly_->functions.back()->instructions)
|
||||||
|
{
|
||||||
|
if (opcode(entry->opcode) == opcode::OP_Switch)
|
||||||
|
{
|
||||||
|
if (entry->data[0] == itr->second)
|
||||||
|
{
|
||||||
|
labels_.erase(script_->pos());
|
||||||
|
|
||||||
|
const auto label = utils::string::va("loc_%X", inst->index);
|
||||||
|
const auto& itr2 = labels_.find(inst->index);
|
||||||
|
|
||||||
|
if (itr2 == labels_.end())
|
||||||
|
{
|
||||||
|
labels_.insert({ inst->index, label });
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->data[0] = label;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto count = script_->read<std::uint32_t>();
|
||||||
|
inst->data.push_back(utils::string::va("%i", count));
|
||||||
|
|
||||||
|
for (auto i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
const auto value = script_->read<std::uint32_t>();
|
||||||
|
|
||||||
|
if (value < 0x40000 && value > 0)
|
||||||
|
{
|
||||||
|
inst->data.push_back("case");
|
||||||
|
const auto& entry = string_refs_.at(script_->pos() - 2);
|
||||||
|
inst->data.push_back(utils::string::quote(entry->name, false));
|
||||||
|
}
|
||||||
|
else if (value == 0)
|
||||||
|
{
|
||||||
|
inst->data.push_back("default");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inst->data.push_back("case");
|
||||||
|
inst->data.push_back(utils::string::va("%i", (value - 0x800000) & 0xFFFFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto addr = script_->read<std::int32_t>() + script_->pos();
|
||||||
|
const auto label = utils::string::va("loc_%X", addr);
|
||||||
|
|
||||||
|
inst->data.push_back(label);
|
||||||
|
labels_.insert({ addr, label });
|
||||||
|
|
||||||
|
inst->size += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::disassemble_devblock(const instruction::ptr& inst)
|
||||||
|
{
|
||||||
|
inst->size += script_->align(2);
|
||||||
|
|
||||||
|
const auto addr = inst->index + inst->size + script_->read<std::int16_t>();
|
||||||
|
const auto label = utils::string::va("loc_%X", addr);
|
||||||
|
|
||||||
|
inst->data.push_back(label);
|
||||||
|
labels_.insert({ addr, label });
|
||||||
|
}
|
||||||
|
|
||||||
|
void disassembler::print_function(const function::ptr& func)
|
||||||
|
{
|
||||||
|
output_->write_string("\n");
|
||||||
|
output_->write_string(utils::string::va("sub_%s %i %i\n", func->name.data(), func->params, func->flags));
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->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 (opcode(inst->opcode))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
output_->write_string("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (const auto& d : inst->data)
|
||||||
|
{
|
||||||
|
output_->write_string(utils::string::va(" %s", d.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
output_->write_string("\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
48
src/t6/xsk/disassembler.hpp
Normal file
48
src/t6/xsk/disassembler.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
class disassembler : public arc::disassembler
|
||||||
|
{
|
||||||
|
std::string filename_;
|
||||||
|
utils::byte_buffer::ptr script_;
|
||||||
|
utils::byte_buffer::ptr output_;
|
||||||
|
assembly::ptr assembly_;
|
||||||
|
std::vector<export_ref::ptr> exports_;
|
||||||
|
std::vector<import_ref::ptr> imports_;
|
||||||
|
std::vector<string_ref::ptr> strings_;
|
||||||
|
std::vector<animtree_ref::ptr> animtrees_;
|
||||||
|
std::map<std::uint32_t, std::string> stringlist_;
|
||||||
|
std::map<std::uint32_t, import_ref::ptr> import_refs_;
|
||||||
|
std::map<std::uint32_t, string_ref::ptr> string_refs_;
|
||||||
|
std::map<std::uint32_t, animtree_ref::ptr> anim_refs_;
|
||||||
|
std::unordered_map<std::uint32_t, std::string> labels_;
|
||||||
|
header header_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto output() -> assembly::ptr;
|
||||||
|
auto output_data() -> std::vector<std::uint8_t>;
|
||||||
|
void disassemble(const std::string& file, std::vector<std::uint8_t>& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void disassemble_function(const function::ptr& func);
|
||||||
|
void disassemble_instruction(const instruction::ptr& inst);
|
||||||
|
void disassemble_string(const instruction::ptr& inst);
|
||||||
|
void disassemble_animation(const instruction::ptr& inst);
|
||||||
|
void disassemble_localvars(const instruction::ptr& inst);
|
||||||
|
void disassemble_import(const instruction::ptr& inst);
|
||||||
|
void disassemble_jump(const instruction::ptr& inst);
|
||||||
|
void disassemble_switch(const instruction::ptr& inst);
|
||||||
|
void disassemble_end_switch(const instruction::ptr& inst);
|
||||||
|
void disassemble_devblock(const instruction::ptr& inst);
|
||||||
|
void print_function(const function::ptr& func);
|
||||||
|
void print_instruction(const instruction::ptr& inst);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
890
src/t6/xsk/lexer.cpp
Normal file
890
src/t6/xsk/lexer.cpp
Normal file
@ -0,0 +1,890 @@
|
|||||||
|
// 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 "t6.hpp"
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "lexer.hpp"
|
||||||
|
|
||||||
|
xsk::arc::t6::parser::symbol_type T6lex(xsk::arc::t6::lexer& lexer)
|
||||||
|
{
|
||||||
|
return lexer.lex();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
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 },
|
||||||
|
{ "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 },
|
||||||
|
{ "true", parser::token::TRUE },
|
||||||
|
{ "false", parser::token::FALSE },
|
||||||
|
{ "undefined", parser::token::UNDEFINED },
|
||||||
|
{ "game", parser::token::GAME },
|
||||||
|
{ "self", parser::token::SELF },
|
||||||
|
{ "anim", parser::token::ANIM },
|
||||||
|
{ "level", parser::token::LEVEL },
|
||||||
|
{ "isdefined", parser::token::ISDEFINED },
|
||||||
|
{ "vectorscale", parser::token::VECTORSCALE },
|
||||||
|
{ "anglestoup", parser::token::ANGLESTOUP },
|
||||||
|
{ "anglestoright", parser::token::ANGLESTORIGHT },
|
||||||
|
{ "anglestoforward", parser::token::ANGLESTOFORWARD },
|
||||||
|
{ "angleclamp180", parser::token::ANGLECLAMP180 },
|
||||||
|
{ "vectortoangles", parser::token::VECTORTOANGLES },
|
||||||
|
{ "abs", parser::token::ABS },
|
||||||
|
{ "gettime", parser::token::GETTIME },
|
||||||
|
{ "getdvar", parser::token::GETDVAR },
|
||||||
|
{ "getdvarint", parser::token::GETDVARINT },
|
||||||
|
{ "getdvarfloat", parser::token::GETDVARFLOAT },
|
||||||
|
{ "getdvarvector", parser::token::GETDVARVECTOR },
|
||||||
|
{ "getdvarcolorred", parser::token::GETDVARCOLORRED },
|
||||||
|
{ "getdvarcolorgreen", parser::token::GETDVARCOLORGREEN },
|
||||||
|
{ "getdvarcolorblue", parser::token::GETDVARCOLORBLUE },
|
||||||
|
{ "getdvarcoloralpha", parser::token::GETDVARCOLORALPHA },
|
||||||
|
{ "getfirstarraykey", parser::token::GETFIRSTARRAYKEY },
|
||||||
|
{ "getnextarraykey", parser::token::GETNEXTARRAYKEY },
|
||||||
|
}};
|
||||||
|
|
||||||
|
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() : state(reader::end), buffer_pos(0), bytes_remaining(0),
|
||||||
|
last_byte(0), current_byte(0) { }
|
||||||
|
|
||||||
|
void reader::init(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
if (data && size)
|
||||||
|
{
|
||||||
|
state = reader::ok;
|
||||||
|
buffer_pos = data;
|
||||||
|
bytes_remaining = 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) : indev_(false), clean_(true), loc_(location(&name)),
|
||||||
|
mode_(mode), header_top_(0), locs_(std::stack<location>()), readers_(std::stack<reader>())
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
loc_.step();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto& state = reader_.state;
|
||||||
|
auto& last = reader_.last_byte;
|
||||||
|
auto& curr = reader_.current_byte;
|
||||||
|
auto path = false;
|
||||||
|
|
||||||
|
if (state == reader::end)
|
||||||
|
{
|
||||||
|
if (indev_)
|
||||||
|
throw comp_error(loc_, "unmatched devblock start ('/#')");
|
||||||
|
|
||||||
|
if (header_top_ > 0)
|
||||||
|
pop_header();
|
||||||
|
else
|
||||||
|
return parser::make_T6EOF(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 != '=')
|
||||||
|
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 == '/')
|
||||||
|
{
|
||||||
|
reader_.advance();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_.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 == '/')
|
||||||
|
{
|
||||||
|
reader_.advance();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (last == '/')
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (state == reader::end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (last == '\\' && (curr == '\r' || curr == '\n'))
|
||||||
|
{
|
||||||
|
reader_.advance();
|
||||||
|
|
||||||
|
if (state == reader::end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (last == '\r')
|
||||||
|
{
|
||||||
|
if (curr != '\n')
|
||||||
|
throw comp_error(loc_, "invalid token ('\')");
|
||||||
|
|
||||||
|
reader_.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
loc_.lines();
|
||||||
|
loc_.step();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr == '\n')
|
||||||
|
break;
|
||||||
|
|
||||||
|
reader_.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case '#':
|
||||||
|
if (curr == '/')
|
||||||
|
{
|
||||||
|
if (!indev_)
|
||||||
|
throw comp_error(loc_, "unmatched devblock end ('#/')");
|
||||||
|
|
||||||
|
indev_ = false;
|
||||||
|
advance();
|
||||||
|
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 '.':
|
||||||
|
advance();
|
||||||
|
|
||||||
|
if (state == reader::end)
|
||||||
|
throw comp_error(loc_, "unterminated field ('.')");
|
||||||
|
|
||||||
|
state_ = state::field;
|
||||||
|
goto lex_name_or_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:
|
||||||
|
lex_name_or_number:
|
||||||
|
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::field)
|
||||||
|
{
|
||||||
|
if (path)
|
||||||
|
throw comp_error(loc_, "invalid field token '\\'");
|
||||||
|
|
||||||
|
if (std::string_view(buffer_.data, buffer_.length) == "size")
|
||||||
|
{
|
||||||
|
return parser::make_SIZE(loc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser::make_FIELD(std::string(buffer_.data, buffer_.length), loc_);
|
||||||
|
}
|
||||||
|
else if (state_ == state::preprocessor)
|
||||||
|
{
|
||||||
|
if (path)
|
||||||
|
throw comp_error(loc_, "invalid preprocessor directive");
|
||||||
|
|
||||||
|
auto token = parser::token::T6UNDEF;
|
||||||
|
|
||||||
|
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(token);
|
||||||
|
state_ = state::start;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < buffer_.length; i++)
|
||||||
|
{
|
||||||
|
auto c = buffer_.data[i];
|
||||||
|
|
||||||
|
if (c > 64 && c < 91)
|
||||||
|
buffer_.data[i] = c + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::string_view(buffer_.data, buffer_.length).starts_with("_hash_"))
|
||||||
|
{
|
||||||
|
return parser::make_HASH(std::string(&buffer_.data[6], buffer_.length - 6), loc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser::make_IDENTIFIER(resolver::make_token(std::string_view(buffer_.data, buffer_.length)), loc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
lex_number:
|
||||||
|
if (state_ == state::field)
|
||||||
|
buffer_.push('.');
|
||||||
|
|
||||||
|
if (state_ == state::field || last == '.' || last != '0' || (last == '0' && (curr != 'o' && curr != 'b' && curr != 'x')))
|
||||||
|
{
|
||||||
|
buffer_.push(last);
|
||||||
|
|
||||||
|
auto dot = 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 (state_ == state::field && dot || dot > 1 || flt > 1 || flt && buffer_.data[buffer_.length - 1] != 'f')
|
||||||
|
throw comp_error(loc_, "invalid number literal");
|
||||||
|
|
||||||
|
if (state_ == state::field || 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(xsk::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(xsk::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(xsk::utils::string::hex_to_dec(buffer_.data), loc_);
|
||||||
|
}
|
||||||
|
// cant get here!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer::advance()
|
||||||
|
{
|
||||||
|
reader_.advance();
|
||||||
|
|
||||||
|
// dont wrap comment marks '/\/' '/\*' outside strings
|
||||||
|
if (state_ == state::start && reader_.last_byte == '/')
|
||||||
|
return;
|
||||||
|
|
||||||
|
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(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::arc::t6
|
72
src/t6/xsk/lexer.hpp
Normal file
72
src/t6/xsk/lexer.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr size_t max_buf_size = 0x2000;
|
||||||
|
|
||||||
|
struct buffer
|
||||||
|
{
|
||||||
|
char* data;
|
||||||
|
int 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& operator=(const reader& r)
|
||||||
|
{
|
||||||
|
std::memcpy(this, &r, 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, field, 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(parser::token::token_kind_type token);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
5178
src/t6/xsk/parser.cpp
Normal file
5178
src/t6/xsk/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
6277
src/t6/xsk/parser.hpp
Normal file
6277
src/t6/xsk/parser.hpp
Normal file
File diff suppressed because it is too large
Load Diff
3577
src/t6/xsk/resolver.cpp
Normal file
3577
src/t6/xsk/resolver.cpp
Normal file
File diff suppressed because it is too large
Load Diff
23
src/t6/xsk/resolver.hpp
Normal file
23
src/t6/xsk/resolver.hpp
Normal 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
class resolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static auto opcode_id(const std::string& name) -> std::uint8_t;
|
||||||
|
static auto opcode_name(std::uint8_t id) -> std::string;
|
||||||
|
static auto dvar_name(std::uint32_t id) -> std::string;
|
||||||
|
|
||||||
|
static auto make_token(std::string_view str) -> std::string;
|
||||||
|
static auto file_data(const std::string& name) -> std::tuple<const std::string*, char*, size_t>;
|
||||||
|
static void set_reader(std::function<std::vector<std::uint8_t>(const std::string&)> callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
153
src/t6/xsk/t6.cpp
Normal file
153
src/t6/xsk/t6.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// 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 "t6.hpp"
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
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_GetLevelObject:
|
||||||
|
case opcode::OP_GetAnimObject:
|
||||||
|
case opcode::OP_GetSelf:
|
||||||
|
case opcode::OP_GetLevel:
|
||||||
|
case opcode::OP_GetGame:
|
||||||
|
case opcode::OP_GetAnim:
|
||||||
|
case opcode::OP_GetGameRef:
|
||||||
|
case opcode::OP_CreateLocalVariable:
|
||||||
|
case opcode::OP_RemoveLocalVariables:
|
||||||
|
case opcode::OP_EvalArray:
|
||||||
|
case opcode::OP_EvalLocalArrayRefCached:
|
||||||
|
case opcode::OP_EvalArrayRef:
|
||||||
|
case opcode::OP_ClearArray:
|
||||||
|
case opcode::OP_EmptyArray:
|
||||||
|
case opcode::OP_GetSelfObject:
|
||||||
|
case opcode::OP_SafeSetVariableFieldCached:
|
||||||
|
case opcode::OP_ClearParams:
|
||||||
|
case opcode::OP_CheckClearParams:
|
||||||
|
case opcode::OP_SetVariableField:
|
||||||
|
case opcode::OP_Wait:
|
||||||
|
case opcode::OP_WaitTillFrameEnd:
|
||||||
|
case opcode::OP_PreScriptCall:
|
||||||
|
case opcode::OP_DecTop:
|
||||||
|
case opcode::OP_CastFieldObject:
|
||||||
|
case opcode::OP_CastBool:
|
||||||
|
case opcode::OP_BoolNot:
|
||||||
|
case opcode::OP_BoolComplement:
|
||||||
|
case opcode::OP_Inc:
|
||||||
|
case opcode::OP_Dec:
|
||||||
|
case opcode::OP_Bit_Or:
|
||||||
|
case opcode::OP_Bit_Xor:
|
||||||
|
case opcode::OP_Bit_And:
|
||||||
|
case opcode::OP_Equal:
|
||||||
|
case opcode::OP_NotEqual:
|
||||||
|
case opcode::OP_LessThan:
|
||||||
|
case opcode::OP_GreaterThan:
|
||||||
|
case opcode::OP_LessThanOrEqualTo:
|
||||||
|
case opcode::OP_GreaterThanOrEqualTo:
|
||||||
|
case opcode::OP_ShiftLeft:
|
||||||
|
case opcode::OP_ShiftRight:
|
||||||
|
case opcode::OP_Plus:
|
||||||
|
case opcode::OP_Minus:
|
||||||
|
case opcode::OP_Multiply:
|
||||||
|
case opcode::OP_Divide:
|
||||||
|
case opcode::OP_Modulus:
|
||||||
|
case opcode::OP_SizeOf:
|
||||||
|
case opcode::OP_WaitTill:
|
||||||
|
case opcode::OP_Notify:
|
||||||
|
case opcode::OP_EndOn:
|
||||||
|
case opcode::OP_VoidCodePos:
|
||||||
|
case opcode::OP_Vector:
|
||||||
|
case opcode::OP_RealWait:
|
||||||
|
case opcode::OP_IsDefined:
|
||||||
|
case opcode::OP_VectorScale:
|
||||||
|
case opcode::OP_AnglesToUp:
|
||||||
|
case opcode::OP_AnglesToRight:
|
||||||
|
case opcode::OP_AnglesToForward:
|
||||||
|
case opcode::OP_AngleClamp180:
|
||||||
|
case opcode::OP_VectorToAngles:
|
||||||
|
case opcode::OP_Abs:
|
||||||
|
case opcode::OP_GetTime:
|
||||||
|
case opcode::OP_GetDvar:
|
||||||
|
case opcode::OP_GetDvarInt:
|
||||||
|
case opcode::OP_GetDvarFloat:
|
||||||
|
case opcode::OP_GetDvarVector:
|
||||||
|
case opcode::OP_GetDvarColorRed:
|
||||||
|
case opcode::OP_GetDvarColorGreen:
|
||||||
|
case opcode::OP_GetDvarColorBlue:
|
||||||
|
case opcode::OP_GetDvarColorAlpha:
|
||||||
|
case opcode::OP_FirstArrayKey:
|
||||||
|
case opcode::OP_NextArrayKey:
|
||||||
|
case opcode::OP_ProfileStart:
|
||||||
|
case opcode::OP_ProfileStop:
|
||||||
|
case opcode::OP_SafeDecTop:
|
||||||
|
case opcode::OP_Nop:
|
||||||
|
case opcode::OP_Abort:
|
||||||
|
case opcode::OP_Object:
|
||||||
|
case opcode::OP_ThreadObject:
|
||||||
|
case opcode::OP_EvalLocalVariable:
|
||||||
|
case opcode::OP_EvalLocalVariableRef:
|
||||||
|
case opcode::OP_Breakpoint:
|
||||||
|
return 1;
|
||||||
|
case opcode::OP_GetByte:
|
||||||
|
case opcode::OP_GetNegByte:
|
||||||
|
case opcode::OP_SafeCreateLocalVariables:
|
||||||
|
case opcode::OP_EvalLocalVariableCached:
|
||||||
|
case opcode::OP_SafeSetWaittillVariableFieldCached:
|
||||||
|
case opcode::OP_EvalLocalVariableRefCached:
|
||||||
|
case opcode::OP_ScriptFunctionCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodCallPointer:
|
||||||
|
case opcode::OP_ScriptThreadCallPointer:
|
||||||
|
case opcode::OP_ScriptMethodThreadCallPointer:
|
||||||
|
case opcode::OP_WaitTillMatch:
|
||||||
|
case opcode::OP_VectorConstant:
|
||||||
|
return 2;
|
||||||
|
case opcode::OP_GetUnsignedShort:
|
||||||
|
case opcode::OP_GetNegUnsignedShort:
|
||||||
|
case opcode::OP_GetString:
|
||||||
|
case opcode::OP_GetIString:
|
||||||
|
case opcode::OP_EvalFieldVariable:
|
||||||
|
case opcode::OP_EvalFieldVariableRef:
|
||||||
|
case opcode::OP_ClearFieldVariable:
|
||||||
|
case opcode::OP_JumpOnFalse:
|
||||||
|
case opcode::OP_JumpOnTrue:
|
||||||
|
case opcode::OP_JumpOnFalseExpr:
|
||||||
|
case opcode::OP_JumpOnTrueExpr:
|
||||||
|
case opcode::OP_Jump:
|
||||||
|
case opcode::OP_JumpBack:
|
||||||
|
case opcode::OP_DevblockBegin:
|
||||||
|
case opcode::OP_DevblockEnd:
|
||||||
|
return 3;
|
||||||
|
case opcode::OP_GetInteger:
|
||||||
|
case opcode::OP_GetFloat:
|
||||||
|
case opcode::OP_GetAnimation:
|
||||||
|
case opcode::OP_GetFunction:
|
||||||
|
case opcode::OP_Switch:
|
||||||
|
case opcode::OP_EndSwitch:
|
||||||
|
case opcode::OP_GetHash:
|
||||||
|
return 5;
|
||||||
|
case opcode::OP_CallBuiltin:
|
||||||
|
case opcode::OP_CallBuiltinMethod:
|
||||||
|
case opcode::OP_ScriptFunctionCall:
|
||||||
|
case opcode::OP_ScriptMethodCall:
|
||||||
|
case opcode::OP_ScriptThreadCall:
|
||||||
|
case opcode::OP_ScriptMethodThreadCall:
|
||||||
|
return 6;
|
||||||
|
case opcode::OP_GetVector:
|
||||||
|
return 13;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Couldn't resolve instruction size for " + std::to_string(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
154
src/t6/xsk/t6.hpp
Normal file
154
src/t6/xsk/t6.hpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#include "utils/xsk/utils.hpp"
|
||||||
|
|
||||||
|
#include "assembler.hpp"
|
||||||
|
#include "disassembler.hpp"
|
||||||
|
#include "decompiler.hpp"
|
||||||
|
#include "compiler.hpp"
|
||||||
|
#include "resolver.hpp"
|
||||||
|
|
||||||
|
namespace xsk::arc::t6
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr std::uint64_t magic = 0x06000A0D43534780;
|
||||||
|
|
||||||
|
enum class opcode : std::uint8_t
|
||||||
|
{
|
||||||
|
OP_End = 0x0,
|
||||||
|
OP_Return = 0x1,
|
||||||
|
OP_GetUndefined = 0x2,
|
||||||
|
OP_GetZero = 0x3,
|
||||||
|
OP_GetByte = 0x4,
|
||||||
|
OP_GetNegByte = 0x5,
|
||||||
|
OP_GetUnsignedShort = 0x6,
|
||||||
|
OP_GetNegUnsignedShort = 0x7,
|
||||||
|
OP_GetInteger = 0x8,
|
||||||
|
OP_GetFloat = 0x9,
|
||||||
|
OP_GetString = 0xA,
|
||||||
|
OP_GetIString = 0xB,
|
||||||
|
OP_GetVector = 0xC,
|
||||||
|
OP_GetLevelObject = 0xD,
|
||||||
|
OP_GetAnimObject = 0xE,
|
||||||
|
OP_GetSelf = 0xF,
|
||||||
|
OP_GetLevel = 0x10,
|
||||||
|
OP_GetGame = 0x11,
|
||||||
|
OP_GetAnim = 0x12,
|
||||||
|
OP_GetAnimation = 0x13,
|
||||||
|
OP_GetGameRef = 0x14,
|
||||||
|
OP_GetFunction = 0x15,
|
||||||
|
OP_CreateLocalVariable = 0x16,
|
||||||
|
OP_SafeCreateLocalVariables = 0x17,
|
||||||
|
OP_RemoveLocalVariables = 0x18,
|
||||||
|
OP_EvalLocalVariableCached = 0x19,
|
||||||
|
OP_EvalArray = 0x1A,
|
||||||
|
OP_EvalLocalArrayRefCached = 0x1B,
|
||||||
|
OP_EvalArrayRef = 0x1C,
|
||||||
|
OP_ClearArray = 0x1D,
|
||||||
|
OP_EmptyArray = 0x1E,
|
||||||
|
OP_GetSelfObject = 0x1F,
|
||||||
|
OP_EvalFieldVariable = 0x20,
|
||||||
|
OP_EvalFieldVariableRef = 0x21,
|
||||||
|
OP_ClearFieldVariable = 0x22,
|
||||||
|
OP_SafeSetVariableFieldCached = 0x23,
|
||||||
|
OP_SafeSetWaittillVariableFieldCached = 0x24,
|
||||||
|
OP_ClearParams = 0x25,
|
||||||
|
OP_CheckClearParams = 0x26,
|
||||||
|
OP_EvalLocalVariableRefCached = 0x27,
|
||||||
|
OP_SetVariableField = 0x28,
|
||||||
|
OP_CallBuiltin = 0x29,
|
||||||
|
OP_CallBuiltinMethod = 0x2A,
|
||||||
|
OP_Wait = 0x2B,
|
||||||
|
OP_WaitTillFrameEnd = 0x2C,
|
||||||
|
OP_PreScriptCall = 0x2D,
|
||||||
|
OP_ScriptFunctionCall = 0x2E,
|
||||||
|
OP_ScriptFunctionCallPointer = 0x2F,
|
||||||
|
OP_ScriptMethodCall = 0x30,
|
||||||
|
OP_ScriptMethodCallPointer = 0x31,
|
||||||
|
OP_ScriptThreadCall = 0x32,
|
||||||
|
OP_ScriptThreadCallPointer = 0x33,
|
||||||
|
OP_ScriptMethodThreadCall = 0x34,
|
||||||
|
OP_ScriptMethodThreadCallPointer = 0x35,
|
||||||
|
OP_DecTop = 0x36,
|
||||||
|
OP_CastFieldObject = 0x37,
|
||||||
|
OP_CastBool = 0x38,
|
||||||
|
OP_BoolNot = 0x39,
|
||||||
|
OP_BoolComplement = 0x3A,
|
||||||
|
OP_JumpOnFalse = 0x3B,
|
||||||
|
OP_JumpOnTrue = 0x3C,
|
||||||
|
OP_JumpOnFalseExpr = 0x3D,
|
||||||
|
OP_JumpOnTrueExpr = 0x3E,
|
||||||
|
OP_Jump = 0x3F,
|
||||||
|
OP_JumpBack = 0x40,
|
||||||
|
OP_Inc = 0x41,
|
||||||
|
OP_Dec = 0x42,
|
||||||
|
OP_Bit_Or = 0x43,
|
||||||
|
OP_Bit_Xor = 0x44,
|
||||||
|
OP_Bit_And = 0x45,
|
||||||
|
OP_Equal = 0x46,
|
||||||
|
OP_NotEqual = 0x47,
|
||||||
|
OP_LessThan = 0x48,
|
||||||
|
OP_GreaterThan = 0x49,
|
||||||
|
OP_LessThanOrEqualTo = 0x4A,
|
||||||
|
OP_GreaterThanOrEqualTo = 0x4B,
|
||||||
|
OP_ShiftLeft = 0x4C,
|
||||||
|
OP_ShiftRight = 0x4D,
|
||||||
|
OP_Plus = 0x4E,
|
||||||
|
OP_Minus = 0x4F,
|
||||||
|
OP_Multiply = 0x50,
|
||||||
|
OP_Divide = 0x51,
|
||||||
|
OP_Modulus = 0x52,
|
||||||
|
OP_SizeOf = 0x53,
|
||||||
|
OP_WaitTillMatch = 0x54,
|
||||||
|
OP_WaitTill = 0x55,
|
||||||
|
OP_Notify = 0x56,
|
||||||
|
OP_EndOn = 0x57,
|
||||||
|
OP_VoidCodePos = 0x58,
|
||||||
|
OP_Switch = 0x59,
|
||||||
|
OP_EndSwitch = 0x5A,
|
||||||
|
OP_Vector = 0x5B,
|
||||||
|
OP_GetHash = 0x5C,
|
||||||
|
OP_RealWait = 0x5D,
|
||||||
|
OP_VectorConstant = 0x5E,
|
||||||
|
OP_IsDefined = 0x5F,
|
||||||
|
OP_VectorScale = 0x60,
|
||||||
|
OP_AnglesToUp = 0x61,
|
||||||
|
OP_AnglesToRight = 0x62,
|
||||||
|
OP_AnglesToForward = 0x63,
|
||||||
|
OP_AngleClamp180 = 0x64,
|
||||||
|
OP_VectorToAngles = 0x65,
|
||||||
|
OP_Abs = 0x66,
|
||||||
|
OP_GetTime = 0x67,
|
||||||
|
OP_GetDvar = 0x68,
|
||||||
|
OP_GetDvarInt = 0x69,
|
||||||
|
OP_GetDvarFloat = 0x6A,
|
||||||
|
OP_GetDvarVector = 0x6B,
|
||||||
|
OP_GetDvarColorRed = 0x6C,
|
||||||
|
OP_GetDvarColorGreen = 0x6D,
|
||||||
|
OP_GetDvarColorBlue = 0x6E,
|
||||||
|
OP_GetDvarColorAlpha = 0x6F,
|
||||||
|
OP_FirstArrayKey = 0x70,
|
||||||
|
OP_NextArrayKey = 0x71,
|
||||||
|
OP_ProfileStart = 0x72,
|
||||||
|
OP_ProfileStop = 0x73,
|
||||||
|
OP_SafeDecTop = 0x74,
|
||||||
|
OP_Nop = 0x75,
|
||||||
|
OP_Abort = 0x76,
|
||||||
|
OP_Object = 0x77,
|
||||||
|
OP_ThreadObject = 0x78,
|
||||||
|
OP_EvalLocalVariable = 0x79,
|
||||||
|
OP_EvalLocalVariableRef = 0x7A,
|
||||||
|
OP_DevblockBegin = 0x7B,
|
||||||
|
OP_DevblockEnd = 0x7C,
|
||||||
|
OP_Breakpoint = 0x7D,
|
||||||
|
OP_Count = 0x7E,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto opcode_size(std::uint8_t id) -> std::uint32_t;
|
||||||
|
|
||||||
|
} // namespace xsk::arc::t6
|
24
src/utils/xsk/arc/block.hpp
Normal file
24
src/utils/xsk/arc/block.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
struct block
|
||||||
|
{
|
||||||
|
using ptr = std::unique_ptr<block>;
|
||||||
|
|
||||||
|
std::string loc_end;
|
||||||
|
std::string loc_break;
|
||||||
|
std::string loc_continue;
|
||||||
|
abort_t abort;
|
||||||
|
bool is_dev;
|
||||||
|
|
||||||
|
block() : is_dev(false), abort(abort_t::abort_none) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
22
src/utils/xsk/arc/interfaces/assembler.hpp
Normal file
22
src/utils/xsk/arc/interfaces/assembler.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
class assembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ptr = std::unique_ptr<assembler>;
|
||||||
|
|
||||||
|
virtual ~assembler() = default;
|
||||||
|
virtual auto output() -> std::vector<std::uint8_t> = 0;
|
||||||
|
virtual void assemble(const std::string& file, std::vector<std::uint8_t>& data) = 0;
|
||||||
|
virtual void assemble(const std::string& file, assembly::ptr& data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
23
src/utils/xsk/arc/interfaces/compiler.hpp
Normal file
23
src/utils/xsk/arc/interfaces/compiler.hpp
Normal 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace xsk::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
class compiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ptr = std::unique_ptr<compiler>;
|
||||||
|
|
||||||
|
virtual ~compiler() = default;
|
||||||
|
virtual auto output() -> assembly::ptr = 0;
|
||||||
|
virtual auto output_data() -> std::vector<std::uint8_t> = 0;
|
||||||
|
virtual void compile(const std::string& file, std::vector<std::uint8_t>& data) = 0;
|
||||||
|
virtual void read_callback(std::function<std::vector<std::uint8_t>(const std::string&)> func) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
21
src/utils/xsk/arc/interfaces/decompiler.hpp
Normal file
21
src/utils/xsk/arc/interfaces/decompiler.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
class decompiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ptr = std::unique_ptr<decompiler>;
|
||||||
|
|
||||||
|
virtual ~decompiler() = default;
|
||||||
|
virtual auto output() -> std::vector<std::uint8_t> = 0;
|
||||||
|
virtual void decompile(const std::string& file, const assembly::ptr& data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
22
src/utils/xsk/arc/interfaces/disassembler.hpp
Normal file
22
src/utils/xsk/arc/interfaces/disassembler.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
class disassembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ptr = std::unique_ptr<disassembler>;
|
||||||
|
|
||||||
|
virtual ~disassembler() = default;
|
||||||
|
virtual auto output() -> assembly::ptr = 0;
|
||||||
|
virtual auto output_data() -> std::vector<std::uint8_t> = 0;
|
||||||
|
virtual void disassemble(const std::string& file, std::vector<std::uint8_t>& data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
46
src/utils/xsk/arc/interfaces/exception.hpp
Normal file
46
src/utils/xsk/arc/interfaces/exception.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
class error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
error(const std::string& what)
|
||||||
|
: std::runtime_error("[ERROR]: "s + what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class asm_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
asm_error(const std::string& what)
|
||||||
|
: std::runtime_error("[ERROR]:assembler: "s + what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class disasm_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
disasm_error(const std::string& what)
|
||||||
|
: std::runtime_error("[ERROR]:disassembler: "s + what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class comp_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
comp_error(const location& loc, const std::string& what)
|
||||||
|
: std::runtime_error("[ERROR]:compiler:" + loc.print() + ": " + what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class decomp_error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
decomp_error(const std::string& what)
|
||||||
|
: std::runtime_error("[ERROR]:decompiler: "s + what) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
229
src/utils/xsk/arc/location.hpp
Normal file
229
src/utils/xsk/arc/location.hpp
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
// 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::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
/// A point in a source file.
|
||||||
|
class position
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Type for file name.
|
||||||
|
typedef const std::string filename_type;
|
||||||
|
/// Type for line and column numbers.
|
||||||
|
typedef int counter_type;
|
||||||
|
|
||||||
|
/// Construct a position.
|
||||||
|
explicit position(filename_type *f = nullptr, counter_type l = 1, counter_type c = 1)
|
||||||
|
: filename(f), line(l), column(c) {}
|
||||||
|
|
||||||
|
/// Initialization.
|
||||||
|
void initialize(filename_type *fn = nullptr, counter_type l = 1, counter_type c = 1)
|
||||||
|
{
|
||||||
|
filename = fn;
|
||||||
|
line = l;
|
||||||
|
column = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \name Line and Column related manipulators
|
||||||
|
** \{ */
|
||||||
|
/// (line related) Advance to the COUNT next lines.
|
||||||
|
void lines(counter_type count = 1)
|
||||||
|
{
|
||||||
|
if (count)
|
||||||
|
{
|
||||||
|
column = 1;
|
||||||
|
line = add_(line, count, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (column related) Advance to the COUNT next columns.
|
||||||
|
void columns(counter_type count = 1)
|
||||||
|
{
|
||||||
|
column = add_(column, count, 1);
|
||||||
|
}
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/// File name to which this position refers.
|
||||||
|
filename_type *filename;
|
||||||
|
/// Current line number.
|
||||||
|
counter_type line;
|
||||||
|
/// Current column number.
|
||||||
|
counter_type column;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Compute max (min, lhs+rhs).
|
||||||
|
static counter_type add_(counter_type lhs, counter_type rhs, counter_type min)
|
||||||
|
{
|
||||||
|
return lhs + rhs < min ? min : lhs + rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Add \a width columns, in place.
|
||||||
|
inline position& operator+=(position &res, position::counter_type width)
|
||||||
|
{
|
||||||
|
res.columns(width);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add \a width columns.
|
||||||
|
inline position operator+(position res, position::counter_type width)
|
||||||
|
{
|
||||||
|
return res += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract \a width columns, in place.
|
||||||
|
inline position& operator-=(position &res, position::counter_type width)
|
||||||
|
{
|
||||||
|
return res += -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract \a width columns.
|
||||||
|
inline position operator-(position res, position::counter_type width)
|
||||||
|
{
|
||||||
|
return res -= width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Intercept output stream redirection.
|
||||||
|
** \param ostr the destination output stream
|
||||||
|
** \param pos a reference to the position to redirect
|
||||||
|
*/
|
||||||
|
template <typename YYChar>
|
||||||
|
std::basic_ostream<YYChar>& operator<<(std::basic_ostream<YYChar> &ostr, const position &pos)
|
||||||
|
{
|
||||||
|
if (pos.filename)
|
||||||
|
ostr << *pos.filename << ':';
|
||||||
|
return ostr << pos.line << '.' << pos.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Two points in a source file.
|
||||||
|
class location
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Type for file name.
|
||||||
|
typedef position::filename_type filename_type;
|
||||||
|
/// Type for line and column numbers.
|
||||||
|
typedef position::counter_type counter_type;
|
||||||
|
|
||||||
|
/// Construct a location from \a b to \a e.
|
||||||
|
location(const position &b, const position &e)
|
||||||
|
: begin(b), end(e) {}
|
||||||
|
|
||||||
|
/// Construct a 0-width location in \a p.
|
||||||
|
explicit location(const position &p = position())
|
||||||
|
: begin(p), end(p) {}
|
||||||
|
|
||||||
|
/// Construct a 0-width location in \a f, \a l, \a c.
|
||||||
|
explicit location(filename_type *f, counter_type l = 1, counter_type c = 1)
|
||||||
|
: begin(f, l, c), end(f, l, c) {}
|
||||||
|
|
||||||
|
/// Initialization.
|
||||||
|
void initialize(filename_type *f = nullptr, counter_type l = 1, counter_type c = 1)
|
||||||
|
{
|
||||||
|
begin.initialize(f, l, c);
|
||||||
|
end = begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \name Line and Column related manipulators
|
||||||
|
** \{ */
|
||||||
|
public:
|
||||||
|
/// Reset initial location to final location.
|
||||||
|
void step()
|
||||||
|
{
|
||||||
|
begin = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extend the current location to the COUNT next columns.
|
||||||
|
void columns(counter_type count = 1)
|
||||||
|
{
|
||||||
|
end += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extend the current location to the COUNT next lines.
|
||||||
|
void lines(counter_type count = 1)
|
||||||
|
{
|
||||||
|
end.lines(count);
|
||||||
|
}
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto print() const -> std::string
|
||||||
|
{
|
||||||
|
return *begin.filename + ":" + std::to_string(begin.line) + ":" + std::to_string(begin.column);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto label() const -> std::string
|
||||||
|
{
|
||||||
|
return utils::string::va("loc_%X", begin.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Beginning of the located region.
|
||||||
|
position begin;
|
||||||
|
/// End of the located region.
|
||||||
|
position end;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Join two locations, in place.
|
||||||
|
inline location& operator+=(location &res, const location &end)
|
||||||
|
{
|
||||||
|
res.end = end.end;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join two locations.
|
||||||
|
inline location operator+(location res, const location &end)
|
||||||
|
{
|
||||||
|
return res += end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add \a width columns to the end position, in place.
|
||||||
|
inline location& operator+=(location &res, location::counter_type width)
|
||||||
|
{
|
||||||
|
res.columns(width);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add \a width columns to the end position.
|
||||||
|
inline location operator+(location res, location::counter_type width)
|
||||||
|
{
|
||||||
|
return res += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract \a width columns to the end position, in place.
|
||||||
|
inline location& operator-=(location &res, location::counter_type width)
|
||||||
|
{
|
||||||
|
return res += -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtract \a width columns to the end position.
|
||||||
|
inline location operator-(location res, location::counter_type width)
|
||||||
|
{
|
||||||
|
return res -= width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Intercept output stream redirection.
|
||||||
|
** \param ostr the destination output stream
|
||||||
|
** \param loc a reference to the location to redirect
|
||||||
|
**
|
||||||
|
** Avoid duplicate information.
|
||||||
|
*/
|
||||||
|
template <typename YYChar>
|
||||||
|
std::basic_ostream<YYChar>& operator<<(std::basic_ostream<YYChar> &ostr, const location &loc)
|
||||||
|
{
|
||||||
|
location::counter_type end_col = 0 < loc.end.column ? loc.end.column - 1 : 0;
|
||||||
|
ostr << loc.begin;
|
||||||
|
if (loc.end.filename && (!loc.begin.filename || *loc.begin.filename != *loc.end.filename))
|
||||||
|
ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col;
|
||||||
|
else if (loc.begin.line < loc.end.line)
|
||||||
|
ostr << '-' << loc.end.line << '.' << end_col;
|
||||||
|
else if (loc.begin.column < end_col)
|
||||||
|
ostr << '-' << end_col;
|
||||||
|
return ostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
1917
src/utils/xsk/arc/nodetree.cpp
Normal file
1917
src/utils/xsk/arc/nodetree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1924
src/utils/xsk/arc/nodetree.hpp
Normal file
1924
src/utils/xsk/arc/nodetree.hpp
Normal file
File diff suppressed because it is too large
Load Diff
179
src/utils/xsk/arc/types.hpp
Normal file
179
src/utils/xsk/arc/types.hpp
Normal 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace xsk::arc
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class build
|
||||||
|
{
|
||||||
|
dev,
|
||||||
|
prod,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class abort_t
|
||||||
|
{
|
||||||
|
abort_none = 0,
|
||||||
|
abort_continue = 1,
|
||||||
|
abort_break = 2,
|
||||||
|
abort_return = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct include_t
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> funcs;
|
||||||
|
|
||||||
|
include_t(const std::string& name, const std::vector<std::string>& funcs) : name(name), funcs(funcs) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct animtree_t
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
bool loaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct instruction
|
||||||
|
{
|
||||||
|
using ptr = std::unique_ptr<instruction>;
|
||||||
|
|
||||||
|
std::uint32_t index;
|
||||||
|
std::uint32_t size;
|
||||||
|
std::uint8_t opcode;
|
||||||
|
std::vector<std::string> data;
|
||||||
|
|
||||||
|
instruction() : index(0), size(0), opcode(0xFF) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct function
|
||||||
|
{
|
||||||
|
using ptr = std::unique_ptr<function>;
|
||||||
|
|
||||||
|
std::uint32_t index;
|
||||||
|
std::uint32_t size;
|
||||||
|
std::uint8_t params;
|
||||||
|
std::uint8_t flags;
|
||||||
|
std::string name;
|
||||||
|
std::vector<instruction::ptr> instructions;
|
||||||
|
std::unordered_map<std::uint32_t, std::string> labels;
|
||||||
|
|
||||||
|
function() : index(0), size(0), params(0), flags(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct animation_ref
|
||||||
|
{
|
||||||
|
using ptr = std::shared_ptr<animation_ref>;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::uint32_t ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct animtree_ref
|
||||||
|
{
|
||||||
|
using ptr = std::shared_ptr<animtree_ref>;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::uint32_t> refs;
|
||||||
|
std::vector<animation_ref> anims;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class string_type : std::uint8_t
|
||||||
|
{
|
||||||
|
literal = 0,
|
||||||
|
canonical = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_ref
|
||||||
|
{
|
||||||
|
using ptr = std::shared_ptr<string_ref>;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::uint8_t type;
|
||||||
|
std::vector<std::uint32_t> refs;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class export_flags : std::uint8_t
|
||||||
|
{
|
||||||
|
none = 0x00,
|
||||||
|
vis_public = 0x01,
|
||||||
|
vis_autoexec = 0x02,
|
||||||
|
vis_private = 0x04,
|
||||||
|
unk1 = 0x08, // never seen
|
||||||
|
unk2 = 0x10, // inside dev /##/ ?
|
||||||
|
varargs_may = 0x20, // T7, T8, T9
|
||||||
|
};
|
||||||
|
|
||||||
|
struct export_ref
|
||||||
|
{
|
||||||
|
using ptr = std::shared_ptr<export_ref>;
|
||||||
|
|
||||||
|
std::uint32_t checksum;
|
||||||
|
std::uint32_t offset;
|
||||||
|
std::string name;
|
||||||
|
std::string space;
|
||||||
|
std::uint8_t params;
|
||||||
|
std::uint8_t flags;
|
||||||
|
std::uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class import_flags : std::uint8_t
|
||||||
|
{
|
||||||
|
none = 0,
|
||||||
|
func_reference = 1,
|
||||||
|
func_call = 2,
|
||||||
|
func_call_thread = 3,
|
||||||
|
meth_call = 4,
|
||||||
|
meth_call_thread = 5,
|
||||||
|
developer = 0x10,
|
||||||
|
unk = 0x20, // T7, T8, T9
|
||||||
|
};
|
||||||
|
|
||||||
|
struct import_ref
|
||||||
|
{
|
||||||
|
using ptr = std::shared_ptr<import_ref>;
|
||||||
|
|
||||||
|
std::string space;
|
||||||
|
std::string name;
|
||||||
|
std::uint8_t params;
|
||||||
|
std::uint8_t flags;
|
||||||
|
std::vector<std::uint32_t> refs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct header
|
||||||
|
{
|
||||||
|
std::uint64_t magic;
|
||||||
|
std::uint32_t source_crc;
|
||||||
|
std::uint32_t include_offset;
|
||||||
|
std::uint32_t animtree_offset;
|
||||||
|
std::uint32_t cseg_offset;
|
||||||
|
std::uint32_t stringtablefixup_offset;
|
||||||
|
std::uint32_t exports_offset;
|
||||||
|
std::uint32_t imports_offset;
|
||||||
|
std::uint32_t fixup_offset;
|
||||||
|
std::uint32_t profile_offset;
|
||||||
|
std::uint32_t cseg_size;
|
||||||
|
std::uint16_t name;
|
||||||
|
std::uint16_t stringtablefixup_count;
|
||||||
|
std::uint16_t exports_count;
|
||||||
|
std::uint16_t imports_count;
|
||||||
|
std::uint16_t fixup_count;
|
||||||
|
std::uint16_t profile_count;
|
||||||
|
std::uint8_t include_count;
|
||||||
|
std::uint8_t animtree_count;
|
||||||
|
std::uint8_t flags;
|
||||||
|
// char[1] pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct assembly
|
||||||
|
{
|
||||||
|
using ptr = std::unique_ptr<assembly>;
|
||||||
|
|
||||||
|
std::vector<function::ptr> functions;
|
||||||
|
std::vector<std::string> includes;
|
||||||
|
std::vector<animtree_ref> animtrees;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xsk::arc
|
@ -131,7 +131,7 @@ auto node::precedence() -> std::uint8_t
|
|||||||
case kind::expr_shift_right: return 8;
|
case kind::expr_shift_right: return 8;
|
||||||
case kind::expr_add: return 9;
|
case kind::expr_add: return 9;
|
||||||
case kind::expr_sub: return 9;
|
case kind::expr_sub: return 9;
|
||||||
case kind::expr_mul: return 10;
|
case kind::expr_mul: return 10;
|
||||||
case kind::expr_div: return 10;
|
case kind::expr_div: return 10;
|
||||||
case kind::expr_mod: return 10;
|
case kind::expr_mod: return 10;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
|
@ -24,3 +24,16 @@
|
|||||||
#include "gsc/interfaces/disassembler.hpp"
|
#include "gsc/interfaces/disassembler.hpp"
|
||||||
#include "gsc/interfaces/compiler.hpp"
|
#include "gsc/interfaces/compiler.hpp"
|
||||||
#include "gsc/interfaces/decompiler.hpp"
|
#include "gsc/interfaces/decompiler.hpp"
|
||||||
|
|
||||||
|
// ARC Types
|
||||||
|
#include "arc/location.hpp"
|
||||||
|
#include "arc/types.hpp"
|
||||||
|
#include "arc/block.hpp"
|
||||||
|
#include "arc/nodetree.hpp"
|
||||||
|
|
||||||
|
// ARC Interfaces
|
||||||
|
#include "arc/interfaces/exception.hpp"
|
||||||
|
#include "arc/interfaces/assembler.hpp"
|
||||||
|
#include "arc/interfaces/disassembler.hpp"
|
||||||
|
#include "arc/interfaces/compiler.hpp"
|
||||||
|
#include "arc/interfaces/decompiler.hpp"
|
||||||
|
Loading…
Reference in New Issue
Block a user