chore: align iw3,iw4,iw5 menu code (#987)

* chore: small adjustments to iw3 menu dumper

* chore: align iw4 menu loader closer to iw3

* chore: align iw4 menu dumper closer to iw3

* chore: adjust iw4 menu expression dumping

* chore: adjust iw4 menu expression converting

* chore: align iw5 menu loader closer to iw3

* chore: align iw4 menu dumper closer to iw3

* chore: adjust iw5 menu expression dumping

* chore: adjust iw5 menu expression converting

* fix: not respecting reference menus when inlining menu list menus

* fix: compilation on linux
This commit is contained in:
Jan Laupetin
2026-08-31 22:10:37 +02:00
committed by GitHub
parent 51770e7ae5
commit 662780a896
24 changed files with 1331 additions and 1427 deletions
@@ -10,6 +10,7 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <format>
#include <sstream>
#include <vector>
@@ -1,15 +1,12 @@
#include "LoaderMenuListIW4.h"
#include "Game/IW4/IW4.h"
#include "Game/IW4/Menu/MenuConversionZoneStateIW4.h"
#include "Game/IW4/Menu/MenuConverterIW4.h"
#include "ObjLoading.h"
#include "Parsing/Menu/MenuFileReader.h"
#include "Utils/Logging/Log.h"
#include <cstring>
#include <format>
#include <iostream>
#include <deque>
using namespace IW4;
@@ -44,7 +41,8 @@ namespace
const auto menuListResult = ParseMenuFile(*file.m_stream, assetName, zoneState);
if (menuListResult)
{
ProcessParsedResults(assetName, context, *menuListResult, zoneState, conversionState, menus, registration);
if (!ProcessParsedResults(assetName, context, *menuListResult, zoneState, conversionState, menus, registration))
return AssetCreationResult::Failure();
for (const auto& menuToLoad : menuListResult->m_menus_to_load)
menuLoadQueue.emplace_back(menuToLoad);
@@ -59,7 +57,8 @@ namespace
{
const auto& menuFileToLoad = menuLoadQueue.front();
LoadMenuFileFromQueue(menuFileToLoad, context, zoneState, conversionState, menus, registration);
if (!LoadMenuFileFromQueue(menuFileToLoad, context, zoneState, conversionState, menus, registration))
return AssetCreationResult::Failure();
menuLoadQueue.pop_front();
}
@@ -108,7 +107,8 @@ namespace
const auto menuFileResult = ParseMenuFile(*file.m_stream, menuFilePath, zoneState);
if (menuFileResult)
{
ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration);
if (!ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration))
return false;
if (!menuFileResult->m_menus_to_load.empty())
con::warn("Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"", menuFilePath);
@@ -158,8 +158,7 @@ namespace
auto* menuAsset = m_memory.Alloc<menuDef_t>();
AssetRegistration<AssetMenu> menuRegistration(commonMenu->m_name, menuAsset);
converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration);
if (menuAsset == nullptr)
if (!converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration))
{
con::error("Failed to convert menu file \"{}\"", commonMenu->m_name);
return false;
@@ -10,4 +10,4 @@
namespace menu
{
std::unique_ptr<AssetCreator<IW4::AssetMenuList>> CreateMenuListLoaderIW4(MemoryManager& memory, ISearchPath& searchPath);
} // namespace menu
}
@@ -74,7 +74,7 @@ const char* MenuConversionZoneState::AddString(const std::string& str)
void MenuConversionZoneState::AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile)
{
m_menus_by_filename.emplace(std::make_pair(std::move(loadedFileName), std::move(menusOfFile)));
m_menus_by_filename.emplace(std::move(loadedFileName), std::move(menusOfFile));
}
void MenuConversionZoneState::FinalizeSupportingData() const
@@ -3,26 +3,15 @@
#include "Asset/IZoneAssetCreationState.h"
#include "Game/IW4/IW4.h"
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
namespace IW4
{
class MenuConversionZoneState final : public IZoneAssetCreationState
{
Zone* m_zone;
std::vector<Statement_s*> m_functions;
std::map<std::string, Statement_s*> m_function_by_name;
std::vector<StaticDvar*> m_static_dvars;
std::map<std::string, size_t> m_dvars_by_name;
std::vector<const char*> m_strings;
std::map<std::string, const char*> m_strings_by_value;
public:
std::map<std::string, std::vector<XAssetInfo<menuDef_t>*>> m_menus_by_filename;
ExpressionSupportingData* m_supporting_data;
MenuConversionZoneState();
void Inject(ZoneAssetCreationInjection& inject) override;
@@ -35,5 +24,19 @@ namespace IW4
void AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile);
void FinalizeSupportingData() const;
std::unordered_map<std::string, std::vector<XAssetInfo<menuDef_t>*>> m_menus_by_filename;
ExpressionSupportingData* m_supporting_data;
private:
Zone* m_zone;
std::vector<Statement_s*> m_functions;
std::unordered_map<std::string, Statement_s*> m_function_by_name;
std::vector<StaticDvar*> m_static_dvars;
std::unordered_map<std::string, size_t> m_dvars_by_name;
std::vector<const char*> m_strings;
std::unordered_map<std::string, const char*> m_strings_by_value;
};
} // namespace IW4
+259 -343
View File
@@ -13,35 +13,37 @@
#include "Parsing/Simple/Expression/SimpleExpressionUnaryOperation.h"
#include "Utils/StringUtils.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <format>
#include <sstream>
#include <vector>
using namespace IW4;
using namespace menu;
namespace
{
class MenuConverter : public AbstractMenuConverter, public IMenuConverter
class MenuConverter final : 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),
.x = static_cast<float>(rect.x),
.y = static_cast<float>(rect.y),
.w = static_cast<float>(rect.w),
.h = static_cast<float>(rect.h),
.horzAlign = static_cast<unsigned char>(rect.horizontalAlign),
.vertAlign = static_cast<unsigned char>(rect.verticalAlign),
};
}
[[nodiscard]] static rectDef_s ConvertRectDefRelativeTo(const rectDef_s& rect, const rectDef_s& rectRelativeTo)
[[nodiscard]] static rectDef_s ConvertRectDefRelativeTo(const rectDef_s& rect, const rectDef_s& relativeTo)
{
return rectDef_s{
.x = rectRelativeTo.x + rect.x,
.y = rectRelativeTo.y + rect.y,
.x = relativeTo.x + rect.x,
.y = relativeTo.y + rect.y,
.w = static_cast<float>(rect.w),
.h = static_cast<float>(rect.h),
.horzAlign = static_cast<unsigned char>(rect.horzAlign),
@@ -57,7 +59,7 @@ namespace
output[3] = static_cast<float>(input.a);
}
static void ApplyFlag(int& flags, const bool shouldApply, const int flagValue)
static void ApplyFlag(unsigned& flags, const bool shouldApply, const unsigned flagValue)
{
if (!shouldApply)
return;
@@ -65,16 +67,6 @@ namespace
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())
@@ -99,181 +91,14 @@ namespace
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)]{
constexpr static expressionOperatorType_e UNARY_OPERATION_MAPPING[]{
OP_NOT,
OP_BITWISENOT,
OP_SUBTRACT,
};
static_assert(std::size(UNARY_OPERATION_MAPPING) == static_cast<unsigned>(SimpleUnaryOperationId::COUNT));
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)]{
constexpr static expressionOperatorType_e BINARY_OPERATION_MAPPING[]{
OP_ADD,
OP_SUBTRACT,
OP_MULTIPLY,
@@ -292,75 +117,195 @@ namespace
OP_AND,
OP_OR,
};
static_assert(std::size(BINARY_OPERATION_MAPPING) == static_cast<unsigned>(SimpleBinaryOperationId::COUNT));
[[nodiscard]] bool IsOperation(const ISimpleExpression* expression) const
{
if (!m_disable_optimizations && expression->IsStatic())
return false;
return dynamic_cast<const SimpleExpressionBinaryOperation*>(expression) != nullptr
|| dynamic_cast<const SimpleExpressionUnaryOperation*>(expression) != nullptr;
}
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;
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = targetFunctionIndex}});
const auto dvarIntVal = static_cast<int>(m_conversion_zone_state.AddStaticDvar(*staticDvarNameExpressionValue.m_string_value));
entries.emplace_back(expressionEntry{.type = EET_OPERAND, .data = {.operand = {.dataType = VAL_INT, .internals = {.intVal = dvarIntVal}}}});
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
gameStatement->supportingData = m_conversion_zone_state.m_supporting_data;
return true;
}
bool HandleSpecialBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* baseFunction) const
{
switch (baseFunction->m_function_index)
{
case EXP_FUNC_DVAR_INT:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_INT);
case EXP_FUNC_DVAR_BOOL:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_BOOL);
case EXP_FUNC_DVAR_FLOAT:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_FLOAT);
case EXP_FUNC_DVAR_STRING:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_STRING);
default:
break;
}
return false;
}
void ConvertExpressionEntryBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* baseFunction,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
if (!HandleSpecialBaseFunctionCall(gameStatement, entries, baseFunction))
{
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = static_cast<int>(baseFunction->m_function_index)}});
auto firstArg = true;
for (const auto& arg : baseFunction->m_args)
{
if (firstArg)
firstArg = false;
else
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_COMMA}});
ConvertExpressionEntry(gameStatement, entries, arg.get(), menu, item);
}
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
}
void ConvertExpressionEntryCustomFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionCustomFunctionCall* customFunction,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
std::string lowerCaseFunctionName(customFunction->m_function_name);
utils::MakeStringLowerCase(lowerCaseFunctionName);
auto* functionStatement = m_conversion_zone_state.FindFunction(lowerCaseFunctionName);
if (!functionStatement)
{
// 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(
std::format("Failed to find definition for custom function \"{}\"", customFunction->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);
}
entries.emplace_back(expressionEntry{
.type = EET_OPERAND,
.data = {.operand = {.dataType = VAL_FUNCTION, .internals = {.function = functionStatement}}},
});
// Statement uses custom function so it needs supporting data
gameStatement->supportingData = m_conversion_zone_state.m_supporting_data;
}
void ConvertExpressionEntryUnaryOperation(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionUnaryOperation* unary,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(static_cast<unsigned>(unary->m_operation_type->m_id) < static_cast<unsigned>(SimpleUnaryOperationId::COUNT));
entries.emplace_back(expressionEntry{
.type = EET_OPERATOR,
.data = {.op = UNARY_OPERATION_MAPPING[static_cast<unsigned>(unary->m_operation_type->m_id)]},
});
const auto wrapOperand = IsOperation(unary->m_operand.get());
if (wrapOperand)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, unary->m_operand.get(), menu, item);
if (wrapOperand)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
void ConvertExpressionEntryBinaryOperation(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionBinaryOperation* binaryOperation,
const SimpleExpressionBinaryOperation* binary,
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);
const auto wrapLeft = IsOperation(binary->m_operand1.get());
if (wrapLeft)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, binary->m_operand1.get(), menu, item);
if (wrapLeft)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
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);
assert(static_cast<unsigned>(binary->m_operation_type->m_id) < static_cast<unsigned>(SimpleBinaryOperationId::COUNT));
entries.emplace_back(expressionEntry{
.type = EET_OPERATOR,
.data = {.op = BINARY_OPERATION_MAPPING[static_cast<unsigned>(binary->m_operation_type->m_id)]},
});
// 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);
const auto wrapRight = IsOperation(binary->m_operand2.get());
if (wrapRight)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, binary->m_operand2.get(), menu, item);
if (wrapRight)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
void ConvertExpressionEntryExpressionValue(std::vector<expressionEntry>& entries, const SimpleExpressionValue* expressionValue) const
void ConvertExpressionValue(std::vector<expressionEntry>& entries, const SimpleExpressionValue& value) const
{
expressionEntry entry{};
entry.type = EET_OPERAND;
if (expressionValue->m_type == SimpleExpressionValue::Type::INT)
switch (value.m_type)
{
case 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.internals.intVal = value.m_int_value;
break;
case 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.internals.floatVal = static_cast<float>(value.m_double_value);
break;
case SimpleExpressionValue::Type::STRING:
entry.data.operand.dataType = VAL_STRING;
entry.data.operand.internals.stringVal.string = m_conversion_zone_state.AddString(*expressionValue->m_string_value);
entry.data.operand.internals.stringVal.string = m_conversion_zone_state.AddString(*value.m_string_value);
break;
}
entries.emplace_back(entry);
@@ -374,28 +319,28 @@ namespace
{
if (!m_disable_optimizations && expression->IsStatic())
{
const auto expressionStaticValue = expression->EvaluateStatic();
ConvertExpressionEntryExpressionValue(entries, &expressionStaticValue);
const auto staticValue = expression->EvaluateStatic();
ConvertExpressionValue(entries, staticValue);
}
else if (const auto* expressionValue = dynamic_cast<const SimpleExpressionValue*>(expression))
else if (const auto* value = dynamic_cast<const SimpleExpressionValue*>(expression))
{
ConvertExpressionEntryExpressionValue(entries, expressionValue);
ConvertExpressionValue(entries, *value);
}
else if (const auto* binaryOperation = dynamic_cast<const SimpleExpressionBinaryOperation*>(expression))
else if (const auto* binary = dynamic_cast<const SimpleExpressionBinaryOperation*>(expression))
{
ConvertExpressionEntryBinaryOperation(gameStatement, entries, binaryOperation, menu, item);
ConvertExpressionEntryBinaryOperation(gameStatement, entries, binary, menu, item);
}
else if (const auto* unaryOperation = dynamic_cast<const SimpleExpressionUnaryOperation*>(expression))
else if (const auto* unary = dynamic_cast<const SimpleExpressionUnaryOperation*>(expression))
{
ConvertExpressionEntryUnaryOperation(gameStatement, entries, unaryOperation, menu, item);
ConvertExpressionEntryUnaryOperation(gameStatement, entries, unary, menu, item);
}
else if (const auto* baseFunctionCall = dynamic_cast<const CommonExpressionBaseFunctionCall*>(expression))
else if (const auto* baseFunction = dynamic_cast<const CommonExpressionBaseFunctionCall*>(expression))
{
ConvertExpressionEntryBaseFunctionCall(gameStatement, entries, baseFunctionCall, menu, item);
ConvertExpressionEntryBaseFunctionCall(gameStatement, entries, baseFunction, menu, item);
}
else if (const auto* customFunctionCall = dynamic_cast<const CommonExpressionCustomFunctionCall*>(expression))
else if (const auto* customFunction = dynamic_cast<const CommonExpressionCustomFunctionCall*>(expression))
{
ConvertExpressionEntryCustomFunctionCall(gameStatement, entries, customFunctionCall, menu, item);
ConvertExpressionEntryCustomFunctionCall(gameStatement, entries, customFunction, menu, item);
}
else if (dynamic_cast<const SimpleExpressionConditionalOperator*>(expression))
{
@@ -414,18 +359,14 @@ namespace
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> entries;
ConvertExpressionEntry(statement, entries, expression, menu, item);
std::vector<expressionEntry> expressionEntries;
ConvertExpressionEntry(statement, expressionEntries, expression, menu, item);
auto* outEntries = m_memory.Alloc<expressionEntry>(entries.size());
std::memcpy(outEntries, entries.data(), sizeof(expressionEntry) * entries.size());
auto* outputExpressionEntries = m_memory.Alloc<expressionEntry>(expressionEntries.size());
std::memcpy(outputExpressionEntries, expressionEntries.data(), sizeof(expressionEntry) * expressionEntries.size());
statement->entries = outputExpressionEntries;
statement->numEntries = static_cast<int>(expressionEntries.size());
statement->numEntries = static_cast<int>(entries.size());
statement->entries = outEntries;
return statement;
}
@@ -435,26 +376,20 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && 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:
if (value.m_type == SimpleExpressionValue::Type::INT)
staticValue = static_cast<float>(value.m_int_value);
break;
case SimpleExpressionValue::Type::STRING:
else if (value.m_type == SimpleExpressionValue::Type::DOUBLE)
staticValue = static_cast<float>(value.m_double_value);
else
throw MenuConversionException("Cannot convert string expression value to floating point", menu, item);
}
return nullptr;
}
@@ -466,25 +401,17 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && 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:
if (value.m_type != SimpleExpressionValue::Type::STRING)
throw MenuConversionException("Cannot convert numeric expression value to string", menu, item);
}
staticValue = m_memory.Dup(value.m_string_value->c_str());
return nullptr;
}
@@ -496,37 +423,29 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && expression->IsStatic())
{
const auto value = expression->EvaluateStatic();
switch (value.m_type)
{
case SimpleExpressionValue::Type::STRING:
staticValue = ConvertMaterial(*value.m_string_value, menu, item);
break;
if (value.m_type != SimpleExpressionValue::Type::STRING)
throw MenuConversionException("Cannot convert numeric expression value to material", menu, item);
staticValue = ConvertMaterial(*value.m_string_value, menu, item);
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,
[[nodiscard]] Statement_s* ConvertVisibleExpression(windowDef_t& window,
const ISimpleExpression* expression,
const CommonMenuDef* commonMenu,
const CommonItemDef* commonItem = nullptr) const
{
if (expression == nullptr)
if (!expression)
return nullptr;
bool isStatic;
@@ -547,11 +466,11 @@ namespace
if (isStatic)
{
if (isTruthy)
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
window.dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return nullptr;
}
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
window.dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return ConvertExpression(expression, commonMenu, commonItem);
}
@@ -637,7 +556,7 @@ namespace
outputCondition->eventExpression = ConvertExpression(condition->m_condition.get(), menu, item);
outputCondition->eventHandlerSet = ConvertEventHandlerSet(condition->m_condition_elements.get(), menu, item);
elements.push_back(outputHandler);
elements.emplace_back(outputHandler);
if (condition->m_else_elements)
{
@@ -645,7 +564,7 @@ namespace
outputElseHandler->eventType = EVENT_ELSE;
outputElseHandler->eventData.elseScript = ConvertEventHandlerSet(condition->m_else_elements.get(), menu, item);
elements.push_back(outputElseHandler);
elements.emplace_back(outputElseHandler);
}
}
}
@@ -1011,11 +930,11 @@ namespace
}
[[nodiscard]] listBoxDef_s* ConvertListBoxFeatures(itemDef_s* item,
CommonItemFeaturesListBox* commonListBox,
const CommonItemFeaturesListBox* commonListBox,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonListBox == nullptr)
if (!commonListBox)
return nullptr;
auto* listBox = m_memory.Alloc<listBoxDef_s>();
@@ -1030,7 +949,7 @@ namespace
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()));
listBox->numColumns = static_cast<int>(std::min(std::size(listBox->columnInfo), commonListBox->m_columns.size()));
for (auto i = 0; i < listBox->numColumns; i++)
{
auto& col = listBox->columnInfo[i];
@@ -1045,12 +964,9 @@ namespace
return listBox;
}
[[nodiscard]] editFieldDef_s* ConvertEditFieldFeatures(itemDef_s* item,
CommonItemFeaturesEditField* commonEditField,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] editFieldDef_s* ConvertEditFieldFeatures(itemDef_s* item, const CommonItemFeaturesEditField* commonEditField) const
{
if (commonEditField == nullptr)
if (!commonEditField)
return nullptr;
auto* editField = m_memory.Alloc<editFieldDef_s>();
@@ -1065,16 +981,13 @@ namespace
return editField;
}
[[nodiscard]] multiDef_s* ConvertMultiValueFeatures(itemDef_s* item,
CommonItemFeaturesMultiValue* commonMultiValue,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] multiDef_s* ConvertMultiValueFeatures(const CommonItemFeaturesMultiValue* commonMultiValue) const
{
if (commonMultiValue == nullptr)
if (!commonMultiValue)
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->count = static_cast<int>(std::min(std::size(multiValue->dvarList), commonMultiValue->m_step_names.size()));
multiValue->strDef = !commonMultiValue->m_string_values.empty() ? 1 : 0;
for (auto i = 0; i < multiValue->count; i++)
@@ -1096,12 +1009,9 @@ namespace
return multiValue;
}
[[nodiscard]] newsTickerDef_s* ConvertNewsTickerFeatures(itemDef_s* item,
CommonItemFeaturesNewsTicker* commonNewsTicker,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] newsTickerDef_s* ConvertNewsTickerFeatures(const CommonItemFeaturesNewsTicker* commonNewsTicker) const
{
if (commonNewsTicker == nullptr)
if (!commonNewsTicker)
return nullptr;
auto* newsTicker = m_memory.Alloc<newsTickerDef_s>();
@@ -1112,7 +1022,7 @@ namespace
return newsTicker;
}
[[nodiscard]] itemDef_s* ConvertItem(const CommonMenuDef& commonParentMenu, const menuDef_t& parentMenu, const CommonItemDef& commonItem) const
[[nodiscard]] itemDef_s* ConvertItem(const CommonMenuDef& commonParentMenu, menuDef_t& parentMenu, const CommonItemDef& commonItem) const
{
auto* item = m_memory.Alloc<itemDef_s>();
@@ -1126,11 +1036,11 @@ namespace
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->type = commonItem.m_type;
item->dataType = commonItem.m_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(), &commonParentMenu, &commonItem);
item->visibleExp = ConvertVisibleExpression(item->window, commonItem.m_visible_expression.get(), &commonParentMenu, &commonItem);
item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get(), &commonParentMenu, &commonItem);
item->window.ownerDraw = commonItem.m_owner_draw;
item->window.ownerDrawFlags = commonItem.m_owner_draw_flags;
@@ -1140,12 +1050,11 @@ namespace
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);
item->fontEnum = 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;
ApplyFlag(item->window.dynamicFlags[0], !commonItem.m_fore_color.Equals(CommonColor(1.0, 1.0, 1.0, 1.0)), WINDOW_FLAG_NON_DEFAULT_FORECOLOR);
ConvertColor(item->window.borderColor, commonItem.m_border_color);
ConvertColor(item->window.outlineColor, commonItem.m_outline_color);
@@ -1159,8 +1068,9 @@ namespace
item->mouseEnterText = ConvertEventHandlerSet(commonItem.m_on_mouse_enter_text.get(), &commonParentMenu, &commonItem);
item->mouseExitText = ConvertEventHandlerSet(commonItem.m_on_mouse_exit_text.get(), &commonParentMenu, &commonItem);
item->action = ConvertEventHandlerSet(commonItem.m_on_action.get(), &commonParentMenu, &commonItem);
item->accept = ConvertEventHandlerSet(commonItem.m_on_accept.get(), &commonParentMenu, &commonItem);
item->onAccept = ConvertEventHandlerSet(commonItem.m_on_accept.get(), &commonParentMenu, &commonItem);
item->focusSound = ConvertSound(commonItem.m_focus_sound, &commonParentMenu, &commonItem);
item->dvar = ConvertString(commonItem.m_dvar);
item->dvarTest = ConvertString(commonItem.m_dvar_test);
item->enableDvar = ConvertEnableDvar(commonItem, item->dvarFlags);
item->onKey = ConvertKeyHandler(commonItem.m_key_handlers, &commonParentMenu, &commonItem);
@@ -1173,7 +1083,6 @@ namespace
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)
{
@@ -1182,11 +1091,11 @@ namespace
break;
case CommonItemFeatureType::EDIT_FIELD:
item->typeData.editField = ConvertEditFieldFeatures(item, commonItem.m_edit_field_features.get(), commonParentMenu, commonItem);
item->typeData.editField = ConvertEditFieldFeatures(item, commonItem.m_edit_field_features.get());
break;
case CommonItemFeatureType::MULTI_VALUE:
item->typeData.multi = ConvertMultiValueFeatures(item, commonItem.m_multi_value_features.get(), commonParentMenu, commonItem);
item->typeData.multi = ConvertMultiValueFeatures(commonItem.m_multi_value_features.get());
break;
case CommonItemFeatureType::ENUM_DVAR:
@@ -1194,7 +1103,7 @@ namespace
break;
case CommonItemFeatureType::NEWS_TICKER:
item->typeData.ticker = ConvertNewsTickerFeatures(item, commonItem.m_news_ticker_features.get(), commonParentMenu, commonItem);
item->typeData.ticker = ConvertNewsTickerFeatures(commonItem.m_news_ticker_features.get());
break;
case CommonItemFeatureType::NONE:
@@ -1208,10 +1117,12 @@ namespace
// Do this last so any optimizations are considered
item->window.rect = ConvertRectDefRelativeTo(item->window.rectClient, parentMenu.window.rect);
item->parent = &parentMenu;
return item;
}
itemDef_s** ConvertMenuItems(const CommonMenuDef& commonMenu, const menuDef_t& menu, int& itemCount) const
itemDef_s** ConvertMenuItems(const CommonMenuDef& commonMenu, menuDef_t& menu, int& itemCount) const
{
if (commonMenu.m_items.empty())
{
@@ -1236,15 +1147,16 @@ namespace
{
}
void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) override
bool ConvertMenu(const 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;
menu.fullScreen = commonMenu.m_full_screen ? 1 : 0;
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.rectClient = menu.window.rect;
menu.window.style = commonMenu.m_style;
menu.window.border = commonMenu.m_border;
menu.window.borderSize = static_cast<float>(commonMenu.m_border_size);
@@ -1269,7 +1181,7 @@ namespace
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.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);
@@ -1287,7 +1199,10 @@ namespace
catch (const MenuConversionException& e)
{
PrintConversionExceptionDetails(e);
return false;
}
return true;
}
MenuConversionZoneState& m_conversion_zone_state;
@@ -1295,7 +1210,8 @@ namespace
};
} // namespace
std::unique_ptr<IMenuConverter> IMenuConverter::Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
std::unique_ptr<IMenuConverter>
IMenuConverter::Create(const bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
{
return std::make_unique<MenuConverter>(disableOptimizations, searchPath, memory, context);
}
@@ -6,6 +6,8 @@
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace IW4
{
class IMenuConverter
@@ -14,7 +16,7 @@ namespace IW4
IMenuConverter() = default;
virtual ~IMenuConverter() = default;
virtual void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
virtual bool ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
static std::unique_ptr<IMenuConverter> Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context);
};
@@ -1,15 +1,12 @@
#include "LoaderMenuListIW5.h"
#include "Game/IW5/IW5.h"
#include "Game/IW5/Menu/MenuConversionZoneStateIW5.h"
#include "Game/IW5/Menu/MenuConverterIW5.h"
#include "ObjLoading.h"
#include "Parsing/Menu/MenuFileReader.h"
#include "Utils/Logging/Log.h"
#include <cstring>
#include <format>
#include <iostream>
#include <deque>
using namespace IW5;
@@ -44,7 +41,8 @@ namespace
const auto menuListResult = ParseMenuFile(*file.m_stream, assetName, zoneState);
if (menuListResult)
{
ProcessParsedResults(assetName, context, *menuListResult, zoneState, conversionState, menus, registration);
if (!ProcessParsedResults(assetName, context, *menuListResult, zoneState, conversionState, menus, registration))
return AssetCreationResult::Failure();
for (const auto& menuToLoad : menuListResult->m_menus_to_load)
menuLoadQueue.emplace_back(menuToLoad);
@@ -59,7 +57,8 @@ namespace
{
const auto& menuFileToLoad = menuLoadQueue.front();
LoadMenuFileFromQueue(menuFileToLoad, context, zoneState, conversionState, menus, registration);
if (!LoadMenuFileFromQueue(menuFileToLoad, context, zoneState, conversionState, menus, registration))
return AssetCreationResult::Failure();
menuLoadQueue.pop_front();
}
@@ -108,7 +107,8 @@ namespace
const auto menuFileResult = ParseMenuFile(*file.m_stream, menuFilePath, zoneState);
if (menuFileResult)
{
ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration);
if (!ProcessParsedResults(menuFilePath, context, *menuFileResult, zoneState, conversionState, menus, registration))
return false;
if (!menuFileResult->m_menus_to_load.empty())
con::warn("Menu file has menus to load even though it is not a menu list, ignoring: \"{}\"", menuFilePath);
@@ -158,8 +158,7 @@ namespace
auto* menuAsset = m_memory.Alloc<menuDef_t>();
AssetRegistration<AssetMenu> menuRegistration(commonMenu->m_name, menuAsset);
converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration);
if (menuAsset == nullptr)
if (!converter->ConvertMenu(*commonMenu, *menuAsset, menuRegistration))
{
con::error("Failed to convert menu file \"{}\"", commonMenu->m_name);
return false;
@@ -214,6 +213,7 @@ namespace
} // namespace
namespace menu
{
std::unique_ptr<AssetCreator<AssetMenuList>> CreateMenuListLoaderIW5(MemoryManager& memory, ISearchPath& searchPath)
{
@@ -10,4 +10,4 @@
namespace menu
{
std::unique_ptr<AssetCreator<IW5::AssetMenuList>> CreateMenuListLoaderIW5(MemoryManager& memory, ISearchPath& searchPath);
} // namespace menu
}
@@ -74,7 +74,7 @@ const char* MenuConversionZoneState::AddString(const std::string& str)
void MenuConversionZoneState::AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile)
{
m_menus_by_filename.emplace(std::make_pair(std::move(loadedFileName), std::move(menusOfFile)));
m_menus_by_filename.emplace(std::move(loadedFileName), std::move(menusOfFile));
}
void MenuConversionZoneState::FinalizeSupportingData() const
@@ -3,26 +3,15 @@
#include "Asset/IZoneAssetCreationState.h"
#include "Game/IW5/IW5.h"
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
namespace IW5
{
class MenuConversionZoneState final : public IZoneAssetCreationState
{
Zone* m_zone;
std::vector<Statement_s*> m_functions;
std::map<std::string, Statement_s*> m_function_by_name;
std::vector<StaticDvar*> m_static_dvars;
std::map<std::string, size_t> m_dvars_by_name;
std::vector<const char*> m_strings;
std::map<std::string, const char*> m_strings_by_value;
public:
std::map<std::string, std::vector<XAssetInfo<menuDef_t>*>> m_menus_by_filename;
ExpressionSupportingData* m_supporting_data;
MenuConversionZoneState();
void Inject(ZoneAssetCreationInjection& inject) override;
@@ -35,5 +24,19 @@ namespace IW5
void AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile);
void FinalizeSupportingData() const;
std::unordered_map<std::string, std::vector<XAssetInfo<menuDef_t>*>> m_menus_by_filename;
ExpressionSupportingData* m_supporting_data;
private:
Zone* m_zone;
std::vector<Statement_s*> m_functions;
std::unordered_map<std::string, Statement_s*> m_function_by_name;
std::vector<StaticDvar*> m_static_dvars;
std::unordered_map<std::string, size_t> m_dvars_by_name;
std::vector<const char*> m_strings;
std::unordered_map<std::string, const char*> m_strings_by_value;
};
} // namespace IW5
+258 -343
View File
@@ -13,10 +13,12 @@
#include "Parsing/Simple/Expression/SimpleExpressionUnaryOperation.h"
#include "Utils/StringUtils.h"
#include <algorithm>
#include <cassert>
#include <cstring>
#include <format>
#include <sstream>
#include <vector>
using namespace IW5;
using namespace menu;
@@ -28,20 +30,20 @@ namespace
[[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),
.x = static_cast<float>(rect.x),
.y = static_cast<float>(rect.y),
.w = static_cast<float>(rect.w),
.h = static_cast<float>(rect.h),
.horzAlign = static_cast<unsigned char>(rect.horizontalAlign),
.vertAlign = static_cast<unsigned char>(rect.verticalAlign),
};
}
[[nodiscard]] static rectDef_s ConvertRectDefRelativeTo(const rectDef_s& rect, const rectDef_s& rectRelativeTo)
[[nodiscard]] static rectDef_s ConvertRectDefRelativeTo(const rectDef_s& rect, const rectDef_s& relativeTo)
{
return rectDef_s{
.x = rectRelativeTo.x + rect.x,
.y = rectRelativeTo.y + rect.y,
.x = relativeTo.x + rect.x,
.y = relativeTo.y + rect.y,
.w = static_cast<float>(rect.w),
.h = static_cast<float>(rect.h),
.horzAlign = static_cast<unsigned char>(rect.horzAlign),
@@ -57,7 +59,7 @@ namespace
output[3] = static_cast<float>(input.a);
}
static void ApplyFlag(int& flags, const bool shouldApply, const int flagValue)
static void ApplyFlag(unsigned& flags, const bool shouldApply, const unsigned flagValue)
{
if (!shouldApply)
return;
@@ -65,16 +67,6 @@ namespace
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())
@@ -99,181 +91,14 @@ namespace
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(lowerCaseFunctionName, 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)]{
constexpr static expressionOperatorType_e UNARY_OPERATION_MAPPING[]{
OP_NOT,
OP_BITWISENOT,
OP_SUBTRACT,
};
static_assert(std::size(UNARY_OPERATION_MAPPING) == static_cast<unsigned>(SimpleUnaryOperationId::COUNT));
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)]{
constexpr static expressionOperatorType_e BINARY_OPERATION_MAPPING[]{
OP_ADD,
OP_SUBTRACT,
OP_MULTIPLY,
@@ -292,75 +117,195 @@ namespace
OP_AND,
OP_OR,
};
static_assert(std::size(BINARY_OPERATION_MAPPING) == static_cast<unsigned>(SimpleBinaryOperationId::COUNT));
[[nodiscard]] bool IsOperation(const ISimpleExpression* expression) const
{
if (!m_disable_optimizations && expression->IsStatic())
return false;
return dynamic_cast<const SimpleExpressionBinaryOperation*>(expression) != nullptr
|| dynamic_cast<const SimpleExpressionUnaryOperation*>(expression) != nullptr;
}
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;
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = targetFunctionIndex}});
const auto dvarIntVal = static_cast<int>(m_conversion_zone_state.AddStaticDvar(*staticDvarNameExpressionValue.m_string_value));
entries.emplace_back(expressionEntry{.type = EET_OPERAND, .data = {.operand = {.dataType = VAL_INT, .internals = {.intVal = dvarIntVal}}}});
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
gameStatement->supportingData = m_conversion_zone_state.m_supporting_data;
return true;
}
bool HandleSpecialBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* baseFunction) const
{
switch (baseFunction->m_function_index)
{
case EXP_FUNC_DVAR_INT:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_INT);
case EXP_FUNC_DVAR_BOOL:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_BOOL);
case EXP_FUNC_DVAR_FLOAT:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_FLOAT);
case EXP_FUNC_DVAR_STRING:
return HandleStaticDvarFunctionCall(gameStatement, entries, baseFunction, EXP_FUNC_STATIC_DVAR_STRING);
default:
break;
}
return false;
}
void ConvertExpressionEntryBaseFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionBaseFunctionCall* baseFunction,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
if (!HandleSpecialBaseFunctionCall(gameStatement, entries, baseFunction))
{
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = static_cast<int>(baseFunction->m_function_index)}});
auto firstArg = true;
for (const auto& arg : baseFunction->m_args)
{
if (firstArg)
firstArg = false;
else
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_COMMA}});
ConvertExpressionEntry(gameStatement, entries, arg.get(), menu, item);
}
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
}
void ConvertExpressionEntryCustomFunctionCall(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const CommonExpressionCustomFunctionCall* customFunction,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
std::string lowerCaseFunctionName(customFunction->m_function_name);
utils::MakeStringLowerCase(lowerCaseFunctionName);
auto* functionStatement = m_conversion_zone_state.FindFunction(lowerCaseFunctionName);
if (!functionStatement)
{
// 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(
std::format("Failed to find definition for custom function \"{}\"", customFunction->m_function_name), menu, item);
}
functionStatement = ConvertExpression(foundCommonFunction->second->m_value.get(), menu, item);
functionStatement = m_conversion_zone_state.AddFunction(lowerCaseFunctionName, functionStatement);
}
entries.emplace_back(expressionEntry{
.type = EET_OPERAND,
.data = {.operand = {.dataType = VAL_FUNCTION, .internals = {.function = functionStatement}}},
});
// Statement uses custom function so it needs supporting data
gameStatement->supportingData = m_conversion_zone_state.m_supporting_data;
}
void ConvertExpressionEntryUnaryOperation(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionUnaryOperation* unary,
const CommonMenuDef* menu,
const CommonItemDef* item) const
{
assert(static_cast<unsigned>(unary->m_operation_type->m_id) < static_cast<unsigned>(SimpleUnaryOperationId::COUNT));
entries.emplace_back(expressionEntry{
.type = EET_OPERATOR,
.data = {.op = UNARY_OPERATION_MAPPING[static_cast<unsigned>(unary->m_operation_type->m_id)]},
});
const auto wrapOperand = IsOperation(unary->m_operand.get());
if (wrapOperand)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, unary->m_operand.get(), menu, item);
if (wrapOperand)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
void ConvertExpressionEntryBinaryOperation(Statement_s* gameStatement,
std::vector<expressionEntry>& entries,
const SimpleExpressionBinaryOperation* binaryOperation,
const SimpleExpressionBinaryOperation* binary,
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);
const auto wrapLeft = IsOperation(binary->m_operand1.get());
if (wrapLeft)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, binary->m_operand1.get(), menu, item);
if (wrapLeft)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
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);
assert(static_cast<unsigned>(binary->m_operation_type->m_id) < static_cast<unsigned>(SimpleBinaryOperationId::COUNT));
entries.emplace_back(expressionEntry{
.type = EET_OPERATOR,
.data = {.op = BINARY_OPERATION_MAPPING[static_cast<unsigned>(binary->m_operation_type->m_id)]},
});
// 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);
const auto wrapRight = IsOperation(binary->m_operand2.get());
if (wrapRight)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_LEFTPAREN}});
ConvertExpressionEntry(gameStatement, entries, binary->m_operand2.get(), menu, item);
if (wrapRight)
entries.emplace_back(expressionEntry{.type = EET_OPERATOR, .data = {.op = OP_RIGHTPAREN}});
}
void ConvertExpressionEntryExpressionValue(std::vector<expressionEntry>& entries, const SimpleExpressionValue* expressionValue) const
void ConvertExpressionValue(std::vector<expressionEntry>& entries, const SimpleExpressionValue& value) const
{
expressionEntry entry{};
entry.type = EET_OPERAND;
if (expressionValue->m_type == SimpleExpressionValue::Type::INT)
switch (value.m_type)
{
case 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.internals.intVal = value.m_int_value;
break;
case 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.internals.floatVal = static_cast<float>(value.m_double_value);
break;
case SimpleExpressionValue::Type::STRING:
entry.data.operand.dataType = VAL_STRING;
entry.data.operand.internals.stringVal.string = m_conversion_zone_state.AddString(*expressionValue->m_string_value);
entry.data.operand.internals.stringVal.string = m_conversion_zone_state.AddString(*value.m_string_value);
break;
}
entries.emplace_back(entry);
@@ -374,28 +319,28 @@ namespace
{
if (!m_disable_optimizations && expression->IsStatic())
{
const auto expressionStaticValue = expression->EvaluateStatic();
ConvertExpressionEntryExpressionValue(entries, &expressionStaticValue);
const auto staticValue = expression->EvaluateStatic();
ConvertExpressionValue(entries, staticValue);
}
else if (const auto* expressionValue = dynamic_cast<const SimpleExpressionValue*>(expression))
else if (const auto* value = dynamic_cast<const SimpleExpressionValue*>(expression))
{
ConvertExpressionEntryExpressionValue(entries, expressionValue);
ConvertExpressionValue(entries, *value);
}
else if (const auto* binaryOperation = dynamic_cast<const SimpleExpressionBinaryOperation*>(expression))
else if (const auto* binary = dynamic_cast<const SimpleExpressionBinaryOperation*>(expression))
{
ConvertExpressionEntryBinaryOperation(gameStatement, entries, binaryOperation, menu, item);
ConvertExpressionEntryBinaryOperation(gameStatement, entries, binary, menu, item);
}
else if (const auto* unaryOperation = dynamic_cast<const SimpleExpressionUnaryOperation*>(expression))
else if (const auto* unary = dynamic_cast<const SimpleExpressionUnaryOperation*>(expression))
{
ConvertExpressionEntryUnaryOperation(gameStatement, entries, unaryOperation, menu, item);
ConvertExpressionEntryUnaryOperation(gameStatement, entries, unary, menu, item);
}
else if (const auto* baseFunctionCall = dynamic_cast<const CommonExpressionBaseFunctionCall*>(expression))
else if (const auto* baseFunction = dynamic_cast<const CommonExpressionBaseFunctionCall*>(expression))
{
ConvertExpressionEntryBaseFunctionCall(gameStatement, entries, baseFunctionCall, menu, item);
ConvertExpressionEntryBaseFunctionCall(gameStatement, entries, baseFunction, menu, item);
}
else if (const auto* customFunctionCall = dynamic_cast<const CommonExpressionCustomFunctionCall*>(expression))
else if (const auto* customFunction = dynamic_cast<const CommonExpressionCustomFunctionCall*>(expression))
{
ConvertExpressionEntryCustomFunctionCall(gameStatement, entries, customFunctionCall, menu, item);
ConvertExpressionEntryCustomFunctionCall(gameStatement, entries, customFunction, menu, item);
}
else if (dynamic_cast<const SimpleExpressionConditionalOperator*>(expression))
{
@@ -414,20 +359,15 @@ namespace
return nullptr;
auto* statement = m_memory.Alloc<Statement_s>();
for (auto& result : statement->persistentState.lastResult)
result = Operand{};
for (auto& lastExecutionTime : statement->persistentState.lastExecuteTime)
lastExecutionTime = 0;
statement->supportingData = nullptr; // Supporting data is set upon using it
std::vector<expressionEntry> expressionEntries;
ConvertExpressionEntry(statement, expressionEntries, expression, menu, item);
std::vector<expressionEntry> entries;
ConvertExpressionEntry(statement, entries, expression, menu, item);
auto* outputExpressionEntries = m_memory.Alloc<expressionEntry>(expressionEntries.size());
std::memcpy(outputExpressionEntries, expressionEntries.data(), sizeof(expressionEntry) * expressionEntries.size());
auto* outEntries = m_memory.Alloc<expressionEntry>(entries.size());
std::memcpy(outEntries, entries.data(), sizeof(expressionEntry) * entries.size());
statement->entries = outputExpressionEntries;
statement->numEntries = static_cast<int>(expressionEntries.size());
statement->numEntries = static_cast<int>(entries.size());
statement->entries = outEntries;
return statement;
}
@@ -437,26 +377,20 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && 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:
if (value.m_type == SimpleExpressionValue::Type::INT)
staticValue = static_cast<float>(value.m_int_value);
break;
case SimpleExpressionValue::Type::STRING:
else if (value.m_type == SimpleExpressionValue::Type::DOUBLE)
staticValue = static_cast<float>(value.m_double_value);
else
throw MenuConversionException("Cannot convert string expression value to floating point", menu, item);
}
return nullptr;
}
@@ -468,25 +402,17 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && 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:
if (value.m_type != SimpleExpressionValue::Type::STRING)
throw MenuConversionException("Cannot convert numeric expression value to string", menu, item);
}
staticValue = m_memory.Dup(value.m_string_value->c_str());
return nullptr;
}
@@ -498,37 +424,29 @@ namespace
const CommonMenuDef* menu,
const CommonItemDef* item = nullptr) const
{
if (m_disable_optimizations)
return ConvertExpression(expression, menu, item);
if (!expression)
return nullptr;
if (expression->IsStatic())
if (!m_disable_optimizations && expression->IsStatic())
{
const auto value = expression->EvaluateStatic();
switch (value.m_type)
{
case SimpleExpressionValue::Type::STRING:
staticValue = ConvertMaterial(*value.m_string_value, menu, item);
break;
if (value.m_type != SimpleExpressionValue::Type::STRING)
throw MenuConversionException("Cannot convert numeric expression value to material", menu, item);
staticValue = ConvertMaterial(*value.m_string_value, menu, item);
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,
[[nodiscard]] Statement_s* ConvertVisibleExpression(windowDef_t& window,
const ISimpleExpression* expression,
const CommonMenuDef* commonMenu,
const CommonItemDef* commonItem = nullptr) const
{
if (expression == nullptr)
if (!expression)
return nullptr;
bool isStatic;
@@ -549,11 +467,11 @@ namespace
if (isStatic)
{
if (isTruthy)
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
window.dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return nullptr;
}
window->dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
window.dynamicFlags[0] |= WINDOW_FLAG_VISIBLE;
return ConvertExpression(expression, commonMenu, commonItem);
}
@@ -639,7 +557,7 @@ namespace
outputCondition->eventExpression = ConvertExpression(condition->m_condition.get(), menu, item);
outputCondition->eventHandlerSet = ConvertEventHandlerSet(condition->m_condition_elements.get(), menu, item);
elements.push_back(outputHandler);
elements.emplace_back(outputHandler);
if (condition->m_else_elements)
{
@@ -647,7 +565,7 @@ namespace
outputElseHandler->eventType = EVENT_ELSE;
outputElseHandler->eventData.elseScript = ConvertEventHandlerSet(condition->m_else_elements.get(), menu, item);
elements.push_back(outputElseHandler);
elements.emplace_back(outputElseHandler);
}
}
}
@@ -1013,11 +931,11 @@ namespace
}
[[nodiscard]] listBoxDef_s* ConvertListBoxFeatures(itemDef_s* item,
CommonItemFeaturesListBox* commonListBox,
const CommonItemFeaturesListBox* commonListBox,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
{
if (commonListBox == nullptr)
if (!commonListBox)
return nullptr;
auto* listBox = m_memory.Alloc<listBoxDef_s>();
@@ -1034,7 +952,7 @@ namespace
listBox->elementHeightExp =
ConvertOrApplyStatement(listBox->elementHeight, commonListBox->m_element_height_expression.get(), &parentMenu, &commonItem);
listBox->numColumns = static_cast<int>(std::min(std::extent_v<decltype(listBoxDef_s::columnInfo)>, commonListBox->m_columns.size()));
listBox->numColumns = static_cast<int>(std::min(std::size(listBox->columnInfo), commonListBox->m_columns.size()));
for (auto i = 0; i < listBox->numColumns; i++)
{
auto& col = listBox->columnInfo[i];
@@ -1050,12 +968,9 @@ namespace
return listBox;
}
[[nodiscard]] editFieldDef_s* ConvertEditFieldFeatures(itemDef_s* item,
CommonItemFeaturesEditField* commonEditField,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] editFieldDef_s* ConvertEditFieldFeatures(itemDef_s* item, const CommonItemFeaturesEditField* commonEditField) const
{
if (commonEditField == nullptr)
if (!commonEditField)
return nullptr;
auto* editField = m_memory.Alloc<editFieldDef_s>();
@@ -1070,16 +985,13 @@ namespace
return editField;
}
[[nodiscard]] multiDef_s* ConvertMultiValueFeatures(itemDef_s* item,
CommonItemFeaturesMultiValue* commonMultiValue,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] multiDef_s* ConvertMultiValueFeatures(const CommonItemFeaturesMultiValue* commonMultiValue) const
{
if (commonMultiValue == nullptr)
if (!commonMultiValue)
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->count = static_cast<int>(std::min(std::size(multiValue->dvarList), commonMultiValue->m_step_names.size()));
multiValue->strDef = !commonMultiValue->m_string_values.empty() ? 1 : 0;
for (auto i = 0; i < multiValue->count; i++)
@@ -1101,12 +1013,9 @@ namespace
return multiValue;
}
[[nodiscard]] newsTickerDef_s* ConvertNewsTickerFeatures(itemDef_s* item,
CommonItemFeaturesNewsTicker* commonNewsTicker,
const CommonMenuDef& parentMenu,
const CommonItemDef& commonItem) const
[[nodiscard]] newsTickerDef_s* ConvertNewsTickerFeatures(const CommonItemFeaturesNewsTicker* commonNewsTicker) const
{
if (commonNewsTicker == nullptr)
if (!commonNewsTicker)
return nullptr;
auto* newsTicker = m_memory.Alloc<newsTickerDef_s>();
@@ -1117,7 +1026,7 @@ namespace
return newsTicker;
}
[[nodiscard]] itemDef_s* ConvertItem(const CommonMenuDef& commonParentMenu, const menuDef_t& parentMenu, const CommonItemDef& commonItem) const
[[nodiscard]] itemDef_s* ConvertItem(const CommonMenuDef& commonParentMenu, menuDef_t& parentMenu, const CommonItemDef& commonItem) const
{
auto* item = m_memory.Alloc<itemDef_s>();
memset(item, 0, sizeof(itemDef_s));
@@ -1132,11 +1041,11 @@ namespace
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->type = commonItem.m_type;
item->dataType = commonItem.m_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(), &commonParentMenu, &commonItem);
item->visibleExp = ConvertVisibleExpression(item->window, commonItem.m_visible_expression.get(), &commonParentMenu, &commonItem);
item->disabledExp = ConvertExpression(commonItem.m_disabled_expression.get(), &commonParentMenu, &commonItem);
item->window.ownerDraw = commonItem.m_owner_draw;
item->window.ownerDrawFlags = commonItem.m_owner_draw_flags;
@@ -1146,12 +1055,11 @@ namespace
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);
item->fontEnum = 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;
ApplyFlag(item->window.dynamicFlags[0], !commonItem.m_fore_color.Equals(CommonColor(1.0, 1.0, 1.0, 1.0)), WINDOW_FLAG_NON_DEFAULT_FORECOLOR);
ConvertColor(item->window.borderColor, commonItem.m_border_color);
ConvertColor(item->window.outlineColor, commonItem.m_outline_color);
@@ -1166,8 +1074,9 @@ namespace
item->mouseEnterText = ConvertEventHandlerSet(commonItem.m_on_mouse_enter_text.get(), &commonParentMenu, &commonItem);
item->mouseExitText = ConvertEventHandlerSet(commonItem.m_on_mouse_exit_text.get(), &commonParentMenu, &commonItem);
item->action = ConvertEventHandlerSet(commonItem.m_on_action.get(), &commonParentMenu, &commonItem);
item->accept = ConvertEventHandlerSet(commonItem.m_on_accept.get(), &commonParentMenu, &commonItem);
item->onAccept = ConvertEventHandlerSet(commonItem.m_on_accept.get(), &commonParentMenu, &commonItem);
item->focusSound = ConvertSound(commonItem.m_focus_sound, &commonParentMenu, &commonItem);
item->dvar = ConvertString(commonItem.m_dvar);
item->dvarTest = ConvertString(commonItem.m_dvar_test);
item->enableDvar = ConvertEnableDvar(commonItem, item->dvarFlags);
item->onKey = ConvertKeyHandler(commonItem.m_key_handlers, &commonParentMenu, &commonItem);
@@ -1181,7 +1090,6 @@ namespace
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)
{
@@ -1190,11 +1098,11 @@ namespace
break;
case CommonItemFeatureType::EDIT_FIELD:
item->typeData.editField = ConvertEditFieldFeatures(item, commonItem.m_edit_field_features.get(), commonParentMenu, commonItem);
item->typeData.editField = ConvertEditFieldFeatures(item, commonItem.m_edit_field_features.get());
break;
case CommonItemFeatureType::MULTI_VALUE:
item->typeData.multi = ConvertMultiValueFeatures(item, commonItem.m_multi_value_features.get(), commonParentMenu, commonItem);
item->typeData.multi = ConvertMultiValueFeatures(commonItem.m_multi_value_features.get());
break;
case CommonItemFeatureType::ENUM_DVAR:
@@ -1202,7 +1110,7 @@ namespace
break;
case CommonItemFeatureType::NEWS_TICKER:
item->typeData.ticker = ConvertNewsTickerFeatures(item, commonItem.m_news_ticker_features.get(), commonParentMenu, commonItem);
item->typeData.ticker = ConvertNewsTickerFeatures(commonItem.m_news_ticker_features.get());
break;
case CommonItemFeatureType::NONE:
@@ -1216,10 +1124,12 @@ namespace
// Do this last so any optimizations are considered
item->window.rect = ConvertRectDefRelativeTo(item->window.rectClient, parentMenu.window.rect);
item->parent = &parentMenu;
return item;
}
itemDef_s** ConvertMenuItems(const CommonMenuDef& commonMenu, const menuDef_t& menu, int& itemCount) const
itemDef_s** ConvertMenuItems(const CommonMenuDef& commonMenu, menuDef_t& menu, int& itemCount) const
{
if (commonMenu.m_items.empty())
{
@@ -1244,7 +1154,7 @@ namespace
{
}
void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) override
bool ConvertMenu(const CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) override
{
try
{
@@ -1252,10 +1162,11 @@ namespace
menu.data = menuData;
menu.window.name = m_memory.Dup(commonMenu.m_name.c_str());
menuData->fullScreen = commonMenu.m_full_screen;
menuData->fullScreen = commonMenu.m_full_screen ? 1 : 0;
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.rectClient = menu.window.rect;
menu.window.style = commonMenu.m_style;
menu.window.border = commonMenu.m_border;
menu.window.borderSize = static_cast<float>(commonMenu.m_border_size);
@@ -1280,7 +1191,7 @@ namespace
ApplyFlag(menu.window.staticFlags, commonMenu.m_hidden_during_ui, WINDOW_FLAG_HIDDEN_DURING_UI);
menuData->allowedBinding = ConvertString(commonMenu.m_allowed_binding);
ApplyFlag(menu.window.staticFlags, commonMenu.m_text_only_focus, WINDOW_FLAG_TEXT_ONLY_FOCUS);
menuData->visibleExp = ConvertVisibleExpression(&menu.window, commonMenu.m_visible_expression.get(), &commonMenu);
menuData->visibleExp = ConvertVisibleExpression(menu.window, commonMenu.m_visible_expression.get(), &commonMenu);
menuData->rectXExp = ConvertOrApplyStatement(menu.window.rect.x, commonMenu.m_rect_x_exp.get(), &commonMenu);
menuData->rectYExp = ConvertOrApplyStatement(menu.window.rect.y, commonMenu.m_rect_y_exp.get(), &commonMenu);
menuData->rectWExp = ConvertOrApplyStatement(menu.window.rect.w, commonMenu.m_rect_w_exp.get(), &commonMenu);
@@ -1299,7 +1210,10 @@ namespace
catch (const MenuConversionException& e)
{
PrintConversionExceptionDetails(e);
return false;
}
return true;
}
MenuConversionZoneState& m_conversion_zone_state;
@@ -1307,7 +1221,8 @@ namespace
};
} // namespace
std::unique_ptr<IMenuConverter> IMenuConverter::Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
std::unique_ptr<IMenuConverter>
IMenuConverter::Create(const bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context)
{
return std::make_unique<MenuConverter>(disableOptimizations, searchPath, memory, context);
}
@@ -6,6 +6,8 @@
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace IW5
{
class IMenuConverter
@@ -14,7 +16,7 @@ namespace IW5
IMenuConverter() = default;
virtual ~IMenuConverter() = default;
virtual void ConvertMenu(const ::menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
virtual bool ConvertMenu(const ::menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
static std::unique_ptr<IMenuConverter> Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context);
};