mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 16:15:43 +00:00
Dump IW4 menu statements
This commit is contained in:
parent
868bd070d0
commit
0f7a8ca1c9
@ -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;
|
||||||
|
@ -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,10 +327,13 @@ 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;
|
||||||
|
|
||||||
WriteStringProperty(propertyKey, materialValue->info.name);
|
if (materialValue->info.name[0] == ',')
|
||||||
|
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
|
||||||
|
else
|
||||||
|
WriteStringProperty(propertyKey, materialValue->info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
|
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
|
||||||
@ -340,60 +344,236 @@ class MenuDumperIw4 : public MenuDumper
|
|||||||
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
|
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
|
||||||
|
{
|
||||||
|
assert(statement->numEntries >= 0);
|
||||||
|
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statement->numEntries);
|
||||||
|
|
||||||
|
// 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++)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return statementEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 << " ";
|
||||||
|
|
||||||
|
if (expEntry.data.op == OP_LEFTPAREN)
|
||||||
|
{
|
||||||
|
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
switch (operand.dataType)
|
||||||
|
{
|
||||||
|
case VAL_FLOAT:
|
||||||
|
m_stream << operand.internals.floatVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_INT:
|
||||||
|
m_stream << operand.internals.intVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_STRING:
|
||||||
|
m_stream << "\"" << operand.internals.stringVal.string << "\"";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_FUNCTION:
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functionIndex >= 0)
|
||||||
|
m_stream << "FUNC_" << functionIndex;
|
||||||
|
else
|
||||||
|
m_stream << "INVALID_FUNC";
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, const bool isBooleanStatement) const
|
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, const bool isBooleanStatement) const
|
||||||
{
|
{
|
||||||
if (statementValue == nullptr)
|
if (statementValue == nullptr || statementValue->numEntries < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Indent();
|
Indent();
|
||||||
WriteKey(propertyKey);
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
|
||||||
|
|
||||||
if (isBooleanStatement)
|
if (isBooleanStatement)
|
||||||
{
|
{
|
||||||
m_stream << "when";
|
m_stream << "when";
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0; i < statementValue->numEntries; i++)
|
// Add a space when first entry is not (
|
||||||
{
|
if (statementEnd < 1
|
||||||
const auto& expEntry = statementValue->entries[i];
|
|| statementValue->entries[0].type != EET_OPERATOR
|
||||||
|
|| statementValue->entries[0].data.op != OP_LEFTPAREN)
|
||||||
if (i > 0)
|
{
|
||||||
m_stream << " ";
|
m_stream << " ";
|
||||||
|
|
||||||
if (expEntry.type == EET_OPERATOR)
|
|
||||||
{
|
|
||||||
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
|
||||||
m_stream << g_expFunctionNames[expEntry.data.op];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& operand = expEntry.data.operand;
|
|
||||||
|
|
||||||
switch (operand.dataType)
|
|
||||||
{
|
|
||||||
case VAL_FLOAT:
|
|
||||||
m_stream << operand.internals.floatVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VAL_INT:
|
|
||||||
m_stream << operand.internals.intVal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VAL_STRING:
|
|
||||||
m_stream << "\"" << operand.internals.stringVal.string << "\"";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VAL_FUNCTION:
|
|
||||||
m_stream << "FUNC";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stream << "\n";
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user