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
@@ -4,6 +4,7 @@
#include "MenuWriterIW3.h"
#include "ObjWriting.h"
#include <format>
#include <string>
using namespace IW3;
@@ -15,7 +16,7 @@ namespace
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset.Asset());
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
return "ui_mp/" + std::string(asset.Asset()->window.name) + ".menu";
return std::format("ui_mp/{}.menu", asset.Asset()->window.name);
return menuDumpingState->second.m_path;
}
@@ -31,7 +32,7 @@ namespace menu
if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
{
// Make sure menu paths based on menu lists are created
auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (auto* menuListAsset : menuListAssets)
CreateDumpingStateForMenuListIW3(zoneState, menuListAsset->Asset());
}
@@ -42,7 +43,7 @@ namespace menu
if (!assetFile)
return;
auto menuWriter = CreateMenuWriterIW3(*assetFile);
const auto menuWriter = CreateMenuWriterIW3(*assetFile);
menuWriter->Start();
menuWriter->WriteMenu(*menu);
@@ -26,8 +26,11 @@ namespace
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
continue;
const auto* menuAssetName = menu->window.name;
const auto isReference = menuAssetName && menuAssetName[0] == ',';
// If the menu was embedded directly as menu list write its data in the menu list file
if (menuDumpingState->second.m_alias_menu_list == menuList)
if (!isReference && menuDumpingState->second.m_alias_menu_list == menuList)
menuDumper.WriteMenu(*menu);
else
menuDumper.IncludeMenu(menuDumpingState->second.m_path);
@@ -111,7 +114,7 @@ namespace menu
{
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (const auto* asset : menuListAssets)
CreateDumpingStateForMenuListIW3(zoneState, asset->Asset());
@@ -4,7 +4,7 @@
#include "MenuWriterIW4.h"
#include "ObjWriting.h"
#include <filesystem>
#include <format>
#include <string>
using namespace IW4;
@@ -16,7 +16,7 @@ namespace
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset.Asset());
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
return "ui_mp/" + std::string(asset.Asset()->window.name) + ".menu";
return std::format("ui_mp/{}.menu", asset.Asset()->window.name);
return menuDumpingState->second.m_path;
}
@@ -32,7 +32,7 @@ namespace menu
if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
{
// Make sure menu paths based on menu lists are created
auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (auto* menuListAsset : menuListAssets)
CreateDumpingStateForMenuListIW4(zoneState, menuListAsset->Asset());
}
@@ -43,7 +43,7 @@ namespace menu
if (!assetFile)
return;
auto menuWriter = CreateMenuWriterIW4(*assetFile);
const auto menuWriter = CreateMenuWriterIW4(*assetFile);
menuWriter->Start();
menuWriter->WriteMenu(*menu);
@@ -7,8 +7,8 @@
#include <cassert>
#include <filesystem>
#include <set>
#include <sstream>
#include <format>
#include <unordered_set>
namespace fs = std::filesystem;
@@ -19,7 +19,7 @@ namespace
std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList)
{
std::vector<const ExpressionSupportingData*> result;
std::set<const ExpressionSupportingData*> alreadyAddedSupportingData;
std::unordered_set<const ExpressionSupportingData*> alreadyAddedSupportingData;
if (menuList->menus == nullptr)
return result;
@@ -34,9 +34,9 @@ namespace
if (menu->expressionData == nullptr)
continue;
if (alreadyAddedSupportingData.find(menu->expressionData) == alreadyAddedSupportingData.end())
if (!alreadyAddedSupportingData.contains(menu->expressionData))
{
result.push_back(menu->expressionData);
result.emplace_back(menu->expressionData);
alreadyAddedSupportingData.emplace(menu->expressionData);
}
}
@@ -59,13 +59,10 @@ namespace
for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++)
{
const auto* function = supportingData->uifunctions.functions[i];
if (function != nullptr)
{
std::stringstream ss;
ss << "FUNC_" << functionIndex;
if (!function)
continue;
menuDumper.WriteFunctionDef(ss.str(), function);
}
menuDumper.WriteFunctionDef(std::format("FUNC_{}", functionIndex), function);
functionIndex++;
}
@@ -77,13 +74,18 @@ namespace
for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++)
{
const auto* menu = menuList->menus[menuNum];
if (!menu)
continue;
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu);
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
continue;
const auto* menuAssetName = menu->window.name;
const auto isReference = menuAssetName && menuAssetName[0] == ',';
// If the menu was embedded directly as menu list write its data in the menu list file
if (menuDumpingState->second.m_alias_menu_list == menuList)
if (!isReference && menuDumpingState->second.m_alias_menu_list == menuList)
menuDumper.WriteMenu(*menu);
else
menuDumper.IncludeMenu(menuDumpingState->second.m_path);
@@ -95,15 +97,12 @@ namespace
const auto* menuAssetName = menu->window.name;
if (!menuAssetName)
return "";
return {};
if (menuAssetName[0] == ',')
menuAssetName = &menuAssetName[1];
std::ostringstream ss;
ss << menuListParentPath << menuAssetName << ".menu";
return ss.str();
return std::format("{}{}.menu", menuListParentPath, menuAssetName);
}
} // namespace
@@ -111,32 +110,34 @@ namespace menu
{
void CreateDumpingStateForMenuListIW4(MenuDumpingZoneState* zoneState, const MenuList* menuList)
{
if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr)
if (!menuList || menuList->menuCount <= 0 || !menuList->menus || !menuList->name)
return;
const std::string menuListName(menuList->name);
const fs::path p(menuListName);
std::string parentPath;
if (p.has_parent_path())
parentPath = p.parent_path().string() + "/";
parentPath = p.parent_path().generic_string() + "/";
for (auto i = 0; i < menuList->menuCount; i++)
{
auto* menu = menuList->menus[i];
const auto* menu = menuList->menus[i];
if (menu == nullptr)
if (!menu)
continue;
auto menuPath = PathForMenu(parentPath, menu);
if (menuPath.empty())
continue;
auto existingState = zoneState->m_menu_dumping_state_map.find(menu);
if (existingState == zoneState->m_menu_dumping_state_map.end())
{
auto menuPath = PathForMenu(parentPath, menu);
const auto isTheSameAsMenuList = menuPath == menuListName;
zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr);
}
else if (existingState->second.m_alias_menu_list == nullptr)
else if (!existingState->second.m_alias_menu_list)
{
auto menuPath = PathForMenu(parentPath, menu);
const auto isTheSameAsMenuList = menuPath == menuListName;
if (isTheSameAsMenuList)
{
@@ -157,15 +158,12 @@ namespace menu
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
auto menuWriter = CreateMenuWriterIW4(*assetFile);
const auto menuWriter = CreateMenuWriterIW4(*assetFile);
menuWriter->Start();
if (!ObjWriting::Configuration.MenuLegacyMode)
DumpFunctions(*menuWriter, menuList);
DumpMenus(*menuWriter, zoneState, menuList);
menuWriter->End();
}
@@ -173,7 +171,7 @@ namespace menu
{
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (const auto* asset : menuListAssets)
CreateDumpingStateForMenuListIW4(zoneState, asset->Asset());
+298 -286
View File
@@ -6,34 +6,29 @@
#include <cassert>
#include <cmath>
#include <limits>
#include <sstream>
using namespace IW4;
// Uncomment this macro to skip interpretative expression dumping
// #define DUMP_NAIVE
#ifdef DUMP_NAIVE
#define DUMP_FUNC WriteStatementNaive
#else
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
#endif
namespace
{
size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
{
assert(statement->numEntries >= 0);
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
// Set this to true to skip interpretative expression dumping
constexpr auto DUMP_NAIVE = false;
const auto statementEnd = static_cast<size_t>(statement->numEntries);
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];
const auto& expEntry = statement.entries[currentSearchPosition];
if (expEntry.type != EET_OPERATOR)
continue;
@@ -54,7 +49,7 @@ namespace
return statementEnd;
}
class MenuWriter final : public ::menu::AbstractBaseWriter, public menu::IWriterIW4
class MenuWriter final : public menu::AbstractBaseWriter, public menu::IWriterIW4
{
public:
explicit MenuWriter(std::ostream& stream)
@@ -75,9 +70,7 @@ namespace
void WriteMenu(const menuDef_t& menu) override
{
StartMenuDefScope();
WriteMenuData(&menu);
WriteMenuData(menu);
EndScope();
}
@@ -97,17 +90,22 @@ namespace
}
private:
void WriteStatementNaive(const Statement_s* statement) const
static bool HasStatement(const Statement_s* statement)
{
const auto entryCount = static_cast<size_t>(statement->numEntries);
return statement && statement->numEntries > 0 && statement->entries;
}
const auto missingClosingParenthesis = statement->numEntries > 0 && statement->entries[0].type == EET_OPERATOR
&& statement->entries[0].data.op == OP_LEFTPAREN
&& FindStatementClosingParenthesis(statement, 0) >= static_cast<size_t>(statement->numEntries);
void WriteStatementNaive(const Statement_s& statement) const
{
const auto entryCount = static_cast<size_t>(statement.numEntries);
const auto missingClosingParenthesis = statement.numEntries > 0 && statement.entries[0].type == EET_OPERATOR
&& statement.entries[0].data.op == OP_LEFTPAREN
&& FindStatementClosingParenthesis(statement, 0) >= static_cast<size_t>(statement.numEntries);
for (auto i = 0uz; i < entryCount; i++)
{
const auto& entry = statement->entries[i];
const auto& entry = statement.entries[i];
if (entry.type == EET_OPERAND)
{
size_t pos = i;
@@ -144,14 +142,14 @@ namespace
if (closingParenPos - i + 1u >= 1u)
{
const auto& staticDvarEntry = statement->entries[i + 1];
const auto& staticDvarEntry = statement.entries[i + 1];
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
{
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
if (statement.supportingData && statement.supportingData->staticDvarList.staticDvars
&& staticDvarEntry.data.operand.internals.intVal >= 0
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
&& staticDvarEntry.data.operand.internals.intVal < statement.supportingData->staticDvarList.numStaticDvars)
{
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
const auto* staticDvar = statement.supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
if (staticDvar && staticDvar->dvarName)
m_stream << staticDvar->dvarName;
}
@@ -183,9 +181,9 @@ namespace
m_stream << ")";
}
void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
void WriteStatementOperator(const Statement_s& statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (spaceNext && expEntry.data.op != OP_COMMA)
m_stream << " ";
@@ -230,14 +228,14 @@ namespace
if (closingParenPos - currentPos + 1 >= 1)
{
const auto& staticDvarEntry = statement->entries[currentPos + 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
if (statement.supportingData && statement.supportingData->staticDvarList.staticDvars
&& staticDvarEntry.data.operand.internals.intVal >= 0
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
&& staticDvarEntry.data.operand.internals.intVal < statement.supportingData->staticDvarList.numStaticDvars)
{
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
const auto* staticDvar = statement.supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
if (staticDvar && staticDvar->dvarName)
m_stream << staticDvar->dvarName;
}
@@ -277,22 +275,22 @@ namespace
}
}
void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const
void WriteStatementOperandFunction(const Statement_s& statement, const size_t currentPos) const
{
const auto& operand = statement->entries[currentPos].data.operand;
const auto& operand = statement.entries[currentPos].data.operand;
if (operand.internals.function == nullptr)
if (!operand.internals.function)
return;
if (!ObjWriting::Configuration.MenuLegacyMode)
{
int functionIndex = -1;
if (statement->supportingData && statement->supportingData->uifunctions.functions)
if (statement.supportingData && statement.supportingData->uifunctions.functions)
{
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions;
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement.supportingData->uifunctions.totalFunctions;
supportingFunctionIndex++)
{
if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
if (statement.supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
{
functionIndex = supportingFunctionIndex;
break;
@@ -309,14 +307,14 @@ namespace
else
{
m_stream << "(";
WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function);
WriteStatementSkipInitialUnnecessaryParenthesis(*operand.internals.function);
m_stream << ")";
}
}
void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
void WriteStatementOperand(const Statement_s& statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (spaceNext)
m_stream << " ";
@@ -349,16 +347,16 @@ namespace
spaceNext = true;
}
void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const
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));
assert(endOffset <= static_cast<size_t>(statement.numEntries));
auto currentPos = startOffset;
auto spaceNext = false;
while (currentPos < endOffset)
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (expEntry.type == EET_OPERATOR)
{
@@ -371,41 +369,41 @@ namespace
}
}
void WriteStatement(const Statement_s* statement) const
void WriteStatement(const Statement_s& statement) const
{
if (statement == nullptr || statement->numEntries < 0)
if (!statement.entries || statement.numEntries < 0)
return;
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement->numEntries));
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement.numEntries));
}
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s& statement) const
{
if (statementValue == nullptr || statementValue->numEntries < 0)
if (!statement.entries || statement.numEntries < 0)
return;
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
const auto statementEnd = static_cast<size_t>(statement.numEntries);
if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN)
if (statement.numEntries >= 1 && statement.entries[0].type == EET_OPERATOR && statement.entries[0].data.op == OP_LEFTPAREN)
{
const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0);
const auto parenthesisEnd = FindStatementClosingParenthesis(statement, 0);
if (parenthesisEnd >= statementEnd)
WriteStatementEntryRange(statementValue, 1, statementEnd);
WriteStatementEntryRange(statement, 1, statementEnd);
else if (parenthesisEnd == statementEnd - 1)
WriteStatementEntryRange(statementValue, 1, statementEnd - 1);
WriteStatementEntryRange(statement, 1, statementEnd - 1);
else
WriteStatementEntryRange(statementValue, 0, statementEnd);
WriteStatementEntryRange(statement, 0, statementEnd);
}
else
{
WriteStatementEntryRange(statementValue, 0, statementEnd);
WriteStatementEntryRange(statement, 0, statementEnd);
}
}
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statement, const bool isBooleanStatement) const
{
if (statementValue == nullptr || statementValue->numEntries < 0)
if (!HasStatement(statement))
return;
Indent();
@@ -414,24 +412,30 @@ namespace
if (isBooleanStatement)
{
m_stream << "when(";
DUMP_FUNC(statementValue);
if constexpr (DUMP_NAIVE)
WriteStatementNaive(*statement);
else
WriteStatementSkipInitialUnnecessaryParenthesis(*statement);
m_stream << ");\n";
}
else
{
DUMP_FUNC(statementValue);
if constexpr (DUMP_NAIVE)
WriteStatementNaive(*statement);
else
WriteStatement(*statement);
m_stream << ";\n";
}
}
void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const
{
if (setLocalVarData == nullptr)
if (!setLocalVarData || !setLocalVarData->expression)
return;
Indent();
m_stream << setFunction << " " << setLocalVarData->localVarName << " ";
WriteStatement(setLocalVarData->expression);
WriteStatement(*setLocalVarData->expression);
m_stream << ";\n";
}
@@ -453,7 +457,6 @@ namespace
{
if (token == ";")
continue;
Indent();
}
@@ -470,7 +473,7 @@ namespace
isNewStatement = false;
if (DoesTokenNeedQuotationMarks(token))
m_stream << "\"" << token << "\"";
WriteEscapedString(token);
else
m_stream << token;
}
@@ -485,10 +488,10 @@ namespace
m_stream << "{\n";
IncIndent();
for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++)
for (auto eventHandlerIndex = 0; eventHandlerIndex < eventHandlerSet->eventHandlerCount; eventHandlerIndex++)
{
const auto* eventHandler = eventHandlerSet->eventHandlers[i];
if (eventHandler == nullptr)
const auto* eventHandler = eventHandlerSet->eventHandlers[eventHandlerIndex];
if (!eventHandler)
continue;
switch (eventHandler->eventType)
@@ -498,21 +501,21 @@ namespace
break;
case EVENT_IF:
if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr
|| eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr)
if (!eventHandler->eventData.conditionalScript || !eventHandler->eventData.conditionalScript->eventExpression
|| !eventHandler->eventData.conditionalScript->eventHandlerSet)
{
continue;
}
Indent();
m_stream << "if (";
WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression);
WriteStatementSkipInitialUnnecessaryParenthesis(*eventHandler->eventData.conditionalScript->eventExpression);
m_stream << ")\n";
WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet);
break;
case EVENT_ELSE:
if (eventHandler->eventData.elseScript == nullptr)
if (!eventHandler->eventData.elseScript)
continue;
Indent();
@@ -548,7 +551,7 @@ namespace
void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue)
{
if (eventHandlerSetValue == nullptr)
if (!eventHandlerSetValue)
return;
Indent();
@@ -564,51 +567,44 @@ namespace
<< static_cast<int>(rect.vertAlign) << "\n";
}
void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const
void WriteMaterialProperty(const std::string& propertyKey, const Material* material) const
{
if (materialValue == nullptr || materialValue->info.name == nullptr)
if (!material || !material->info.name)
return;
if (materialValue->info.name[0] == ',')
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
else
WriteStringProperty(propertyKey, materialValue->info.name);
const auto* materialName = material->info.name;
if (materialName[0] == ',')
materialName++;
WriteStringProperty(propertyKey, materialName);
}
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAlias) const
{
if (soundAliasValue == nullptr)
return;
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
if (soundAlias)
WriteStringProperty(propertyKey, soundAlias->aliasName);
}
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s& item) const
{
if (!item->decayActive)
if (!item.decayActive)
return;
Indent();
WriteKey(propertyKey);
m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n";
m_stream << item.fxLetterTime << " " << item.fxDecayStartTime << " " << item.fxDecayDuration << "\n";
}
void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue)
void WriteItemKeyHandlers(const ItemKeyHandler* handler)
{
for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next)
for (const auto* current = handler; current; current = current->next)
{
if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"')
{
std::ostringstream ss;
ss << "execKey \"" << static_cast<char>(currentHandler->key) << "\"";
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
}
std::string key;
if (current->key >= '!' && current->key <= '~' && current->key != '"')
key = std::format("execKey \"{}\"", static_cast<char>(current->key));
else
{
std::ostringstream ss;
ss << "execKeyInt " << currentHandler->key;
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
}
key = std::format("execKeyInt {}", current->key);
WriteMenuEventHandlerSetProperty(key, current->action);
}
}
@@ -619,18 +615,18 @@ namespace
Indent();
WriteKey(propertyKey);
m_stream << "{ ";
const auto tokenList = CreateScriptTokenList(value);
auto firstToken = true;
m_stream << "{ ";
for (const auto& token : tokenList)
{
if (firstToken)
firstToken = false;
else
m_stream << ";";
m_stream << "\"" << token << "\"";
WriteEscapedString(token);
}
if (!firstToken)
m_stream << " ";
@@ -656,58 +652,61 @@ namespace
}
}
void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const
void WriteColumnProperty(const listBoxDef_s& listBox) const
{
if (listBox->numColumns <= 0)
if (listBox.numColumns <= 0)
return;
Indent();
WriteKey(propertyKey);
m_stream << listBox->numColumns << "\n";
WriteKey("columns");
m_stream << listBox.numColumns << "\n";
for (auto col = 0; col < listBox->numColumns; col++)
const auto columnCount = std::min<size_t>(listBox.numColumns, std::size(listBox.columnInfo));
for (size_t columnIndex = 0u; columnIndex < columnCount; columnIndex++)
{
const auto& column = listBox.columnInfo[columnIndex];
Indent();
for (auto i = 0u; i < MENU_KEY_SPACING; i++)
m_stream << " ";
m_stream << listBox->columnInfo[col].pos << " " << listBox->columnInfo[col].width << " " << listBox->columnInfo[col].maxChars << " "
<< listBox->columnInfo[col].alignment << "\n";
m_stream << column.pos << " " << column.width << " " << column.maxChars << " " << column.alignment << "\n";
}
}
void WriteListBoxProperties(const itemDef_s* item)
void WriteListBoxProperties(const itemDef_s& item)
{
if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr)
if (item.type != ITEM_TYPE_LISTBOX || !item.typeData.listBox)
return;
const auto* listBox = item->typeData.listBox;
WriteKeywordProperty("notselectable", listBox->notselectable != 0);
WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0);
WriteKeywordProperty("usepaging", listBox->usePaging != 0);
WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f);
WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f);
WriteFloatProperty("feeder", item->special, 0.0f);
WriteIntProperty("elementtype", listBox->elementStyle, 0);
WriteColumnProperty("columns", listBox);
WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick);
WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000);
WriteMaterialProperty("selectIcon", listBox->selectIcon);
const auto& listBox = *item.typeData.listBox;
WriteKeywordProperty("notselectable", listBox.notselectable != 0);
WriteKeywordProperty("noscrollbars", listBox.noScrollBars != 0);
WriteKeywordProperty("usepaging", listBox.usePaging != 0);
WriteFloatProperty("elementwidth", listBox.elementWidth, 0.0f);
WriteFloatProperty("elementheight", listBox.elementHeight, 0.0f);
WriteFloatProperty("feeder", item.special, 0.0f);
WriteIntProperty("elementtype", listBox.elementStyle, 0);
WriteColumnProperty(listBox);
WriteMenuEventHandlerSetProperty("doubleclick", listBox.onDoubleClick);
WriteColorProperty("selectBorder", listBox.selectBorder, COLOR_0000);
WriteMaterialProperty("selectIcon", listBox.selectIcon);
}
void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
void WriteDvarFloatProperty(const itemDef_s& item, const editFieldDef_s& editField) const
{
if (item->dvar == nullptr)
if (!item.dvar)
return;
Indent();
WriteKey(propertyKey);
m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n";
WriteKey("dvarFloat");
WriteEscapedString(item.dvar);
m_stream << " " << editField.defVal << " " << editField.minVal << " " << editField.maxVal << "\n";
}
void WriteEditFieldProperties(const itemDef_s* item) const
void WriteEditFieldProperties(const itemDef_s& item) const
{
switch (item->type)
switch (item.type)
{
case ITEM_TYPE_TEXT:
case ITEM_TYPE_EDITFIELD:
@@ -726,153 +725,161 @@ namespace
return;
}
if (item->typeData.editField == nullptr)
if (!item.typeData.editField)
return;
const auto* editField = item->typeData.editField;
if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
const auto& editField = *item.typeData.editField;
if (std::fabs(-1.0f - editField.defVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField.minVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField.maxVal) >= std::numeric_limits<float>::epsilon())
{
WriteDvarFloatProperty("dvarFloat", item, editField);
WriteDvarFloatProperty(item, editField);
}
else
{
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("dvar", item.dvar);
}
WriteStringProperty("localvar", item->localVar);
WriteIntProperty("maxChars", editField->maxChars, 0);
WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0);
WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0);
WriteStringProperty("localvar", item.localVar);
WriteIntProperty("maxChars", editField.maxChars, 0);
WriteKeywordProperty("maxCharsGotoNext", editField.maxCharsGotoNext != 0);
WriteIntProperty("maxPaintChars", editField.maxPaintChars, 0);
}
void WriteMultiValueProperty(const multiDef_s* multiDef) const
void WriteMultiValueProperty(const multiDef_s& multi) const
{
Indent();
if (multiDef->strDef)
WriteKey("dvarStrList");
else
WriteKey("dvarFloatList");
if (multi.count <= 0)
return;
Indent();
WriteKey(multi.strDef ? "dvarStrList" : "dvarFloatList");
m_stream << "{";
for (auto i = 0; i < multiDef->count; i++)
const auto valueCount = std::min<size_t>(multi.count, std::size(multi.dvarValue));
for (size_t valueIndex = 0u; valueIndex < valueCount; valueIndex++)
{
if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr)
if (!multi.dvarList[valueIndex] || (multi.strDef && !multi.dvarStr[valueIndex]))
continue;
m_stream << " \"" << multiDef->dvarList[i] << "\"";
if (multiDef->strDef)
m_stream << " \"" << multiDef->dvarStr[i] << "\"";
m_stream << " ";
WriteEscapedString(multi.dvarList[valueIndex]);
m_stream << " ";
if (multi.strDef)
WriteEscapedString(multi.dvarStr[valueIndex]);
else
m_stream << " " << multiDef->dvarValue[i] << "";
m_stream << multi.dvarValue[valueIndex];
}
m_stream << " }\n";
}
void WriteMultiProperties(const itemDef_s* item) const
void WriteMultiProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr)
if (item.type != ITEM_TYPE_MULTI || !item.typeData.multi)
return;
const auto* multiDef = item->typeData.multi;
if (multiDef->count <= 0)
return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteMultiValueProperty(multiDef);
WriteStringProperty("dvar", item.dvar);
WriteStringProperty("localvar", item.localVar);
WriteMultiValueProperty(*item.typeData.multi);
}
void WriteEnumDvarProperties(const itemDef_s* item) const
void WriteEnumDvarProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_DVARENUM)
if (item.type != ITEM_TYPE_DVARENUM)
return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
WriteStringProperty("dvar", item.dvar);
WriteStringProperty("localvar", item.localVar);
WriteStringProperty("dvarEnumList", item.typeData.enumDvarName);
}
void WriteTickerProperties(const itemDef_s* item) const
void WriteTickerProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr)
if (item.type != ITEM_TYPE_NEWS_TICKER || item.typeData.ticker == nullptr)
return;
const auto* newsTickerDef = item->typeData.ticker;
const auto* newsTickerDef = item.typeData.ticker;
WriteIntProperty("spacing", newsTickerDef->spacing, 0);
WriteIntProperty("speed", newsTickerDef->speed, 0);
WriteIntProperty("newsfeed", newsTickerDef->feedId, 0);
}
void WriteItemData(const itemDef_s* item)
void WriteItemTextProperty(const char* text) const
{
WriteStringProperty("name", item->window.name);
WriteStringProperty("text", item->text);
WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO);
WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE);
WriteStringProperty("group", item->window.group);
WriteRectProperty("rect", item->window.rectClient);
WriteIntProperty("style", item->window.style, 0);
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
WriteIntProperty("border", item->window.border, 0);
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
// This distinguishes explicitly empty text from null text, which falls back to the item's dvar.
if (!text)
return;
if (item->visibleExp)
WriteStatementProperty("visible", item->visibleExp, true);
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
Indent();
WriteKey("text");
WriteEscapedString(text);
m_stream << "\n";
}
void WriteItemData(const itemDef_s& item)
{
WriteStringProperty("name", item.window.name);
WriteItemTextProperty(item.text);
WriteKeywordProperty("textsavegame", item.itemFlags & ITEM_FLAG_SAVE_GAME_INFO);
WriteKeywordProperty("textcinematicsubtitle", item.itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE);
WriteStringProperty("group", item.window.group);
WriteRectProperty("rect", item.window.rectClient);
WriteIntProperty("style", item.window.style, 0);
WriteKeywordProperty("decoration", item.window.staticFlags & WINDOW_FLAG_DECORATION);
WriteKeywordProperty("autowrapped", item.window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
WriteKeywordProperty("horizontalscroll", item.window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
WriteIntProperty("type", item.type, ITEM_TYPE_TEXT);
WriteIntProperty("border", item.window.border, 0);
WriteFloatProperty("borderSize", item.window.borderSize, 0.0f);
if (HasStatement(item.visibleExp))
WriteStatementProperty("visible", item.visibleExp, true);
else if (item.window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
WriteIntProperty("visible", 1, 0);
WriteStatementProperty("disabled", item->disabledExp, true);
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
WriteIntProperty("align", item->alignment, 0);
WriteIntProperty("textalign", item->textAlignMode, 0);
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
WriteFloatProperty("textscale", item->textscale, 0.0f);
WriteIntProperty("textstyle", item->textStyle, 0);
WriteIntProperty("textfont", item->fontEnum, 0);
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
WriteMaterialProperty("background", item->window.background);
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
WriteMenuEventHandlerSetProperty("action", item->action);
WriteMenuEventHandlerSetProperty("accept", item->accept);
// WriteFloatProperty("special", item->special, 0.0f);
WriteSoundAliasProperty("focusSound", item->focusSound);
WriteStringProperty("dvarTest", item->dvarTest);
WriteStatementProperty("disabled", item.disabledExp, true);
WriteIntProperty("ownerdraw", item.window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", item.window.ownerDrawFlags);
WriteIntProperty("align", item.alignment, 0);
WriteIntProperty("textalign", item.textAlignMode, 0);
WriteFloatProperty("textalignx", item.textalignx, 0.0f);
WriteFloatProperty("textaligny", item.textaligny, 0.0f);
WriteFloatProperty("textscale", item.textscale, 0.0f);
WriteIntProperty("textstyle", item.textStyle, 0);
WriteIntProperty("textfont", item.fontEnum, 0);
WriteColorProperty("backcolor", item.window.backColor, COLOR_0000);
WriteColorProperty("forecolor", item.window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", item.window.borderColor, COLOR_0000);
WriteColorProperty("outlinecolor", item.window.outlineColor, COLOR_0000);
WriteColorProperty("disablecolor", item.window.disableColor, COLOR_0000);
WriteColorProperty("glowcolor", item.glowColor, COLOR_0000);
WriteMaterialProperty("background", item.window.background);
WriteMenuEventHandlerSetProperty("onFocus", item.onFocus);
WriteMenuEventHandlerSetProperty("leaveFocus", item.leaveFocus);
WriteMenuEventHandlerSetProperty("mouseEnter", item.mouseEnter);
WriteMenuEventHandlerSetProperty("mouseExit", item.mouseExit);
WriteMenuEventHandlerSetProperty("mouseEnterText", item.mouseEnterText);
WriteMenuEventHandlerSetProperty("mouseExitText", item.mouseExitText);
WriteMenuEventHandlerSetProperty("action", item.action);
WriteMenuEventHandlerSetProperty("accept", item.onAccept);
// WriteFloatProperty("special", item.special, 0.0f);
WriteSoundAliasProperty("focusSound", item.focusSound);
WriteStringProperty("dvarTest", item.dvarTest);
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
if (item.dvarFlags & ITEM_DVAR_FLAG_ENABLE)
WriteMultiTokenStringProperty("enableDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_DISABLE)
WriteMultiTokenStringProperty("disableDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_SHOW)
WriteMultiTokenStringProperty("showDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_HIDE)
WriteMultiTokenStringProperty("hideDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_FOCUS)
WriteMultiTokenStringProperty("focusDvar", item.enableDvar);
WriteItemKeyHandlerProperty(item->onKey);
WriteStatementProperty("exp text", item->textExp, false);
WriteStatementProperty("exp material", item->materialExp, false);
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
WriteItemKeyHandlers(item.onKey);
WriteStatementProperty("exp text", item.textExp, false);
WriteStatementProperty("exp material", item.materialExp, false);
WriteFloatExpressionsProperty(item.floatExpressions, item.floatExpressionCount);
WriteIntProperty("gamemsgwindowindex", item.gameMsgWindowIndex, 0);
WriteIntProperty("gamemsgwindowmode", item.gameMsgWindowMode, 0);
WriteDecodeEffectProperty("decodeEffect", item);
WriteListBoxProperties(item);
@@ -882,68 +889,73 @@ namespace
WriteTickerProperties(item);
}
void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount)
void WriteItemDefs(const itemDef_s* const* items, const size_t itemCount)
{
for (auto i = 0u; i < itemCount; i++)
if (!items || itemCount <= 0)
return;
for (size_t itemIndex = 0u; itemIndex < itemCount; itemIndex++)
{
const auto* item = items[itemIndex];
if (!item)
continue;
StartItemDefScope();
WriteItemData(itemDefs[i]);
WriteItemData(*item);
EndScope();
}
}
void WriteMenuData(const menuDef_t* menu)
void WriteMenuData(const menuDef_t& menu)
{
WriteStringProperty("name", menu->window.name);
WriteBoolProperty("fullscreen", menu->fullScreen, false);
WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION);
WriteRectProperty("rect", menu->window.rect);
WriteIntProperty("style", menu->window.style, 0);
WriteIntProperty("border", menu->window.border, 0);
WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f);
WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000);
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu->window.background);
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
WriteStringProperty("soundLoop", menu->soundName);
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
WriteFloatProperty("fadeClamp", menu->fadeClamp, 0.0f);
WriteIntProperty("fadeCycle", menu->fadeCycle, 0);
WriteFloatProperty("fadeAmount", menu->fadeAmount, 0.0f);
WriteFloatProperty("fadeInAmount", menu->fadeInAmount, 0.0f);
WriteFloatProperty("blurWorld", menu->blurRadius, 0.0f);
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
WriteStringProperty("allowedBinding", menu->allowedBinding);
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
WriteStringProperty("name", menu.window.name);
WriteBoolProperty("fullscreen", menu.fullScreen, false);
WriteKeywordProperty("screenSpace", menu.window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
WriteKeywordProperty("decoration", menu.window.staticFlags & WINDOW_FLAG_DECORATION);
WriteRectProperty("rect", menu.window.rect);
WriteIntProperty("style", menu.window.style, 0);
WriteIntProperty("border", menu.window.border, 0);
WriteFloatProperty("borderSize", menu.window.borderSize, 0.0f);
WriteColorProperty("backcolor", menu.window.backColor, COLOR_0000);
WriteColorProperty("forecolor", menu.window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu.window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu.focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu.window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu.window.background);
WriteIntProperty("ownerdraw", menu.window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu.window.ownerDrawFlags);
WriteKeywordProperty("outOfBoundsClick", menu.window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
WriteStringProperty("soundLoop", menu.soundName);
WriteKeywordProperty("popup", menu.window.staticFlags & WINDOW_FLAG_POPUP);
WriteFloatProperty("fadeClamp", menu.fadeClamp, 0.0f);
WriteIntProperty("fadeCycle", menu.fadeCycle, 0);
WriteFloatProperty("fadeAmount", menu.fadeAmount, 0.0f);
WriteFloatProperty("fadeInAmount", menu.fadeInAmount, 0.0f);
WriteFloatProperty("blurWorld", menu.blurRadius, 0.0f);
WriteKeywordProperty("legacySplitScreenScale", menu.window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
WriteKeywordProperty("hiddenDuringScope", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
WriteKeywordProperty("hiddenDuringFlashbang", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
WriteKeywordProperty("hiddenDuringUI", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
WriteStringProperty("allowedBinding", menu.allowedBinding);
WriteKeywordProperty("textOnlyFocus", menu.window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
if (menu->visibleExp)
WriteStatementProperty("visible", menu->visibleExp, true);
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
if (HasStatement(menu.visibleExp))
WriteStatementProperty("visible", menu.visibleExp, true);
else if (menu.window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
WriteIntProperty("visible", 1, 0);
WriteStatementProperty("exp rect X", menu->rectXExp, false);
WriteStatementProperty("exp rect Y", menu->rectYExp, false);
WriteStatementProperty("exp rect W", menu->rectWExp, false);
WriteStatementProperty("exp rect H", menu->rectHExp, false);
WriteStatementProperty("exp openSound", menu->openSoundExp, false);
WriteStatementProperty("exp closeSound", menu->closeSoundExp, false);
WriteMenuEventHandlerSetProperty("onOpen", menu->onOpen);
WriteMenuEventHandlerSetProperty("onClose", menu->onClose);
WriteMenuEventHandlerSetProperty("onRequestClose", menu->onCloseRequest);
WriteMenuEventHandlerSetProperty("onESC", menu->onESC);
WriteItemKeyHandlerProperty(menu->onKey);
WriteItemDefs(menu->items, menu->itemCount);
WriteStatementProperty("exp rect X", menu.rectXExp, false);
WriteStatementProperty("exp rect Y", menu.rectYExp, false);
WriteStatementProperty("exp rect W", menu.rectWExp, false);
WriteStatementProperty("exp rect H", menu.rectHExp, false);
WriteStatementProperty("exp openSound", menu.openSoundExp, false);
WriteStatementProperty("exp closeSound", menu.closeSoundExp, false);
WriteMenuEventHandlerSetProperty("onOpen", menu.onOpen);
WriteMenuEventHandlerSetProperty("onClose", menu.onClose);
WriteMenuEventHandlerSetProperty("onRequestClose", menu.onCloseRequest);
WriteMenuEventHandlerSetProperty("onESC", menu.onESC);
WriteItemKeyHandlers(menu.onKey);
WriteItemDefs(menu.items, menu.itemCount);
}
};
} // namespace
+12 -36
View File
@@ -1,49 +1,24 @@
#include "MenuDumperIW5.h"
#include "MenuListDumperIW5.h"
#include "MenuWriterIW5.h"
#include "ObjWriting.h"
#include <filesystem>
#include <format>
#include <string>
namespace fs = std::filesystem;
using namespace IW5;
namespace
{
const MenuList* GetParentMenuList(const XAssetInfo<menuDef_t>& asset)
std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, const XAssetInfo<menuDef_t>& asset)
{
const auto* menu = asset.Asset();
auto zoneMenuListPool = asset.m_zone->m_pools.PoolAssets<AssetMenuList>();
for (const auto* menuList : zoneMenuListPool)
{
const auto* menuListAsset = menuList->Asset();
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset.Asset());
for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++)
{
if (menuListAsset->menus[menuIndex] == menu)
return menuListAsset;
}
}
return nullptr;
}
std::string GetPathForMenu(const XAssetInfo<menuDef_t>& asset)
{
const auto* list = GetParentMenuList(asset);
if (!list)
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
return std::format("ui_mp/{}.menu", asset.Asset()->window.name);
const fs::path p(list->name);
std::string parentPath;
if (p.has_parent_path())
parentPath = p.parent_path().string() + "/";
return std::format("{}{}.menu", parentPath, asset.Asset()->window.name);
return menuDumpingState->second.m_path;
}
} // namespace
@@ -52,22 +27,23 @@ namespace menu
void MenuDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo<AssetMenu::Type>& asset)
{
const auto* menu = asset.Asset();
const auto menuFilePath = GetPathForMenu(asset);
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
if (ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
{
// Don't dump menu file separately if the name matches the menu list
const auto* menuListParent = GetParentMenuList(asset);
if (menuListParent && menuFilePath == menuListParent->name)
return;
// Make sure menu paths based on menu lists are created
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (auto* menuListAsset : menuListAssets)
CreateDumpingStateForMenuListIW5(zoneState, menuListAsset->Asset());
}
const auto menuFilePath = GetPathForMenu(zoneState, asset);
const auto assetFile = context.OpenAssetFile(menuFilePath);
if (!assetFile)
return;
auto menuWriter = CreateMenuWriterIW5(*assetFile);
const auto menuWriter = CreateMenuWriterIW5(*assetFile);
menuWriter->Start();
menuWriter->WriteMenu(*menu);
@@ -8,8 +8,7 @@
#include <cassert>
#include <filesystem>
#include <format>
#include <set>
#include <sstream>
#include <unordered_set>
namespace fs = std::filesystem;
@@ -20,24 +19,24 @@ namespace
std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList)
{
std::vector<const ExpressionSupportingData*> result;
std::set<const ExpressionSupportingData*> alreadyAddedSupportingData;
std::unordered_set<const ExpressionSupportingData*> alreadyAddedSupportingData;
if (menuList->menus == nullptr)
if (!menuList->menus)
return result;
for (auto i = 0; i < menuList->menuCount; i++)
{
if (menuList->menus[i] == nullptr)
if (!menuList->menus[i])
continue;
const auto* menu = menuList->menus[i];
if (menu->data == nullptr || menu->data->expressionData == nullptr)
if (!menu->data || !menu->data->expressionData)
continue;
if (alreadyAddedSupportingData.find(menu->data->expressionData) == alreadyAddedSupportingData.end())
if (!alreadyAddedSupportingData.contains(menu->data->expressionData))
{
result.push_back(menu->data->expressionData);
result.emplace_back(menu->data->expressionData);
alreadyAddedSupportingData.emplace(menu->data->expressionData);
}
}
@@ -60,7 +59,7 @@ namespace
for (auto i = 0; i < supportingData->uifunctions.totalFunctions; i++)
{
const auto* function = supportingData->uifunctions.functions[i];
if (function == nullptr)
if (!function)
continue;
menuDumper.WriteFunctionDef(std::format("FUNC_{}", functionIndex), function);
@@ -70,42 +69,85 @@ namespace
}
}
void DumpMenus(menu::IWriterIW5& menuDumper, const MenuList* menuList)
void DumpMenus(menu::IWriterIW5& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList)
{
const fs::path p(menuList->name);
std::string parentPath;
if (p.has_parent_path())
parentPath = p.parent_path().string() + "/";
for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++)
{
const auto* menu = menuList->menus[menuNum];
if (!menu)
continue;
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu);
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
continue;
const auto* menuAssetName = menu->window.name;
bool isReference = false;
if (menuAssetName && menuAssetName[0] == ',')
{
menuAssetName = &menuAssetName[1];
isReference = true;
}
std::ostringstream ss;
ss << parentPath << menuAssetName << ".menu";
const auto menuName = ss.str();
const auto isReference = menuAssetName && menuAssetName[0] == ',';
// If the menu was embedded directly as menu list write its data in the menu list file
if (!isReference && menuName == menuList->name)
if (!isReference && menuDumpingState->second.m_alias_menu_list == menuList)
menuDumper.WriteMenu(*menu);
else
menuDumper.IncludeMenu(ss.str());
menuDumper.IncludeMenu(menuDumpingState->second.m_path);
}
}
std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu)
{
const auto* menuAssetName = menu->window.name;
if (!menuAssetName)
return {};
if (menuAssetName[0] == ',')
menuAssetName = &menuAssetName[1];
return std::format("{}{}.menu", menuListParentPath, menuAssetName);
}
} // namespace
namespace menu
{
void CreateDumpingStateForMenuListIW5(MenuDumpingZoneState* zoneState, const MenuList* menuList)
{
if (!menuList || menuList->menuCount <= 0 || !menuList->menus || !menuList->name)
return;
const std::string menuListName(menuList->name);
const fs::path p(menuListName);
std::string parentPath;
if (p.has_parent_path())
parentPath = p.parent_path().generic_string() + "/";
for (auto i = 0; i < menuList->menuCount; i++)
{
const auto* menu = menuList->menus[i];
if (!menu)
continue;
auto menuPath = PathForMenu(parentPath, menu);
if (menuPath.empty())
continue;
auto existingState = zoneState->m_menu_dumping_state_map.find(menu);
if (existingState == zoneState->m_menu_dumping_state_map.end())
{
const auto isTheSameAsMenuList = menuPath == menuListName;
zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr);
}
else if (!existingState->second.m_alias_menu_list)
{
const auto isTheSameAsMenuList = menuPath == menuListName;
if (isTheSameAsMenuList)
{
existingState->second.m_alias_menu_list = menuList;
existingState->second.m_path = std::move(menuPath);
}
}
}
}
void MenuListDumperIW5::DumpAsset(AssetDumpingContext& context, const XAssetInfo<AssetMenuList::Type>& asset)
{
const auto* menuList = asset.Asset();
@@ -114,15 +156,25 @@ namespace menu
if (!assetFile)
return;
auto menuWriter = CreateMenuWriterIW5(*assetFile);
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
const auto menuWriter = CreateMenuWriterIW5(*assetFile);
menuWriter->Start();
if (!ObjWriting::Configuration.MenuLegacyMode)
DumpFunctions(*menuWriter, menuList);
DumpMenus(*menuWriter, menuList);
DumpMenus(*menuWriter, zoneState, menuList);
menuWriter->End();
}
void MenuListDumperIW5::Dump(AssetDumpingContext& context)
{
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
const auto menuListAssets = context.m_zone.m_pools.PoolAssets<AssetMenuList>();
for (const auto* asset : menuListAssets)
CreateDumpingStateForMenuListIW5(zoneState, asset->Asset());
AbstractAssetDumper::Dump(context);
}
} // namespace menu
@@ -2,11 +2,17 @@
#include "Dumping/AbstractAssetDumper.h"
#include "Game/IW5/IW5.h"
#include "Menu/MenuDumpingZoneState.h"
namespace menu
{
void CreateDumpingStateForMenuListIW5(MenuDumpingZoneState* zoneState, const IW5::MenuList* menuList);
class MenuListDumperIW5 final : public AbstractAssetDumper<IW5::AssetMenuList>
{
public:
void Dump(AssetDumpingContext& context) override;
protected:
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<IW5::AssetMenuList::Type>& asset) override;
};
+302 -287
View File
@@ -6,34 +6,29 @@
#include <cassert>
#include <cmath>
#include <limits>
#include <sstream>
using namespace IW5;
// Uncomment this macro to skip interpretative expression dumping
// #define DUMP_NAIVE
#ifdef DUMP_NAIVE
#define DUMP_FUNC WriteStatementNaive
#else
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
#endif
namespace
{
size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
{
assert(statement->numEntries >= 0);
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
// Set this to true to skip interpretative expression dumping
constexpr auto DUMP_NAIVE = false;
const auto statementEnd = static_cast<size_t>(statement->numEntries);
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];
const auto& expEntry = statement.entries[currentSearchPosition];
if (expEntry.type != EET_OPERATOR)
continue;
@@ -78,9 +73,7 @@ namespace
void WriteMenu(const menuDef_t& menu) override
{
StartMenuDefScope();
WriteMenuData(&menu);
WriteMenuData(menu);
EndScope();
}
@@ -100,12 +93,22 @@ namespace
}
private:
void WriteStatementNaive(const Statement_s* statement) const
static bool HasStatement(const Statement_s* statement)
{
const auto entryCount = static_cast<unsigned>(statement->numEntries);
return statement && statement->numEntries > 0 && statement->entries;
}
void WriteStatementNaive(const Statement_s& statement) const
{
const auto entryCount = static_cast<unsigned>(statement.numEntries);
const auto missingClosingParenthesis = statement.numEntries > 0 && statement.entries[0].type == EET_OPERATOR
&& statement.entries[0].data.op == OP_LEFTPAREN
&& FindStatementClosingParenthesis(statement, 0) >= static_cast<size_t>(statement.numEntries);
for (auto i = 0uz; i < entryCount; i++)
{
const auto& entry = statement->entries[i];
const auto& entry = statement.entries[i];
if (entry.type == EET_OPERAND)
{
size_t pos = i;
@@ -142,14 +145,14 @@ namespace
if (closingParenPos - i + 1u >= 1u)
{
const auto& staticDvarEntry = statement->entries[i + 1u];
const auto& staticDvarEntry = statement.entries[i + 1u];
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
{
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
if (statement.supportingData && statement.supportingData->staticDvarList.staticDvars
&& staticDvarEntry.data.operand.internals.intVal >= 0
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
&& staticDvarEntry.data.operand.internals.intVal < statement.supportingData->staticDvarList.numStaticDvars)
{
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
const auto* staticDvar = statement.supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
if (staticDvar && staticDvar->dvarName)
m_stream << staticDvar->dvarName;
}
@@ -178,9 +181,9 @@ namespace
}
}
void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
void WriteStatementOperator(const Statement_s& statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (spaceNext && expEntry.data.op != OP_COMMA)
m_stream << " ";
@@ -225,14 +228,14 @@ namespace
if (closingParenPos - currentPos + 1 >= 1)
{
const auto& staticDvarEntry = statement->entries[currentPos + 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
if (statement.supportingData && statement.supportingData->staticDvarList.staticDvars
&& staticDvarEntry.data.operand.internals.intVal >= 0
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
&& staticDvarEntry.data.operand.internals.intVal < statement.supportingData->staticDvarList.numStaticDvars)
{
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
const auto* staticDvar = statement.supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
if (staticDvar && staticDvar->dvarName)
m_stream << staticDvar->dvarName;
}
@@ -272,22 +275,22 @@ namespace
}
}
void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const
void WriteStatementOperandFunction(const Statement_s& statement, const size_t currentPos) const
{
const auto& operand = statement->entries[currentPos].data.operand;
const auto& operand = statement.entries[currentPos].data.operand;
if (operand.internals.function == nullptr)
if (!operand.internals.function)
return;
if (!ObjWriting::Configuration.MenuLegacyMode)
{
int functionIndex = -1;
if (statement->supportingData && statement->supportingData->uifunctions.functions)
if (statement.supportingData && statement.supportingData->uifunctions.functions)
{
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions;
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement.supportingData->uifunctions.totalFunctions;
supportingFunctionIndex++)
{
if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
if (statement.supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
{
functionIndex = supportingFunctionIndex;
break;
@@ -304,14 +307,14 @@ namespace
else
{
m_stream << "(";
WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function);
WriteStatementSkipInitialUnnecessaryParenthesis(*operand.internals.function);
m_stream << ")";
}
}
void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
void WriteStatementOperand(const Statement_s& statement, size_t& currentPos, bool& spaceNext) const
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (spaceNext)
m_stream << " ";
@@ -344,16 +347,16 @@ namespace
spaceNext = true;
}
void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const
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));
assert(endOffset <= static_cast<size_t>(statement.numEntries));
auto currentPos = startOffset;
auto spaceNext = false;
while (currentPos < endOffset)
{
const auto& expEntry = statement->entries[currentPos];
const auto& expEntry = statement.entries[currentPos];
if (expEntry.type == EET_OPERATOR)
{
@@ -366,41 +369,41 @@ namespace
}
}
void WriteStatement(const Statement_s* statement) const
void WriteStatement(const Statement_s& statement) const
{
if (statement == nullptr || statement->numEntries < 0)
if (!statement.entries || statement.numEntries < 0)
return;
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement->numEntries));
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement.numEntries));
}
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s& statement) const
{
if (statementValue == nullptr || statementValue->numEntries < 0)
if (!statement.entries || statement.numEntries < 0)
return;
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
const auto statementEnd = static_cast<size_t>(statement.numEntries);
if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN)
if (statement.numEntries >= 1 && statement.entries[0].type == EET_OPERATOR && statement.entries[0].data.op == OP_LEFTPAREN)
{
const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0);
const auto parenthesisEnd = FindStatementClosingParenthesis(statement, 0);
if (parenthesisEnd >= statementEnd)
WriteStatementEntryRange(statementValue, 1, statementEnd);
WriteStatementEntryRange(statement, 1, statementEnd);
else if (parenthesisEnd == statementEnd - 1)
WriteStatementEntryRange(statementValue, 1, statementEnd - 1);
WriteStatementEntryRange(statement, 1, statementEnd - 1);
else
WriteStatementEntryRange(statementValue, 0, statementEnd);
WriteStatementEntryRange(statement, 0, statementEnd);
}
else
{
WriteStatementEntryRange(statementValue, 0, statementEnd);
WriteStatementEntryRange(statement, 0, statementEnd);
}
}
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statement, const bool isBooleanStatement) const
{
if (statementValue == nullptr || statementValue->numEntries < 0)
if (!HasStatement(statement))
return;
Indent();
@@ -409,24 +412,30 @@ namespace
if (isBooleanStatement)
{
m_stream << "when(";
DUMP_FUNC(statementValue);
if constexpr (DUMP_NAIVE)
WriteStatementNaive(*statement);
else
WriteStatementSkipInitialUnnecessaryParenthesis(*statement);
m_stream << ");\n";
}
else
{
DUMP_FUNC(statementValue);
if constexpr (DUMP_NAIVE)
WriteStatementNaive(*statement);
else
WriteStatement(*statement);
m_stream << ";\n";
}
}
void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const
{
if (setLocalVarData == nullptr)
if (!setLocalVarData || !setLocalVarData->expression)
return;
Indent();
m_stream << setFunction << " " << setLocalVarData->localVarName << " ";
WriteStatement(setLocalVarData->expression);
WriteStatement(*setLocalVarData->expression);
m_stream << ";\n";
}
@@ -448,7 +457,6 @@ namespace
{
if (token == ";")
continue;
Indent();
}
@@ -465,7 +473,7 @@ namespace
isNewStatement = false;
if (DoesTokenNeedQuotationMarks(token))
m_stream << "\"" << token << "\"";
WriteEscapedString(token);
else
m_stream << token;
}
@@ -480,10 +488,10 @@ namespace
m_stream << "{\n";
IncIndent();
for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++)
for (auto eventHandlerIndex = 0; eventHandlerIndex < eventHandlerSet->eventHandlerCount; eventHandlerIndex++)
{
const auto* eventHandler = eventHandlerSet->eventHandlers[i];
if (eventHandler == nullptr)
const auto* eventHandler = eventHandlerSet->eventHandlers[eventHandlerIndex];
if (!eventHandler)
continue;
switch (eventHandler->eventType)
@@ -493,21 +501,21 @@ namespace
break;
case EVENT_IF:
if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr
|| eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr)
if (!eventHandler->eventData.conditionalScript || !eventHandler->eventData.conditionalScript->eventExpression
|| !eventHandler->eventData.conditionalScript->eventHandlerSet)
{
continue;
}
Indent();
m_stream << "if (";
WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression);
WriteStatementSkipInitialUnnecessaryParenthesis(*eventHandler->eventData.conditionalScript->eventExpression);
m_stream << ")\n";
WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet);
break;
case EVENT_ELSE:
if (eventHandler->eventData.elseScript == nullptr)
if (!eventHandler->eventData.elseScript)
continue;
Indent();
@@ -543,7 +551,7 @@ namespace
void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue)
{
if (eventHandlerSetValue == nullptr)
if (!eventHandlerSetValue)
return;
Indent();
@@ -559,51 +567,44 @@ namespace
<< static_cast<int>(rect.vertAlign) << "\n";
}
void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const
void WriteMaterialProperty(const std::string& propertyKey, const Material* material) const
{
if (materialValue == nullptr || materialValue->info.name == nullptr)
if (!material || !material->info.name)
return;
if (materialValue->info.name[0] == ',')
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
else
WriteStringProperty(propertyKey, materialValue->info.name);
const auto* materialName = material->info.name;
if (materialName[0] == ',')
materialName++;
WriteStringProperty(propertyKey, materialName);
}
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAlias) const
{
if (soundAliasValue == nullptr)
return;
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
if (soundAlias)
WriteStringProperty(propertyKey, soundAlias->aliasName);
}
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s& item) const
{
if (!item->decayActive)
if (!item.decayActive)
return;
Indent();
WriteKey(propertyKey);
m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n";
m_stream << item.fxLetterTime << " " << item.fxDecayStartTime << " " << item.fxDecayDuration << "\n";
}
void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue)
void WriteItemKeyHandlers(const ItemKeyHandler* handler)
{
for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next)
for (const auto* current = handler; current; current = current->next)
{
if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"')
{
std::ostringstream ss;
ss << "execKey \"" << static_cast<char>(currentHandler->key) << "\"";
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
}
std::string key;
if (current->key >= '!' && current->key <= '~' && current->key != '"')
key = std::format("execKey \"{}\"", static_cast<char>(current->key));
else
{
std::ostringstream ss;
ss << "execKeyInt " << currentHandler->key;
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
}
key = std::format("execKeyInt {}", current->key);
WriteMenuEventHandlerSetProperty(key, current->action);
}
}
@@ -614,18 +615,18 @@ namespace
Indent();
WriteKey(propertyKey);
m_stream << "{ ";
const auto tokenList = CreateScriptTokenList(value);
auto firstToken = true;
m_stream << "{ ";
for (const auto& token : tokenList)
{
if (firstToken)
firstToken = false;
else
m_stream << ";";
m_stream << "\"" << token << "\"";
WriteEscapedString(token);
}
if (!firstToken)
m_stream << " ";
@@ -651,59 +652,63 @@ namespace
}
}
void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const
void WriteColumnProperty(const listBoxDef_s& listBox) const
{
if (listBox->numColumns <= 0)
if (listBox.numColumns <= 0)
return;
Indent();
WriteKey(propertyKey);
m_stream << listBox->numColumns << "\n";
WriteKey("columns");
m_stream << listBox.numColumns << "\n";
for (auto col = 0; col < listBox->numColumns; col++)
const auto columnCount = std::min<size_t>(listBox.numColumns, std::size(listBox.columnInfo));
for (size_t columnIndex = 0u; columnIndex < columnCount; columnIndex++)
{
const auto& column = listBox.columnInfo[columnIndex];
Indent();
for (auto i = 0u; i < MENU_KEY_SPACING; i++)
m_stream << " ";
m_stream << listBox->columnInfo[col].xpos << " " << listBox->columnInfo[col].ypos << " " << listBox->columnInfo[col].width << " "
<< listBox->columnInfo[col].height << " " << listBox->columnInfo[col].maxChars << " " << listBox->columnInfo[col].alignment << "\n";
m_stream << column.xpos << " " << column.ypos << " " << column.width << " " << column.height << " " << column.maxChars << " "
<< column.alignment << "\n";
}
}
void WriteListBoxProperties(const itemDef_s* item)
void WriteListBoxProperties(const itemDef_s& item)
{
if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr)
if (item.type != ITEM_TYPE_LISTBOX || !item.typeData.listBox)
return;
const auto* listBox = item->typeData.listBox;
WriteKeywordProperty("notselectable", listBox->notselectable != 0);
WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0);
WriteKeywordProperty("usepaging", listBox->usePaging != 0);
WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f);
WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f);
WriteFloatProperty("feeder", item->special, 0.0f);
WriteIntProperty("elementtype", listBox->elementStyle, 0);
WriteColumnProperty("columns", listBox);
WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick);
WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000);
WriteMaterialProperty("selectIcon", listBox->selectIcon);
WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false);
const auto& listBox = *item.typeData.listBox;
WriteKeywordProperty("notselectable", listBox.notselectable != 0);
WriteKeywordProperty("noscrollbars", listBox.noScrollBars != 0);
WriteKeywordProperty("usepaging", listBox.usePaging != 0);
WriteFloatProperty("elementwidth", listBox.elementWidth, 0.0f);
WriteFloatProperty("elementheight", listBox.elementHeight, 0.0f);
WriteFloatProperty("feeder", item.special, 0.0f);
WriteIntProperty("elementtype", listBox.elementStyle, 0);
WriteColumnProperty(listBox);
WriteMenuEventHandlerSetProperty("doubleclick", listBox.onDoubleClick);
WriteColorProperty("selectBorder", listBox.selectBorder, COLOR_0000);
WriteMaterialProperty("selectIcon", listBox.selectIcon);
WriteStatementProperty("exp elementheight", listBox.elementHeightExp, false);
}
void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
void WriteDvarFloatProperty(const itemDef_s& item, const editFieldDef_s& editField) const
{
if (item->dvar == nullptr)
if (!item.dvar)
return;
Indent();
WriteKey(propertyKey);
m_stream << "\"" << item->dvar << "\" " << editField->stepVal << " " << editField->minVal << " " << editField->maxVal << "\n";
WriteKey("dvarFloat");
WriteEscapedString(item.dvar);
m_stream << " " << editField.stepVal << " " << editField.minVal << " " << editField.maxVal << "\n";
}
void WriteEditFieldProperties(const itemDef_s* item) const
void WriteEditFieldProperties(const itemDef_s& item) const
{
switch (item->type)
switch (item.type)
{
case ITEM_TYPE_TEXT:
case ITEM_TYPE_EDITFIELD:
@@ -722,155 +727,160 @@ namespace
return;
}
if (item->typeData.editField == nullptr)
if (!item.typeData.editField)
return;
const auto* editField = item->typeData.editField;
if (std::fabs(-1.0f - editField->stepVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
const auto& editField = *item.typeData.editField;
if (std::fabs(-1.0f - editField.stepVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField.minVal) >= std::numeric_limits<float>::epsilon()
|| std::fabs(-1.0f - editField.maxVal) >= std::numeric_limits<float>::epsilon())
{
WriteDvarFloatProperty("dvarFloat", item, editField);
WriteDvarFloatProperty(item, editField);
}
else
{
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("dvar", item.dvar);
}
WriteStringProperty("localvar", item->localVar);
WriteIntProperty("maxChars", editField->maxChars, 0);
WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0);
WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0);
WriteStringProperty("localvar", item.localVar);
WriteIntProperty("maxChars", editField.maxChars, 0);
WriteKeywordProperty("maxCharsGotoNext", editField.maxCharsGotoNext != 0);
WriteIntProperty("maxPaintChars", editField.maxPaintChars, 0);
}
void WriteMultiValueProperty(const multiDef_s* multiDef) const
void WriteMultiValueProperty(const multiDef_s& multi) const
{
Indent();
if (multiDef->strDef)
WriteKey("dvarStrList");
else
WriteKey("dvarFloatList");
WriteKey(multi.strDef ? "dvarStrList" : "dvarFloatList");
m_stream << "{";
for (auto i = 0; i < multiDef->count; i++)
const auto valueCount = std::min<size_t>(multi.count, std::size(multi.dvarValue));
for (size_t valueIndex = 0u; valueIndex < valueCount; valueIndex++)
{
if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr)
if (!multi.dvarList[valueIndex] || (multi.strDef && !multi.dvarStr[valueIndex]))
continue;
m_stream << " \"" << multiDef->dvarList[i] << "\"";
if (multiDef->strDef)
m_stream << " \"" << multiDef->dvarStr[i] << "\"";
m_stream << " ";
WriteEscapedString(multi.dvarList[valueIndex]);
m_stream << " ";
if (multi.strDef)
WriteEscapedString(multi.dvarStr[valueIndex]);
else
m_stream << " " << multiDef->dvarValue[i] << "";
m_stream << multi.dvarValue[valueIndex];
}
m_stream << " }\n";
}
void WriteMultiProperties(const itemDef_s* item) const
void WriteMultiProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr)
if (item.type != ITEM_TYPE_MULTI || !item.typeData.multi)
return;
const auto* multiDef = item->typeData.multi;
if (multiDef->count <= 0)
return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteMultiValueProperty(multiDef);
WriteStringProperty("dvar", item.dvar);
WriteStringProperty("localvar", item.localVar);
WriteMultiValueProperty(*item.typeData.multi);
}
void WriteEnumDvarProperties(const itemDef_s* item) const
void WriteEnumDvarProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_DVARENUM)
if (item.type != ITEM_TYPE_DVARENUM)
return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
WriteStringProperty("dvar", item.dvar);
WriteStringProperty("localvar", item.localVar);
WriteStringProperty("dvarEnumList", item.typeData.enumDvarName);
}
void WriteTickerProperties(const itemDef_s* item) const
void WriteTickerProperties(const itemDef_s& item) const
{
if (item->type != ITEM_TYPE_NEWS_TICKER || item->typeData.ticker == nullptr)
if (item.type != ITEM_TYPE_NEWS_TICKER || !item.typeData.ticker)
return;
const auto* newsTickerDef = item->typeData.ticker;
const auto* newsTickerDef = item.typeData.ticker;
WriteIntProperty("spacing", newsTickerDef->spacing, 0);
WriteIntProperty("speed", newsTickerDef->speed, 0);
WriteIntProperty("newsfeed", newsTickerDef->feedId, 0);
}
void WriteItemData(const itemDef_s* item)
void WriteItemTextProperty(const char* text) const
{
WriteStringProperty("name", item->window.name);
WriteStringProperty("text", item->text);
WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO);
WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE);
WriteStringProperty("group", item->window.group);
WriteRectProperty("rect", item->window.rectClient);
WriteIntProperty("style", item->window.style, 0);
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
WriteIntProperty("border", item->window.border, 0);
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
// This distinguishes explicitly empty text from null text, which falls back to the item's dvar.
if (!text)
return;
if (item->visibleExp)
WriteStatementProperty("visible", item->visibleExp, true);
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
Indent();
WriteKey("text");
WriteEscapedString(text);
m_stream << "\n";
}
void WriteItemData(const itemDef_s& item)
{
WriteStringProperty("name", item.window.name);
WriteItemTextProperty(item.text);
WriteKeywordProperty("textsavegame", item.itemFlags & ITEM_FLAG_SAVE_GAME_INFO);
WriteKeywordProperty("textcinematicsubtitle", item.itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE);
WriteStringProperty("group", item.window.group);
WriteRectProperty("rect", item.window.rectClient);
WriteIntProperty("style", item.window.style, 0);
WriteKeywordProperty("decoration", item.window.staticFlags & WINDOW_FLAG_DECORATION);
WriteKeywordProperty("autowrapped", item.window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
WriteKeywordProperty("horizontalscroll", item.window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
WriteIntProperty("type", item.type, ITEM_TYPE_TEXT);
WriteIntProperty("border", item.window.border, 0);
WriteFloatProperty("borderSize", item.window.borderSize, 0.0f);
if (HasStatement(item.visibleExp))
WriteStatementProperty("visible", item.visibleExp, true);
else if (item.window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
WriteIntProperty("visible", 1, 0);
WriteStatementProperty("disabled", item->disabledExp, true);
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
WriteIntProperty("align", item->alignment, 0);
WriteIntProperty("textalign", item->textAlignMode, 0);
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
WriteFloatProperty("textscale", item->textscale, 0.0f);
WriteIntProperty("textstyle", item->textStyle, 0);
WriteIntProperty("textfont", item->fontEnum, 0);
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
WriteMaterialProperty("background", item->window.background);
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
WriteMenuEventHandlerSetProperty("hasFocus", item->hasFocus);
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
WriteMenuEventHandlerSetProperty("action", item->action);
WriteMenuEventHandlerSetProperty("accept", item->accept);
// WriteFloatProperty("special", item->special, 0.0f);
WriteSoundAliasProperty("focusSound", item->focusSound);
WriteStringProperty("dvarTest", item->dvarTest);
WriteStatementProperty("disabled", item.disabledExp, true);
WriteIntProperty("ownerdraw", item.window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", item.window.ownerDrawFlags);
WriteIntProperty("align", item.alignment, 0);
WriteIntProperty("textalign", item.textAlignMode, 0);
WriteFloatProperty("textalignx", item.textalignx, 0.0f);
WriteFloatProperty("textaligny", item.textaligny, 0.0f);
WriteFloatProperty("textscale", item.textscale, 0.0f);
WriteIntProperty("textstyle", item.textStyle, 0);
WriteIntProperty("textfont", item.fontEnum, 0);
WriteColorProperty("backcolor", item.window.backColor, COLOR_0000);
WriteColorProperty("forecolor", item.window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", item.window.borderColor, COLOR_0000);
WriteColorProperty("outlinecolor", item.window.outlineColor, COLOR_0000);
WriteColorProperty("disablecolor", item.window.disableColor, COLOR_0000);
WriteColorProperty("glowcolor", item.glowColor, COLOR_0000);
WriteMaterialProperty("background", item.window.background);
WriteMenuEventHandlerSetProperty("onFocus", item.onFocus);
WriteMenuEventHandlerSetProperty("hasFocus", item.hasFocus);
WriteMenuEventHandlerSetProperty("leaveFocus", item.leaveFocus);
WriteMenuEventHandlerSetProperty("mouseEnter", item.mouseEnter);
WriteMenuEventHandlerSetProperty("mouseExit", item.mouseExit);
WriteMenuEventHandlerSetProperty("mouseEnterText", item.mouseEnterText);
WriteMenuEventHandlerSetProperty("mouseExitText", item.mouseExitText);
WriteMenuEventHandlerSetProperty("action", item.action);
WriteMenuEventHandlerSetProperty("accept", item.onAccept);
// WriteFloatProperty("special", item.special, 0.0f);
WriteSoundAliasProperty("focusSound", item.focusSound);
WriteStringProperty("dvarTest", item.dvarTest);
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
if (item.dvarFlags & ITEM_DVAR_FLAG_ENABLE)
WriteMultiTokenStringProperty("enableDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_DISABLE)
WriteMultiTokenStringProperty("disableDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_SHOW)
WriteMultiTokenStringProperty("showDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_HIDE)
WriteMultiTokenStringProperty("hideDvar", item.enableDvar);
else if (item.dvarFlags & ITEM_DVAR_FLAG_FOCUS)
WriteMultiTokenStringProperty("focusDvar", item.enableDvar);
WriteItemKeyHandlerProperty(item->onKey);
WriteStatementProperty("exp text", item->textExp, false);
WriteStatementProperty("exp textaligny", item->textAlignYExp, false);
WriteStatementProperty("exp material", item->materialExp, false);
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
WriteItemKeyHandlers(item.onKey);
WriteStatementProperty("exp text", item.textExp, false);
WriteStatementProperty("exp textaligny", item.textAlignYExp, false);
WriteStatementProperty("exp material", item.materialExp, false);
WriteFloatExpressionsProperty(item.floatExpressions, item.floatExpressionCount);
WriteIntProperty("gamemsgwindowindex", item.gameMsgWindowIndex, 0);
WriteIntProperty("gamemsgwindowmode", item.gameMsgWindowMode, 0);
WriteDecodeEffectProperty("decodeEffect", item);
WriteListBoxProperties(item);
@@ -880,70 +890,75 @@ namespace
WriteTickerProperties(item);
}
void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount)
void WriteItemDefs(const itemDef_s* const* items, const size_t itemCount)
{
for (auto i = 0u; i < itemCount; i++)
if (!items || itemCount <= 0)
return;
for (size_t itemIndex = 0u; itemIndex < itemCount; itemIndex++)
{
const auto* item = items[itemIndex];
if (!item)
continue;
StartItemDefScope();
WriteItemData(itemDefs[i]);
WriteItemData(*item);
EndScope();
}
}
void WriteMenuData(const menuDef_t* menu)
void WriteMenuData(const menuDef_t& menu)
{
WriteStringProperty("name", menu->window.name);
WriteBoolProperty("fullscreen", menu->data->fullScreen, false);
WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION);
WriteRectProperty("rect", menu->window.rect);
WriteIntProperty("style", menu->window.style, 0);
WriteIntProperty("border", menu->window.border, 0);
WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f);
WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000);
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu->window.background);
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
WriteStringProperty("soundLoop", menu->data->soundName);
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
WriteFloatProperty("fadeClamp", menu->data->fadeClamp, 0.0f);
WriteIntProperty("fadeCycle", menu->data->fadeCycle, 0);
WriteFloatProperty("fadeAmount", menu->data->fadeAmount, 0.0f);
WriteFloatProperty("fadeInAmount", menu->data->fadeInAmount, 0.0f);
WriteFloatProperty("blurWorld", menu->data->blurRadius, 0.0f);
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
WriteStringProperty("allowedBinding", menu->data->allowedBinding);
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
WriteStringProperty("name", menu.window.name);
WriteBoolProperty("fullscreen", menu.data->fullScreen, false);
WriteKeywordProperty("screenSpace", menu.window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
WriteKeywordProperty("decoration", menu.window.staticFlags & WINDOW_FLAG_DECORATION);
WriteRectProperty("rect", menu.window.rect);
WriteIntProperty("style", menu.window.style, 0);
WriteIntProperty("border", menu.window.border, 0);
WriteFloatProperty("borderSize", menu.window.borderSize, 0.0f);
WriteColorProperty("backcolor", menu.window.backColor, COLOR_0000);
WriteColorProperty("forecolor", menu.window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu.window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu.data->focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu.window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu.window.background);
WriteIntProperty("ownerdraw", menu.window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu.window.ownerDrawFlags);
WriteKeywordProperty("outOfBoundsClick", menu.window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
WriteStringProperty("soundLoop", menu.data->soundName);
WriteKeywordProperty("popup", menu.window.staticFlags & WINDOW_FLAG_POPUP);
WriteFloatProperty("fadeClamp", menu.data->fadeClamp, 0.0f);
WriteIntProperty("fadeCycle", menu.data->fadeCycle, 0);
WriteFloatProperty("fadeAmount", menu.data->fadeAmount, 0.0f);
WriteFloatProperty("fadeInAmount", menu.data->fadeInAmount, 0.0f);
WriteFloatProperty("blurWorld", menu.data->blurRadius, 0.0f);
WriteKeywordProperty("legacySplitScreenScale", menu.window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
WriteKeywordProperty("hiddenDuringScope", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
WriteKeywordProperty("hiddenDuringFlashbang", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
WriteKeywordProperty("hiddenDuringUI", menu.window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
WriteStringProperty("allowedBinding", menu.data->allowedBinding);
WriteKeywordProperty("textOnlyFocus", menu.window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
if (menu->data->visibleExp)
WriteStatementProperty("visible", menu->data->visibleExp, true);
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
if (HasStatement(menu.data->visibleExp))
WriteStatementProperty("visible", menu.data->visibleExp, true);
else if (menu.window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
WriteIntProperty("visible", 1, 0);
WriteStatementProperty("exp rect X", menu->data->rectXExp, false);
WriteStatementProperty("exp rect Y", menu->data->rectYExp, false);
WriteStatementProperty("exp rect W", menu->data->rectWExp, false);
WriteStatementProperty("exp rect H", menu->data->rectHExp, false);
WriteStatementProperty("exp openSound", menu->data->openSoundExp, false);
WriteStatementProperty("exp closeSound", menu->data->closeSoundExp, false);
WriteStatementProperty("exp soundLoop", menu->data->soundLoopExp, false);
WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen);
WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose);
WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest);
WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC);
WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu->data->onFocusDueToClose);
WriteItemKeyHandlerProperty(menu->data->onKey);
WriteItemDefs(menu->items, menu->itemCount);
WriteStatementProperty("exp rect X", menu.data->rectXExp, false);
WriteStatementProperty("exp rect Y", menu.data->rectYExp, false);
WriteStatementProperty("exp rect W", menu.data->rectWExp, false);
WriteStatementProperty("exp rect H", menu.data->rectHExp, false);
WriteStatementProperty("exp openSound", menu.data->openSoundExp, false);
WriteStatementProperty("exp closeSound", menu.data->closeSoundExp, false);
WriteStatementProperty("exp soundLoop", menu.data->soundLoopExp, false);
WriteMenuEventHandlerSetProperty("onOpen", menu.data->onOpen);
WriteMenuEventHandlerSetProperty("onClose", menu.data->onClose);
WriteMenuEventHandlerSetProperty("onRequestClose", menu.data->onCloseRequest);
WriteMenuEventHandlerSetProperty("onESC", menu.data->onESC);
WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu.data->onFocusDueToClose);
WriteItemKeyHandlers(menu.data->onKey);
WriteItemDefs(menu.items, menu.itemCount);
}
};
} // namespace