blackops2 implementation

This commit is contained in:
xensik 2022-02-18 19:07:37 +01:00
parent c9c5e7c259
commit 2f402a10cc
35 changed files with 28817 additions and 4 deletions

1
.gitignore vendored
View File

@ -158,3 +158,4 @@ data/h2/
data/s1/
data/s2/
data/s4/
data/t6/

View File

@ -16,13 +16,13 @@ A utility to compile & decompile IW engine game scripts.
- **H2** *(Call of Duty: Modern Warfare 2 Campaign Remastered)*
- **T4** *(Call of Duty: World at War)* ***\*WIP\****
- **T5** *(Call of Duty: Black Ops)* ***\*WIP\****
- **T6** *(Call of Duty: Black Ops II)* ***\*WIP\****
- **T6** *(Call of Duty: Black Ops II)*
## Usage
``./gsc-tool.exe <mode> <game> <path>``
**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)

9
gen/t6/Makefile Normal file
View 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
View 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);
}

View File

@ -58,6 +58,7 @@ project "xsk-gsc-tool"
dependson "xsk-gsc-s4"
dependson "xsk-gsc-h1"
dependson "xsk-gsc-h2"
dependson "xsk-arc-t6"
pchheader "stdafx.hpp"
pchsource "src/tool/stdafx.cpp"
@ -78,7 +79,8 @@ project "xsk-gsc-tool"
"xsk-gsc-s2",
"xsk-gsc-s4",
"xsk-gsc-h1",
"xsk-gsc-h2"
"xsk-gsc-h2",
"xsk-arc-t6"
}
includedirs {
@ -270,5 +272,23 @@ project "xsk-gsc-h2"
"./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"
zlib:project()

6
src/t6/stdafx.cpp Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

172
src/t6/xsk/compiler.hpp Normal file
View 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

File diff suppressed because it is too large Load Diff

98
src/t6/xsk/decompiler.hpp Normal file
View 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
View 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

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

23
src/t6/xsk/resolver.hpp Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#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
View 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
View 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

View 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

View 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

View File

@ -0,0 +1,23 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

179
src/utils/xsk/arc/types.hpp Normal file
View File

@ -0,0 +1,179 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#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

View File

@ -24,3 +24,16 @@
#include "gsc/interfaces/disassembler.hpp"
#include "gsc/interfaces/compiler.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"