feat(iw9): template
This commit is contained in:
parent
d47c0d1520
commit
532e5f5e88
1
.gitignore
vendored
1
.gitignore
vendored
@ -164,6 +164,7 @@ data/iw5/
|
||||
data/iw6/
|
||||
data/iw7/
|
||||
data/iw8/
|
||||
data/iw9/
|
||||
data/h1/
|
||||
data/h2/
|
||||
data/s1/
|
||||
|
@ -12,6 +12,7 @@ A utility to compile & decompile IW engine game scripts.
|
||||
- **IW6** *(Call of Duty: Ghosts)* `PC` `PS3` `PS4` `Xbox 360` `Xbox One`
|
||||
- **IW7** *(Call of Duty: Infinite Warfare)* `PC` `PS4` `Xbox One`
|
||||
- **IW8** *(Call of Duty: Modern Warfare (2019) / Warzone)* `PC` `PS4` `PS5` `Xbox One` `Xbox Series X|S`
|
||||
- **IW9** *(Call of Duty: Modern Warfare II (2022) / Warzone 2)* ***\*WIP\****
|
||||
- **S1** *(Call of Duty: Advanced Warfare)* `PC` `PS3` `PS4` `Xbox 360` `Xbox One`
|
||||
- **S2** *(Call of Duty: WWII)* `PC` `PS4` `Xbox One`
|
||||
- **S4** *(Call of Duty: Vanguard)* `PC` `PS4` `PS5` `Xbox One` `Xbox Series X|S`
|
||||
|
9
gen/iw9/Makefile
Normal file
9
gen/iw9/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
generate: iw9
|
||||
|
||||
clean:
|
||||
rm -rf ./parser.hpp
|
||||
rm -rf ./parser.cpp
|
||||
|
||||
iw9: parser.ypp
|
||||
bison parser.ypp -Wcounterexamples
|
||||
mv parser.hpp parser.cpp ../../src/iw9/xsk/
|
914
gen/iw9/parser.ypp
Normal file
914
gen/iw9/parser.ypp
Normal file
@ -0,0 +1,914 @@
|
||||
/* 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 {IW9}
|
||||
%define api.namespace {xsk::gsc::iw9}
|
||||
%define api.location.type {xsk::gsc::location}
|
||||
%define api.value.type variant
|
||||
%define api.token.constructor
|
||||
%define api.token.raw
|
||||
%define parse.assert
|
||||
%define parse.trace
|
||||
%define parse.error detailed
|
||||
%define parse.lac full
|
||||
%locations
|
||||
%lex-param { xsk::gsc::iw9::lexer& lexer }
|
||||
%parse-param { xsk::gsc::iw9::lexer& lexer }
|
||||
%parse-param { xsk::gsc::ast::program::ptr& ast }
|
||||
|
||||
%code requires
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4065)
|
||||
#pragma warning(disable:4127)
|
||||
#endif
|
||||
#include "iw9.hpp"
|
||||
namespace xsk::gsc::iw9 { class lexer; }
|
||||
}
|
||||
|
||||
%code top
|
||||
{
|
||||
#include "stdafx.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "lexer.hpp"
|
||||
using namespace xsk::gsc;
|
||||
xsk::gsc::iw9::parser::symbol_type IW9lex(xsk::gsc::iw9::lexer& lexer);
|
||||
}
|
||||
|
||||
%token SH_DEFINE "#define"
|
||||
%token SH_UNDEF "#undef"
|
||||
%token SH_IFDEF "#ifdef"
|
||||
%token SH_IFNDEF "#ifndef"
|
||||
%token SH_IF "#if"
|
||||
%token SH_ELIF "#elif"
|
||||
%token SH_ELSE "#else"
|
||||
%token SH_ENDIF "#endif"
|
||||
%token DEVBEGIN "/#"
|
||||
%token DEVEND "#/"
|
||||
%token INLINE "#inline"
|
||||
%token INCLUDE "#include"
|
||||
%token USINGTREE "#using_animtree"
|
||||
%token ANIMTREE "#animtree"
|
||||
%token ENDON "endon"
|
||||
%token NOTIFY "notify"
|
||||
%token WAIT "wait"
|
||||
%token WAITTILL "waittill"
|
||||
%token WAITTILLMATCH "waittillmatch"
|
||||
%token WAITTILLFRAMEEND "waittillframeend"
|
||||
%token WAITFRAME "waitframe"
|
||||
%token IF "if"
|
||||
%token ELSE "else"
|
||||
%token DO "do"
|
||||
%token WHILE "while"
|
||||
%token FOR "for"
|
||||
%token FOREACH "foreach"
|
||||
%token IN "in"
|
||||
%token SWITCH "switch"
|
||||
%token CASE "case"
|
||||
%token DEFAULT "default"
|
||||
%token BREAK "break"
|
||||
%token CONTINUE "continue"
|
||||
%token RETURN "return"
|
||||
%token BREAKPOINT "breakpoint"
|
||||
%token PROFBEGIN "prof_begin"
|
||||
%token PROFEND "prof_end"
|
||||
%token THREAD "thread"
|
||||
%token CHILDTHREAD "childthread"
|
||||
%token THISTHREAD "thisthread"
|
||||
%token CALL "call"
|
||||
%token TRUE "true"
|
||||
%token FALSE "false"
|
||||
%token UNDEFINED "undefined"
|
||||
%token SIZE "size"
|
||||
%token GAME "game"
|
||||
%token SELF "self"
|
||||
%token ANIM "anim"
|
||||
%token LEVEL "level"
|
||||
%token ISDEFINED "isdefined"
|
||||
%token ISTRUE "istrue"
|
||||
%token LPAREN "("
|
||||
%token RPAREN ")"
|
||||
%token LBRACE "{"
|
||||
%token RBRACE "}"
|
||||
%token LBRACKET "["
|
||||
%token RBRACKET "]"
|
||||
%token COMMA ","
|
||||
%token DOT "."
|
||||
%token DOUBLECOLON "::"
|
||||
%token COLON ":"
|
||||
%token SEMICOLON ";"
|
||||
%token QMARK "?"
|
||||
%token INCREMENT "++"
|
||||
%token DECREMENT "--"
|
||||
%token LSHIFT "<<"
|
||||
%token RSHIFT ">>"
|
||||
%token OR "||"
|
||||
%token AND "&&"
|
||||
%token EQUALITY "=="
|
||||
%token INEQUALITY "!="
|
||||
%token LESS_EQUAL "<="
|
||||
%token GREATER_EQUAL ">="
|
||||
%token LESS "<"
|
||||
%token GREATER ">"
|
||||
%token NOT "!"
|
||||
%token COMPLEMENT "~"
|
||||
%token ASSIGN "="
|
||||
%token ASSIGN_ADD "+="
|
||||
%token ASSIGN_SUB "-="
|
||||
%token ASSIGN_MUL "*="
|
||||
%token ASSIGN_DIV "/="
|
||||
%token ASSIGN_MOD "%="
|
||||
%token ASSIGN_BW_OR "|="
|
||||
%token ASSIGN_BW_AND "&="
|
||||
%token ASSIGN_BW_EXOR "^="
|
||||
%token ASSIGN_RSHIFT ">>="
|
||||
%token ASSIGN_LSHIFT "<<="
|
||||
%token BITWISE_OR "|"
|
||||
%token BITWISE_AND "&"
|
||||
%token BITWISE_EXOR "^"
|
||||
%token ADD "+"
|
||||
%token SUB "-"
|
||||
%token MUL "*"
|
||||
%token DIV "/"
|
||||
%token MOD "%"
|
||||
%token <std::string> PATH "path"
|
||||
%token <std::string> IDENTIFIER "identifier"
|
||||
%token <std::string> STRING "string literal"
|
||||
%token <std::string> ISTRING "localized string"
|
||||
%token <std::string> FLOAT "float"
|
||||
%token <std::string> INTEGER "integer"
|
||||
|
||||
%type <ast::program::ptr> program
|
||||
%type <ast::include::ptr> include
|
||||
%type <ast::decl> declaration
|
||||
%type <ast::decl_usingtree::ptr> decl_usingtree
|
||||
%type <ast::decl_constant::ptr> decl_constant
|
||||
%type <ast::decl_thread::ptr> decl_thread
|
||||
%type <ast::stmt> stmt
|
||||
%type <ast::stmt> stmt_or_dev
|
||||
%type <ast::stmt_list::ptr> stmt_list
|
||||
%type <ast::stmt_list::ptr> stmt_or_dev_list
|
||||
%type <ast::stmt_dev::ptr> stmt_dev
|
||||
%type <ast::stmt_list::ptr> stmt_block
|
||||
%type <ast::stmt_expr::ptr> stmt_expr
|
||||
%type <ast::stmt_call::ptr> stmt_call
|
||||
%type <ast::stmt_assign::ptr> stmt_assign
|
||||
%type <ast::stmt_endon::ptr> stmt_endon
|
||||
%type <ast::stmt_notify::ptr> stmt_notify
|
||||
%type <ast::stmt_wait::ptr> stmt_wait
|
||||
%type <ast::stmt_waittill::ptr> stmt_waittill
|
||||
%type <ast::stmt_waittillmatch::ptr> stmt_waittillmatch
|
||||
%type <ast::stmt_waittillframeend::ptr> stmt_waittillframeend
|
||||
%type <ast::stmt_waitframe::ptr> stmt_waitframe
|
||||
%type <ast::stmt_if::ptr> stmt_if
|
||||
%type <ast::stmt_ifelse::ptr> stmt_ifelse
|
||||
%type <ast::stmt_while::ptr> stmt_while
|
||||
%type <ast::stmt_dowhile::ptr> stmt_dowhile
|
||||
%type <ast::stmt_for::ptr> stmt_for
|
||||
%type <ast::stmt_foreach::ptr> stmt_foreach
|
||||
%type <ast::stmt_switch::ptr> stmt_switch
|
||||
%type <ast::stmt_case::ptr> stmt_case
|
||||
%type <ast::stmt_default::ptr> stmt_default
|
||||
%type <ast::stmt_break::ptr> stmt_break
|
||||
%type <ast::stmt_continue::ptr> stmt_continue
|
||||
%type <ast::stmt_return::ptr> stmt_return
|
||||
%type <ast::stmt_breakpoint::ptr> stmt_breakpoint
|
||||
%type <ast::stmt_prof_begin::ptr> stmt_prof_begin
|
||||
%type <ast::stmt_prof_end::ptr> stmt_prof_end
|
||||
%type <ast::expr> expr
|
||||
%type <ast::expr> expr_or_empty
|
||||
%type <ast::expr> expr_assign
|
||||
%type <ast::expr> expr_increment
|
||||
%type <ast::expr> expr_decrement
|
||||
%type <ast::expr> expr_ternary
|
||||
%type <ast::expr> expr_binary
|
||||
%type <ast::expr> expr_primitive
|
||||
%type <ast::expr_complement::ptr> expr_complement
|
||||
%type <ast::expr_negate::ptr> expr_negate
|
||||
%type <ast::expr_not::ptr> expr_not
|
||||
%type <ast::expr_call::ptr> expr_call
|
||||
%type <ast::expr_method::ptr> expr_method
|
||||
%type <ast::call> expr_function
|
||||
%type <ast::call> expr_pointer
|
||||
%type <ast::expr_add_array::ptr> expr_add_array
|
||||
%type <ast::expr_parameters::ptr> expr_parameters
|
||||
%type <ast::expr_arguments::ptr> expr_arguments
|
||||
%type <ast::expr_arguments::ptr> expr_arguments_no_empty
|
||||
%type <ast::expr_isdefined::ptr> expr_isdefined
|
||||
%type <ast::expr_istrue::ptr> expr_istrue
|
||||
%type <ast::expr_reference::ptr> expr_reference
|
||||
%type <ast::expr> expr_tuple
|
||||
%type <ast::expr_tuple::ptr> expr_tuple_arguments
|
||||
%type <ast::expr> expr_tuple_types
|
||||
%type <ast::expr_array::ptr> expr_array
|
||||
%type <ast::expr_field::ptr> expr_field
|
||||
%type <ast::expr_size::ptr> expr_size
|
||||
%type <ast::expr_paren::ptr> expr_paren
|
||||
%type <ast::expr> expr_object
|
||||
%type <ast::expr_thisthread::ptr> expr_thisthread
|
||||
%type <ast::expr_empty_array::ptr> expr_empty_array
|
||||
%type <ast::expr_undefined::ptr> expr_undefined
|
||||
%type <ast::expr_game::ptr> expr_game
|
||||
%type <ast::expr_self::ptr> expr_self
|
||||
%type <ast::expr_anim::ptr> expr_anim
|
||||
%type <ast::expr_level::ptr> expr_level
|
||||
%type <ast::expr_animation::ptr> expr_animation
|
||||
%type <ast::expr_animtree::ptr> expr_animtree
|
||||
%type <ast::expr_identifier::ptr> expr_identifier_nosize
|
||||
%type <ast::expr_identifier::ptr> expr_identifier
|
||||
%type <ast::expr_path::ptr> expr_path
|
||||
%type <ast::expr_istring::ptr> expr_istring
|
||||
%type <ast::expr_string::ptr> expr_string
|
||||
%type <ast::expr_vector::ptr> expr_vector
|
||||
%type <ast::expr_float::ptr> expr_float
|
||||
%type <ast::expr_integer::ptr> expr_integer
|
||||
%type <ast::expr_false::ptr> expr_false
|
||||
%type <ast::expr_true::ptr> expr_true
|
||||
|
||||
%nonassoc SIZEOF
|
||||
%nonassoc ADD_ARRAY
|
||||
%nonassoc RBRACKET
|
||||
%nonassoc THEN
|
||||
%nonassoc ELSE
|
||||
%nonassoc INCREMENT DECREMENT
|
||||
|
||||
%precedence TERN
|
||||
%right QMARK
|
||||
%left OR
|
||||
%left AND
|
||||
%left BITWISE_OR
|
||||
%left BITWISE_EXOR
|
||||
%left BITWISE_AND
|
||||
%left EQUALITY INEQUALITY
|
||||
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
|
||||
%left LSHIFT RSHIFT
|
||||
%left ADD SUB
|
||||
%left MUL DIV MOD
|
||||
%right NOT COMPLEMENT
|
||||
|
||||
%precedence NEG
|
||||
%precedence ANIMREF
|
||||
%precedence PREINC PREDEC
|
||||
%precedence POSTINC POSTDEC
|
||||
|
||||
%start root
|
||||
|
||||
%%
|
||||
|
||||
root
|
||||
: program { ast = std::move($1); }
|
||||
| { ast = std::make_unique<ast::program>(@$); }
|
||||
;
|
||||
|
||||
program
|
||||
: program inline
|
||||
{ $$ = std::move($1); }
|
||||
| program include
|
||||
{ $$ = std::move($1); $$->includes.push_back(std::move($2)); }
|
||||
| program declaration
|
||||
{ $$ = std::move($1); $$->declarations.push_back(std::move($2)); }
|
||||
| inline
|
||||
{ $$ = std::make_unique<ast::program>(@$); }
|
||||
| include
|
||||
{ $$ = std::make_unique<ast::program>(@$); $$->includes.push_back(std::move($1)); }
|
||||
| declaration
|
||||
{ $$ = std::make_unique<ast::program>(@$); $$->declarations.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
inline
|
||||
: INLINE expr_path SEMICOLON { lexer.push_header($2->value); }
|
||||
;
|
||||
|
||||
include
|
||||
: INCLUDE expr_path SEMICOLON
|
||||
{ $$ = std::make_unique<ast::include>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
declaration
|
||||
: DEVBEGIN { $$.as_dev_begin = std::make_unique<ast::decl_dev_begin>(@$); }
|
||||
| DEVEND { $$.as_dev_end = std::make_unique<ast::decl_dev_end>(@$); }
|
||||
| decl_usingtree { $$.as_usingtree = std::move($1); }
|
||||
| decl_constant { $$.as_constant = std::move($1); }
|
||||
| decl_thread { $$.as_thread = std::move($1); }
|
||||
;
|
||||
|
||||
decl_usingtree
|
||||
: USINGTREE LPAREN expr_string RPAREN SEMICOLON
|
||||
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_usingtree>(@$, std::move($3)); }
|
||||
;
|
||||
|
||||
decl_constant
|
||||
: expr_identifier ASSIGN expr SEMICOLON
|
||||
{ $$ = std::make_unique<ast::decl_constant>(@$, std::move($1), std::move($3)); }
|
||||
;
|
||||
|
||||
decl_thread
|
||||
: expr_identifier LPAREN expr_parameters RPAREN stmt_block
|
||||
{ lexer.ban_header(@$); $$ = std::make_unique<ast::decl_thread>(@$, std::move($1), std::move($3), std::move($5)); }
|
||||
;
|
||||
|
||||
stmt
|
||||
: stmt_block { $$.as_list = std::move($1); }
|
||||
| stmt_call { $$.as_call = std::move($1); }
|
||||
| stmt_assign { $$.as_assign = std::move($1); }
|
||||
| stmt_endon { $$.as_endon = std::move($1); }
|
||||
| stmt_notify { $$.as_notify = std::move($1); }
|
||||
| stmt_wait { $$.as_wait = std::move($1); }
|
||||
| stmt_waittill { $$.as_waittill = std::move($1); }
|
||||
| stmt_waittillmatch { $$.as_waittillmatch = std::move($1); }
|
||||
| stmt_waittillframeend { $$.as_waittillframeend = std::move($1); }
|
||||
| stmt_waitframe { $$.as_waitframe = std::move($1); }
|
||||
| stmt_if { $$.as_if = std::move($1); }
|
||||
| stmt_ifelse { $$.as_ifelse = std::move($1); }
|
||||
| stmt_while { $$.as_while = std::move($1); }
|
||||
| stmt_dowhile { $$.as_dowhile = std::move($1); }
|
||||
| stmt_for { $$.as_for = std::move($1); }
|
||||
| stmt_foreach { $$.as_foreach = std::move($1); }
|
||||
| stmt_switch { $$.as_switch = std::move($1); }
|
||||
| stmt_case { $$.as_case = std::move($1); }
|
||||
| stmt_default { $$.as_default = std::move($1); }
|
||||
| stmt_break { $$.as_break = std::move($1); }
|
||||
| stmt_continue { $$.as_continue = std::move($1); }
|
||||
| stmt_return { $$.as_return = std::move($1); }
|
||||
| stmt_breakpoint { $$.as_breakpoint = std::move($1); }
|
||||
| stmt_prof_begin { $$.as_prof_begin = std::move($1); }
|
||||
| stmt_prof_end { $$.as_prof_end = std::move($1); }
|
||||
;
|
||||
|
||||
stmt_or_dev
|
||||
: stmt { $$ = std::move($1); }
|
||||
| stmt_dev { $$.as_dev = std::move($1); }
|
||||
;
|
||||
|
||||
stmt_list
|
||||
: stmt_list stmt
|
||||
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
|
||||
| stmt
|
||||
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
stmt_or_dev_list
|
||||
: stmt_or_dev_list stmt_or_dev
|
||||
{ $$ = std::move($1); $$->list.push_back(std::move($2)); }
|
||||
| stmt_or_dev
|
||||
{ $$ = std::make_unique<ast::stmt_list>(@$); $$->list.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
stmt_dev
|
||||
: DEVBEGIN stmt_list DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::move($2)); }
|
||||
| DEVBEGIN DEVEND { $$ = std::make_unique<ast::stmt_dev>(@$, std::make_unique<ast::stmt_list>(@$)); }
|
||||
;
|
||||
|
||||
stmt_block
|
||||
: LBRACE stmt_or_dev_list RBRACE { $$ = std::move($2); }
|
||||
| LBRACE RBRACE { $$ = std::make_unique<ast::stmt_list>(@$); }
|
||||
;
|
||||
|
||||
stmt_expr
|
||||
: expr_assign
|
||||
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
|
||||
| expr_increment
|
||||
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
|
||||
| expr_decrement
|
||||
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::move($1)); }
|
||||
|
|
||||
{ $$ = std::make_unique<ast::stmt_expr>(@$, std::make_unique<ast::node>(@$)); }
|
||||
;
|
||||
|
||||
stmt_call
|
||||
: expr_call SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
|
||||
| expr_method SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_call>(@$, ast::expr(std::move($1))); }
|
||||
;
|
||||
|
||||
stmt_assign
|
||||
: expr_assign SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
|
||||
| expr_increment SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
|
||||
| expr_decrement SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_assign>(@$, std::move($1)); }
|
||||
;
|
||||
|
||||
stmt_endon
|
||||
: expr_object ENDON LPAREN expr RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_endon>(@$, std::move($1), std::move($4)); }
|
||||
;
|
||||
|
||||
stmt_notify
|
||||
: expr_object NOTIFY LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::move($6)); }
|
||||
| expr_object NOTIFY LPAREN expr RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_notify>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
|
||||
;
|
||||
|
||||
stmt_wait
|
||||
: WAIT expr SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_wait>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
stmt_waittill
|
||||
: expr_object WAITTILL LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::move($6)); }
|
||||
| expr_object WAITTILL LPAREN expr RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waittill>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
|
||||
;
|
||||
|
||||
stmt_waittillmatch
|
||||
: expr_object WAITTILLMATCH LPAREN expr COMMA expr_arguments_no_empty RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::move($6)); }
|
||||
| expr_object WAITTILLMATCH LPAREN expr RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waittillmatch>(@$, std::move($1), std::move($4), std::make_unique<ast::expr_arguments>(@$)); }
|
||||
;
|
||||
|
||||
stmt_waittillframeend
|
||||
: WAITTILLFRAMEEND SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waittillframeend>(@$); }
|
||||
;
|
||||
|
||||
stmt_waitframe
|
||||
: WAITFRAME SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waitframe>(@$); }
|
||||
| WAITFRAME LPAREN RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_waitframe>(@$); }
|
||||
;
|
||||
|
||||
stmt_if
|
||||
: IF LPAREN expr RPAREN stmt %prec THEN
|
||||
{ $$ = std::make_unique<ast::stmt_if>(@$, std::move($3), std::move($5)); }
|
||||
;
|
||||
|
||||
stmt_ifelse
|
||||
: IF LPAREN expr RPAREN stmt ELSE stmt
|
||||
{ $$ = std::make_unique<ast::stmt_ifelse>(@$, std::move($3), std::move($5), std::move($7)); }
|
||||
;
|
||||
|
||||
stmt_while
|
||||
: WHILE LPAREN expr RPAREN stmt
|
||||
{ $$ = std::make_unique<ast::stmt_while>(@$, std::move($3), std::move($5)); }
|
||||
;
|
||||
|
||||
stmt_dowhile
|
||||
: DO stmt WHILE LPAREN expr RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_dowhile>(@$, std::move($5), std::move($2)); }
|
||||
;
|
||||
|
||||
stmt_for
|
||||
: FOR LPAREN stmt_expr SEMICOLON expr_or_empty SEMICOLON stmt_expr RPAREN stmt
|
||||
{ $$ = std::make_unique<ast::stmt_for>(@$, ast::stmt(std::move($3)), std::move($5), ast::stmt(std::move($7)), std::move($9)); }
|
||||
;
|
||||
|
||||
stmt_foreach
|
||||
: FOREACH LPAREN expr_identifier IN expr RPAREN stmt
|
||||
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), std::move($5), std::move($7)); }
|
||||
| FOREACH LPAREN expr_identifier COMMA expr_identifier IN expr RPAREN stmt
|
||||
{ $$ = std::make_unique<ast::stmt_foreach>(@$, ast::expr(std::move($3)), ast::expr(std::move($5)), std::move($7), std::move($9)); }
|
||||
;
|
||||
|
||||
stmt_switch
|
||||
: SWITCH LPAREN expr RPAREN stmt_block
|
||||
{ $$ = std::make_unique<ast::stmt_switch>(@$, std::move($3), std::move($5)); }
|
||||
;
|
||||
|
||||
stmt_case
|
||||
: CASE expr_integer COLON
|
||||
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
|
||||
| CASE expr_string COLON
|
||||
{ $$ = std::make_unique<ast::stmt_case>(@$, ast::expr(std::move($2)), std::make_unique<ast::stmt_list>(@$)); }
|
||||
;
|
||||
|
||||
stmt_default
|
||||
: DEFAULT COLON
|
||||
{ $$ = std::make_unique<ast::stmt_default>(@$, std::make_unique<ast::stmt_list>(@$)); }
|
||||
;
|
||||
|
||||
stmt_break
|
||||
: BREAK SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_break>(@$); }
|
||||
;
|
||||
|
||||
stmt_continue
|
||||
: CONTINUE SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_continue>(@$); }
|
||||
;
|
||||
|
||||
stmt_return
|
||||
: RETURN expr SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_return>(@$, std::move($2)); }
|
||||
| RETURN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_return>(@$, std::make_unique<ast::node>(@$)); }
|
||||
;
|
||||
|
||||
stmt_breakpoint
|
||||
: BREAKPOINT SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_breakpoint>(@$); }
|
||||
;
|
||||
|
||||
stmt_prof_begin
|
||||
: PROFBEGIN LPAREN expr_arguments RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_prof_begin>(@$, std::move($3)); }
|
||||
;
|
||||
|
||||
stmt_prof_end
|
||||
: PROFEND LPAREN expr_arguments RPAREN SEMICOLON
|
||||
{ $$ = std::make_unique<ast::stmt_prof_end>(@$, std::move($3)); }
|
||||
;
|
||||
|
||||
expr
|
||||
: expr_ternary { $$ = std::move($1); }
|
||||
| expr_binary { $$ = std::move($1); }
|
||||
| expr_primitive { $$ = std::move($1); }
|
||||
;
|
||||
|
||||
expr_or_empty
|
||||
: expr { $$ = std::move($1); }
|
||||
| { $$.as_node = std::make_unique<ast::node>(@$); }
|
||||
;
|
||||
|
||||
expr_assign
|
||||
: expr_tuple ASSIGN expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_equal>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_BW_OR expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_or>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_BW_AND expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_and>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_BW_EXOR expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_bitwise_exor>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_LSHIFT expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_shift_left>(@$, std::move($1),std::move( $3)); }
|
||||
| expr_object ASSIGN_RSHIFT expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_shift_right>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_ADD expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_add>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_SUB expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_sub>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_MUL expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_mul>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_DIV expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_div>(@$, std::move($1), std::move($3)); }
|
||||
| expr_object ASSIGN_MOD expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_assign_mod>(@$, std::move($1), std::move($3)); }
|
||||
;
|
||||
|
||||
expr_increment
|
||||
: INCREMENT expr_object %prec PREINC
|
||||
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($2), true); }
|
||||
| expr_object INCREMENT %prec POSTINC
|
||||
{ $$.as_node = std::make_unique<ast::expr_increment>(@$, std::move($1), false); }
|
||||
;
|
||||
|
||||
expr_decrement
|
||||
: DECREMENT expr_object %prec PREDEC
|
||||
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($2), true); }
|
||||
| expr_object DECREMENT %prec POSTDEC
|
||||
{ $$.as_node = std::make_unique<ast::expr_decrement>(@$, std::move($1), false); }
|
||||
;
|
||||
|
||||
expr_ternary
|
||||
: expr QMARK expr COLON expr %prec TERN
|
||||
{ $$.as_node = std::make_unique<ast::expr_ternary>(@$, std::move($1), std::move($3), std::move($5)); }
|
||||
;
|
||||
|
||||
expr_binary
|
||||
: expr OR expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_or>(@$, std::move($1), std::move($3)); }
|
||||
| expr AND expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_and>(@$, std::move($1), std::move($3)); }
|
||||
| expr EQUALITY expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_equality>(@$, std::move($1), std::move($3)); }
|
||||
| expr INEQUALITY expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_inequality>(@$, std::move($1), std::move($3)); }
|
||||
| expr LESS_EQUAL expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_less_equal>(@$, std::move($1), std::move($3)); }
|
||||
| expr GREATER_EQUAL expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_greater_equal>(@$, std::move($1), std::move($3)); }
|
||||
| expr LESS expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_less>(@$, std::move($1), std::move($3)); }
|
||||
| expr GREATER expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_greater>(@$, std::move($1), std::move($3)); }
|
||||
| expr BITWISE_OR expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_bitwise_or>(@$, std::move($1), std::move($3)); }
|
||||
| expr BITWISE_AND expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_bitwise_and>(@$, std::move($1), std::move($3)); }
|
||||
| expr BITWISE_EXOR expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_bitwise_exor>(@$, std::move($1), std::move($3)); }
|
||||
| expr LSHIFT expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_shift_left>(@$, std::move($1), std::move($3)); }
|
||||
| expr RSHIFT expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_shift_right>(@$, std::move($1), std::move($3)); }
|
||||
| expr ADD expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_add>(@$, std::move($1), std::move($3)); }
|
||||
| expr SUB expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_sub>(@$, std::move($1), std::move($3)); }
|
||||
| expr MUL expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_mul>(@$, std::move($1), std::move($3)); }
|
||||
| expr DIV expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_div>(@$, std::move($1), std::move($3)); }
|
||||
| expr MOD expr
|
||||
{ $$.as_node = std::make_unique<ast::expr_mod>(@$, std::move($1), std::move($3)); }
|
||||
;
|
||||
|
||||
expr_primitive
|
||||
: expr_complement { $$.as_node = std::move($1); }
|
||||
| expr_negate { $$.as_node = std::move($1); }
|
||||
| expr_not { $$.as_node = std::move($1); }
|
||||
| expr_call { $$.as_node = std::move($1); }
|
||||
| expr_method { $$.as_node = std::move($1); }
|
||||
| expr_add_array { $$.as_node = std::move($1); }
|
||||
| expr_isdefined { $$.as_node = std::move($1); }
|
||||
| expr_istrue { $$.as_node = std::move($1); }
|
||||
| expr_reference { $$.as_node = std::move($1); }
|
||||
| expr_array { $$.as_node = std::move($1); }
|
||||
| expr_field { $$.as_node = std::move($1); }
|
||||
| expr_size { $$.as_node = std::move($1); }
|
||||
| expr_paren { $$.as_node = std::move($1); }
|
||||
| expr_thisthread { $$.as_node = std::move($1); }
|
||||
| expr_empty_array { $$.as_node = std::move($1); }
|
||||
| expr_undefined { $$.as_node = std::move($1); }
|
||||
| expr_game { $$.as_node = std::move($1); }
|
||||
| expr_self { $$.as_node = std::move($1); }
|
||||
| expr_anim { $$.as_node = std::move($1); }
|
||||
| expr_level { $$.as_node = std::move($1); }
|
||||
| expr_animation { $$.as_node = std::move($1); }
|
||||
| expr_animtree { $$.as_node = std::move($1); }
|
||||
| expr_identifier { $$.as_node = std::move($1); }
|
||||
| expr_istring { $$.as_node = std::move($1); }
|
||||
| expr_string { $$.as_node = std::move($1); }
|
||||
| expr_vector { $$.as_node = std::move($1); }
|
||||
| expr_float { $$.as_node = std::move($1); }
|
||||
| expr_integer { $$.as_node = std::move($1); }
|
||||
| expr_false { $$.as_node = std::move($1); }
|
||||
| expr_true { $$.as_node = std::move($1); }
|
||||
;
|
||||
|
||||
expr_complement
|
||||
: COMPLEMENT expr
|
||||
{ $$ = std::make_unique<ast::expr_complement>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
expr_negate
|
||||
: SUB expr_identifier %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
|
||||
| SUB expr_paren %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
|
||||
| SUB expr_array %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
|
||||
| SUB expr_field %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_negate>(@$, ast::expr(std::move($2))); }
|
||||
;
|
||||
|
||||
expr_not
|
||||
: NOT expr
|
||||
{ $$ = std::make_unique<ast::expr_not>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
expr_call
|
||||
: expr_function { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
|
||||
| expr_pointer { $$ = std::make_unique<ast::expr_call>(@$, std::move($1)); }
|
||||
;
|
||||
expr_method
|
||||
: expr_object expr_function { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
|
||||
| expr_object expr_pointer { $$ = std::make_unique<ast::expr_method>(@$, std::move($1), std::move($2)); }
|
||||
;
|
||||
|
||||
expr_function
|
||||
: expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($1), std::move($3), ast::call::mode::normal); }
|
||||
| expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($1), std::move($3), std::move($5), ast::call::mode::normal); }
|
||||
| THREAD expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::thread); }
|
||||
| THREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::thread); }
|
||||
| CHILDTHREAD expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::make_unique<ast::expr_path>(@$), std::move($2), std::move($4), ast::call::mode::childthread); }
|
||||
| CHILDTHREAD expr_path DOUBLECOLON expr_identifier LPAREN expr_arguments RPAREN
|
||||
{ $$.as_function = std::make_unique<ast::expr_function>(@$, std::move($2), std::move($4), std::move($6), ast::call::mode::childthread); }
|
||||
;
|
||||
|
||||
expr_pointer
|
||||
: LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
|
||||
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($3), std::move($7), ast::call::mode::normal); }
|
||||
| THREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
|
||||
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::thread); }
|
||||
| CHILDTHREAD LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
|
||||
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::childthread); }
|
||||
| CALL LBRACKET LBRACKET expr RBRACKET RBRACKET LPAREN expr_arguments RPAREN
|
||||
{ $$.as_pointer = std::make_unique<ast::expr_pointer>(@$, std::move($4), std::move($8), ast::call::mode::builtin); }
|
||||
;
|
||||
|
||||
expr_add_array
|
||||
: LBRACKET expr_arguments_no_empty RBRACKET
|
||||
{ $$ = std::make_unique<ast::expr_add_array>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
expr_parameters
|
||||
: expr_parameters COMMA expr_identifier
|
||||
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
|
||||
| expr_identifier
|
||||
{ $$ = std::make_unique<ast::expr_parameters>(@$); $$->list.push_back(std::move($1)); }
|
||||
|
|
||||
{ $$ = std::make_unique<ast::expr_parameters>(@$); }
|
||||
;
|
||||
|
||||
expr_arguments
|
||||
: expr_arguments_no_empty
|
||||
{ $$ = std::move($1); }
|
||||
|
|
||||
{ $$ = std::make_unique<ast::expr_arguments>(@$); }
|
||||
;
|
||||
|
||||
expr_arguments_no_empty
|
||||
: expr_arguments COMMA expr
|
||||
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
|
||||
| expr %prec ADD_ARRAY
|
||||
{ $$ = std::make_unique<ast::expr_arguments>(@$); $$->list.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
expr_isdefined
|
||||
: ISDEFINED LPAREN expr RPAREN
|
||||
{ $$ = std::make_unique<ast::expr_isdefined>(@$, std::move($3)); }
|
||||
;
|
||||
|
||||
expr_istrue
|
||||
: ISTRUE LPAREN expr RPAREN
|
||||
{ $$ = std::make_unique<ast::expr_istrue>(@$, 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_tuple
|
||||
: LBRACKET expr_tuple_arguments RBRACKET
|
||||
{ $$.as_node = std::move($2); }
|
||||
;
|
||||
|
||||
expr_tuple_arguments
|
||||
: expr_tuple_arguments COMMA expr_tuple_types
|
||||
{ $$ = std::move($1); $$->list.push_back(std::move($3)); }
|
||||
| expr_tuple_types
|
||||
{ $$ = std::make_unique<ast::expr_tuple>(@$); $$->list.push_back(std::move($1)); }
|
||||
;
|
||||
|
||||
expr_tuple_types
|
||||
: expr_array { $$.as_node = std::move($1); }
|
||||
| expr_field { $$.as_node = std::move($1); }
|
||||
| expr_identifier { $$.as_node = std::move($1); }
|
||||
;
|
||||
|
||||
expr_array
|
||||
: expr_object LBRACKET expr RBRACKET
|
||||
{ $$ = std::make_unique<ast::expr_array>(@$, std::move($1), std::move($3)); }
|
||||
;
|
||||
|
||||
expr_field
|
||||
: expr_object DOT expr_identifier_nosize
|
||||
{ $$ = std::make_unique<ast::expr_field>(@$, std::move($1), std::move($3)); }
|
||||
;
|
||||
|
||||
expr_size
|
||||
: expr_object DOT SIZE %prec SIZEOF
|
||||
{ $$ = std::make_unique<ast::expr_size>(@$, std::move($1)); }
|
||||
;
|
||||
|
||||
expr_paren
|
||||
: LPAREN expr RPAREN
|
||||
{ $$ = std::make_unique<ast::expr_paren>(@$, std::move($2)); }
|
||||
;
|
||||
|
||||
expr_object
|
||||
: expr_call { $$.as_node = std::move($1); }
|
||||
| expr_method { $$.as_node = std::move($1); }
|
||||
| expr_array { $$.as_node = std::move($1); }
|
||||
| expr_field { $$.as_node = std::move($1); }
|
||||
| expr_game { $$.as_node = std::move($1); }
|
||||
| expr_self { $$.as_node = std::move($1); }
|
||||
| expr_anim { $$.as_node = std::move($1); }
|
||||
| expr_level { $$.as_node = std::move($1); }
|
||||
| expr_identifier { $$.as_node = std::move($1); }
|
||||
;
|
||||
|
||||
expr_thisthread
|
||||
: THISTHREAD
|
||||
{ $$ = std::make_unique<ast::expr_thisthread>(@$); };
|
||||
;
|
||||
|
||||
expr_empty_array
|
||||
: LBRACKET RBRACKET
|
||||
{ $$ = std::make_unique<ast::expr_empty_array>(@$); };
|
||||
;
|
||||
|
||||
expr_undefined
|
||||
: UNDEFINED
|
||||
{ $$ = std::make_unique<ast::expr_undefined>(@$); };
|
||||
;
|
||||
|
||||
expr_game
|
||||
: GAME
|
||||
{ $$ = std::make_unique<ast::expr_game>(@$); };
|
||||
;
|
||||
|
||||
expr_self
|
||||
: SELF
|
||||
{ $$ = std::make_unique<ast::expr_self>(@$); };
|
||||
;
|
||||
|
||||
expr_anim
|
||||
: ANIM
|
||||
{ $$ = std::make_unique<ast::expr_anim>(@$); };
|
||||
;
|
||||
|
||||
expr_level
|
||||
: LEVEL
|
||||
{ $$ = std::make_unique<ast::expr_level>(@$); };
|
||||
;
|
||||
|
||||
expr_animation
|
||||
: MOD IDENTIFIER %prec ANIMREF
|
||||
{ $$ = std::make_unique<ast::expr_animation>(@$, $2); };
|
||||
;
|
||||
|
||||
expr_animtree
|
||||
: ANIMTREE
|
||||
{ $$ = std::make_unique<ast::expr_animtree>(@$); };
|
||||
;
|
||||
|
||||
expr_identifier_nosize
|
||||
: IDENTIFIER
|
||||
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_identifier
|
||||
: IDENTIFIER
|
||||
{ $$ = std::make_unique<ast::expr_identifier>(@$, $1); };
|
||||
| SIZE
|
||||
{ $$ = std::make_unique<ast::expr_identifier>(@$, "size"); };
|
||||
;
|
||||
|
||||
expr_path
|
||||
: IDENTIFIER
|
||||
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
|
||||
| PATH
|
||||
{ $$ = std::make_unique<ast::expr_path>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_istring
|
||||
: ISTRING
|
||||
{ $$ = std::make_unique<ast::expr_istring>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_string
|
||||
: STRING
|
||||
{ $$ = std::make_unique<ast::expr_string>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_vector
|
||||
: LPAREN expr COMMA expr COMMA expr RPAREN
|
||||
{ $$ = std::make_unique<ast::expr_vector>(@$, std::move($2), std::move($4), std::move($6)); };
|
||||
;
|
||||
|
||||
expr_float
|
||||
: SUB FLOAT %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_float>(@$, "-" + $2); };
|
||||
| FLOAT
|
||||
{ $$ = std::make_unique<ast::expr_float>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_integer
|
||||
: SUB INTEGER %prec NEG
|
||||
{ $$ = std::make_unique<ast::expr_integer>(@$, "-" + $2); };
|
||||
| INTEGER
|
||||
{ $$ = std::make_unique<ast::expr_integer>(@$, $1); };
|
||||
;
|
||||
|
||||
expr_false
|
||||
: FALSE
|
||||
{ $$ = std::make_unique<ast::expr_false>(@$); };
|
||||
;
|
||||
|
||||
expr_true
|
||||
: TRUE
|
||||
{ $$ = std::make_unique<ast::expr_true>(@$); };
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void xsk::gsc::iw9::parser::error(const xsk::gsc::location& loc, const std::string& msg)
|
||||
{
|
||||
throw xsk::gsc::comp_error(loc, msg);
|
||||
}
|
20
premake5.lua
20
premake5.lua
@ -57,6 +57,7 @@ project "xsk-gsc-tool"
|
||||
dependson "xsk-gsc-iw6"
|
||||
dependson "xsk-gsc-iw7"
|
||||
dependson "xsk-gsc-iw8"
|
||||
dependson "xsk-gsc-iw9"
|
||||
dependson "xsk-gsc-s1"
|
||||
dependson "xsk-gsc-s2"
|
||||
dependson "xsk-gsc-s4"
|
||||
@ -82,6 +83,7 @@ project "xsk-gsc-tool"
|
||||
"xsk-gsc-iw6",
|
||||
"xsk-gsc-iw7",
|
||||
"xsk-gsc-iw8",
|
||||
"xsk-gsc-iw9",
|
||||
"xsk-gsc-s1",
|
||||
"xsk-gsc-s2",
|
||||
"xsk-gsc-s4",
|
||||
@ -226,6 +228,24 @@ project "xsk-gsc-iw8"
|
||||
"./src"
|
||||
}
|
||||
|
||||
project "xsk-gsc-iw9"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
pchheader "stdafx.hpp"
|
||||
pchsource "src/iw9/stdafx.cpp"
|
||||
|
||||
files {
|
||||
"./src/iw9/**.h",
|
||||
"./src/iw9/**.hpp",
|
||||
"./src/iw9/**.cpp"
|
||||
}
|
||||
|
||||
includedirs {
|
||||
"./src/iw9",
|
||||
"./src"
|
||||
}
|
||||
|
||||
project "xsk-gsc-s1"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
6
src/iw9/stdafx.cpp
Normal file
6
src/iw9/stdafx.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#include "stdafx.hpp"
|
26
src/iw9/stdafx.hpp
Normal file
26
src/iw9/stdafx.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 <cstdio>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std::literals;
|
23
src/iw9/xsk/context.cpp
Normal file
23
src/iw9/xsk/context.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#include "stdafx.hpp"
|
||||
#include "iw9.hpp"
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
void context::init(build mode, read_cb_type callback)
|
||||
{
|
||||
compiler_.mode(mode);
|
||||
resolver::init(callback);
|
||||
}
|
||||
|
||||
void context::cleanup()
|
||||
{
|
||||
resolver::cleanup();
|
||||
}
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
25
src/iw9/xsk/context.hpp
Normal file
25
src/iw9/xsk/context.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
class context : public gsc::context
|
||||
{
|
||||
iw9::disassembler disassembler_;
|
||||
|
||||
public:
|
||||
void init(build mode, read_cb_type callback);
|
||||
void cleanup();
|
||||
|
||||
auto assembler() -> gsc::assembler& { throw std::runtime_error("not impl"); }
|
||||
auto disassembler() -> gsc::disassembler& { return disassembler_; }
|
||||
auto compiler() -> gsc::compiler& { throw std::runtime_error("not impl"); }
|
||||
auto decompiler() -> gsc::decompiler& { throw std::runtime_error("not impl"); }
|
||||
};
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
393
src/iw9/xsk/disassembler.cpp
Normal file
393
src/iw9/xsk/disassembler.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
// 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 "iw9.hpp"
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
auto disassembler::output() -> std::vector<function::ptr>
|
||||
{
|
||||
return std::move(functions_);
|
||||
}
|
||||
|
||||
auto disassembler::output_data() -> std::vector<std::uint8_t>
|
||||
{
|
||||
output_ = std::make_unique<utils::byte_buffer>(0x100000);
|
||||
|
||||
output_->write_string("// IW9 GSC ASSEMBLY\n");
|
||||
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
|
||||
|
||||
for (const auto& func : functions_)
|
||||
{
|
||||
print_function(func);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> output;
|
||||
|
||||
output.resize(output_->pos());
|
||||
std::memcpy(output.data(), output_->buffer().data(), output.size());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void disassembler::disassemble(const std::string& file, std::vector<std::uint8_t>& script, std::vector<std::uint8_t>& stack)
|
||||
{
|
||||
filename_ = file;
|
||||
script_ = std::make_unique<utils::byte_buffer>(script);
|
||||
stack_ = std::make_unique<utils::byte_buffer>(stack);
|
||||
functions_.clear();
|
||||
|
||||
script_->seek(1);
|
||||
|
||||
while (stack_->is_avail() && script_->is_avail())
|
||||
{
|
||||
functions_.push_back(std::make_unique<function>());
|
||||
const auto& func = functions_.back();
|
||||
|
||||
func->index = static_cast<std::uint32_t>(script_->pos());
|
||||
func->size = stack_->read<std::uint32_t>();
|
||||
func->id = stack_->read<std::uint32_t>();
|
||||
func->name = func->id == 0 ? decrypt_string(stack_->read_c_string()) : resolver::token_name(func->id);
|
||||
|
||||
dissasemble_function(func);
|
||||
|
||||
func->labels = labels_;
|
||||
labels_.clear();
|
||||
}
|
||||
|
||||
resolve_local_functions();
|
||||
}
|
||||
|
||||
void disassembler::dissasemble_function(const function::ptr& func)
|
||||
{
|
||||
auto size = func->size;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
func->instructions.push_back(std::make_unique<instruction>());
|
||||
|
||||
const auto& inst = func->instructions.back();
|
||||
inst->index = static_cast<std::uint32_t>(script_->pos());
|
||||
inst->opcode = script_->read<std::uint8_t>();
|
||||
inst->size = opcode_size(inst->opcode);
|
||||
|
||||
dissasemble_instruction(inst);
|
||||
|
||||
size -= inst->size;
|
||||
}
|
||||
}
|
||||
|
||||
void disassembler::dissasemble_instruction(const instruction::ptr& inst)
|
||||
{
|
||||
switch (static_cast<opcode>(inst->opcode))
|
||||
{
|
||||
|
||||
default:
|
||||
throw disasm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
|
||||
}
|
||||
}
|
||||
|
||||
void disassembler::disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||
}
|
||||
|
||||
const auto id = script_->read<std::uint16_t>();
|
||||
const auto name = method ? resolver::method_name(id) : resolver::function_name(id);
|
||||
inst->data.emplace(inst->data.begin(), name);
|
||||
}
|
||||
|
||||
void disassembler::disassemble_local_call(const instruction::ptr& inst, bool thread)
|
||||
{
|
||||
const auto offset = disassemble_offset();
|
||||
|
||||
inst->data.push_back(utils::string::va("%X", offset + inst->index + 1));
|
||||
|
||||
if (thread)
|
||||
{
|
||||
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||
}
|
||||
}
|
||||
|
||||
void disassembler::disassemble_far_call(const instruction::ptr& inst, bool thread)
|
||||
{
|
||||
script_->seek(3);
|
||||
|
||||
if (thread)
|
||||
{
|
||||
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
|
||||
}
|
||||
|
||||
const auto file_id = stack_->read<std::uint32_t>();
|
||||
const auto file_name = file_id == 0 ? decrypt_string(stack_->read_c_string()) : resolver::token_name(file_id);
|
||||
const auto func_id = stack_->read<std::uint32_t>();
|
||||
const auto func_name = func_id == 0 ? decrypt_string(stack_->read_c_string()) : resolver::token_name(func_id);
|
||||
|
||||
inst->data.emplace(inst->data.begin(), func_name);
|
||||
inst->data.emplace(inst->data.begin(), file_name);
|
||||
}
|
||||
|
||||
void disassembler::disassemble_switch(const instruction::ptr& inst)
|
||||
{
|
||||
const auto addr = inst->index + 4 + script_->read<std::int32_t>();
|
||||
const auto label = utils::string::va("loc_%X", addr);
|
||||
|
||||
inst->data.push_back(label);
|
||||
labels_.insert({ addr, label });
|
||||
}
|
||||
|
||||
void disassembler::disassemble_end_switch(const instruction::ptr& inst)
|
||||
{
|
||||
const auto count = script_->read<std::uint16_t>();
|
||||
inst->data.push_back(utils::string::va("%i", count));
|
||||
|
||||
std::uint32_t index = inst->index + 3;
|
||||
|
||||
if (count)
|
||||
{
|
||||
for (auto i = count; i > 0; i--)
|
||||
{
|
||||
const auto value = script_->read<std::uint32_t>();
|
||||
|
||||
if (value < 0x100000 && value > 0)
|
||||
{
|
||||
inst->data.push_back("case");
|
||||
inst->data.push_back(utils::string::quote(decrypt_string(stack_->read_c_string()), false));
|
||||
}
|
||||
else if (value == 0)
|
||||
{
|
||||
inst->data.push_back("default");
|
||||
stack_->read_c_string(); // this should be always [0x01 0x00] unencrypted
|
||||
}
|
||||
else
|
||||
{
|
||||
inst->data.push_back("case");
|
||||
inst->data.push_back(utils::string::va("%i", (value - 0x800000) & 0xFFFFFF));
|
||||
}
|
||||
|
||||
index += 4;
|
||||
|
||||
const auto addr = disassemble_offset() + index;
|
||||
const auto label = utils::string::va("loc_%X", addr);
|
||||
|
||||
inst->data.push_back(label);
|
||||
labels_.insert({ addr, label });
|
||||
|
||||
index += 3;
|
||||
inst->size += 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disassembler::disassemble_field_variable(const instruction::ptr& inst)
|
||||
{
|
||||
const auto id = script_->read<std::uint32_t>();
|
||||
std::string name;
|
||||
|
||||
if (id > max_string_id)
|
||||
{
|
||||
auto temp = stack_->read<std::uint32_t>();
|
||||
name = temp == 0 ? decrypt_string(stack_->read_c_string()) : std::to_string(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = resolver::token_name(id);
|
||||
}
|
||||
|
||||
inst->data.push_back(name);
|
||||
}
|
||||
|
||||
void disassembler::disassemble_formal_params(const instruction::ptr& inst)
|
||||
{
|
||||
const auto count = script_->read<std::uint8_t>();
|
||||
|
||||
inst->size += count;
|
||||
inst->data.push_back(utils::string::va("%i", count));
|
||||
|
||||
for (auto i = 0u; i < count; i++)
|
||||
{
|
||||
inst->data.push_back(utils::string::va("%d", script_->read<std::uint8_t>()));
|
||||
}
|
||||
}
|
||||
|
||||
void disassembler::disassemble_jump(const instruction::ptr& inst, bool expr, bool back)
|
||||
{
|
||||
std::int32_t addr;
|
||||
|
||||
if (expr)
|
||||
{
|
||||
addr = inst->index + 3 + script_->read<std::int16_t>();
|
||||
}
|
||||
else if (back)
|
||||
{
|
||||
addr = inst->index + 3 - script_->read<std::uint16_t>();
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = inst->index + 5 + script_->read<std::int32_t>();
|
||||
}
|
||||
|
||||
const auto label = utils::string::va("loc_%X", addr);
|
||||
|
||||
inst->data.push_back(label);
|
||||
labels_.insert({ addr, label });
|
||||
}
|
||||
|
||||
auto disassembler::disassemble_offset() -> std::int32_t
|
||||
{
|
||||
std::array<std::uint8_t, 4> bytes = {};
|
||||
|
||||
for (auto i = 0; i < 3; i++)
|
||||
{
|
||||
bytes[i] = script_->read<std::uint8_t>();
|
||||
}
|
||||
|
||||
auto offset = *reinterpret_cast<std::int32_t*>(bytes.data());
|
||||
|
||||
offset = (offset << 8) >> 8;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void disassembler::resolve_local_functions()
|
||||
{
|
||||
for (const auto& func : functions_)
|
||||
{
|
||||
for (const auto& inst : func->instructions)
|
||||
{
|
||||
switch (static_cast<opcode>(inst->opcode))
|
||||
{
|
||||
/*case opcode::OP_GetLocalFunction:
|
||||
case opcode::OP_ScriptLocalFunctionCall:
|
||||
case opcode::OP_ScriptLocalFunctionCall2:
|
||||
case opcode::OP_ScriptLocalMethodCall:
|
||||
case opcode::OP_ScriptLocalThreadCall:
|
||||
case opcode::OP_ScriptLocalChildThreadCall:
|
||||
case opcode::OP_ScriptLocalMethodThreadCall:
|
||||
case opcode::OP_ScriptLocalMethodChildThreadCall:
|
||||
inst->data[0] = resolve_function(inst->data[0]);
|
||||
break;*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto disassembler::resolve_function(const std::string& index) -> std::string
|
||||
{
|
||||
if (utils::string::is_hex_number(index))
|
||||
{
|
||||
std::uint32_t idx = std::stoul(index, nullptr, 16);
|
||||
|
||||
for (const auto& func : functions_)
|
||||
{
|
||||
if (func->index == idx)
|
||||
{
|
||||
return func->name;
|
||||
}
|
||||
}
|
||||
|
||||
throw disasm_error(utils::string::va("couldn't resolve function name at index '0x%04X'!", idx));
|
||||
}
|
||||
|
||||
throw disasm_error(utils::string::va("\"%s\" is not valid function address!", index.data()));
|
||||
}
|
||||
|
||||
auto disassembler::decrypt_string(const std::string& str) -> std::string
|
||||
{
|
||||
if (str.size() > 0 && ((static_cast<std::uint8_t>(str[0]) & 0xC0) == 0x80))
|
||||
{
|
||||
std::string data = "_encstr_";
|
||||
|
||||
for (auto i = 0u; i < str.size(); i++)
|
||||
{
|
||||
data = utils::string::va("%s%02X", data.data(), static_cast<std::uint8_t>(str[i]));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void disassembler::print_function(const function::ptr& func)
|
||||
{
|
||||
output_->write_string("\n");
|
||||
output_->write_string(utils::string::va("sub_%s\n", func->name.data()));
|
||||
|
||||
for (const auto& inst : func->instructions)
|
||||
{
|
||||
const auto itr = func->labels.find(inst->index);
|
||||
|
||||
if (itr != func->labels.end())
|
||||
{
|
||||
output_->write_string(utils::string::va("\t%s\n", itr->second.data()));
|
||||
}
|
||||
|
||||
print_instruction(inst);
|
||||
}
|
||||
|
||||
output_->write_string(utils::string::va("end_%s\n", func->name.data()));
|
||||
}
|
||||
|
||||
void disassembler::print_instruction(const instruction::ptr& inst)
|
||||
{
|
||||
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
|
||||
|
||||
switch (static_cast<opcode>(inst->opcode))
|
||||
{
|
||||
/*case opcode::OP_GetLocalFunction:
|
||||
case opcode::OP_ScriptLocalFunctionCall:
|
||||
case opcode::OP_ScriptLocalFunctionCall2:
|
||||
case opcode::OP_ScriptLocalMethodCall:
|
||||
output_->write_string(utils::string::va(" sub_%s", inst->data[0].data()));
|
||||
break;
|
||||
case opcode::OP_ScriptLocalThreadCall:
|
||||
case opcode::OP_ScriptLocalChildThreadCall:
|
||||
case opcode::OP_ScriptLocalMethodThreadCall:
|
||||
case opcode::OP_ScriptLocalMethodChildThreadCall:
|
||||
output_->write_string(utils::string::va(" sub_%s %s\n", inst->data[0].data(), inst->data[1].data()));
|
||||
break;
|
||||
case opcode::OP_endswitch:
|
||||
output_->write_string(utils::string::va(" %s\n", inst->data[0].data()));
|
||||
{
|
||||
std::uint32_t totalcase = std::stoul(inst->data[0]);
|
||||
auto index = 0;
|
||||
for (auto casenum = 0u; casenum < totalcase; casenum++)
|
||||
{
|
||||
if (inst->data[1 + index] == "case")
|
||||
{
|
||||
output_->write_string(utils::string::va("\t\t\t%s %s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data(), inst->data[1 + index + 2].data()));
|
||||
index += 3;
|
||||
}
|
||||
else if (inst->data[1 + index] == "default")
|
||||
{
|
||||
output_->write_string(utils::string::va("\t\t\t%s %s", inst->data[1 + index].data(), inst->data[1 + index + 1].data()));
|
||||
index += 2;
|
||||
}
|
||||
if (casenum != totalcase - 1)
|
||||
{
|
||||
output_->write_string("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;*/
|
||||
default:
|
||||
for (auto& data : inst->data)
|
||||
{
|
||||
output_->write_string(utils::string::va(" %s", data.data()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
output_->write_string("\n");
|
||||
}
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
44
src/iw9/xsk/disassembler.hpp
Normal file
44
src/iw9/xsk/disassembler.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
class disassembler : public gsc::disassembler
|
||||
{
|
||||
std::string filename_;
|
||||
utils::byte_buffer::ptr script_;
|
||||
utils::byte_buffer::ptr stack_;
|
||||
utils::byte_buffer::ptr output_;
|
||||
std::vector<function::ptr> functions_;
|
||||
std::unordered_map<std::uint32_t, std::string> labels_;
|
||||
|
||||
public:
|
||||
auto output() -> std::vector<function::ptr>;
|
||||
auto output_data() -> std::vector<std::uint8_t>;
|
||||
void disassemble(const std::string& file, std::vector<std::uint8_t>& script, std::vector<std::uint8_t>& stack);
|
||||
|
||||
private:
|
||||
void dissasemble_function(const function::ptr& func);
|
||||
void dissasemble_instruction(const instruction::ptr& inst);
|
||||
void disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args);
|
||||
void disassemble_local_call(const instruction::ptr& inst, bool thread);
|
||||
void disassemble_far_call(const instruction::ptr& inst, bool thread);
|
||||
void disassemble_switch(const instruction::ptr& inst);
|
||||
void disassemble_end_switch(const instruction::ptr& inst);
|
||||
void disassemble_field_variable(const instruction::ptr& inst);
|
||||
void disassemble_formal_params(const instruction::ptr& inst);
|
||||
void disassemble_jump(const instruction::ptr& inst, bool expr, bool back);
|
||||
auto disassemble_offset() -> std::int32_t;
|
||||
void resolve_local_functions();
|
||||
auto resolve_function(const std::string& index) -> std::string;
|
||||
auto decrypt_string(const std::string& str) -> std::string;
|
||||
void print_function(const function::ptr& func);
|
||||
void print_instruction(const instruction::ptr& inst);
|
||||
};
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
22
src/iw9/xsk/iw9.cpp
Normal file
22
src/iw9/xsk/iw9.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#include "stdafx.hpp"
|
||||
#include "iw9.hpp"
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
auto opcode_size(std::uint8_t id) -> std::uint32_t
|
||||
{
|
||||
switch (static_cast<opcode>(id))
|
||||
{
|
||||
|
||||
default:
|
||||
throw error("couldn't resolve instruction size for " + std::to_string(id));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
193
src/iw9/xsk/iw9.hpp
Normal file
193
src/iw9/xsk/iw9.hpp
Normal file
@ -0,0 +1,193 @@
|
||||
// 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 "disassembler.hpp"
|
||||
#include "resolver.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
constexpr std::uint32_t max_string_id = 0x1472F;
|
||||
|
||||
enum class opcode : std::uint8_t
|
||||
{
|
||||
unk_000 = 0x00,
|
||||
unk_001 = 0x01,
|
||||
unk_002 = 0x02,
|
||||
unk_003 = 0x03,
|
||||
unk_004 = 0x04,
|
||||
unk_005 = 0x05,
|
||||
unk_006 = 0x06,
|
||||
unk_007 = 0x07,
|
||||
unk_008 = 0x08,
|
||||
unk_009 = 0x09,
|
||||
unk_010 = 0x0A,
|
||||
unk_011 = 0x0B,
|
||||
unk_012 = 0x0C,
|
||||
unk_013 = 0x0D,
|
||||
unk_014 = 0x0E,
|
||||
unk_015 = 0x0F,
|
||||
unk_016 = 0x10,
|
||||
unk_017 = 0x11,
|
||||
unk_018 = 0x12,
|
||||
unk_019 = 0x13,
|
||||
unk_020 = 0x14,
|
||||
unk_021 = 0x15,
|
||||
unk_022 = 0x16,
|
||||
unk_023 = 0x17,
|
||||
unk_024 = 0x18,
|
||||
unk_025 = 0x19,
|
||||
unk_026 = 0x1A,
|
||||
unk_027 = 0x1B,
|
||||
unk_028 = 0x1C,
|
||||
unk_029 = 0x1D,
|
||||
unk_030 = 0x1E,
|
||||
unk_031 = 0x1F,
|
||||
unk_032 = 0x20,
|
||||
unk_033 = 0x21,
|
||||
unk_034 = 0x22,
|
||||
unk_035 = 0x23,
|
||||
unk_036 = 0x24,
|
||||
unk_037 = 0x25,
|
||||
unk_038 = 0x26,
|
||||
unk_039 = 0x27,
|
||||
unk_040 = 0x28,
|
||||
unk_041 = 0x29,
|
||||
unk_042 = 0x2A,
|
||||
unk_043 = 0x2B,
|
||||
unk_044 = 0x2C,
|
||||
unk_045 = 0x2D,
|
||||
unk_046 = 0x2E,
|
||||
unk_047 = 0x2F,
|
||||
unk_048 = 0x30,
|
||||
unk_049 = 0x31,
|
||||
unk_050 = 0x32,
|
||||
unk_051 = 0x33,
|
||||
unk_052 = 0x34,
|
||||
unk_053 = 0x35,
|
||||
unk_054 = 0x36,
|
||||
unk_055 = 0x37,
|
||||
unk_056 = 0x38,
|
||||
unk_057 = 0x39,
|
||||
unk_058 = 0x3A,
|
||||
unk_059 = 0x3B,
|
||||
unk_060 = 0x3C,
|
||||
unk_061 = 0x3D,
|
||||
unk_062 = 0x3E,
|
||||
unk_063 = 0x3F,
|
||||
unk_064 = 0x40,
|
||||
unk_065 = 0x41,
|
||||
unk_066 = 0x42,
|
||||
unk_067 = 0x43,
|
||||
unk_068 = 0x44,
|
||||
unk_069 = 0x45,
|
||||
unk_070 = 0x46,
|
||||
unk_071 = 0x47,
|
||||
unk_072 = 0x48,
|
||||
unk_073 = 0x49,
|
||||
unk_074 = 0x4A,
|
||||
unk_075 = 0x4B,
|
||||
unk_076 = 0x4C,
|
||||
unk_077 = 0x4D,
|
||||
unk_078 = 0x4E,
|
||||
unk_079 = 0x4F,
|
||||
unk_080 = 0x50,
|
||||
unk_081 = 0x51,
|
||||
unk_082 = 0x52,
|
||||
unk_083 = 0x53,
|
||||
unk_084 = 0x54,
|
||||
unk_085 = 0x55,
|
||||
unk_086 = 0x56,
|
||||
unk_087 = 0x57,
|
||||
unk_088 = 0x58,
|
||||
unk_089 = 0x59,
|
||||
unk_090 = 0x5A,
|
||||
unk_091 = 0x5B,
|
||||
unk_092 = 0x5C,
|
||||
unk_093 = 0x5D,
|
||||
unk_094 = 0x5E,
|
||||
unk_095 = 0x5F,
|
||||
unk_096 = 0x60,
|
||||
unk_097 = 0x61,
|
||||
unk_098 = 0x62,
|
||||
unk_099 = 0x63,
|
||||
unk_100 = 0x64,
|
||||
unk_101 = 0x65,
|
||||
unk_102 = 0x66,
|
||||
unk_103 = 0x67,
|
||||
unk_104 = 0x68,
|
||||
unk_105 = 0x69,
|
||||
unk_106 = 0x6A,
|
||||
unk_107 = 0x6B,
|
||||
unk_108 = 0x6C,
|
||||
unk_109 = 0x6D,
|
||||
unk_110 = 0x6E,
|
||||
unk_111 = 0x6F,
|
||||
unk_112 = 0x70,
|
||||
unk_113 = 0x71,
|
||||
unk_114 = 0x72,
|
||||
unk_115 = 0x73,
|
||||
unk_116 = 0x74,
|
||||
unk_117 = 0x75,
|
||||
unk_118 = 0x76,
|
||||
unk_119 = 0x77,
|
||||
unk_120 = 0x78,
|
||||
unk_121 = 0x79,
|
||||
unk_122 = 0x7A,
|
||||
unk_123 = 0x7B,
|
||||
unk_124 = 0x7C,
|
||||
unk_125 = 0x7D,
|
||||
unk_126 = 0x7E,
|
||||
unk_127 = 0x7F,
|
||||
unk_128 = 0x80,
|
||||
unk_129 = 0x81,
|
||||
unk_130 = 0x82,
|
||||
unk_131 = 0x83,
|
||||
unk_132 = 0x84,
|
||||
unk_133 = 0x85,
|
||||
unk_134 = 0x86,
|
||||
unk_135 = 0x87,
|
||||
unk_136 = 0x88,
|
||||
unk_137 = 0x89,
|
||||
unk_138 = 0x8A,
|
||||
unk_139 = 0x8B,
|
||||
unk_140 = 0x8C,
|
||||
unk_141 = 0x8D,
|
||||
unk_142 = 0x8E,
|
||||
unk_143 = 0x8F,
|
||||
unk_144 = 0x90,
|
||||
unk_145 = 0x91,
|
||||
unk_146 = 0x92,
|
||||
unk_147 = 0x93,
|
||||
unk_148 = 0x94,
|
||||
unk_149 = 0x95,
|
||||
unk_150 = 0x96,
|
||||
unk_151 = 0x97,
|
||||
unk_152 = 0x98,
|
||||
unk_153 = 0x99,
|
||||
unk_154 = 0x9A,
|
||||
unk_155 = 0x9B,
|
||||
unk_156 = 0x9C,
|
||||
unk_157 = 0x9D,
|
||||
unk_158 = 0x9E,
|
||||
unk_159 = 0x9F,
|
||||
unk_160 = 0xA0,
|
||||
unk_161 = 0xA1,
|
||||
unk_162 = 0xA2,
|
||||
unk_163 = 0xA3,
|
||||
unk_164 = 0xA4,
|
||||
unk_165 = 0xA5,
|
||||
unk_166 = 0xA6,
|
||||
OP_count = 0xA7,
|
||||
};
|
||||
|
||||
auto opcode_size(std::uint8_t op) -> std::uint32_t;
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
530
src/iw9/xsk/resolver.cpp
Normal file
530
src/iw9/xsk/resolver.cpp
Normal file
@ -0,0 +1,530 @@
|
||||
// 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 "iw9.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4244)
|
||||
#endif
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
std::unordered_map<std::uint8_t, std::string_view> opcode_map;
|
||||
std::unordered_map<std::uint16_t, std::string_view> function_map;
|
||||
std::unordered_map<std::uint16_t, std::string_view> method_map;
|
||||
std::unordered_map<std::uint32_t, std::string_view> token_map;
|
||||
std::unordered_map<std::string_view, std::uint8_t> opcode_map_rev;
|
||||
std::unordered_map<std::string_view, std::uint16_t> function_map_rev;
|
||||
std::unordered_map<std::string_view, std::uint16_t> method_map_rev;
|
||||
std::unordered_map<std::string_view, std::uint32_t> token_map_rev;
|
||||
std::unordered_map<std::string, std::vector<std::uint8_t>> files;
|
||||
read_cb_type read_callback = nullptr;
|
||||
std::set<std::string> string_map;
|
||||
|
||||
void resolver::init(read_cb_type callback)
|
||||
{
|
||||
read_callback = callback;
|
||||
}
|
||||
|
||||
void resolver::cleanup()
|
||||
{
|
||||
files.clear();
|
||||
}
|
||||
|
||||
auto resolver::opcode_id(const std::string& name) -> std::uint8_t
|
||||
{
|
||||
const auto itr = opcode_map_rev.find(name);
|
||||
|
||||
if (itr != opcode_map_rev.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
throw error(utils::string::va("couldn't resolve opcode id for name '%s'!", name.data()));
|
||||
}
|
||||
|
||||
auto resolver::opcode_name(std::uint8_t id) -> std::string
|
||||
{
|
||||
const auto itr = opcode_map.find(id);
|
||||
|
||||
if (itr != opcode_map.end())
|
||||
{
|
||||
return std::string(itr->second);
|
||||
}
|
||||
|
||||
throw error(utils::string::va("couldn't resolve opcode name for id '0x%hhX'!", id));
|
||||
}
|
||||
|
||||
auto resolver::function_id(const std::string& name) -> std::uint16_t
|
||||
{
|
||||
if (name.starts_with("_func_"))
|
||||
{
|
||||
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
|
||||
}
|
||||
|
||||
const auto itr = function_map_rev.find(name);
|
||||
|
||||
if (itr != function_map_rev.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
throw error(utils::string::va("couldn't resolve builtin function id for name '%s'!", name.data()));
|
||||
}
|
||||
|
||||
auto resolver::function_name(std::uint16_t id) -> std::string
|
||||
{
|
||||
const auto itr = function_map.find(id);
|
||||
|
||||
if (itr != function_map.end())
|
||||
{
|
||||
return std::string(itr->second);
|
||||
}
|
||||
|
||||
return utils::string::va("_func_%04X", id);
|
||||
}
|
||||
|
||||
auto resolver::method_id(const std::string& name) -> std::uint16_t
|
||||
{
|
||||
if (name.starts_with("_meth_"))
|
||||
{
|
||||
return static_cast<std::uint16_t>(std::stoul(name.substr(6), nullptr, 16));
|
||||
}
|
||||
|
||||
const auto itr = method_map_rev.find(name);
|
||||
|
||||
if (itr != method_map_rev.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
throw error(utils::string::va("couldn't resolve builtin method id for name '%s'!", name.data()));
|
||||
}
|
||||
|
||||
auto resolver::method_name(std::uint16_t id) -> std::string
|
||||
{
|
||||
const auto itr = method_map.find(id);
|
||||
|
||||
if (itr != method_map.end())
|
||||
{
|
||||
return std::string(itr->second);
|
||||
}
|
||||
|
||||
return utils::string::va("_meth_%04X", id);
|
||||
}
|
||||
|
||||
auto resolver::token_id(const std::string& name) -> std::uint32_t
|
||||
{
|
||||
if (name.starts_with("_id_"))
|
||||
{
|
||||
return static_cast<std::uint32_t>(std::stoul(name.substr(4), nullptr, 16));
|
||||
}
|
||||
|
||||
const auto itr = token_map_rev.find(name);
|
||||
|
||||
if (itr != token_map_rev.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto resolver::token_name(std::uint32_t id) -> std::string
|
||||
{
|
||||
const auto itr = token_map.find(id);
|
||||
|
||||
if (itr != token_map.end())
|
||||
{
|
||||
return std::string(itr->second);
|
||||
}
|
||||
|
||||
return utils::string::va("_id_%04X", id);
|
||||
}
|
||||
|
||||
auto resolver::find_function(const std::string& name) -> bool
|
||||
{
|
||||
if (name.starts_with("_func_")) return true;
|
||||
|
||||
const auto itr = function_map_rev.find(name);
|
||||
|
||||
if (itr != function_map_rev.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto resolver::find_method(const std::string& name) -> bool
|
||||
{
|
||||
if (name.starts_with("_meth_")) return true;
|
||||
|
||||
const auto itr = method_map_rev.find(name);
|
||||
|
||||
if (itr != method_map_rev.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resolver::add_function(const std::string& name, std::uint16_t id)
|
||||
{
|
||||
const auto itr = function_map_rev.find(name);
|
||||
|
||||
if (itr != function_map_rev.end())
|
||||
{
|
||||
throw error(utils::string::va("builtin function '%s' already defined.", name.data()));
|
||||
}
|
||||
|
||||
const auto str = string_map.find(name);
|
||||
|
||||
if (str != string_map.end())
|
||||
{
|
||||
function_map.insert({ id, *str });
|
||||
function_map_rev.insert({ *str, id });
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ins = string_map.insert(name);
|
||||
|
||||
if (ins.second)
|
||||
{
|
||||
function_map.insert({ id, *ins.first });
|
||||
function_map_rev.insert({ *ins.first, id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolver::add_method(const std::string& name, std::uint16_t id)
|
||||
{
|
||||
const auto itr = method_map_rev.find(name);
|
||||
|
||||
if (itr != method_map_rev.end())
|
||||
{
|
||||
throw error(utils::string::va("builtin method '%s' already defined.", name.data()));
|
||||
}
|
||||
|
||||
const auto str = string_map.find(name);
|
||||
|
||||
if (str != string_map.end())
|
||||
{
|
||||
method_map.insert({ id, *str });
|
||||
method_map_rev.insert({ *str, id });
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ins = string_map.insert(name);
|
||||
|
||||
if (ins.second)
|
||||
{
|
||||
method_map.insert({ id, *ins.first });
|
||||
method_map_rev.insert({ *ins.first, id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto resolver::make_token(std::string_view str) -> std::string
|
||||
{
|
||||
if (str.starts_with("_id_") || str.starts_with("_func_") || str.starts_with("_meth_"))
|
||||
{
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
auto data = std::string(str.begin(), str.end());
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
data[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(str[i])));
|
||||
if (data[i] == '\\') data[i] = '/';
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto resolver::file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>
|
||||
{
|
||||
const auto itr = files.find(name);
|
||||
|
||||
if (itr != files.end())
|
||||
{
|
||||
return { &itr->first ,reinterpret_cast<const char*>(itr->second.data()), itr->second.size() };
|
||||
}
|
||||
|
||||
auto data = read_callback(name);
|
||||
|
||||
const auto res = files.insert({ name, std::move(data)});
|
||||
|
||||
if (res.second)
|
||||
{
|
||||
return { &res.first->first, reinterpret_cast<const char*>(res.first->second.data()), res.first->second.size() };
|
||||
}
|
||||
|
||||
throw error("couldn't open gsc file '" + name + "'");
|
||||
}
|
||||
|
||||
std::set<std::string_view> paths
|
||||
{
|
||||
};
|
||||
|
||||
auto resolver::fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path
|
||||
{
|
||||
auto result = std::filesystem::path();
|
||||
auto root = false;
|
||||
|
||||
for (auto& entry : file)
|
||||
{
|
||||
if (!root && paths.contains(entry.string()))
|
||||
{
|
||||
result = entry;
|
||||
root = true;
|
||||
}
|
||||
else if (paths.contains(result.string()))
|
||||
{
|
||||
result /= entry;
|
||||
}
|
||||
}
|
||||
|
||||
return result.empty() ? file : result;
|
||||
}
|
||||
|
||||
const std::array<std::pair<std::uint8_t, const char*>, 167> opcode_list
|
||||
{{
|
||||
{ 0x00, "unk_000" },
|
||||
{ 0x01, "unk_001" },
|
||||
{ 0x02, "unk_002" },
|
||||
{ 0x03, "unk_003" },
|
||||
{ 0x04, "unk_004" },
|
||||
{ 0x05, "unk_005" },
|
||||
{ 0x06, "unk_006" },
|
||||
{ 0x07, "unk_007" },
|
||||
{ 0x08, "unk_008" },
|
||||
{ 0x09, "unk_009" },
|
||||
{ 0x0A, "unk_010" },
|
||||
{ 0x0B, "unk_011" },
|
||||
{ 0x0C, "unk_012" },
|
||||
{ 0x0D, "unk_013" },
|
||||
{ 0x0E, "unk_014" },
|
||||
{ 0x0F, "unk_015" },
|
||||
{ 0x10, "unk_016" },
|
||||
{ 0x11, "unk_017" },
|
||||
{ 0x12, "unk_018" },
|
||||
{ 0x13, "unk_019" },
|
||||
{ 0x14, "unk_020" },
|
||||
{ 0x15, "unk_021" },
|
||||
{ 0x16, "unk_022" },
|
||||
{ 0x17, "unk_023" },
|
||||
{ 0x18, "unk_024" },
|
||||
{ 0x19, "unk_025" },
|
||||
{ 0x1A, "unk_026" },
|
||||
{ 0x1B, "unk_027" },
|
||||
{ 0x1C, "unk_028" },
|
||||
{ 0x1D, "unk_029" },
|
||||
{ 0x1E, "unk_030" },
|
||||
{ 0x1F, "unk_031" },
|
||||
{ 0x20, "unk_032" },
|
||||
{ 0x21, "unk_033" },
|
||||
{ 0x22, "unk_034" },
|
||||
{ 0x23, "unk_035" },
|
||||
{ 0x24, "unk_036" },
|
||||
{ 0x25, "unk_037" },
|
||||
{ 0x26, "unk_038" },
|
||||
{ 0x27, "unk_039" },
|
||||
{ 0x28, "unk_040" },
|
||||
{ 0x29, "unk_041" },
|
||||
{ 0x2A, "unk_042" },
|
||||
{ 0x2B, "unk_043" },
|
||||
{ 0x2C, "unk_044" },
|
||||
{ 0x2D, "unk_045" },
|
||||
{ 0x2E, "unk_046" },
|
||||
{ 0x2F, "unk_047" },
|
||||
{ 0x30, "unk_048" },
|
||||
{ 0x31, "unk_049" },
|
||||
{ 0x32, "unk_050" },
|
||||
{ 0x33, "unk_051" },
|
||||
{ 0x34, "unk_052" },
|
||||
{ 0x35, "unk_053" },
|
||||
{ 0x36, "unk_054" },
|
||||
{ 0x37, "unk_055" },
|
||||
{ 0x38, "unk_056" },
|
||||
{ 0x39, "unk_057" },
|
||||
{ 0x3A, "unk_058" },
|
||||
{ 0x3B, "unk_059" },
|
||||
{ 0x3C, "unk_060" },
|
||||
{ 0x3D, "unk_061" },
|
||||
{ 0x3E, "unk_062" },
|
||||
{ 0x3F, "unk_063" },
|
||||
{ 0x40, "unk_064" },
|
||||
{ 0x41, "unk_065" },
|
||||
{ 0x42, "unk_066" },
|
||||
{ 0x43, "unk_067" },
|
||||
{ 0x44, "unk_068" },
|
||||
{ 0x45, "unk_069" },
|
||||
{ 0x46, "unk_070" },
|
||||
{ 0x47, "unk_071" },
|
||||
{ 0x48, "unk_072" },
|
||||
{ 0x49, "unk_073" },
|
||||
{ 0x4A, "unk_074" },
|
||||
{ 0x4B, "unk_075" },
|
||||
{ 0x4C, "unk_076" },
|
||||
{ 0x4D, "unk_077" },
|
||||
{ 0x4E, "unk_078" },
|
||||
{ 0x4F, "unk_079" },
|
||||
{ 0x50, "unk_080" },
|
||||
{ 0x51, "unk_081" },
|
||||
{ 0x52, "unk_082" },
|
||||
{ 0x53, "unk_083" },
|
||||
{ 0x54, "unk_084" },
|
||||
{ 0x55, "unk_085" },
|
||||
{ 0x56, "unk_086" },
|
||||
{ 0x57, "unk_087" },
|
||||
{ 0x58, "unk_088" },
|
||||
{ 0x59, "unk_089" },
|
||||
{ 0x5A, "unk_090" },
|
||||
{ 0x5B, "unk_091" },
|
||||
{ 0x5C, "unk_092" },
|
||||
{ 0x5D, "unk_093" },
|
||||
{ 0x5E, "unk_094" },
|
||||
{ 0x5F, "unk_095" },
|
||||
{ 0x60, "unk_096" },
|
||||
{ 0x61, "unk_097" },
|
||||
{ 0x62, "unk_098" },
|
||||
{ 0x63, "unk_099" },
|
||||
{ 0x64, "unk_100" },
|
||||
{ 0x65, "unk_101" },
|
||||
{ 0x66, "unk_102" },
|
||||
{ 0x67, "unk_103" },
|
||||
{ 0x68, "unk_104" },
|
||||
{ 0x69, "unk_105" },
|
||||
{ 0x6A, "unk_106" },
|
||||
{ 0x6B, "unk_107" },
|
||||
{ 0x6C, "unk_108" },
|
||||
{ 0x6D, "unk_109" },
|
||||
{ 0x6E, "unk_110" },
|
||||
{ 0x6F, "unk_111" },
|
||||
{ 0x70, "unk_112" },
|
||||
{ 0x71, "unk_113" },
|
||||
{ 0x72, "unk_114" },
|
||||
{ 0x73, "unk_115" },
|
||||
{ 0x74, "unk_116" },
|
||||
{ 0x75, "unk_117" },
|
||||
{ 0x76, "unk_118" },
|
||||
{ 0x77, "unk_119" },
|
||||
{ 0x78, "unk_120" },
|
||||
{ 0x79, "unk_121" },
|
||||
{ 0x7A, "unk_122" },
|
||||
{ 0x7B, "unk_123" },
|
||||
{ 0x7C, "unk_124" },
|
||||
{ 0x7D, "unk_125" },
|
||||
{ 0x7E, "unk_126" },
|
||||
{ 0x7F, "unk_127" },
|
||||
{ 0x80, "unk_128" },
|
||||
{ 0x81, "unk_129" },
|
||||
{ 0x82, "unk_130" },
|
||||
{ 0x83, "unk_131" },
|
||||
{ 0x84, "unk_132" },
|
||||
{ 0x85, "unk_133" },
|
||||
{ 0x86, "unk_134" },
|
||||
{ 0x87, "unk_135" },
|
||||
{ 0x88, "unk_136" },
|
||||
{ 0x89, "unk_137" },
|
||||
{ 0x8A, "unk_138" },
|
||||
{ 0x8B, "unk_139" },
|
||||
{ 0x8C, "unk_140" },
|
||||
{ 0x8D, "unk_141" },
|
||||
{ 0x8E, "unk_142" },
|
||||
{ 0x8F, "unk_143" },
|
||||
{ 0x90, "unk_144" },
|
||||
{ 0x91, "unk_145" },
|
||||
{ 0x92, "unk_146" },
|
||||
{ 0x93, "unk_147" },
|
||||
{ 0x94, "unk_148" },
|
||||
{ 0x95, "unk_149" },
|
||||
{ 0x96, "unk_150" },
|
||||
{ 0x97, "unk_151" },
|
||||
{ 0x98, "unk_152" },
|
||||
{ 0x99, "unk_153" },
|
||||
{ 0x9A, "unk_154" },
|
||||
{ 0x9B, "unk_155" },
|
||||
{ 0x9C, "unk_156" },
|
||||
{ 0x9D, "unk_157" },
|
||||
{ 0x9E, "unk_158" },
|
||||
{ 0x9F, "unk_159" },
|
||||
{ 0xA0, "unk_160" },
|
||||
{ 0xA1, "unk_161" },
|
||||
{ 0xA2, "unk_162" },
|
||||
{ 0xA3, "unk_163" },
|
||||
{ 0xA4, "unk_164" },
|
||||
{ 0xA5, "unk_165" },
|
||||
{ 0xA6, "unk_166" },
|
||||
}};
|
||||
|
||||
const std::array<std::pair<std::uint16_t, const char*>, 0> function_list
|
||||
{{
|
||||
}};
|
||||
|
||||
const std::array<std::pair<std::uint16_t, const char*>, 0> method_list
|
||||
{{
|
||||
}};
|
||||
|
||||
const std::array<std::pair<std::uint32_t, const char*>, 0> token_list
|
||||
{{
|
||||
}};
|
||||
|
||||
struct __init__
|
||||
{
|
||||
__init__()
|
||||
{
|
||||
static bool init = false;
|
||||
if (init) return;
|
||||
init = true;
|
||||
|
||||
opcode_map.reserve(opcode_list.size());
|
||||
opcode_map_rev.reserve(opcode_list.size());
|
||||
function_map.reserve(function_list.size());
|
||||
function_map_rev.reserve(function_list.size());
|
||||
method_map.reserve(method_list.size());
|
||||
method_map_rev.reserve(method_list.size());
|
||||
token_map.reserve(token_list.size());
|
||||
token_map_rev.reserve(token_list.size());
|
||||
|
||||
for (const auto& entry : opcode_list)
|
||||
{
|
||||
opcode_map.insert({ entry.first, entry.second });
|
||||
opcode_map_rev.insert({ entry.second, entry.first });
|
||||
}
|
||||
|
||||
for (const auto& entry : function_list)
|
||||
{
|
||||
function_map.insert({ entry.first, entry.second });
|
||||
function_map_rev.insert({ entry.second, entry.first });
|
||||
}
|
||||
|
||||
for (const auto& entry : method_list)
|
||||
{
|
||||
method_map.insert({ entry.first, entry.second });
|
||||
method_map_rev.insert({ entry.second, entry.first });
|
||||
}
|
||||
|
||||
for (const auto& entry : token_list)
|
||||
{
|
||||
token_map.insert({ entry.first, entry.second });
|
||||
token_map_rev.insert({ entry.second, entry.first });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
__init__ _;
|
||||
|
||||
} // namespace xsk::gsc::iw8
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
40
src/iw9/xsk/resolver.hpp
Normal file
40
src/iw9/xsk/resolver.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2022 xensik. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a GNU GPLv3 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace xsk::gsc::iw9
|
||||
{
|
||||
|
||||
class resolver
|
||||
{
|
||||
public:
|
||||
static void init(read_cb_type callback);
|
||||
static void cleanup();
|
||||
|
||||
static auto opcode_id(const std::string& name) -> std::uint8_t;
|
||||
static auto opcode_name(std::uint8_t id) -> std::string;
|
||||
|
||||
static auto function_id(const std::string& name) -> std::uint16_t;
|
||||
static auto function_name(std::uint16_t id) -> std::string;
|
||||
|
||||
static auto method_id(const std::string& name) -> std::uint16_t;
|
||||
static auto method_name(std::uint16_t id) -> std::string;
|
||||
|
||||
static auto token_id(const std::string& name) -> std::uint32_t;
|
||||
static auto token_name(std::uint32_t id) -> std::string;
|
||||
|
||||
static auto find_function(const std::string& name) -> bool;
|
||||
static auto find_method(const std::string& name) -> bool;
|
||||
|
||||
static void add_function(const std::string& name, std::uint16_t id);
|
||||
static void add_method(const std::string& name, std::uint16_t id);
|
||||
|
||||
static auto make_token(std::string_view str) -> std::string;
|
||||
static auto file_data(const std::string& name) -> std::tuple<const std::string*, const char*, size_t>;
|
||||
static auto fs_to_game_path(const std::filesystem::path& file) -> std::filesystem::path;
|
||||
};
|
||||
|
||||
} // namespace xsk::gsc::iw9
|
@ -12,6 +12,7 @@
|
||||
#include "iw6/xsk/iw6.hpp"
|
||||
#include "iw7/xsk/iw7.hpp"
|
||||
#include "iw8/xsk/iw8.hpp"
|
||||
#include "iw9/xsk/iw9.hpp"
|
||||
#include "s1/xsk/s1.hpp"
|
||||
#include "s2/xsk/s2.hpp"
|
||||
#include "s4/xsk/s4.hpp"
|
||||
@ -24,7 +25,7 @@ namespace xsk
|
||||
|
||||
enum class encd { _, source, assembly, binary };
|
||||
enum class mode { _, assemble, disassemble, compile, decompile };
|
||||
enum class game { _, iw5c, iw6c, s1c, iw5, iw6, iw7, iw8, s1, s2, s4, h1, h2, t6 };
|
||||
enum class game { _, iw5c, iw6c, s1c, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6 };
|
||||
|
||||
const std::map<std::string, encd> exts =
|
||||
{
|
||||
@ -51,6 +52,7 @@ const std::map<std::string, game> games =
|
||||
{ "iw6", game::iw6 },
|
||||
{ "iw7", game::iw7 },
|
||||
{ "iw8", game::iw8 },
|
||||
{ "iw9", game::iw9 },
|
||||
{ "s1", game::s1 },
|
||||
{ "s2", game::s2 },
|
||||
{ "s4", game::s4 },
|
||||
@ -118,6 +120,8 @@ auto choose_resolver_file_name(uint32_t id, game& game) -> std::string
|
||||
return iw7::resolver::token_name(id);
|
||||
case game::iw8:
|
||||
return iw8::resolver::token_name(id);
|
||||
case game::iw9:
|
||||
return iw9::resolver::token_name(id);
|
||||
case game::s1:
|
||||
return s1::resolver::token_name(static_cast<std::uint16_t>(id));
|
||||
case game::s2:
|
||||
@ -426,6 +430,8 @@ void init()
|
||||
contexts[game::iw7]->init(build::prod, utils::file::read);
|
||||
contexts[game::iw8] = std::make_unique<iw8::context>();
|
||||
contexts[game::iw8]->init(build::prod, utils::file::read);
|
||||
contexts[game::iw9] = std::make_unique<iw9::context>();
|
||||
contexts[game::iw9]->init(build::prod, utils::file::read);
|
||||
contexts[game::s1] = std::make_unique<s1::context>();
|
||||
contexts[game::s1]->init(build::prod, utils::file::read);
|
||||
contexts[game::s2] = std::make_unique<s2::context>();
|
||||
|
Loading…
Reference in New Issue
Block a user