OpenAssetTools/src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp
2025-01-01 18:14:41 +01:00

1185 lines
58 KiB
C++

#include "MenuConverterIW4.h"
#include "Menu/AbstractMenuConverter.h"
#include "MenuConversionZoneStateIW4.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/CommonExpressionBaseFunctionCall.h"
#include "Parsing/Menu/Domain/Expression/CommonExpressionCustomFunctionCall.h"
#include "Parsing/Menu/MenuAssetZoneState.h"
#include "Parsing/Simple/Expression/SimpleExpressionBinaryOperation.h"
#include "Parsing/Simple/Expression/SimpleExpressionConditionalOperator.h"
#include "Parsing/Simple/Expression/SimpleExpressionUnaryOperation.h"
#include "Utils/ClassUtils.h"
#include "Utils/StringUtils.h"
#include <cassert>
#include <cstring>
#include <format>
#include <sstream>
using namespace IW4;
using namespace menu;
namespace
{
class MenuConverter : public AbstractMenuConverter, public IMenuConverter
{
[[nodiscard]] static rectDef_s ConvertRectDef(const CommonRect& rect)
{
return rectDef_s{
static_cast<float>(rect.x),
static_cast<float>(rect.y),
static_cast<float>(rect.w),
static_cast<float>(rect.h),
static_cast<unsigned char>(rect.horizontalAlign),
static_cast<unsigned char>(rect.verticalAlign),
};
}
[[nodiscard]] static rectDef_s ConvertRectDefRelativeTo(const CommonRect& rect, const CommonRect& rectRelativeTo)
{
return rectDef_s{
static_cast<float>(rectRelativeTo.x + rect.x),
static_cast<float>(rectRelativeTo.y + rect.y),
static_cast<float>(rect.w),
static_cast<float>(rect.h),
static_cast<unsigned char>(rect.horizontalAlign),
static_cast<unsigned char>(rect.verticalAlign),
};
}
static void ConvertColor(float (&output)[4], const CommonColor& input)
{
output[0] = static_cast<float>(input.r);
output[1] = static_cast<float>(input.g);
output[2] = static_cast<float>(input.b);
output[3] = static_cast<float>(input.a);
}
static void ApplyFlag(int& flags, const bool shouldApply, const int flagValue)
{
if (!shouldApply)
return;
flags |= flagValue;
}
static int ConvertItemType(const int input)
{
return input;
}
static int ConvertTextFont(const int input)
{
return input;
}
[[nodiscard]] Material* ConvertMaterial(const std::string& materialName, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const
{
if (materialName.empty())
return nullptr;
auto* materialDependency = m_context.LoadDependency<AssetMaterial>(materialName);
if (!materialDependency)
throw MenuConversionException(std::format("Failed to load material \"{}\"", materialName), menu, item);
return materialDependency->Asset();
}
[[nodiscard]] snd_alias_list_t* ConvertSound(const std::string& soundName, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const
{
if (soundName.empty())
return nullptr;
auto* soundDependency = m_context.LoadDependency<AssetSound>(soundName);
if (!soundDependency)
throw MenuConversionException(std::format("Failed to load sound \"{}\"", soundName), menu, item);
return soundDependency->Asset();
}
bool HandleStaticDvarFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* functionCall,
const int targetFunctionIndex) const
{
if (functionCall->m_args.size() != 1)
return false;
const auto* dvarNameExpression = functionCall->m_args[0].get();
if (!dvarNameExpression->IsStatic())
return false;
const auto staticDvarNameExpressionValue = dvarNameExpression->EvaluateStatic();
if (staticDvarNameExpressionValue.m_type != SimpleExpressionValue::Type::STRING)
return false;
expressionEntry functionEntry{};
functionEntry.type = EET_OPERATOR;
functionEntry.data.op = targetFunctionIndex;
entries.emplace_back(functionEntry);
expressionEntry staticDvarIndexEntry{};
staticDvarIndexEntry.type = EET_OPERAND;
staticDvarIndexEntry.data.operand.dataType = VAL_INT;
staticDvarIndexEntry.data.operand.internals.intVal =
static_cast<int>(m_conversion_zone_state->AddStaticDvar(*staticDvarNameExpressionValue.m_string_value));
entries.emplace_back(staticDvarIndexEntry);
expressionEntry parenRight{};
parenRight.type = EET_OPERATOR;
parenRight.data.op = OP_RIGHTPAREN;
entries.emplace_back(parenRight);
gameStatement->supportingData = m_conversion_zone_state->m_supporting_data;
return true;
}
bool HandleSpecialBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* functionCall,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
switch (functionCall->m_function_index)
{
case EXP_FUNC_DVAR_INT:
return HandleStaticDvarFunctionCall(gameStatement, entries, functionCall, EXP_FUNC_STATIC_DVAR_INT);
case EXP_FUNC_DVAR_BOOL:
return HandleStaticDvarFunctionCall(gameStatement, entries, functionCall, EXP_FUNC_STATIC_DVAR_BOOL);
case EXP_FUNC_DVAR_FLOAT:
return HandleStaticDvarFunctionCall(gameStatement, entries, functionCall, EXP_FUNC_STATIC_DVAR_FLOAT);
case EXP_FUNC_DVAR_STRING:
return HandleStaticDvarFunctionCall(gameStatement, entries, functionCall, EXP_FUNC_STATIC_DVAR_STRING);
default:
break;
}
return false;
}
void ConvertExpressionEntryBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* functionCall,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
if (!HandleSpecialBaseFunctionCall(gameStatement, entries, functionCall, menu, item))
{
expressionEntry functionEntry{};
functionEntry.type = EET_OPERATOR;
functionEntry.data.op = static_cast<int>(functionCall->m_function_index);
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(gameStatement, entries, arg.get(), menu, item);
}
expressionEntry parenRight{};
parenRight.type = EET_OPERATOR;
parenRight.data.op = OP_RIGHTPAREN;
entries.emplace_back(parenRight);
}
}
void ConvertExpressionEntryCustomFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionCustomFunctionCall* functionCall,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
std::string lowerCaseFunctionName(functionCall->m_function_name);
utils::MakeStringLowerCase(lowerCaseFunctionName);
Statement_s* functionStatement = m_conversion_zone_state->FindFunction(lowerCaseFunctionName);
if (functionStatement == nullptr)
{
// Function was not converted yet: Convert it now
const auto foundCommonFunction = m_parsing_zone_state->m_functions_by_name.find(lowerCaseFunctionName);
if (foundCommonFunction == m_parsing_zone_state->m_functions_by_name.end())
throw MenuConversionException("Failed to find definition for custom function \"" + functionCall->m_function_name + "\"", menu, item);
functionStatement = ConvertExpression(foundCommonFunction->second->m_value.get(), menu, item);
functionStatement = m_conversion_zone_state->AddFunction(foundCommonFunction->second->m_name, functionStatement);
}
expressionEntry functionEntry{};
functionEntry.type = EET_OPERAND;
functionEntry.data.operand.dataType = VAL_FUNCTION;
functionEntry.data.operand.internals.function = functionStatement;
entries.emplace_back(functionEntry);
// Statement uses custom function so it needs supporting data
gameStatement->supportingData = m_conversion_zone_state->m_supporting_data;
}
constexpr static expressionOperatorType_e UNARY_OPERATION_MAPPING[static_cast<unsigned>(SimpleUnaryOperationId::COUNT)]{
OP_NOT,
OP_BITWISENOT,
OP_SUBTRACT,
};
bool IsOperation(const ISimpleExpression* expression) const
{
if (!m_disable_optimizations && expression->IsStatic())
return false;
return dynamic_cast<const SimpleExpressionBinaryOperation*>(expression) || dynamic_cast<const SimpleExpressionUnaryOperation*>(expression);
}
void ConvertExpressionEntryUnaryOperation(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionUnaryOperation* unaryOperation,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(static_cast<unsigned>(unaryOperation->m_operation_type->m_id) < static_cast<unsigned>(SimpleUnaryOperationId::COUNT));
expressionEntry operation{};
operation.type = EET_OPERATOR;
operation.data.op = UNARY_OPERATION_MAPPING[static_cast<unsigned>(unaryOperation->m_operation_type->m_id)];
entries.emplace_back(operation);
if (IsOperation(unaryOperation->m_operand.get()))
{
expressionEntry parenLeft{};
parenLeft.type = EET_OPERATOR;
parenLeft.data.op = OP_LEFTPAREN;
entries.emplace_back(parenLeft);
ConvertExpressionEntry(gameStatement, entries, unaryOperation->m_operand.get(), menu, item);
expressionEntry parenRight{};
parenRight.type = EET_OPERATOR;
parenRight.data.op = OP_RIGHTPAREN;
entries.emplace_back(parenRight);
}
else
ConvertExpressionEntry(gameStatement, entries, unaryOperation->m_operand.get(), menu, item);
}
constexpr static expressionOperatorType_e BINARY_OPERATION_MAPPING[static_cast<unsigned>(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(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionBinaryOperation* binaryOperation,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
// Game needs all nested operations to have parenthesis
if (IsOperation(binaryOperation->m_operand1.get()))
{
expressionEntry parenLeft{};
parenLeft.type = EET_OPERATOR;
parenLeft.data.op = OP_LEFTPAREN;
entries.emplace_back(parenLeft);
ConvertExpressionEntry(gameStatement, entries, binaryOperation->m_operand1.get(), menu, item);
expressionEntry parenRight{};
parenRight.type = EET_OPERATOR;
parenRight.data.op = OP_RIGHTPAREN;
entries.emplace_back(parenRight);
}
else
ConvertExpressionEntry(gameStatement, entries, binaryOperation->m_operand1.get(), menu, item);
assert(static_cast<unsigned>(binaryOperation->m_operation_type->m_id) < static_cast<unsigned>(SimpleBinaryOperationId::COUNT));
expressionEntry operation{};
operation.type = EET_OPERATOR;
operation.data.op = BINARY_OPERATION_MAPPING[static_cast<unsigned>(binaryOperation->m_operation_type->m_id)];
entries.emplace_back(operation);
// Game needs all nested operations to have parenthesis
if (IsOperation(binaryOperation->m_operand2.get()))
{
expressionEntry parenLeft{};
parenLeft.type = EET_OPERATOR;
parenLeft.data.op = OP_LEFTPAREN;
entries.emplace_back(parenLeft);
ConvertExpressionEntry(gameStatement, entries, binaryOperation->m_operand2.get(), menu, item);
expressionEntry parenRight{};
parenRight.type = EET_OPERATOR;
parenRight.data.op = OP_RIGHTPAREN;
entries.emplace_back(parenRight);
}
else
ConvertExpressionEntry(gameStatement, entries, binaryOperation->m_operand2.get(), menu, item);
}
void ConvertExpressionEntryExpressionValue(std::vector<expressionEntry>& 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<float>(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_conversion_zone_state->AddString(*expressionValue->m_string_value);
}
entries.emplace_back(entry);
}
void ConvertExpressionEntry(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const ISimpleExpression* expression,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
if (!m_disable_optimizations && expression->IsStatic())
{
const auto expressionStaticValue = expression->EvaluateStatic();
ConvertExpressionEntryExpressionValue(entries, &expressionStaticValue);
}
else if (const auto* expressionValue = dynamic_cast<const SimpleExpressionValue*>(expression))
{
ConvertExpressionEntryExpressionValue(entries, expressionValue);
}
else if (const auto* binaryOperation = dynamic_cast<const SimpleExpressionBinaryOperation*>(expression))
{
ConvertExpressionEntryBinaryOperation(gameStatement, entries, binaryOperation, menu, item);
}
else if (const auto* unaryOperation = dynamic_cast<const SimpleExpressionUnaryOperation*>(expression))
{
ConvertExpressionEntryUnaryOperation(gameStatement, entries, unaryOperation, menu, item);
}
else if (const auto* baseFunctionCall = dynamic_cast<const CommonExpressionBaseFunctionCall*>(expression))
{
ConvertExpressionEntryBaseFunctionCall(gameStatement, entries, baseFunctionCall, menu, item);
}
else if (const auto* customFunctionCall = dynamic_cast<const CommonExpressionCustomFunctionCall*>(expression))
{
ConvertExpressionEntryCustomFunctionCall(gameStatement, entries, customFunctionCall, menu, item);
}
else if (dynamic_cast<const SimpleExpressionConditionalOperator*>(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;
auto* statement = m_memory.Alloc<Statement_s>();
statement->lastResult = Operand{};
statement->lastExecuteTime = 0;
statement->supportingData = nullptr; // Supporting data is set upon using it
std::vector<expressionEntry> expressionEntries;
ConvertExpressionEntry(statement, expressionEntries, expression, menu, item);
auto* outputExpressionEntries = m_memory.Alloc<expressionEntry>(expressionEntries.size());
memcpy(outputExpressionEntries, expressionEntries.data(), sizeof(expressionEntry) * expressionEntries.size());
statement->entries = outputExpressionEntries;
statement->numEntries = static_cast<int>(expressionEntries.size());
return statement;
}
_NODISCARD Statement_s* ConvertOrApplyStatement(float& staticValue,
const ISimpleExpression* expression,
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
{
const auto value = expression->EvaluateStatic();
switch (value.m_type)
{
case SimpleExpressionValue::Type::DOUBLE:
staticValue = static_cast<float>(value.m_double_value);
break;
case SimpleExpressionValue::Type::INT:
staticValue = static_cast<float>(value.m_int_value);
break;
case SimpleExpressionValue::Type::STRING:
throw MenuConversionException("Cannot convert string expression value to floating point", menu, item);
}
return nullptr;
}
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_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
{
const auto value = expression->EvaluateStatic();
switch (value.m_type)
{
case SimpleExpressionValue::Type::STRING:
staticValue = m_memory.Dup(value.m_string_value->c_str());
break;
case SimpleExpressionValue::Type::DOUBLE:
case SimpleExpressionValue::Type::INT:
throw MenuConversionException("Cannot convert numeric expression value to string", menu, item);
}
return nullptr;
}
return ConvertExpression(expression, menu, item);
}
_NODISCARD Statement_s* ConvertOrApplyStatement(Material*& staticValue,
const ISimpleExpression* expression,
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
{
const auto value = expression->EvaluateStatic();
switch (value.m_type)
{
case SimpleExpressionValue::Type::STRING:
staticValue = ConvertMaterial(*value.m_string_value, menu, item);
break;
case SimpleExpressionValue::Type::DOUBLE:
case SimpleExpressionValue::Type::INT:
throw MenuConversionException("Cannot convert numeric expression value to string", menu, item);
}
return nullptr;
}
return ConvertExpression(expression, menu, item);
}
_NODISCARD Statement_s* ConvertVisibleExpression(windowDef_t* window,
const ISimpleExpression* expression,
const CommonMenuDef* commonMenu,
const CommonItemDef* commonItem = nullptr) const
{
if (expression == nullptr)
return nullptr;
bool isStatic;
bool isTruthy;
if (m_disable_optimizations)
{
const auto* staticValue = dynamic_cast<const SimpleExpressionValue*>(expression);
isStatic = staticValue != nullptr;
isTruthy = isStatic && (staticValue->m_type == SimpleExpressionValue::Type::INT || staticValue->m_type == SimpleExpressionValue::Type::DOUBLE)
&& staticValue->IsTruthy();
}
else
{
isStatic = expression->IsStatic();
isTruthy = isStatic && expression->EvaluateStatic().IsTruthy();
}
if (isStatic)
{
if (isTruthy)
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return nullptr;
}
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return ConvertExpression(expression, commonMenu, commonItem);
}
_NODISCARD static EventType SetLocalVarTypeToEventType(const SetLocalVarType setLocalVarType)
{
switch (setLocalVarType)
{
case SetLocalVarType::BOOL:
return EVENT_SET_LOCAL_VAR_BOOL;
case SetLocalVarType::STRING:
return EVENT_SET_LOCAL_VAR_STRING;
case SetLocalVarType::FLOAT:
return EVENT_SET_LOCAL_VAR_FLOAT;
case SetLocalVarType::INT:
return EVENT_SET_LOCAL_VAR_INT;
default:
case SetLocalVarType::UNKNOWN:
assert(false);
return EVENT_SET_LOCAL_VAR_INT;
}
}
void ConvertEventHandlerSetLocalVar(std::vector<MenuEventHandler*>& elements,
const CommonEventHandlerSetLocalVar* setLocalVar,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(setLocalVar);
if (!setLocalVar)
return;
auto* outputHandler = m_memory.Alloc<MenuEventHandler>();
auto* outputSetLocalVar = m_memory.Alloc<SetLocalVarData>();
outputHandler->eventType = SetLocalVarTypeToEventType(setLocalVar->m_type);
outputHandler->eventData.setLocalVarData = outputSetLocalVar;
outputSetLocalVar->localVarName = m_memory.Dup(setLocalVar->m_var_name.c_str());
outputSetLocalVar->expression = ConvertExpression(setLocalVar->m_value.get(), menu, item);
elements.push_back(outputHandler);
}
void ConvertEventHandlerScript(std::vector<MenuEventHandler*>& elements, const CommonEventHandlerScript* script) const
{
assert(script);
if (!script)
return;
auto* outputHandler = m_memory.Alloc<MenuEventHandler>();
outputHandler->eventType = EVENT_UNCONDITIONAL;
outputHandler->eventData.unconditionalScript = m_memory.Dup(script->m_script.c_str());
elements.push_back(outputHandler);
}
void ConvertEventHandlerCondition(std::vector<MenuEventHandler*>& elements,
const CommonEventHandlerCondition* condition,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(condition);
if (!condition || !condition->m_condition)
return;
if (!m_disable_optimizations && condition->m_condition->IsStatic())
{
const auto staticValueIsTruthy = condition->m_condition->EvaluateStatic().IsTruthy();
if (staticValueIsTruthy)
ConvertEventHandlerElements(elements, condition->m_condition_elements.get(), menu, item);
else if (condition->m_else_elements)
ConvertEventHandlerElements(elements, condition->m_else_elements.get(), menu, item);
}
else
{
auto* outputHandler = m_memory.Alloc<MenuEventHandler>();
auto* outputCondition = m_memory.Alloc<ConditionalScript>();
outputHandler->eventType = EVENT_IF;
outputHandler->eventData.conditionalScript = outputCondition;
outputCondition->eventExpression = ConvertExpression(condition->m_condition.get(), menu, item);
outputCondition->eventHandlerSet = ConvertEventHandlerSet(condition->m_condition_elements.get(), menu, item);
elements.push_back(outputHandler);
if (condition->m_else_elements)
{
auto* outputElseHandler = m_memory.Alloc<MenuEventHandler>();
outputElseHandler->eventType = EVENT_ELSE;
outputElseHandler->eventData.elseScript = ConvertEventHandlerSet(condition->m_else_elements.get(), menu, item);
elements.push_back(outputElseHandler);
}
}
}
void ConvertEventHandler(std::vector<MenuEventHandler*>& elements,
const ICommonEventHandlerElement* eventHandler,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(eventHandler);
if (!eventHandler)
return;
switch (eventHandler->GetType())
{
case CommonEventHandlerElementType::CONDITION:
ConvertEventHandlerCondition(elements, dynamic_cast<const CommonEventHandlerCondition*>(eventHandler), menu, item);
break;
case CommonEventHandlerElementType::SCRIPT:
ConvertEventHandlerScript(elements, dynamic_cast<const CommonEventHandlerScript*>(eventHandler));
break;
case CommonEventHandlerElementType::SET_LOCAL_VAR:
ConvertEventHandlerSetLocalVar(elements, dynamic_cast<const CommonEventHandlerSetLocalVar*>(eventHandler), menu, item);
break;
}
}
void ConvertEventHandlerElements(std::vector<MenuEventHandler*>& elements,
const CommonEventHandlerSet* eventHandlerSet,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
for (const auto& element : eventHandlerSet->m_elements)
ConvertEventHandler(elements, element.get(), menu, item);
}
_NODISCARD MenuEventHandlerSet*
ConvertEventHandlerSet(const CommonEventHandlerSet* eventHandlerSet, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const
{
if (!eventHandlerSet)
return nullptr;
std::vector<MenuEventHandler*> elements;
ConvertEventHandlerElements(elements, eventHandlerSet, menu, item);
if (elements.empty())
return nullptr;
auto* outputSet = m_memory.Alloc<MenuEventHandlerSet>();
auto* outputElements = m_memory.Alloc<MenuEventHandler*>(elements.size());
memcpy(outputElements, elements.data(), sizeof(void*) * elements.size());
outputSet->eventHandlerCount = static_cast<int>(elements.size());
outputSet->eventHandlers = outputElements;
return outputSet;
}
_NODISCARD ItemKeyHandler* ConvertKeyHandler(const std::multimap<int, std::unique_ptr<CommonEventHandlerSet>>& keyHandlers,
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (keyHandlers.empty())
return nullptr;
const auto keyHandlerCount = keyHandlers.size();
auto* output = m_memory.Alloc<ItemKeyHandler>(keyHandlerCount);
auto currentKeyHandler = keyHandlers.cbegin();
for (auto i = 0u; i < keyHandlerCount; i++)
{
output[i].key = currentKeyHandler->first;
output[i].action = ConvertEventHandlerSet(currentKeyHandler->second.get(), menu, item);
if (i + 1 < keyHandlerCount)
output[i].next = &output[i + 1];
else
output[i].next = nullptr;
++currentKeyHandler;
}
return output;
}
ItemFloatExpression*
ConvertFloatExpressions(const CommonItemDef* commonItem, itemDef_s* item, const CommonMenuDef* parentMenu, int& floatExpressionCount) const
{
struct FloatExpressionLocation
{
ISimpleExpression* m_expression;
bool m_expression_is_static;
ItemFloatExpressionTarget m_target;
float* m_static_value;
unsigned m_static_value_array_size;
unsigned m_dynamic_flags_to_set;
};
FloatExpressionLocation locations[]{
{commonItem->m_rect_x_exp.get(), false, ITEM_FLOATEXP_TGT_RECT_X, &item->window.rectClient.x, 1, 0 },
{commonItem->m_rect_y_exp.get(), false, ITEM_FLOATEXP_TGT_RECT_Y, &item->window.rectClient.y, 1, 0 },
{commonItem->m_rect_w_exp.get(), false, ITEM_FLOATEXP_TGT_RECT_W, &item->window.rectClient.w, 1, 0 },
{commonItem->m_rect_h_exp.get(), false, ITEM_FLOATEXP_TGT_RECT_H, &item->window.rectClient.h, 1, 0 },
{commonItem->m_forecolor_expressions.m_r_exp.get(),
false, ITEM_FLOATEXP_TGT_FORECOLOR_R,
&item->window.foreColor[0],
1, WINDOW_FLAG_NON_DEFAULT_FORECOLOR},
{commonItem->m_forecolor_expressions.m_g_exp.get(),
false, ITEM_FLOATEXP_TGT_FORECOLOR_G,
&item->window.foreColor[1],
1, WINDOW_FLAG_NON_DEFAULT_FORECOLOR},
{commonItem->m_forecolor_expressions.m_b_exp.get(),
false, ITEM_FLOATEXP_TGT_FORECOLOR_B,
&item->window.foreColor[2],
1, WINDOW_FLAG_NON_DEFAULT_FORECOLOR},
{commonItem->m_forecolor_expressions.m_a_exp.get(),
false, ITEM_FLOATEXP_TGT_FORECOLOR_A,
&item->window.foreColor[3],
1, WINDOW_FLAG_NON_DEFAULT_FORECOLOR},
{commonItem->m_forecolor_expressions.m_rgb_exp.get(),
false, ITEM_FLOATEXP_TGT_FORECOLOR_RGB,
&item->window.foreColor[0],
3, WINDOW_FLAG_NON_DEFAULT_FORECOLOR},
{commonItem->m_glowcolor_expressions.m_r_exp.get(), false, ITEM_FLOATEXP_TGT_GLOWCOLOR_R, &item->glowColor[0], 1, 0 },
{commonItem->m_glowcolor_expressions.m_g_exp.get(), false, ITEM_FLOATEXP_TGT_GLOWCOLOR_G, &item->glowColor[1], 1, 0 },
{commonItem->m_glowcolor_expressions.m_b_exp.get(), false, ITEM_FLOATEXP_TGT_GLOWCOLOR_B, &item->glowColor[2], 1, 0 },
{commonItem->m_glowcolor_expressions.m_a_exp.get(), false, ITEM_FLOATEXP_TGT_GLOWCOLOR_A, &item->glowColor[3], 1, 0 },
{commonItem->m_glowcolor_expressions.m_rgb_exp.get(), false, ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB, &item->glowColor[0], 3, 0 },
{commonItem->m_backcolor_expressions.m_r_exp.get(), false, ITEM_FLOATEXP_TGT_BACKCOLOR_R, &item->window.backColor[0], 1, 0 },
{commonItem->m_backcolor_expressions.m_g_exp.get(), false, ITEM_FLOATEXP_TGT_BACKCOLOR_G, &item->window.backColor[1], 1, 0 },
{commonItem->m_backcolor_expressions.m_b_exp.get(), false, ITEM_FLOATEXP_TGT_BACKCOLOR_B, &item->window.backColor[2], 1, 0 },
{commonItem->m_backcolor_expressions.m_a_exp.get(), false, ITEM_FLOATEXP_TGT_BACKCOLOR_A, &item->window.backColor[3], 1, 0 },
{commonItem->m_backcolor_expressions.m_rgb_exp.get(), false, ITEM_FLOATEXP_TGT_BACKCOLOR_RGB, &item->window.backColor[0], 3, 0 },
};
floatExpressionCount = 0;
for (auto& [expression, expressionIsStatic, target, staticValue, staticValueArraySize, dynamicFlagsToSet] : locations)
{
expressionIsStatic = !m_disable_optimizations && staticValue != nullptr && expression && expression->IsStatic();
if (expressionIsStatic)
{
const auto evaluatedValue = expression->EvaluateStatic();
if (evaluatedValue.m_type == SimpleExpressionValue::Type::INT)
{
item->window.dynamicFlags[0] |= dynamicFlagsToSet;
auto* staticValuePtr = staticValue;
for (auto i = 0u; i < staticValueArraySize; i++)
{
*staticValuePtr = static_cast<float>(evaluatedValue.m_int_value);
staticValuePtr++;
}
continue;
}
if (evaluatedValue.m_type == SimpleExpressionValue::Type::DOUBLE)
{
item->window.dynamicFlags[0] |= dynamicFlagsToSet;
auto* staticValuePtr = staticValue;
for (auto i = 0u; i < staticValueArraySize; i++)
{
*staticValue = static_cast<float>(evaluatedValue.m_double_value);
staticValuePtr++;
}
continue;
}
// Do not consider this a mistake since the games menus do this by mistake and it should be able to compile them anyway
// But the game should also not know what to do with this i guess
expressionIsStatic = false;
}
if (expression)
floatExpressionCount++;
}
if (floatExpressionCount <= 0)
return nullptr;
auto* floatExpressions = m_memory.Alloc<ItemFloatExpression>(floatExpressionCount);
auto floatExpressionIndex = 0;
for (const auto& [expression, expressionIsStatic, target, staticValue, staticValueArraySize, dynamicFlagsToSet] : locations)
{
if (!expression || expressionIsStatic)
continue;
assert(floatExpressionIndex < floatExpressionCount && floatExpressionIndex >= 0);
floatExpressions[floatExpressionIndex].target = target;
floatExpressions[floatExpressionIndex].expression = ConvertExpression(expression, parentMenu, commonItem);
item->window.dynamicFlags[0] |= dynamicFlagsToSet;
floatExpressionIndex++;
}
return floatExpressions;
}
_NODISCARD const char* CreateEnableDvarString(const std::vector<std::string>& stringElements) const
{
std::ostringstream ss;
for (const auto& element : stringElements)
{
ss << "\"" << element << "\" ";
}
return m_memory.Dup(ss.str().c_str());
}
_NODISCARD const char* ConvertEnableDvar(const CommonItemDef& commonItem, int& dvarFlags) const
{
dvarFlags = 0;
if (!commonItem.m_enable_dvar.empty())
{
dvarFlags |= ITEM_DVAR_FLAG_ENABLE;
return CreateEnableDvarString(commonItem.m_enable_dvar);
}
if (!commonItem.m_disable_dvar.empty())
{
dvarFlags |= ITEM_DVAR_FLAG_DISABLE;
return CreateEnableDvarString(commonItem.m_disable_dvar);
}
if (!commonItem.m_show_dvar.empty())
{
dvarFlags |= ITEM_DVAR_FLAG_SHOW;
return CreateEnableDvarString(commonItem.m_show_dvar);
}
if (!commonItem.m_hide_dvar.empty())
{
dvarFlags |= ITEM_DVAR_FLAG_HIDE;
return CreateEnableDvarString(commonItem.m_hide_dvar);
}
if (!commonItem.m_focus_dvar.empty())
{
dvarFlags |= ITEM_DVAR_FLAG_FOCUS;
return CreateEnableDvarString(commonItem.m_focus_dvar);
}
return nullptr;
}
_NODISCARD listBoxDef_s* ConvertListBoxFeatures(itemDef_s* item,
CommonItemFeaturesListBox* commonListBox,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonListBox == nullptr)
return nullptr;
auto* listBox = m_memory.Alloc<listBoxDef_s>();
listBox->notselectable = commonListBox->m_not_selectable ? 1 : 0;
listBox->noScrollBars = commonListBox->m_no_scrollbars ? 1 : 0;
listBox->usePaging = commonListBox->m_use_paging ? 1 : 0;
listBox->elementWidth = static_cast<float>(commonListBox->m_element_width);
listBox->elementHeight = static_cast<float>(commonListBox->m_element_height);
item->special = static_cast<float>(commonListBox->m_feeder);
listBox->elementStyle = commonListBox->m_element_style;
listBox->onDoubleClick = ConvertEventHandlerSet(commonListBox->m_on_double_click.get(), &parentMenu, &commonItem);
ConvertColor(listBox->selectBorder, commonListBox->m_select_border);
listBox->selectIcon = ConvertMaterial(commonListBox->m_select_icon, &parentMenu, &commonItem);
listBox->numColumns = static_cast<int>(std::min(std::extent_v<decltype(listBoxDef_s::columnInfo)>, commonListBox->m_columns.size()));
for (auto i = 0; i < listBox->numColumns; i++)
{
auto& col = listBox->columnInfo[i];
const auto& commonCol = commonListBox->m_columns[i];
col.pos = commonCol.m_x_pos;
col.width = commonCol.m_width;
col.maxChars = commonCol.m_max_chars;
col.alignment = commonCol.m_alignment;
}
return listBox;
}
_NODISCARD editFieldDef_s* ConvertEditFieldFeatures(itemDef_s* item,
CommonItemFeaturesEditField* commonEditField,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonEditField == nullptr)
return nullptr;
auto* editField = m_memory.Alloc<editFieldDef_s>();
editField->defVal = static_cast<float>(commonEditField->m_def_val);
editField->minVal = static_cast<float>(commonEditField->m_min_val);
editField->maxVal = static_cast<float>(commonEditField->m_max_val);
item->localVar = ConvertString(commonEditField->m_local_var);
editField->maxChars = commonEditField->m_max_chars;
editField->maxCharsGotoNext = commonEditField->m_max_chars_goto_next ? 1 : 0;
editField->maxPaintChars = commonEditField->m_max_paint_chars;
return editField;
}
_NODISCARD multiDef_s* ConvertMultiValueFeatures(itemDef_s* item,
CommonItemFeaturesMultiValue* commonMultiValue,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonMultiValue == nullptr)
return nullptr;
auto* multiValue = m_memory.Alloc<multiDef_s>();
multiValue->count = static_cast<int>(std::min(std::extent_v<decltype(multiDef_s::dvarList)>, commonMultiValue->m_step_names.size()));
multiValue->strDef = !commonMultiValue->m_string_values.empty() ? 1 : 0;
for (auto i = 0; i < multiValue->count; i++)
{
multiValue->dvarList[i] = ConvertString(commonMultiValue->m_step_names[i]);
if (multiValue->strDef)
{
if (commonMultiValue->m_string_values.size() > static_cast<unsigned>(i))
multiValue->dvarStr[i] = ConvertString(commonMultiValue->m_string_values[i]);
}
else
{
if (commonMultiValue->m_double_values.size() > static_cast<unsigned>(i))
multiValue->dvarValue[i] = static_cast<float>(commonMultiValue->m_double_values[i]);
}
}
return multiValue;
}
_NODISCARD newsTickerDef_s* ConvertNewsTickerFeatures(itemDef_s* item,
CommonItemFeaturesNewsTicker* commonNewsTicker,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonNewsTicker == nullptr)
return nullptr;
auto* newsTicker = m_memory.Alloc<newsTickerDef_s>();
newsTicker->spacing = commonNewsTicker->m_spacing;
newsTicker->speed = commonNewsTicker->m_speed;
newsTicker->feedId = commonNewsTicker->m_news_feed_id;
return newsTicker;
}
_NODISCARD itemDef_s* ConvertItem(const CommonMenuDef& parentMenu, const CommonItemDef& commonItem) const
{
auto* item = m_memory.Alloc<itemDef_s>();
item->window.name = ConvertString(commonItem.m_name);
item->text = ConvertString(commonItem.m_text);
ApplyFlag(item->itemFlags, commonItem.m_text_save_game, ITEM_FLAG_SAVE_GAME_INFO);
ApplyFlag(item->itemFlags, commonItem.m_text_cinematic_subtitle, ITEM_FLAG_CINEMATIC_SUBTITLE);
item->window.group = ConvertString(commonItem.m_group);
item->window.rectClient = ConvertRectDef(commonItem.m_rect);
item->window.rect = ConvertRectDefRelativeTo(commonItem.m_rect, parentMenu.m_rect);
item->window.style = commonItem.m_style;
ApplyFlag(item->window.staticFlags, commonItem.m_decoration, WINDOW_FLAG_DECORATION);
ApplyFlag(item->window.staticFlags, commonItem.m_auto_wrapped, WINDOW_FLAG_AUTO_WRAPPED);
ApplyFlag(item->window.staticFlags, commonItem.m_horizontal_scroll, WINDOW_FLAG_HORIZONTAL_SCROLL);
item->type = ConvertItemType(commonItem.m_type);
item->dataType = item->type;
item->window.border = commonItem.m_border;
item->window.borderSize = static_cast<float>(commonItem.m_border_size);
item->visibleExp = ConvertVisibleExpression(&item->window, 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;
item->textAlignMode = commonItem.m_text_align;
item->textalignx = static_cast<float>(commonItem.m_text_align_x);
item->textaligny = static_cast<float>(commonItem.m_text_align_y);
item->textscale = static_cast<float>(commonItem.m_text_scale);
item->textStyle = commonItem.m_text_style;
item->fontEnum = ConvertTextFont(commonItem.m_text_font);
ConvertColor(item->window.backColor, commonItem.m_back_color);
ConvertColor(item->window.foreColor, commonItem.m_fore_color);
if (!commonItem.m_fore_color.Equals(CommonColor(1.0, 1.0, 1.0, 1.0)))
item->window.dynamicFlags[0] |= WINDOW_FLAG_NON_DEFAULT_FORECOLOR;
ConvertColor(item->window.borderColor, commonItem.m_border_color);
ConvertColor(item->window.outlineColor, commonItem.m_outline_color);
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(), &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 = ConvertSound(commonItem.m_focus_sound, &parentMenu, &commonItem);
item->dvarTest = ConvertString(commonItem.m_dvar_test);
item->enableDvar = ConvertEnableDvar(commonItem, item->dvarFlags);
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(), &parentMenu, &commonItem);
item->floatExpressions = ConvertFloatExpressions(&commonItem, item, &parentMenu, item->floatExpressionCount);
item->gameMsgWindowIndex = commonItem.m_game_message_window_index;
item->gameMsgWindowMode = commonItem.m_game_message_window_mode;
item->fxLetterTime = commonItem.m_fx_letter_time;
item->fxDecayStartTime = commonItem.m_fx_decay_start_time;
item->fxDecayDuration = commonItem.m_fx_decay_duration;
item->dvar = ConvertString(commonItem.m_dvar);
switch (commonItem.m_feature_type)
{
case CommonItemFeatureType::LISTBOX:
item->typeData.listBox = ConvertListBoxFeatures(item, commonItem.m_list_box_features.get(), parentMenu, commonItem);
break;
case CommonItemFeatureType::EDIT_FIELD:
item->typeData.editField = ConvertEditFieldFeatures(item, commonItem.m_edit_field_features.get(), parentMenu, commonItem);
break;
case CommonItemFeatureType::MULTI_VALUE:
item->typeData.multi = ConvertMultiValueFeatures(item, commonItem.m_multi_value_features.get(), parentMenu, commonItem);
break;
case CommonItemFeatureType::ENUM_DVAR:
item->typeData.enumDvarName = ConvertString(commonItem.m_enum_dvar_name);
break;
case CommonItemFeatureType::NEWS_TICKER:
item->typeData.ticker = ConvertNewsTickerFeatures(item, commonItem.m_news_ticker_features.get(), parentMenu, commonItem);
break;
case CommonItemFeatureType::NONE:
default:
if (item->type == ITEM_TYPE_TEXT_SCROLL)
item->typeData.scroll = m_memory.Alloc<textScrollDef_s>();
break;
}
return item;
}
itemDef_s** ConvertMenuItems(const CommonMenuDef& commonMenu, int& itemCount) const
{
if (commonMenu.m_items.empty())
{
itemCount = 0;
return nullptr;
}
auto* items = m_memory.Alloc<itemDef_s*>(commonMenu.m_items.size());
for (auto i = 0u; i < commonMenu.m_items.size(); i++)
items[i] = ConvertItem(commonMenu, *commonMenu.m_items[i]);
itemCount = static_cast<int>(commonMenu.m_items.size());
return items;
}
public:
MenuConverter(const bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
: AbstractMenuConverter(disableOptimizations, searchPath, memory, context),
m_conversion_zone_state(context.GetZoneAssetLoaderState<MenuConversionZoneState>()),
m_parsing_zone_state(context.GetZoneAssetLoaderState<MenuAssetZoneState>())
{
assert(m_conversion_zone_state);
assert(m_parsing_zone_state);
}
void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) override
{
try
{
menu.window.name = m_memory.Dup(commonMenu.m_name.c_str());
menu.fullScreen = commonMenu.m_full_screen;
ApplyFlag(menu.window.staticFlags, commonMenu.m_screen_space, WINDOW_FLAG_SCREEN_SPACE);
ApplyFlag(menu.window.staticFlags, commonMenu.m_decoration, WINDOW_FLAG_DECORATION);
menu.window.rect = ConvertRectDef(commonMenu.m_rect);
menu.window.style = commonMenu.m_style;
menu.window.border = commonMenu.m_border;
menu.window.borderSize = static_cast<float>(commonMenu.m_border_size);
ConvertColor(menu.window.backColor, commonMenu.m_back_color);
ConvertColor(menu.window.foreColor, commonMenu.m_fore_color);
ConvertColor(menu.window.borderColor, commonMenu.m_border_color);
ConvertColor(menu.focusColor, commonMenu.m_focus_color);
menu.window.background = ConvertMaterial(commonMenu.m_background, &commonMenu);
menu.window.ownerDraw = commonMenu.m_owner_draw;
menu.window.ownerDrawFlags = commonMenu.m_owner_draw_flags;
ApplyFlag(menu.window.staticFlags, commonMenu.m_out_of_bounds_click, WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
menu.soundName = ConvertString(commonMenu.m_sound_loop);
ApplyFlag(menu.window.staticFlags, commonMenu.m_popup, WINDOW_FLAG_POPUP);
menu.fadeClamp = static_cast<float>(commonMenu.m_fade_clamp);
menu.fadeCycle = commonMenu.m_fade_cycle;
menu.fadeAmount = static_cast<float>(commonMenu.m_fade_amount);
menu.fadeInAmount = static_cast<float>(commonMenu.m_fade_in_amount);
menu.blurRadius = static_cast<float>(commonMenu.m_blur_radius);
ApplyFlag(menu.window.staticFlags, commonMenu.m_legacy_split_screen_scale, WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
ApplyFlag(menu.window.staticFlags, commonMenu.m_hidden_during_scope, WINDOW_FLAG_HIDDEN_DURING_SCOPE);
ApplyFlag(menu.window.staticFlags, commonMenu.m_hidden_during_flashbang, WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
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 = ConvertVisibleExpression(&menu.window, 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(), &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, menu.itemCount);
menu.expressionData = m_conversion_zone_state->m_supporting_data;
}
catch (const MenuConversionException& e)
{
PrintConversionExceptionDetails(e);
}
}
MenuConversionZoneState* m_conversion_zone_state;
MenuAssetZoneState* m_parsing_zone_state;
};
} // namespace
std::unique_ptr<IMenuConverter> IMenuConverter::Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
{
return std::make_unique<MenuConverter>(disableOptimizations, searchPath, memory, context);
}