Dump IW4 menu statements

This commit is contained in:
Jan 2021-10-15 22:19:17 +02:00
parent 868bd070d0
commit 0f7a8ca1c9
2 changed files with 229 additions and 41 deletions

View File

@ -1145,6 +1145,14 @@ namespace IW4
OP_COUNT OP_COUNT
}; };
enum expressionFunction_e
{
EXP_FUNC_STATIC_DVAR_INT = OP_COUNT,
EXP_FUNC_STATIC_DVAR_BOOL,
EXP_FUNC_STATIC_DVAR_FLOAT,
EXP_FUNC_STATIC_DVAR_STRING,
};
union entryInternalData union entryInternalData
{ {
int op; int op;

View File

@ -1,5 +1,6 @@
#include "AssetDumperMenuDef.h" #include "AssetDumperMenuDef.h"
#include <cassert>
#include <filesystem> #include <filesystem>
#include <ostream> #include <ostream>
#include <cmath> #include <cmath>
@ -326,9 +327,12 @@ class MenuDumperIw4 : public MenuDumper
void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const
{ {
if (materialValue == nullptr) if (materialValue == nullptr || materialValue->info.name == nullptr)
return; return;
if (materialValue->info.name[0] == ',')
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
else
WriteStringProperty(propertyKey, materialValue->info.name); WriteStringProperty(propertyKey, materialValue->info.name);
} }
@ -340,33 +344,141 @@ class MenuDumperIw4 : public MenuDumper
WriteStringProperty(propertyKey, soundAliasValue->aliasName); WriteStringProperty(propertyKey, soundAliasValue->aliasName);
} }
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, const bool isBooleanStatement) const static size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
{ {
if (statementValue == nullptr) assert(statement->numEntries >= 0);
return; assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
Indent(); const auto statementEnd = static_cast<size_t>(statement->numEntries);
WriteKey(propertyKey);
if (isBooleanStatement) // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though.
// We will pretend it does since the game does sometimes leave out opening parenthesis from the entries.
auto currentParenthesisDepth = 1;
for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++)
{ {
m_stream << "when"; const auto& expEntry = statement->entries[currentSearchPosition];
if (expEntry.type != EET_OPERATOR)
continue;
// Any function means a "left out" left paren
if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT)
{
currentParenthesisDepth++;
}
else if (expEntry.data.op == OP_RIGHTPAREN)
{
if (currentParenthesisDepth > 0)
currentParenthesisDepth--;
if (currentParenthesisDepth == 0)
return currentSearchPosition;
}
} }
for (auto i = 0; i < statementValue->numEntries; i++) return statementEnd;
{ }
const auto& expEntry = statementValue->entries[i];
if (i > 0) void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
if (spaceNext && expEntry.data.op != OP_COMMA)
m_stream << " "; m_stream << " ";
if (expEntry.type == EET_OPERATOR) if (expEntry.data.op == OP_LEFTPAREN)
{ {
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>) const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
m_stream << g_expFunctionNames[expEntry.data.op]; m_stream << "(";
WriteStatementEntryRange(statement, currentPos + 1, closingParenPos);
m_stream << ")";
currentPos = closingParenPos + 1;
spaceNext = true;
}
else if(expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
{
switch(expEntry.data.op)
{
case EXP_FUNC_STATIC_DVAR_INT:
m_stream << "dvarint";
break;
case EXP_FUNC_STATIC_DVAR_BOOL:
m_stream << "dvarbool";
break;
case EXP_FUNC_STATIC_DVAR_FLOAT:
m_stream << "dvarfloat";
break;
case EXP_FUNC_STATIC_DVAR_STRING:
m_stream << "dvarstring";
break;
default:
break;
}
// Functions do not have opening parenthesis in the entries. We can just pretend they do though
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
m_stream << "(";
if(closingParenPos - currentPos + 1 >= 1)
{
const auto& staticDvarEntry = statement->entries[currentPos + 1];
if(staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
{
if(statement->supportingData
&& statement->supportingData->staticDvarList.staticDvars
&& staticDvarEntry.data.operand.internals.intVal >= 0
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
{
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
if(staticDvar && staticDvar->dvarName)
m_stream << staticDvar->dvarName;
} }
else else
{ {
m_stream << "#INVALID_DVAR_INDEX";
}
}
else
{
m_stream << "#INVALID_DVAR_OPERAND";
}
}
m_stream << ")";
currentPos = closingParenPos + 1;
spaceNext = true;
}
else
{
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
m_stream << g_expFunctionNames[expEntry.data.op];
if (expEntry.data.op >= OP_COUNT)
{
// Functions do not have opening parenthesis in the entries. We can just pretend they do though
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
m_stream << "(";
WriteStatementEntryRange(statement, currentPos + 1, closingParenPos);
m_stream << ")";
currentPos = closingParenPos + 1;
}
else
currentPos++;
spaceNext = expEntry.data.op != OP_NEG;
}
}
void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
if (spaceNext)
m_stream << " ";
const auto& operand = expEntry.data.operand; const auto& operand = expEntry.data.operand;
switch (operand.dataType) switch (operand.dataType)
@ -384,16 +496,84 @@ class MenuDumperIw4 : public MenuDumper
break; break;
case VAL_FUNCTION: case VAL_FUNCTION:
m_stream << "FUNC"; {
int functionIndex = -1;
if (statement->supportingData && statement->supportingData->uifunctions.functions)
{
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++)
{
if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
{
functionIndex = supportingFunctionIndex;
break; break;
}
}
}
if (functionIndex >= 0)
m_stream << "FUNC_" << functionIndex;
else
m_stream << "INVALID_FUNC";
break;
}
default: default:
break; break;
} }
currentPos++;
spaceNext = true;
}
void WriteStatementEntryRange(const Statement_s* statement, const size_t startOffset, const size_t endOffset) const
{
assert(startOffset <= endOffset);
assert(endOffset <= static_cast<size_t>(statement->numEntries));
auto currentPos = startOffset;
auto spaceNext = false;
while (currentPos < endOffset)
{
const auto& expEntry = statement->entries[currentPos];
if (expEntry.type == EET_OPERATOR)
{
WriteStatementOperator(statement, currentPos, spaceNext);
}
else
{
WriteStatementOperand(statement, currentPos, spaceNext);
}
} }
} }
m_stream << "\n"; void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, const bool isBooleanStatement) const
{
if (statementValue == nullptr || statementValue->numEntries < 0)
return;
Indent();
WriteKey(propertyKey);
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
if (isBooleanStatement)
{
m_stream << "when";
// Add a space when first entry is not (
if (statementEnd < 1
|| statementValue->entries[0].type != EET_OPERATOR
|| statementValue->entries[0].data.op != OP_LEFTPAREN)
{
m_stream << " ";
}
}
WriteStatementEntryRange(statementValue, 0, statementEnd);
m_stream << ";\n";
} }
void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerValue) const void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerValue) const