ps3 & xbox360 support for iw5

This commit is contained in:
xensik 2022-08-22 12:03:44 +02:00
parent 9f465644d4
commit 71a3e25be4
18 changed files with 21767 additions and 162 deletions

View File

@ -0,0 +1,577 @@
// 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 "iw5_console.hpp"
namespace xsk::gsc::iw5_console
{
auto assembler::output_script() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> script;
if (script_ == nullptr) return script;
script.resize(script_->pos());
std::memcpy(script.data(), script_->buffer().data(), script.size());
return script;
}
auto assembler::output_stack() -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> stack;
if (stack_ == nullptr) return stack;
stack.resize(stack_->pos());
std::memcpy(stack.data(), stack_->buffer().data(), stack.size());
return stack;
}
void assembler::assemble(const std::string& file, std::vector<std::uint8_t>& data)
{
std::vector<std::string> assembly = utils::string::clean_buffer_lines(data);
std::vector<function::ptr> functions;
function::ptr func = nullptr;
std::uint32_t index = 1;
std::uint16_t switchnum = 0;
for (auto& line : assembly)
{
if (line == "" || line.substr(0, 2) == "//")
{
continue;
}
else if (line.substr(0, 4) == "sub_")
{
func = std::make_unique<function>();
func->index = index;
func->name = line.substr(4);
}
else if (line.substr(0, 4) == "end_")
{
if (func != nullptr)
{
func->size = index - func->index;
functions.push_back(std::move(func));
}
}
else if (line.substr(0, 4) == "loc_")
{
func->labels[index] = line;
}
else
{
auto opdata = utils::string::parse_code(line);
if (switchnum)
{
if (opdata[0] == "case" || opdata[0] == "default")
{
for (auto& entry : opdata)
{
func->instructions.back()->data.push_back(entry);
}
switchnum--;
continue;
}
throw asm_error("invalid instruction inside endswitch \""s + line + "\"!");
}
else
{
auto inst = std::make_unique<instruction>();
inst->index = index;
inst->opcode = static_cast<std::uint8_t>(resolver::opcode_id(opdata[0]));
inst->size = opcode_size(inst->opcode);
opdata.erase(opdata.begin());
inst->data = std::move(opdata);
switch (opcode(inst->opcode))
{
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
inst->data[0] = inst->data[0].substr(4);
break;
case opcode::OP_endswitch:
switchnum = static_cast<std::uint16_t>(std::stoi(inst->data[0]));
inst->size += 7 * switchnum;
break;
default:
break;
}
index += inst->size;
func->instructions.push_back(std::move(inst));
}
}
}
assemble(file, functions);
}
void assembler::assemble(const std::string& file, std::vector<function::ptr>& funcs)
{
script_ = std::make_unique<utils::byte_buffer>(0x100000);
stack_ = std::make_unique<utils::byte_buffer>(0x100000);
filename_ = file;
functions_ = std::move(funcs);
script_->write<std::uint8_t>(static_cast<std::uint8_t>(opcode::OP_End));
for (const auto& func : functions_)
{
assemble_function(func);
}
}
void assembler::assemble_function(const function::ptr& func)
{
labels_ = func->labels;
func->id = resolver::token_id(func->name);
stack_->write_endian<std::uint32_t>(func->size);
stack_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(func->id));
if (func->id == 0)
{
stack_->write_c_string(func->name);
}
for (const auto& inst : func->instructions)
{
assemble_instruction(inst);
}
}
void assembler::assemble_instruction(const instruction::ptr& inst)
{
switch (opcode(inst->opcode))
{
case opcode::OP_End:
case opcode::OP_Return:
case opcode::OP_GetUndefined:
case opcode::OP_GetZero:
case opcode::OP_waittillFrameEnd:
case opcode::OP_EvalLocalVariableCached0:
case opcode::OP_EvalLocalVariableCached1:
case opcode::OP_EvalLocalVariableCached2:
case opcode::OP_EvalLocalVariableCached3:
case opcode::OP_EvalLocalVariableCached4:
case opcode::OP_EvalLocalVariableCached5:
case opcode::OP_EvalArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_ClearArray:
case opcode::OP_EmptyArray:
case opcode::OP_AddArray:
case opcode::OP_PreScriptCall:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_GetLevelObject:
case opcode::OP_GetAnimObject:
case opcode::OP_GetSelf:
case opcode::OP_GetThisthread:
case opcode::OP_GetLevel:
case opcode::OP_GetGame:
case opcode::OP_GetAnim:
case opcode::OP_GetGameRef:
case opcode::OP_inc:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_bit_ex_or:
case opcode::OP_bit_and:
case opcode::OP_equality:
case opcode::OP_inequality:
case opcode::OP_less:
case opcode::OP_greater:
case opcode::OP_less_equal:
case opcode::OP_waittillmatch2:
case opcode::OP_waittill:
case opcode::OP_notify:
case opcode::OP_endon:
case opcode::OP_voidCodepos:
case opcode::OP_vector:
case opcode::OP_greater_equal:
case opcode::OP_shift_left:
case opcode::OP_shift_right:
case opcode::OP_plus:
case opcode::OP_minus:
case opcode::OP_multiply:
case opcode::OP_divide:
case opcode::OP_mod:
case opcode::OP_size:
case opcode::OP_GetSelfObject:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_clearparams:
case opcode::OP_checkclearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_SetVariableField:
case opcode::OP_ClearVariableField:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_wait:
case opcode::OP_DecTop:
case opcode::OP_CastFieldObject:
case opcode::OP_CastBool:
case opcode::OP_BoolNot:
case opcode::OP_BoolComplement:
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_endian<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[0])));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write_endian<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_->write_endian<std::int32_t>(std::stoi(inst->data[0]));
break;
case opcode::OP_GetFloat:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write_endian<float>(std::stof(inst->data[0]));
break;
case opcode::OP_GetVector:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
inst->size += script_->align(4);
script_->write_endian<float>(std::stof(inst->data[0]));
script_->write_endian<float>(std::stof(inst->data[1]));
script_->write_endian<float>(std::stof(inst->data[2]));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
break;
case opcode::OP_GetAnimation:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint32_t>(0);
stack_->write_c_string(inst->data[0]);
stack_->write_c_string(inst->data[1]);
break;
case opcode::OP_GetAnimTree:
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(0);
stack_->write_c_string(inst->data[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_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_EvalLocalArrayCached:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
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_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalSelfFieldVariableRef:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_ClearFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
assemble_field_variable(inst);
break;
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptChildThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
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_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
case opcode::OP_GetLocalFunction:
assemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
assemble_local_call(inst, true);
break;
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
case opcode::OP_GetFarFunction:
assemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
assemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
assemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
assemble_builtin_call(inst, true, true);
break;
case opcode::OP_GetBuiltinFunction:
case opcode::OP_CallBuiltin0:
case opcode::OP_CallBuiltin1:
case opcode::OP_CallBuiltin2:
case opcode::OP_CallBuiltin3:
case opcode::OP_CallBuiltin4:
case opcode::OP_CallBuiltin5:
assemble_builtin_call(inst, false, false);
break;
case opcode::OP_GetBuiltinMethod:
case opcode::OP_CallBuiltinMethod0:
case opcode::OP_CallBuiltinMethod1:
case opcode::OP_CallBuiltinMethod2:
case opcode::OP_CallBuiltinMethod3:
case opcode::OP_CallBuiltinMethod4:
case opcode::OP_CallBuiltinMethod5:
assemble_builtin_call(inst, true, false);
break;
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
assemble_jump(inst, true, false);
break;
case opcode::OP_jumpback:
assemble_jump(inst, false, true);
break;
case opcode::OP_jump:
assemble_jump(inst, false, false);
break;
case opcode::OP_switch:
assemble_switch(inst);
break;
case opcode::OP_endswitch:
assemble_end_switch(inst);
break;
default:
throw asm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void assembler::assemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
if (args)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
const auto id = method ? resolver::method_id(inst->data[0]) : resolver::function_id(inst->data[0]);
script_->write_endian<std::uint16_t>(id);
}
void assembler::assemble_local_call(const instruction::ptr& inst, bool thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
const auto addr = resolve_function(inst->data[0]);
const auto offset = static_cast<std::int32_t>(addr - inst->index - 1);
assemble_offset(offset);
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[1])));
}
}
void assembler::assemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
script_->write<std::uint8_t>(0);
script_->write<std::uint16_t>(0);
if (thread)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(std::stoi(inst->data[2])));
}
const auto file_id = resolver::token_id(inst->data[0]);
const auto func_id = resolver::token_id(inst->data[1]);
stack_->write_endian<std::uint16_t>(file_id);
if (file_id == 0) stack_->write_c_string(inst->data[0]);
stack_->write_endian<std::uint16_t>(func_id);
if (func_id == 0) stack_->write_c_string(inst->data[1]);
}
void assembler::assemble_switch(const instruction::ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
const auto addr = resolve_label(inst->data[0]);
script_->write_endian<std::int32_t>(addr - inst->index - 4);
}
void assembler::assemble_end_switch(const instruction::ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
const auto count = std::stoul(inst->data[0]);
script_->write_endian<std::uint16_t>(static_cast<std::uint16_t>(count));
std::uint32_t index = inst->index + 3;
for (auto i = 0u; i < count; i++)
{
if (inst->data[1 + (3 * i)] == "case")
{
if (utils::string::is_number(inst->data[1 + (3 * i) + 1]))
{
script_->write_endian<uint32_t>((std::stoi(inst->data[1 + (3 * i) + 1]) & 0xFFFFFF) + 0x800000);
}
else
{
script_->write_endian<uint32_t>(i + 1);
stack_->write_c_string(inst->data[1 + (3 * i) + 1]);
}
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 2]);
assemble_offset(addr - index);
index += 3;
}
else if (inst->data[1 + (3 * i)] == "default")
{
script_->write_endian<uint32_t>(0);
stack_->write_c_string("\x01");
index += 4;
const auto addr = resolve_label(inst->data[1 + (3 * i) + 1]);
assemble_offset(addr - index);
index += 3;
}
else
{
throw asm_error("invalid switch case '" + inst->data[1 + (3 * i)] + "'!");
}
}
}
void assembler::assemble_field_variable(const instruction::ptr& inst)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
auto id = resolver::token_id(inst->data[0]);
if (id == 0) id = 0xFFFF;
script_->write_endian<std::uint16_t>(id);
if (id > max_string_id)
{
stack_->write<std::uint16_t>(0);
stack_->write_c_string(inst->data[0]);
}
}
void assembler::assemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
script_->write<std::uint8_t>(static_cast<std::uint8_t>(inst->opcode));
const auto addr = resolve_label(inst->data[0]);
if (expr)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>(addr - inst->index - 3));
}
else if (back)
{
script_->write_endian<std::int16_t>(static_cast<std::int16_t>((inst->index + 3) - addr));
}
else
{
script_->write_endian<std::int32_t>(static_cast<std::int32_t>(addr - inst->index - 5));
}
}
void assembler::assemble_offset(std::int32_t offset)
{
std::array<std::uint8_t, 4> bytes = {};
offset = (offset << 10) >> 8;
*reinterpret_cast<std::int32_t*>(bytes.data()) = offset;
script_->write<std::uint8_t>(bytes[2]);
script_->write<std::uint8_t>(bytes[1]);
script_->write<std::uint8_t>(bytes[0]);
}
auto assembler::resolve_function(const std::string& name) -> std::int32_t
{
for (const auto& entry : functions_)
{
if (entry->name == name)
{
return entry->index;
}
}
throw asm_error("couldn't resolve local function address of '" + name + "'!");
}
auto assembler::resolve_label(const std::string& name) -> std::int32_t
{
for (const auto& entry : labels_)
{
if (entry.second == name)
{
return entry.first;
}
}
throw asm_error("couldn't resolve label address of '" + name + "'!");
}
} // namespace xsk::gsc::iw5_console

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,23 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdafx.hpp"
#include "iw5_console.hpp"
namespace xsk::gsc::iw5_console
{
void context::init(build mode, read_cb_type callback)
{
compiler_.mode(mode);
resolver::init(callback);
}
void context::cleanup()
{
resolver::cleanup();
}
} // namespace xsk::gsc::iw5_console

View File

@ -0,0 +1,28 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::iw5_console
{
class context : public gsc::context
{
iw5_console::assembler assembler_;
iw5_console::disassembler disassembler_;
iw5_console::compiler compiler_;
iw5_console::decompiler decompiler_;
public:
void init(build mode, read_cb_type callback);
void cleanup();
auto assembler() -> gsc::assembler& { return assembler_; }
auto disassembler() -> gsc::disassembler& { return disassembler_; }
auto compiler() -> gsc::compiler& { return compiler_; }
auto decompiler() -> gsc::decompiler& { return decompiler_; }
};
} // namespace xsk::gsc::iw5_console

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,579 @@
// 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 "iw5_console.hpp"
namespace xsk::gsc::iw5_console
{
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("// IW5 GSC ASSEMBLY\n");
output_->write_string("// Disassembled by https://github.com/xensik/gsc-tool\n");
for (const auto& func : functions_)
{
print_function(func);
}
std::vector<std::uint8_t> output;
output.resize(output_->pos());
std::memcpy(output.data(), output_->buffer().data(), output.size());
return output;
}
void disassembler::disassemble(const std::string& file, std::vector<std::uint8_t>& script, std::vector<std::uint8_t>& stack)
{
filename_ = file;
script_ = std::make_unique<utils::byte_buffer>(script);
stack_ = std::make_unique<utils::byte_buffer>(stack);
functions_.clear();
script_->seek(1);
while (stack_->is_avail() && script_->is_avail())
{
functions_.push_back(std::make_unique<function>());
const auto& func = functions_.back();
func->index = static_cast<std::uint32_t>(script_->pos());
func->size = stack_->read_endian<std::uint32_t>();
func->id = stack_->read_endian<std::uint16_t>();
func->name = func->id == 0 ? stack_->read_c_string() : resolver::token_name(static_cast<std::uint16_t>(func->id));
dissasemble_function(func);
func->labels = labels_;
labels_.clear();
}
resolve_local_functions();
}
void disassembler::dissasemble_function(const function::ptr& func)
{
auto size = func->size;
while (size > 0)
{
func->instructions.push_back(std::make_unique<instruction>());
const auto& inst = func->instructions.back();
inst->index = static_cast<std::uint32_t>(script_->pos());
inst->opcode = script_->read<std::uint8_t>();
inst->size = opcode_size(inst->opcode);
dissasemble_instruction(inst);
if(inst->size > size)
{
throw disasm_error("aaaaa");
}
size -= inst->size;
}
}
void disassembler::dissasemble_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_waittillFrameEnd:
case opcode::OP_EvalLocalVariableCached0:
case opcode::OP_EvalLocalVariableCached1:
case opcode::OP_EvalLocalVariableCached2:
case opcode::OP_EvalLocalVariableCached3:
case opcode::OP_EvalLocalVariableCached4:
case opcode::OP_EvalLocalVariableCached5:
case opcode::OP_EvalArray:
case opcode::OP_EvalArrayRef:
case opcode::OP_EvalLocalArrayRefCached0:
case opcode::OP_ClearArray:
case opcode::OP_EmptyArray:
case opcode::OP_AddArray:
case opcode::OP_PreScriptCall:
case opcode::OP_ScriptFunctionCallPointer:
case opcode::OP_ScriptMethodCallPointer:
case opcode::OP_GetLevelObject:
case opcode::OP_GetAnimObject:
case opcode::OP_GetSelf:
case opcode::OP_GetThisthread:
case opcode::OP_GetLevel:
case opcode::OP_GetGame:
case opcode::OP_GetAnim:
case opcode::OP_GetGameRef:
case opcode::OP_inc:
case opcode::OP_dec:
case opcode::OP_bit_or:
case opcode::OP_bit_ex_or:
case opcode::OP_bit_and:
case opcode::OP_equality:
case opcode::OP_inequality:
case opcode::OP_less:
case opcode::OP_greater:
case opcode::OP_less_equal:
case opcode::OP_waittillmatch2:
case opcode::OP_waittill:
case opcode::OP_notify:
case opcode::OP_endon:
case opcode::OP_voidCodepos:
case opcode::OP_vector:
case opcode::OP_greater_equal:
case opcode::OP_shift_left:
case opcode::OP_shift_right:
case opcode::OP_plus:
case opcode::OP_minus:
case opcode::OP_multiply:
case opcode::OP_divide:
case opcode::OP_mod:
case opcode::OP_size:
case opcode::OP_GetSelfObject:
case opcode::OP_SafeSetVariableFieldCached0:
case opcode::OP_clearparams:
case opcode::OP_checkclearparams:
case opcode::OP_EvalLocalVariableRefCached0:
case opcode::OP_EvalNewLocalVariableRefCached0:
case opcode::OP_SetVariableField:
case opcode::OP_ClearVariableField:
case opcode::OP_SetLocalVariableFieldCached0:
case opcode::OP_ClearLocalVariableFieldCached0:
case opcode::OP_wait:
case opcode::OP_DecTop:
case opcode::OP_CastFieldObject:
case opcode::OP_CastBool:
case opcode::OP_BoolNot:
case opcode::OP_BoolComplement:
break;
case opcode::OP_GetByte:
case opcode::OP_GetNegByte:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_GetUnsignedShort:
case opcode::OP_GetNegUnsignedShort:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::uint16_t>()));
break;
case opcode::OP_GetInteger:
inst->data.push_back(utils::string::va("%i", script_->read_endian<std::int32_t>()));
break;
case opcode::OP_GetFloat:
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
break;
case opcode::OP_GetVector:
inst->size += script_->align(4);
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
inst->data.push_back(utils::string::float_string(script_->read_endian<float>()));
break;
case opcode::OP_GetString:
case opcode::OP_GetIString:
script_->seek(2);
inst->data.push_back(utils::string::to_literal(stack_->read_c_string()));
break;
case opcode::OP_GetAnimation:
script_->seek(4);
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_GetAnimTree:
script_->seek(1);
inst->data.push_back(utils::string::quote(stack_->read_c_string(), false));
break;
case opcode::OP_waittillmatch:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_CreateLocalVariable:
case opcode::OP_RemoveLocalVariables:
case opcode::OP_EvalLocalVariableCached:
case opcode::OP_EvalLocalArrayCached:
case opcode::OP_EvalNewLocalArrayRefCached0:
case opcode::OP_EvalLocalArrayRefCached:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
case opcode::OP_EvalLocalVariableRefCached:
case opcode::OP_SetNewLocalVariableFieldCached0:
case opcode::OP_SetLocalVariableFieldCached:
case opcode::OP_ClearLocalVariableFieldCached:
case opcode::OP_EvalLocalVariableObjectCached:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariable:
case opcode::OP_EvalFieldVariable:
case opcode::OP_EvalLevelFieldVariableRef:
case opcode::OP_EvalAnimFieldVariableRef:
case opcode::OP_EvalSelfFieldVariableRef:
case opcode::OP_EvalFieldVariableRef:
case opcode::OP_ClearFieldVariable:
case opcode::OP_SetLevelFieldVariableField:
case opcode::OP_SetAnimFieldVariableField:
case opcode::OP_SetSelfFieldVariableField:
disassemble_field_variable(inst);
break;
case opcode::OP_ScriptThreadCallPointer:
case opcode::OP_ScriptChildThreadCallPointer:
case opcode::OP_ScriptMethodThreadCallPointer:
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
break;
case opcode::OP_GetLocalFunction:
case opcode::OP_ScriptLocalFunctionCall2:
case opcode::OP_ScriptLocalFunctionCall:
case opcode::OP_ScriptLocalMethodCall:
disassemble_local_call(inst, false);
break;
case opcode::OP_ScriptLocalThreadCall:
case opcode::OP_ScriptLocalChildThreadCall:
case opcode::OP_ScriptLocalMethodThreadCall:
case opcode::OP_ScriptLocalMethodChildThreadCall:
disassemble_local_call(inst, true);
break;
case opcode::OP_GetFarFunction:
case opcode::OP_ScriptFarFunctionCall2:
case opcode::OP_ScriptFarFunctionCall:
case opcode::OP_ScriptFarMethodCall:
disassemble_far_call(inst, false);
break;
case opcode::OP_ScriptFarThreadCall:
case opcode::OP_ScriptFarChildThreadCall:
case opcode::OP_ScriptFarMethodThreadCall:
case opcode::OP_ScriptFarMethodChildThreadCall:
disassemble_far_call(inst, true);
break;
case opcode::OP_CallBuiltin:
disassemble_builtin_call(inst, false, true);
break;
case opcode::OP_CallBuiltinMethod:
disassemble_builtin_call(inst, true, true);
break;
case opcode::OP_GetBuiltinFunction:
case opcode::OP_CallBuiltin0:
case opcode::OP_CallBuiltin1:
case opcode::OP_CallBuiltin2:
case opcode::OP_CallBuiltin3:
case opcode::OP_CallBuiltin4:
case opcode::OP_CallBuiltin5:
disassemble_builtin_call(inst, false, false);
break;
case opcode::OP_GetBuiltinMethod:
case opcode::OP_CallBuiltinMethod0:
case opcode::OP_CallBuiltinMethod1:
case opcode::OP_CallBuiltinMethod2:
case opcode::OP_CallBuiltinMethod3:
case opcode::OP_CallBuiltinMethod4:
case opcode::OP_CallBuiltinMethod5:
disassemble_builtin_call(inst, true, false);
break;
case opcode::OP_jump:
disassemble_jump(inst, false, false);
break;
case opcode::OP_jumpback:
disassemble_jump(inst, false, true);
break;
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_JumpOnFalseExpr:
case opcode::OP_JumpOnTrueExpr:
disassemble_jump(inst, true, false);
break;
case opcode::OP_switch:
disassemble_switch(inst);
break;
case opcode::OP_endswitch:
disassemble_end_switch(inst);
break;
default:
throw disasm_error(utils::string::va("unhandled opcode 0x%X at index '%04X'!", inst->opcode, inst->index));
}
}
void disassembler::disassemble_builtin_call(const instruction::ptr& inst, bool method, bool args)
{
if (args)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
const auto id = script_->read_endian<std::uint16_t>();
const auto name = method ? resolver::method_name(id) : resolver::function_name(id);
inst->data.emplace(inst->data.begin(), name);
}
void disassembler::disassemble_local_call(const instruction::ptr& inst, bool thread)
{
const auto offset = disassemble_offset();
inst->data.push_back(utils::string::va("%X", offset + inst->index + 1));
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
}
void disassembler::disassemble_far_call(const instruction::ptr& inst, bool thread)
{
script_->seek(3);
if (thread)
{
inst->data.push_back(utils::string::va("%i", script_->read<std::uint8_t>()));
}
const auto file_id = stack_->read_endian<std::uint16_t>();
const auto file_name = file_id == 0 ? stack_->read_c_string() : resolver::token_name(file_id);
const auto func_id = stack_->read_endian<std::uint16_t>();
const auto func_name = func_id == 0 ? stack_->read_c_string() : resolver::token_name(func_id);
inst->data.emplace(inst->data.begin(), func_name);
inst->data.emplace(inst->data.begin(), file_name);
}
void disassembler::disassemble_switch(const instruction::ptr& inst)
{
const auto addr = inst->index + 4 + script_->read_endian<std::int32_t>();
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
void disassembler::disassemble_end_switch(const instruction::ptr& inst)
{
const auto count = script_->read_endian<std::uint16_t>();
inst->data.push_back(utils::string::va("%i", count));
std::uint32_t index = inst->index + 3;
if (count)
{
for (auto i = count; i > 0; i--)
{
const auto value = script_->read_endian<std::uint32_t>();
if (value < 0x40000)
{
const auto data = stack_->read_c_string();
if (data.data()[0] != 0x01)
{
inst->data.push_back("case");
inst->data.push_back(utils::string::quote(data, false));
}
else
inst->data.push_back("default");
}
else
{
inst->data.push_back("case");
inst->data.push_back(utils::string::va("%i", (value - 0x800000) & 0xFFFFFF));
}
index += 4;
const auto addr = disassemble_offset() + index;
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
index += 3;
inst->size += 7;
}
}
}
void disassembler::disassemble_field_variable(const instruction::ptr& inst)
{
const auto id = script_->read_endian<std::uint16_t>();
std::string name;
if (id > max_string_id)
{
auto temp = stack_->read_endian<std::uint16_t>();
name = temp == 0 ? stack_->read_c_string() : std::to_string(temp);
}
else
{
name = resolver::token_name(id);
}
inst->data.push_back(name);
}
void disassembler::disassemble_jump(const instruction::ptr& inst, bool expr, bool back)
{
std::int32_t addr;
if (expr)
{
addr = inst->index + 3 + script_->read_endian<std::int16_t>();
}
else if (back)
{
addr = inst->index + 3 - script_->read_endian<std::uint16_t>();
}
else
{
addr = inst->index + 5 + script_->read_endian<std::int32_t>();
}
const auto label = utils::string::va("loc_%X", addr);
inst->data.push_back(label);
labels_.insert({ addr, label });
}
auto disassembler::disassemble_offset() -> std::int32_t
{
std::array<std::uint8_t, 4> bytes = {};
for (auto i = 0; i < 3; i++)
{
bytes[2 - i] = script_->read<std::uint8_t>();
}
auto offset = *reinterpret_cast<std::int32_t*>(bytes.data());
offset = (offset << 8) >> 10;
return offset;
}
void disassembler::resolve_local_functions()
{
for (const auto& func : functions_)
{
for (const auto& inst : func->instructions)
{
switch (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 (auto& func : functions_)
{
if (func->index == idx)
{
return func->name;
}
}
//return "error";
throw disasm_error(utils::string::va("couldn't resolve function name at index '0x%04X'!", idx));
}
throw disasm_error(utils::string::va("\"%s\" is not valid function address!", index.data()));
}
void disassembler::print_function(const function::ptr& func)
{
output_->write_string("\n");
output_->write_string(utils::string::va("sub_%s\n", func->name.data()));
for (const auto& inst : func->instructions)
{
const auto itr = func->labels.find(inst->index);
if (itr != func->labels.end())
{
output_->write_string(utils::string::va("\t%s\n", itr->second.data()));
}
print_instruction(inst);
}
output_->write_string(utils::string::va("end_%s\n", func->name.data()));
}
void disassembler::print_instruction(const instruction::ptr& inst)
{
output_->write_string(utils::string::va("\t\t%s", resolver::opcode_name(inst->opcode).data()));
switch (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\n", 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", inst->data[0].data()));
output_->write_string(utils::string::va(" %s\n", 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;
}
output_->write_string("\n");
}
}
break;
default:
for (auto& d : inst->data)
{
output_->write_string(utils::string::va(" %s", d.data()));
}
output_->write_string("\n");
break;
}
}
} // namespace xsk::gsc::iw5_console

View File

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

View File

@ -96,6 +96,7 @@ auto opcode_size(std::uint8_t id) -> std::uint32_t
case opcode::OP_ScriptMethodChildThreadCallPointer:
case opcode::OP_CallBuiltinPointer:
case opcode::OP_CallBuiltinMethodPointer:
case opcode::OP_waittillmatch:
case opcode::OP_SafeCreateVariableFieldCached:
case opcode::OP_SafeSetVariableFieldCached:
case opcode::OP_SafeSetWaittillVariableFieldCached:
@ -118,7 +119,6 @@ auto opcode_size(std::uint8_t id) -> std::uint32_t
case opcode::OP_endswitch:
case opcode::OP_JumpOnFalse:
case opcode::OP_JumpOnTrue:
case opcode::OP_waittillmatch:
case opcode::OP_EvalLevelFieldVariable:
case opcode::OP_EvalAnimFieldVariable:
case opcode::OP_EvalSelfFieldVariable:

View File

@ -7,9 +7,18 @@
#include "utils/xsk/utils.hpp"
#include "assembler.hpp"
#include "disassembler.hpp"
#include "compiler.hpp"
#include "decompiler.hpp"
#include "resolver.hpp"
#include "context.hpp"
namespace xsk::gsc::iw5_console
{
constexpr std::uint16_t max_string_id = 0x8250;
enum class opcode : std::uint8_t
{
OP_End = 0,
@ -120,7 +129,7 @@ enum class opcode : std::uint8_t
OP_ScriptFunctionCallPointer = 0x69,
OP_ScriptMethodCallPointer = 0x6A,
OP_ScriptThreadCallPointer = 0x6B,
OP_ScriptMethodChildThreadCallPointer = 0x6C,
OP_ScriptChildThreadCallPointer = 0x6C,
OP_ScriptMethodThreadCallPointer = 0x6D,
OP_ScriptMethodChildThreadCallPointer = 0x6E,
OP_CallBuiltinPointer = 0x6F,
@ -153,7 +162,7 @@ enum class opcode : std::uint8_t
OP_plus = 0x8A,
OP_minus = 0x8B,
OP_multiply = 0x8C,
OP_divide = 0x8F,
OP_divide = 0x8D,
OP_mod = 0x8E,
OP_size = 0x8F,
OP_waittillmatch = 0x90,

View File

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

View File

@ -0,0 +1,78 @@
// Copyright 2022 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#pragma once
namespace xsk::gsc::iw5_console
{
constexpr size_t max_buf_size = 0x2000;
struct buffer
{
char* data;
size_t length;
buffer();
~buffer();
bool push(char c);
};
struct reader
{
enum state_type : std::uint8_t { end, ok };
const char* buffer_pos;
std::uint32_t bytes_remaining;
char last_byte;
char current_byte;
state_type state;
reader();
reader(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
}
reader& operator=(const reader& obj)
{
std::memcpy(this, &obj, sizeof(reader));
return *this;
}
void init(const char* data, size_t size);
void advance();
};
class lexer
{
enum class state : std::uint8_t { start, string, localize, preprocessor };
reader reader_;
buffer buffer_;
location loc_;
std::stack<location> locs_;
std::stack<reader> readers_;
std::uint32_t header_top_;
state state_;
build mode_;
bool indev_;
bool clean_;
public:
lexer(build mode, const std::string& name, const char* data, size_t size);
auto lex() -> parser::symbol_type;
void push_header(const std::string& file);
void pop_header();
void ban_header(const location& loc);
private:
void advance();
void preprocessor_wrap();
void preprocessor_run(parser::token::token_kind_type token);
};
} // namespace xsk::gsc::iw5_console

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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