diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 67f4c689..0847dbee 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -1151,6 +1151,8 @@ namespace IW4 EXP_FUNC_STATIC_DVAR_BOOL, EXP_FUNC_STATIC_DVAR_FLOAT, EXP_FUNC_STATIC_DVAR_STRING, + + EXP_FUNC_DYN_START }; union entryInternalData diff --git a/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp b/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp index 2dfc2f6b..62320e6b 100644 --- a/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp +++ b/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp @@ -3,11 +3,17 @@ #include #include +#include "Game/IW4/MenuConstantsIW4.h" +#include "Game/T5/T5_Assets.h" #include "Utils/ClassUtils.h" #include "Menu/AbstractMenuConverter.h" #include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerCondition.h" #include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerScript.h" #include "Parsing/Menu/Domain/EventHandler/CommonEventHandlerSetLocalVar.h" +#include "Parsing/Menu/Domain/Expression/CommonExpressionFunctionCall.h" +#include "Parsing/Simple/Expression/SimpleExpressionBinaryOperation.h" +#include "Parsing/Simple/Expression/SimpleExpressionConditionalOperator.h" +#include "Parsing/Simple/Expression/SimpleExpressionUnaryOperation.h" using namespace IW4; using namespace menu; @@ -84,18 +90,245 @@ namespace IW4 return static_cast(materialDependency->m_ptr); } - _NODISCARD Statement_s* ConvertExpression(const ISimpleExpression* expression) const + static const std::map& GetFunctionMap() + { + static std::map mappings; + static bool initialized = false; + + if(!initialized) + { + for(size_t i = EXP_FUNC_DYN_START; i < std::extent_v; i++) + { + mappings[g_expFunctionNames[i]] = static_cast(i); + } + initialized = true; + } + + return mappings; + } + + void ConvertExpressionEntryFunctionCall(std::vector& entries, const CommonExpressionFunctionCall* functionCall, const CommonMenuDef* menu, + const CommonItemDef* item) const + { + const auto& functionMappings = GetFunctionMap(); + + const auto foundMapping = functionMappings.find(functionCall->m_function_name); + if (foundMapping == functionMappings.end()) + throw MenuConversionException("Could not find function \"" + functionCall->m_function_name + "\"", menu, item); + + expressionEntry functionEntry{}; + functionEntry.type = EET_OPERATOR; + functionEntry.data.op = foundMapping->second; + entries.emplace_back(functionEntry); + + auto firstArg = true; + for(const auto& arg : functionCall->m_args) + { + if (!firstArg) + { + expressionEntry argSeparator{}; + argSeparator.type = EET_OPERATOR; + argSeparator.data.op = OP_COMMA; + entries.emplace_back(argSeparator); + } + else + firstArg = false; + + ConvertExpressionEntry(entries, arg.get(), menu, item); + } + + expressionEntry parenRight{}; + parenRight.type = EET_OPERATOR; + parenRight.data.op = OP_RIGHTPAREN; + entries.emplace_back(parenRight); + } + + constexpr static expressionOperatorType_e UNARY_OPERATION_MAPPING[static_cast(SimpleUnaryOperationId::COUNT)] + { + OP_NOT, + OP_BITWISENOT, + OP_SUBTRACT + }; + + void ConvertExpressionEntryUnaryOperation(std::vector& entries, const SimpleExpressionUnaryOperation* unaryOperation, const CommonMenuDef* menu, + const CommonItemDef* item) const + { + assert(static_cast(unaryOperation->m_operation_type->m_id) < static_cast(SimpleUnaryOperationId::COUNT)); + expressionEntry operation{}; + operation.type = EET_OPERATOR; + operation.data.op = UNARY_OPERATION_MAPPING[static_cast(unaryOperation->m_operation_type->m_id)]; + entries.emplace_back(operation); + + if (unaryOperation->OperandNeedsParenthesis()) + { + expressionEntry parenLeft{}; + parenLeft.type = EET_OPERATOR; + parenLeft.data.op = OP_LEFTPAREN; + entries.emplace_back(parenLeft); + + ConvertExpressionEntry(entries, unaryOperation->m_operand.get(), menu, item); + + expressionEntry parenRight{}; + parenRight.type = EET_OPERATOR; + parenRight.data.op = OP_RIGHTPAREN; + entries.emplace_back(parenRight); + } + else + ConvertExpressionEntry(entries, unaryOperation->m_operand.get(), menu, item); + } + + constexpr static expressionOperatorType_e BINARY_OPERATION_MAPPING[static_cast(SimpleBinaryOperationId::COUNT)] + { + OP_ADD, + OP_SUBTRACT, + OP_MULTIPLY, + OP_DIVIDE, + OP_MODULUS, + OP_BITWISEAND, + OP_BITWISEOR, + OP_BITSHIFTLEFT, + OP_BITSHIFTRIGHT, + OP_GREATERTHAN, + OP_GREATERTHANEQUALTO, + OP_LESSTHAN, + OP_LESSTHANEQUALTO, + OP_EQUALS, + OP_NOTEQUAL, + OP_AND, + OP_OR + }; + + void ConvertExpressionEntryBinaryOperation(std::vector& entries, const SimpleExpressionBinaryOperation* binaryOperation, const CommonMenuDef* menu, + const CommonItemDef* item) const + { + if (binaryOperation->Operand1NeedsParenthesis()) + { + expressionEntry parenLeft{}; + parenLeft.type = EET_OPERATOR; + parenLeft.data.op = OP_LEFTPAREN; + entries.emplace_back(parenLeft); + + ConvertExpressionEntry(entries, binaryOperation->m_operand1.get(), menu, item); + + expressionEntry parenRight{}; + parenRight.type = EET_OPERATOR; + parenRight.data.op = OP_RIGHTPAREN; + entries.emplace_back(parenRight); + } + else + ConvertExpressionEntry(entries, binaryOperation->m_operand1.get(), menu, item); + + assert(static_cast(binaryOperation->m_operation_type->m_id) < static_cast(SimpleBinaryOperationId::COUNT)); + expressionEntry operation{}; + operation.type = EET_OPERATOR; + operation.data.op = BINARY_OPERATION_MAPPING[static_cast(binaryOperation->m_operation_type->m_id)]; + entries.emplace_back(operation); + + if (binaryOperation->Operand2NeedsParenthesis()) + { + expressionEntry parenLeft{}; + parenLeft.type = EET_OPERATOR; + parenLeft.data.op = OP_LEFTPAREN; + entries.emplace_back(parenLeft); + + ConvertExpressionEntry(entries, binaryOperation->m_operand2.get(), menu, item); + + expressionEntry parenRight{}; + parenRight.type = EET_OPERATOR; + parenRight.data.op = OP_RIGHTPAREN; + entries.emplace_back(parenRight); + } + else + ConvertExpressionEntry(entries, binaryOperation->m_operand2.get(), menu, item); + } + + void ConvertExpressionEntryExpressionValue(std::vector& entries, const SimpleExpressionValue* expressionValue) const + { + expressionEntry entry{}; + entry.type = EET_OPERAND; + + if (expressionValue->m_type == SimpleExpressionValue::Type::INT) + { + entry.data.operand.dataType = VAL_INT; + entry.data.operand.internals.intVal = expressionValue->m_int_value; + } + else if (expressionValue->m_type == SimpleExpressionValue::Type::DOUBLE) + { + entry.data.operand.dataType = VAL_FLOAT; + entry.data.operand.internals.floatVal = static_cast(expressionValue->m_double_value); + } + else if (expressionValue->m_type == SimpleExpressionValue::Type::STRING) + { + entry.data.operand.dataType = VAL_STRING; + entry.data.operand.internals.stringVal.string = m_memory->Dup(expressionValue->m_string_value->c_str()); + } + + entries.emplace_back(entry); + } + + void ConvertExpressionEntry(std::vector& entries, const ISimpleExpression* expression, const CommonMenuDef* menu, const CommonItemDef* item) const + { + if (!m_legacy_mode && expression->IsStatic()) + { + const auto expressionStaticValue = expression->Evaluate(); + ConvertExpressionEntryExpressionValue(entries, &expressionStaticValue); + } + else if (const auto* expressionValue = dynamic_cast(expression)) + { + ConvertExpressionEntryExpressionValue(entries, expressionValue); + } + else if (const auto* binaryOperation = dynamic_cast(expression)) + { + ConvertExpressionEntryBinaryOperation(entries, binaryOperation, menu, item); + } + else if (const auto* unaryOperation = dynamic_cast(expression)) + { + ConvertExpressionEntryUnaryOperation(entries, unaryOperation, menu, item); + } + else if (const auto* functionCall = dynamic_cast(expression)) + { + ConvertExpressionEntryFunctionCall(entries, functionCall, menu, item); + } + else if (dynamic_cast(expression)) + { + throw MenuConversionException("Cannot use conditional expression in menu expressions", menu, item); + } + else + { + assert(false); + throw MenuConversionException("Unknown expression entry type in menu expressions", menu, item); + } + } + + _NODISCARD Statement_s* ConvertExpression(const ISimpleExpression* expression, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (!expression) return nullptr; - return nullptr; + auto* statement = m_memory->Create(); + statement->lastResult = Operand{}; + statement->lastExecuteTime = 0; + + std::vector expressionEntries; + + ConvertExpressionEntry(expressionEntries, expression, menu, item); + + auto* outputExpressionEntries = static_cast(m_memory->Alloc(sizeof(expressionEntry) * expressionEntries.size())); + memcpy(outputExpressionEntries, expressionEntries.data(), sizeof(expressionEntry) * expressionEntries.size()); + + statement->entries = outputExpressionEntries; + statement->numEntries = static_cast(expressionEntries.size()); + + // TODO: Add supporting data + statement->supportingData = nullptr; + + return statement; } _NODISCARD Statement_s* ConvertOrApplyStatement(float& staticValue, const ISimpleExpression* expression, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (m_legacy_mode) - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); if (!expression) return nullptr; @@ -117,13 +350,13 @@ namespace IW4 return nullptr; } - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); } _NODISCARD Statement_s* ConvertOrApplyStatement(const char*& staticValue, const ISimpleExpression* expression, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (m_legacy_mode) - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); if (!expression) return nullptr; @@ -144,13 +377,13 @@ namespace IW4 return nullptr; } - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); } _NODISCARD Statement_s* ConvertOrApplyStatement(Material*& staticValue, const ISimpleExpression* expression, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (m_legacy_mode) - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); if (!expression) return nullptr; @@ -171,7 +404,7 @@ namespace IW4 return nullptr; } - return ConvertExpression(expression); + return ConvertExpression(expression, menu, item); } _NODISCARD static EventType SetLocalVarTypeToEventType(const SetLocalVarType setLocalVarType) @@ -193,7 +426,7 @@ namespace IW4 } } - MenuEventHandler* ConvertEventHandlerSetLocalVar(const CommonEventHandlerSetLocalVar* setLocalVar) const + MenuEventHandler* ConvertEventHandlerSetLocalVar(const CommonEventHandlerSetLocalVar* setLocalVar, const CommonMenuDef* menu, const CommonItemDef* item) const { assert(setLocalVar); if (!setLocalVar) @@ -206,7 +439,7 @@ namespace IW4 outputHandler->eventData.setLocalVarData = outputSetLocalVar; outputSetLocalVar->localVarName = m_memory->Dup(setLocalVar->m_var_name.c_str()); - outputSetLocalVar->expression = ConvertExpression(setLocalVar->m_value.get()); + outputSetLocalVar->expression = ConvertExpression(setLocalVar->m_value.get(), menu, item); return outputHandler; } @@ -224,7 +457,8 @@ namespace IW4 return outputHandler; } - void ConvertEventHandlerCondition(MenuEventHandler** eventHandlerArray, size_t& eventHandlerArrayIndex, const CommonEventHandlerCondition* condition) const + void ConvertEventHandlerCondition(MenuEventHandler** eventHandlerArray, size_t& eventHandlerArrayIndex, const CommonEventHandlerCondition* condition, const CommonMenuDef* menu, + const CommonItemDef* item) const { assert(condition); if (!condition) @@ -236,24 +470,25 @@ namespace IW4 outputHandler->eventType = EVENT_IF; outputHandler->eventData.conditionalScript = outputCondition; - outputCondition->eventExpression = ConvertExpression(condition->m_condition.get()); - outputCondition->eventHandlerSet = ConvertEventHandlerSet(condition->m_condition_elements.get()); + outputCondition->eventExpression = ConvertExpression(condition->m_condition.get(), menu, item); + outputCondition->eventHandlerSet = ConvertEventHandlerSet(condition->m_condition_elements.get(), menu, item); eventHandlerArray[eventHandlerArrayIndex] = outputHandler; - if(condition->m_else_elements) + if (condition->m_else_elements) { eventHandlerArrayIndex++; auto* outputElseHandler = m_memory->Create(); outputElseHandler->eventType = EVENT_ELSE; - outputElseHandler->eventData.elseScript = ConvertEventHandlerSet(condition->m_else_elements.get()); + outputElseHandler->eventData.elseScript = ConvertEventHandlerSet(condition->m_else_elements.get(), menu, item); eventHandlerArray[eventHandlerArrayIndex] = outputElseHandler; } } - void ConvertEventHandler(MenuEventHandler** eventHandlerArray, size_t& eventHandlerArrayIndex, const ICommonEventHandlerElement* eventHandler) const + void ConvertEventHandler(MenuEventHandler** eventHandlerArray, size_t& eventHandlerArrayIndex, const ICommonEventHandlerElement* eventHandler, const CommonMenuDef* menu, + const CommonItemDef* item) const { assert(eventHandler); if (!eventHandler) @@ -262,7 +497,7 @@ namespace IW4 switch (eventHandler->GetType()) { case CommonEventHandlerElementType::CONDITION: - ConvertEventHandlerCondition(eventHandlerArray, eventHandlerArrayIndex, dynamic_cast(eventHandler)); + ConvertEventHandlerCondition(eventHandlerArray, eventHandlerArrayIndex, dynamic_cast(eventHandler), menu, item); break; case CommonEventHandlerElementType::SCRIPT: @@ -270,7 +505,7 @@ namespace IW4 break; case CommonEventHandlerElementType::SET_LOCAL_VAR: - eventHandlerArray[eventHandlerArrayIndex] = ConvertEventHandlerSetLocalVar(dynamic_cast(eventHandler)); + eventHandlerArray[eventHandlerArrayIndex] = ConvertEventHandlerSetLocalVar(dynamic_cast(eventHandler), menu, item); break; } } @@ -289,7 +524,7 @@ namespace IW4 return elementCount; } - _NODISCARD MenuEventHandlerSet* ConvertEventHandlerSet(const CommonEventHandlerSet* eventHandlerSet) const + _NODISCARD MenuEventHandlerSet* ConvertEventHandlerSet(const CommonEventHandlerSet* eventHandlerSet, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (!eventHandlerSet) return nullptr; @@ -304,14 +539,14 @@ namespace IW4 auto eventHandlerIndex = 0u; for (const auto& element : eventHandlerSet->m_elements) { - ConvertEventHandler(outputElements, eventHandlerIndex, element.get()); + ConvertEventHandler(outputElements, eventHandlerIndex, element.get(), menu, item); eventHandlerIndex++; } return outputSet; } - _NODISCARD ItemKeyHandler* ConvertKeyHandler(const std::map>& keyHandlers) const + _NODISCARD ItemKeyHandler* ConvertKeyHandler(const std::map>& keyHandlers, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const { if (keyHandlers.empty()) return nullptr; @@ -322,7 +557,7 @@ namespace IW4 for (auto i = 0u; i < keyHandlerCount; i++) { output[i].key = currentKeyHandler->first; - output[i].action = ConvertEventHandlerSet(currentKeyHandler->second.get()); + output[i].action = ConvertEventHandlerSet(currentKeyHandler->second.get(), menu, item); if (i + 1 < keyHandlerCount) output[i].next = &output[i + 1]; @@ -352,8 +587,8 @@ namespace IW4 item->type = ConvertItemType(commonItem.m_type); item->window.border = commonItem.m_border; item->window.borderSize = static_cast(commonItem.m_border_size); - item->visibleExp = ConvertExpression(commonItem.m_visible_expression.get()); - item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get()); + item->visibleExp = ConvertExpression(commonItem.m_visible_expression.get(), &parentMenu, &commonItem); + item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get(), &parentMenu, &commonItem); item->window.ownerDraw = commonItem.m_owner_draw; item->window.ownerDrawFlags = commonItem.m_owner_draw_flags; item->alignment = commonItem.m_align; @@ -370,21 +605,21 @@ namespace IW4 ConvertColor(item->window.disableColor, commonItem.m_disable_color); ConvertColor(item->glowColor, commonItem.m_glow_color); item->window.background = ConvertMaterial(commonItem.m_background, &parentMenu, &commonItem); - item->onFocus = ConvertEventHandlerSet(commonItem.m_on_focus.get()); - item->leaveFocus = ConvertEventHandlerSet(commonItem.m_on_leave_focus.get()); - item->mouseEnter = ConvertEventHandlerSet(commonItem.m_on_mouse_enter.get()); - item->mouseExit = ConvertEventHandlerSet(commonItem.m_on_mouse_exit.get()); - item->mouseEnterText = ConvertEventHandlerSet(commonItem.m_on_mouse_enter_text.get()); - item->mouseExitText = ConvertEventHandlerSet(commonItem.m_on_mouse_exit_text.get()); - item->action = ConvertEventHandlerSet(commonItem.m_on_action.get()); - item->accept = ConvertEventHandlerSet(commonItem.m_on_accept.get()); + item->onFocus = ConvertEventHandlerSet(commonItem.m_on_focus.get(), &parentMenu, &commonItem); + item->leaveFocus = ConvertEventHandlerSet(commonItem.m_on_leave_focus.get(), &parentMenu, &commonItem); + item->mouseEnter = ConvertEventHandlerSet(commonItem.m_on_mouse_enter.get(), &parentMenu, &commonItem); + item->mouseExit = ConvertEventHandlerSet(commonItem.m_on_mouse_exit.get(), &parentMenu, &commonItem); + item->mouseEnterText = ConvertEventHandlerSet(commonItem.m_on_mouse_enter_text.get(), &parentMenu, &commonItem); + item->mouseExitText = ConvertEventHandlerSet(commonItem.m_on_mouse_exit_text.get(), &parentMenu, &commonItem); + item->action = ConvertEventHandlerSet(commonItem.m_on_action.get(), &parentMenu, &commonItem); + item->accept = ConvertEventHandlerSet(commonItem.m_on_accept.get(), &parentMenu, &commonItem); // item->focusSound item->dvarTest = ConvertString(commonItem.m_dvar_test); // enableDvar - item->onKey = ConvertKeyHandler(commonItem.m_key_handlers); + item->onKey = ConvertKeyHandler(commonItem.m_key_handlers, &parentMenu, &commonItem); item->textExp = ConvertOrApplyStatement(item->text, commonItem.m_text_expression.get(), &parentMenu, &commonItem); item->materialExp = ConvertOrApplyStatement(item->window.background, commonItem.m_material_expression.get(), &parentMenu, &commonItem); - item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get()); + item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get(), &parentMenu, &commonItem); // FloatExpressions item->gameMsgWindowIndex = commonItem.m_game_message_window_index; item->gameMsgWindowMode = commonItem.m_game_message_window_mode; @@ -449,18 +684,18 @@ namespace IW4 ApplyFlag(menu->window.staticFlags, commonMenu.m_hidden_during_ui, WINDOW_FLAG_HIDDEN_DURING_UI); menu->allowedBinding = ConvertString(commonMenu.m_allowed_binding); ApplyFlag(menu->window.staticFlags, commonMenu.m_text_only_focus, WINDOW_FLAG_TEXT_ONLY_FOCUS); - menu->visibleExp = ConvertExpression(commonMenu.m_visible_expression.get()); + menu->visibleExp = ConvertExpression(commonMenu.m_visible_expression.get(), &commonMenu); menu->rectXExp = ConvertOrApplyStatement(menu->window.rect.x, commonMenu.m_rect_x_exp.get(), &commonMenu); menu->rectYExp = ConvertOrApplyStatement(menu->window.rect.y, commonMenu.m_rect_y_exp.get(), &commonMenu); menu->rectWExp = ConvertOrApplyStatement(menu->window.rect.w, commonMenu.m_rect_w_exp.get(), &commonMenu); menu->rectHExp = ConvertOrApplyStatement(menu->window.rect.h, commonMenu.m_rect_h_exp.get(), &commonMenu); - menu->openSoundExp = ConvertExpression(commonMenu.m_open_sound_exp.get()); - menu->closeSoundExp = ConvertExpression(commonMenu.m_close_sound_exp.get()); - menu->onOpen = ConvertEventHandlerSet(commonMenu.m_on_open.get()); - menu->onClose = ConvertEventHandlerSet(commonMenu.m_on_close.get()); - menu->onCloseRequest = ConvertEventHandlerSet(commonMenu.m_on_request_close.get()); - menu->onESC = ConvertEventHandlerSet(commonMenu.m_on_esc.get()); - menu->onKey = ConvertKeyHandler(commonMenu.m_key_handlers); + menu->openSoundExp = ConvertExpression(commonMenu.m_open_sound_exp.get(), &commonMenu); + menu->closeSoundExp = ConvertExpression(commonMenu.m_close_sound_exp.get(), &commonMenu); + menu->onOpen = ConvertEventHandlerSet(commonMenu.m_on_open.get(), &commonMenu); + menu->onClose = ConvertEventHandlerSet(commonMenu.m_on_close.get(), &commonMenu); + menu->onCloseRequest = ConvertEventHandlerSet(commonMenu.m_on_request_close.get(), &commonMenu); + menu->onESC = ConvertEventHandlerSet(commonMenu.m_on_esc.get(), &commonMenu); + menu->onKey = ConvertKeyHandler(commonMenu.m_key_handlers, &commonMenu); menu->items = ConvertMenuItems(commonMenu); return menu;