From 805c00539b16041654e2ec9c78fddeea48d47a7e Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 24 Oct 2021 14:59:43 +0200 Subject: [PATCH] Move IW4 menu dumping logic to separate class and constants to common scope --- src/ObjCommon/Game/IW4/MenuConstantsIW4.h | 219 +++ src/ObjCommon/Game/IW4/ObjConstantsIW4.h | 20 + .../IW4/AssetDumpers/AssetDumperMenuDef.cpp | 1192 +---------------- .../IW4/AssetDumpers/AssetDumperMenuList.cpp | 3 +- .../Game/IW4/Menu/MenuDumperIW4.cpp | 786 +++++++++++ src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h | 53 + src/ObjWriting/Menu/AbstractMenuDumper.cpp | 250 ++++ src/ObjWriting/Menu/AbstractMenuDumper.h | 51 + src/ObjWriting/Menu/MenuDumper.cpp | 47 - src/ObjWriting/Menu/MenuDumper.h | 23 - 10 files changed, 1384 insertions(+), 1260 deletions(-) create mode 100644 src/ObjCommon/Game/IW4/MenuConstantsIW4.h create mode 100644 src/ObjCommon/Game/IW4/ObjConstantsIW4.h create mode 100644 src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp create mode 100644 src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h create mode 100644 src/ObjWriting/Menu/AbstractMenuDumper.cpp create mode 100644 src/ObjWriting/Menu/AbstractMenuDumper.h delete mode 100644 src/ObjWriting/Menu/MenuDumper.cpp delete mode 100644 src/ObjWriting/Menu/MenuDumper.h diff --git a/src/ObjCommon/Game/IW4/MenuConstantsIW4.h b/src/ObjCommon/Game/IW4/MenuConstantsIW4.h new file mode 100644 index 00000000..744fbf08 --- /dev/null +++ b/src/ObjCommon/Game/IW4/MenuConstantsIW4.h @@ -0,0 +1,219 @@ +#pragma once + +#include "Game/IW4/IW4.h" + +namespace IW4 +{ + inline const char* g_expFunctionNames[] + { + "NOOP", + ")", + "*", + "/", + "%", + "+", + "-", + "!", + "<", + "<=", + ">", + ">=", + "==", + "!=", + "&&", + "||", + "(", + ",", + "&", + "|", + "~", + "<<", + ">>", + "dvarint(static)\x01\x02", + "dvarbool(static)\x01\x03", + "dvarfloat(static)\x01\x04", + "dvarstring(static)\x01\x05", + "int", + "string", + "float", + "sin", + "cos", + "min", + "max", + "milliseconds", + "dvarint", + "dvarbool", + "dvarfloat", + "dvarstring", + "stat", + "ui_active", + "flashbanged", + "usingvehicle", + "missilecam", + "scoped", + "scopedthermal", + "scoreboard_visible", + "inkillcam", + "inkillcamnpc", + "player", + "getperk", + "selecting_location", + "selecting_direction", + "team", + "otherteam", + "marinesfield", + "opforfield", + "menuisopen", + "writingdata", + "inlobby", + "inprivateparty", + "privatepartyhost", + "privatepartyhostinlobby", + "aloneinparty", + "adsjavelin", + "weaplockblink", + "weapattacktop", + "weapattackdirect", + "weaplocking", + "weaplocked", + "weaplocktooclose", + "weaplockscreenposx", + "weaplockscreenposy", + "secondsastime", + "tablelookup", + "tablelookupbyrow", + "tablegetrownum", + "locstring", + "localvarint", + "localvarbool", + "localvarfloat", + "localvarstring", + "timeleft", + "secondsascountdown", + "gamemsgwndactive", + "gametypename", + "gametype", + "gametypedescription", + "scoreatrank", + "friendsonline", + "spectatingclient", + "spectatingfree", + "statrangeanybitsset", + "keybinding", + "actionslotusable", + "hudfade", + "maxrecommendedplayers", + "acceptinginvite", + "isintermission", + "gamehost", + "partyismissingmappack", + "partymissingmappackerror", + "anynewmappacks", + "amiselected", + "partystatusstring", + "attachedcontrollercount", + "issplitscreenonlinepossible", + "splitscreenplayercount", + "getplayerdata", + "getplayerdatasplitscreen", + "experienceforlevel", + "levelforexperience", + "isitemunlocked", + "isitemunlockedsplitscreen", + "debugprint", + "getplayerdataanybooltrue", + "weaponclassnew", + "weaponname", + "isreloading", + "savegameavailable", + "unlockeditemcount", + "unlockeditemcountsplitscreen", + "unlockeditem", + "unlockeditemsplitscreen", + "mailsubject", + "mailfrom", + "mailreceived", + "mailbody", + "maillootlocalized", + "mailgivesloot", + "anynewmail", + "mailtimetofollowup", + "mailloottype", + "mailranlottery", + "lotterylootlocalized", + "radarisjammed", + "radarjamintensity", + "radarisenabled", + "isempjammed", + "playerads", + "weaponheatactive", + "weaponheatvalue", + "weaponheatoverheated", + "getsplashtext", + "getsplashdescription", + "getsplashmaterial", + "splashhasicon", + "splashrownum", + "getfocuseditemname", + "getfocuseditemx", + "getfocuseditemy", + "getfocuseditemwidth", + "getfocuseditemheight", + "getitemx", + "getitemy", + "getitemwidth", + "getitemheight", + "playlist", + "scoreboardexternalmutenotice", + "getclientmatchdata", + "getclientmatchdatadef", + "getmapname", + "getmapimage", + "getmapcustom", + "getmigrationstatus", + "getplayercardinfo", + "isofflineprofileselected", + "coopplayer", + "iscoop", + "getpartystatus", + "getsearchparams", + "gettimeplayed", + "isselectedplayerfriend", + "getcharbyindex", + "getprofiledata", + "isprofilesignedin", + "getwaitpopupstatus", + "getnattype", + "getlocalizednattype", + "getadjustedsafeareahorizontal", + "getadjustedsafeareavertical", + "connectioninfo", + "offlineprofilecansave", + "allsplitscreenprofilescansave", + "allsplitscreenprofilesaresignedin", + "coopready", + }; + + inline const ItemExpressionTargetBinding floatExpressionTargetBindings[ITEM_FLOATEXP_TGT_COUNT] + { + {ITEM_FLOATEXP_TGT_RECT_X, "rect", "x"}, + {ITEM_FLOATEXP_TGT_RECT_Y, "rect", "y"}, + {ITEM_FLOATEXP_TGT_RECT_W, "rect", "w"}, + {ITEM_FLOATEXP_TGT_RECT_H, "rect", "h"}, + {ITEM_FLOATEXP_TGT_FORECOLOR_R, "forecolor", "r"}, + {ITEM_FLOATEXP_TGT_FORECOLOR_G, "forecolor", "g"}, + {ITEM_FLOATEXP_TGT_FORECOLOR_B, "forecolor", "b"}, + {ITEM_FLOATEXP_TGT_FORECOLOR_RGB, "forecolor", "rgb"}, + {ITEM_FLOATEXP_TGT_FORECOLOR_A, "forecolor", "a"}, + {ITEM_FLOATEXP_TGT_GLOWCOLOR_R, "glowcolor", "r"}, + {ITEM_FLOATEXP_TGT_GLOWCOLOR_G, "glowcolor", "g"}, + {ITEM_FLOATEXP_TGT_GLOWCOLOR_B, "glowcolor", "b"}, + {ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB, "glowcolor", "rgb"}, + {ITEM_FLOATEXP_TGT_GLOWCOLOR_A, "glowcolor", "a"}, + {ITEM_FLOATEXP_TGT_BACKCOLOR_R, "backcolor", "r"}, + {ITEM_FLOATEXP_TGT_BACKCOLOR_G, "backcolor", "g"}, + {ITEM_FLOATEXP_TGT_BACKCOLOR_B, "backcolor", "b"}, + {ITEM_FLOATEXP_TGT_BACKCOLOR_RGB, "backcolor", "rgb"}, + {ITEM_FLOATEXP_TGT_BACKCOLOR_A, "backcolor", "a"}, + }; +} \ No newline at end of file diff --git a/src/ObjCommon/Game/IW4/ObjConstantsIW4.h b/src/ObjCommon/Game/IW4/ObjConstantsIW4.h new file mode 100644 index 00000000..68a8fa4a --- /dev/null +++ b/src/ObjCommon/Game/IW4/ObjConstantsIW4.h @@ -0,0 +1,20 @@ +#pragma once + +namespace IW4 +{ + class ObjConstants + { + ObjConstants() = default; + + public: + static constexpr const char* INFO_STRING_PREFIX_PHYS_PRESET = "PHYSIC"; + static constexpr const char* INFO_STRING_PREFIX_TRACER = "TRACER"; + static constexpr const char* INFO_STRING_PREFIX_VEHICLE = "VEHICLEFILE"; + static constexpr const char* INFO_STRING_PREFIX_WEAPON = "WEAPONFILE"; + + static constexpr const char* GDF_FILENAME_PHYS_PRESET = "physpreset.gdf"; + static constexpr const char* GDF_FILENAME_TRACER = "tracer.gdf"; + static constexpr const char* GDF_FILENAME_VEHICLE = "vehicle.gdf"; + static constexpr const char* GDF_FILENAME_WEAPON = "weapon.gdf"; + }; +} \ No newline at end of file diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp index 2785cf19..ef901b1f 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperMenuDef.cpp @@ -1,1203 +1,17 @@ #include "AssetDumperMenuDef.h" -#include #include -#include -#include #include -#include #include "ObjWriting.h" #include "Game/IW4/GameAssetPoolIW4.h" -#include "Menu/MenuDumper.h" -#include "Parsing/Impl/ParserInputStream.h" -#include "Parsing/Simple/SimpleLexer.h" +#include "Game/IW4/Menu/MenuDumperIW4.h" +#include "Menu/AbstractMenuDumper.h" namespace fs = std::filesystem; using namespace IW4; -namespace IW4 -{ - const char* g_expFunctionNames[] - { - "NOOP", - ")", - "*", - "/", - "%", - "+", - "-", - "!", - "<", - "<=", - ">", - ">=", - "==", - "!=", - "&&", - "||", - "(", - ",", - "&", - "|", - "~", - "<<", - ">>", - "dvarint(static)\x01\x02", - "dvarbool(static)\x01\x03", - "dvarfloat(static)\x01\x04", - "dvarstring(static)\x01\x05", - "int", - "string", - "float", - "sin", - "cos", - "min", - "max", - "milliseconds", - "dvarint", - "dvarbool", - "dvarfloat", - "dvarstring", - "stat", - "ui_active", - "flashbanged", - "usingvehicle", - "missilecam", - "scoped", - "scopedthermal", - "scoreboard_visible", - "inkillcam", - "inkillcamnpc", - "player", - "getperk", - "selecting_location", - "selecting_direction", - "team", - "otherteam", - "marinesfield", - "opforfield", - "menuisopen", - "writingdata", - "inlobby", - "inprivateparty", - "privatepartyhost", - "privatepartyhostinlobby", - "aloneinparty", - "adsjavelin", - "weaplockblink", - "weapattacktop", - "weapattackdirect", - "weaplocking", - "weaplocked", - "weaplocktooclose", - "weaplockscreenposx", - "weaplockscreenposy", - "secondsastime", - "tablelookup", - "tablelookupbyrow", - "tablegetrownum", - "locstring", - "localvarint", - "localvarbool", - "localvarfloat", - "localvarstring", - "timeleft", - "secondsascountdown", - "gamemsgwndactive", - "gametypename", - "gametype", - "gametypedescription", - "scoreatrank", - "friendsonline", - "spectatingclient", - "spectatingfree", - "statrangeanybitsset", - "keybinding", - "actionslotusable", - "hudfade", - "maxrecommendedplayers", - "acceptinginvite", - "isintermission", - "gamehost", - "partyismissingmappack", - "partymissingmappackerror", - "anynewmappacks", - "amiselected", - "partystatusstring", - "attachedcontrollercount", - "issplitscreenonlinepossible", - "splitscreenplayercount", - "getplayerdata", - "getplayerdatasplitscreen", - "experienceforlevel", - "levelforexperience", - "isitemunlocked", - "isitemunlockedsplitscreen", - "debugprint", - "getplayerdataanybooltrue", - "weaponclassnew", - "weaponname", - "isreloading", - "savegameavailable", - "unlockeditemcount", - "unlockeditemcountsplitscreen", - "unlockeditem", - "unlockeditemsplitscreen", - "mailsubject", - "mailfrom", - "mailreceived", - "mailbody", - "maillootlocalized", - "mailgivesloot", - "anynewmail", - "mailtimetofollowup", - "mailloottype", - "mailranlottery", - "lotterylootlocalized", - "radarisjammed", - "radarjamintensity", - "radarisenabled", - "isempjammed", - "playerads", - "weaponheatactive", - "weaponheatvalue", - "weaponheatoverheated", - "getsplashtext", - "getsplashdescription", - "getsplashmaterial", - "splashhasicon", - "splashrownum", - "getfocuseditemname", - "getfocuseditemx", - "getfocuseditemy", - "getfocuseditemwidth", - "getfocuseditemheight", - "getitemx", - "getitemy", - "getitemwidth", - "getitemheight", - "playlist", - "scoreboardexternalmutenotice", - "getclientmatchdata", - "getclientmatchdatadef", - "getmapname", - "getmapimage", - "getmapcustom", - "getmigrationstatus", - "getplayercardinfo", - "isofflineprofileselected", - "coopplayer", - "iscoop", - "getpartystatus", - "getsearchparams", - "gettimeplayed", - "isselectedplayerfriend", - "getcharbyindex", - "getprofiledata", - "isprofilesignedin", - "getwaitpopupstatus", - "getnattype", - "getlocalizednattype", - "getadjustedsafeareahorizontal", - "getadjustedsafeareavertical", - "connectioninfo", - "offlineprofilecansave", - "allsplitscreenprofilescansave", - "allsplitscreenprofilesaresignedin", - "coopready", - }; - - const ItemExpressionTargetBinding floatExpressionTargetBindings[ITEM_FLOATEXP_TGT_COUNT] - { - {ITEM_FLOATEXP_TGT_RECT_X, "rect", "x"}, - {ITEM_FLOATEXP_TGT_RECT_Y, "rect", "y"}, - {ITEM_FLOATEXP_TGT_RECT_W, "rect", "w"}, - {ITEM_FLOATEXP_TGT_RECT_H, "rect", "h"}, - {ITEM_FLOATEXP_TGT_FORECOLOR_R, "forecolor", "r"}, - {ITEM_FLOATEXP_TGT_FORECOLOR_G, "forecolor", "g"}, - {ITEM_FLOATEXP_TGT_FORECOLOR_B, "forecolor", "b"}, - {ITEM_FLOATEXP_TGT_FORECOLOR_RGB, "forecolor", "rgb"}, - {ITEM_FLOATEXP_TGT_FORECOLOR_A, "forecolor", "a"}, - {ITEM_FLOATEXP_TGT_GLOWCOLOR_R, "glowcolor", "r"}, - {ITEM_FLOATEXP_TGT_GLOWCOLOR_G, "glowcolor", "g"}, - {ITEM_FLOATEXP_TGT_GLOWCOLOR_B, "glowcolor", "b"}, - {ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB, "glowcolor", "rgb"}, - {ITEM_FLOATEXP_TGT_GLOWCOLOR_A, "glowcolor", "a"}, - {ITEM_FLOATEXP_TGT_BACKCOLOR_R, "backcolor", "r"}, - {ITEM_FLOATEXP_TGT_BACKCOLOR_G, "backcolor", "g"}, - {ITEM_FLOATEXP_TGT_BACKCOLOR_B, "backcolor", "b"}, - {ITEM_FLOATEXP_TGT_BACKCOLOR_RGB, "backcolor", "rgb"}, - {ITEM_FLOATEXP_TGT_BACKCOLOR_A, "backcolor", "a"}, - }; -} - -class MenuDumperIw4 : public MenuDumper -{ - static constexpr auto MENU_KEY_SPACING = 28u; - static const inline std::string BOOL_VALUE_TRUE = "1"; - static const inline std::string BOOL_VALUE_FALSE = "0"; - static constexpr inline float COLOR_0000[4]{0.0f, 0.0f, 0.0f, 0.0f}; - static constexpr inline float COLOR_1111[4]{1.0f, 1.0f, 1.0f, 1.0f}; - - static const std::string& BoolValue(const bool value) - { - return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE; - } - - void WriteKey(const std::string& keyName) const - { - m_stream << keyName; - - if (keyName.size() < MENU_KEY_SPACING) - { - const auto spacingLength = MENU_KEY_SPACING - keyName.size(); - for (auto i = 0u; i < spacingLength; i++) - m_stream << " "; - } - } - - void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const - { - if (propertyValue.empty()) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\"" << propertyValue << "\"\n"; - } - - void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const - { - if (propertyValue == nullptr || propertyValue[0] == '\0') - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\"" << propertyValue << "\"\n"; - } - - void WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const - { - if (propertyValue == defaultValue) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << BoolValue(propertyValue) << "\n"; - } - - void WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const - { - if (propertyValue == defaultValue) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue << "\n"; - } - - void WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const - { - if (std::fabs(propertyValue - defaultValue) < std::numeric_limits::epsilon()) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue << "\n"; - } - - void WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const - { - if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits::epsilon() - && std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits::epsilon()) - { - return; - } - - Indent(); - WriteKey(propertyKey); - m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n"; - } - - void WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const - { - if (!shouldWrite) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\n"; - } - - void WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const - { - for (auto i = 0u; i < sizeof(flagsValue) * 8; i++) - { - if (flagsValue & (1 << i)) - { - Indent(); - WriteKey(propertyKey); - m_stream << i << "\n"; - } - } - } - - void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const - { - Indent(); - WriteKey(propertyKey); - m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " << static_cast(rect.vertAlign) << "\n"; - } - - void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const - { - if (materialValue == nullptr || materialValue->info.name == nullptr) - return; - - if (materialValue->info.name[0] == ',') - WriteStringProperty(propertyKey, &materialValue->info.name[1]); - else - WriteStringProperty(propertyKey, materialValue->info.name); - } - - void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const - { - if (soundAliasValue == nullptr) - return; - - WriteStringProperty(propertyKey, soundAliasValue->aliasName); - } - - void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const - { - if (!item->decayActive) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; - } - - static size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition) - { - assert(statement->numEntries >= 0); - assert(openingParenthesisPosition < static_cast(statement->numEntries)); - - const auto statementEnd = static_cast(statement->numEntries); - - // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. - // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. - auto currentParenthesisDepth = 1; - for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) - { - const auto& expEntry = statement->entries[currentSearchPosition]; - if (expEntry.type != EET_OPERATOR) - continue; - - // Any function means a "left out" left paren - if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) - { - currentParenthesisDepth++; - } - else if (expEntry.data.op == OP_RIGHTPAREN) - { - if (currentParenthesisDepth > 0) - currentParenthesisDepth--; - if (currentParenthesisDepth == 0) - return currentSearchPosition; - } - } - - return statementEnd; - } - - void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const - { - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext && expEntry.data.op != OP_COMMA) - m_stream << " "; - - if (expEntry.data.op == OP_LEFTPAREN) - { - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; - - currentPos = closingParenPos + 1; - spaceNext = true; - } - else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) - { - switch (expEntry.data.op) - { - case EXP_FUNC_STATIC_DVAR_INT: - m_stream << "dvarint"; - break; - - case EXP_FUNC_STATIC_DVAR_BOOL: - m_stream << "dvarbool"; - break; - - case EXP_FUNC_STATIC_DVAR_FLOAT: - m_stream << "dvarfloat"; - break; - - case EXP_FUNC_STATIC_DVAR_STRING: - m_stream << "dvarstring"; - break; - - default: - break; - } - - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - - if (closingParenPos - currentPos + 1 >= 1) - { - const auto& staticDvarEntry = statement->entries[currentPos + 1]; - if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) - { - if (statement->supportingData - && statement->supportingData->staticDvarList.staticDvars - && staticDvarEntry.data.operand.internals.intVal >= 0 - && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) - { - const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; - if (staticDvar && staticDvar->dvarName) - m_stream << staticDvar->dvarName; - } - else - { - m_stream << "#INVALID_DVAR_INDEX"; - } - } - else - { - m_stream << "#INVALID_DVAR_OPERAND"; - } - } - - m_stream << ")"; - currentPos = closingParenPos + 1; - spaceNext = true; - } - else - { - if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) - m_stream << g_expFunctionNames[expEntry.data.op]; - - if (expEntry.data.op >= OP_COUNT) - { - // Functions do not have opening parenthesis in the entries. We can just pretend they do though - const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); - m_stream << "("; - WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); - m_stream << ")"; - currentPos = closingParenPos + 1; - } - else - currentPos++; - - spaceNext = expEntry.data.op != OP_NEG; - } - } - - void WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const - { - const auto& operand = statement->entries[currentPos].data.operand; - - if (operand.internals.function == nullptr) - return; - - if(!ObjWriting::Configuration.MenuLegacyMode) - { - int functionIndex = -1; - if (statement->supportingData && statement->supportingData->uifunctions.functions) - { - for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++) - { - if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) - { - functionIndex = supportingFunctionIndex; - break; - } - } - } - - if (functionIndex >= 0) - m_stream << "FUNC_" << functionIndex; - else - m_stream << "INVALID_FUNC"; - } - else - { - m_stream << "("; - WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); - m_stream << ")"; - } - } - - void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const - { - const auto& expEntry = statement->entries[currentPos]; - - if (spaceNext) - m_stream << " "; - - const auto& operand = expEntry.data.operand; - - switch (operand.dataType) - { - case VAL_FLOAT: - m_stream << operand.internals.floatVal; - break; - - case VAL_INT: - m_stream << operand.internals.intVal; - break; - - case VAL_STRING: - m_stream << "\"" << operand.internals.stringVal.string << "\""; - break; - - case VAL_FUNCTION: - WriteStatementOperandFunction(statement, currentPos); - break; - - default: - break; - } - - currentPos++; - spaceNext = true; - } - - void WriteStatementEntryRange(const Statement_s* statement, const size_t startOffset, const size_t endOffset) const - { - assert(startOffset <= endOffset); - assert(endOffset <= static_cast(statement->numEntries)); - - auto currentPos = startOffset; - auto spaceNext = false; - while (currentPos < endOffset) - { - const auto& expEntry = statement->entries[currentPos]; - - if (expEntry.type == EET_OPERATOR) - { - WriteStatementOperator(statement, currentPos, spaceNext); - } - else - { - WriteStatementOperand(statement, currentPos, spaceNext); - } - } - } - - void WriteStatement(const Statement_s* statement) const - { - if (statement == nullptr || statement->numEntries < 0) - return; - - WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); - } - - void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const - { - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - const auto statementEnd = static_cast(statementValue->numEntries); - - if (statementValue->numEntries >= 1 - && statementValue->entries[0].type == EET_OPERATOR - && statementValue->entries[0].data.op == OP_LEFTPAREN) - { - const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); - - if (parenthesisEnd >= statementEnd) - WriteStatementEntryRange(statementValue, 1, statementEnd); - else if (parenthesisEnd == statementEnd - 1) - WriteStatementEntryRange(statementValue, 1, statementEnd - 1); - else - WriteStatementEntryRange(statementValue, 0, statementEnd); - } - else - { - WriteStatementEntryRange(statementValue, 0, statementEnd); - } - } - - void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, const bool isBooleanStatement) const - { - if (statementValue == nullptr || statementValue->numEntries < 0) - return; - - Indent(); - WriteKey(propertyKey); - - if (isBooleanStatement) - { - m_stream << "when("; - WriteStatementSkipInitialUnnecessaryParenthesis(statementValue); - m_stream << ");\n"; - } - else - { - WriteStatementSkipInitialUnnecessaryParenthesis(statementValue); - m_stream << ";\n"; - } - } - - void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const - { - if (setLocalVarData == nullptr) - return; - - Indent(); - m_stream << setFunction << " " << setLocalVarData->localVarName << " "; - WriteStatement(setLocalVarData->expression); - m_stream << ";\n"; - } - - static std::vector CreateScriptTokenList(const char* script) - { - const std::string scriptString(script); - std::istringstream stringStream(scriptString); - ParserInputStream inputStream(stringStream, "MenuScript"); - SimpleLexer lexer(&inputStream, SimpleLexer::Config{false, true, false}); - - std::vector result; - auto hasLexerTokens = true; - while (hasLexerTokens) - { - const auto& token = lexer.GetToken(0); - switch (token.m_type) - { - case SimpleParserValueType::IDENTIFIER: - result.emplace_back(token.IdentifierValue()); - break; - - case SimpleParserValueType::STRING: - result.emplace_back(token.StringValue()); - break; - - case SimpleParserValueType::CHARACTER: - result.emplace_back(1, token.CharacterValue()); - break; - - case SimpleParserValueType::INVALID: - case SimpleParserValueType::END_OF_FILE: - hasLexerTokens = false; - break; - - default: - assert(false); - break; - } - - lexer.PopTokens(1); - } - - return result; - } - - static bool DoesTokenNeedQuotationMarks(const std::string& token) - { - const auto hasAlNumCharacter = std::any_of(token.begin(), token.end(), [](const char& c) - { - return isalnum(c); - }); - - if (!hasAlNumCharacter) - return false; - - const auto hasNonIdentifierCharacter = std::any_of(token.begin(), token.end(), [](const char& c) - { - return !isalnum(c) && c != '_'; - }); - - return hasNonIdentifierCharacter; - } - -//#define WRITE_ORIGINAL_SCRIPT - - void WriteUnconditionalScript(const char* script) const - { -#ifdef WRITE_ORIGINAL_SCRIPT - Indent(); - m_stream << script << "\n"; - return; -#endif - - const auto tokenList = CreateScriptTokenList(script); - - auto isNewStatement = true; - for (const auto& token : tokenList) - { - if (isNewStatement) - { - if (token == ";") - continue; - - Indent(); - } - - if (token == ";") - { - m_stream << ";\n"; - isNewStatement = true; - continue; - } - - if (!isNewStatement) - m_stream << " "; - else - isNewStatement = false; - - if (DoesTokenNeedQuotationMarks(token)) - m_stream << "\"" << token << "\""; - else - m_stream << token; - } - - if (!isNewStatement) - m_stream << ";\n"; - } - - void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) - { - Indent(); - m_stream << "{\n"; - IncIndent(); - - for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) - { - const auto* eventHandler = eventHandlerSet->eventHandlers[i]; - if (eventHandler == nullptr) - continue; - - switch (eventHandler->eventType) - { - case EVENT_UNCONDITIONAL: - WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); - break; - - case EVENT_IF: - if (eventHandler->eventData.conditionalScript == nullptr - || eventHandler->eventData.conditionalScript->eventExpression == nullptr - || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) - { - continue; - } - - Indent(); - m_stream << "if ("; - WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); - m_stream << ")\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); - break; - - case EVENT_ELSE: - if (eventHandler->eventData.elseScript == nullptr) - continue; - - Indent(); - m_stream << "else\n"; - WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); - break; - - case EVENT_SET_LOCAL_VAR_BOOL: - WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_INT: - WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_FLOAT: - WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); - break; - - case EVENT_SET_LOCAL_VAR_STRING: - WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); - break; - - default: - break; - } - } - - DecIndent(); - Indent(); - m_stream << "}\n"; - } - - void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) - { - if (eventHandlerSetValue == nullptr) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\n"; - WriteMenuEventHandlerSet(eventHandlerSetValue); - } - - void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) - { - for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) - { - if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') - { - std::ostringstream ss; - ss << "execKey \"" << static_cast(currentHandler->key) << "\""; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - else - { - std::ostringstream ss; - ss << "execKeyInt " << currentHandler->key; - WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); - } - } - } - - void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, const int floatExpressionCount) const - { - if (!floatExpressions) - return; - - for (int i = 0; i < floatExpressionCount; i++) - { - const auto& floatExpression = floatExpressions[i]; - - if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) - continue; - - std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") - + floatExpressionTargetBindings[floatExpression.target].componentName; - - WriteStatementProperty(propertyName, floatExpression.expression, false); - } - } - - void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const - { - if (listBox->numColumns <= 0) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << listBox->numColumns << "\n"; - - for (auto col = 0; col < listBox->numColumns; col++) - { - 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"; - } - - } - - void WriteListBoxProperties(const itemDef_s* item) - { - if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) - 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); - } - - void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const - { - if (item->dvar == nullptr) - return; - - Indent(); - WriteKey(propertyKey); - m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n"; - } - - void WriteEditFieldProperties(const itemDef_s* item) const - { - switch (item->type) - { - case ITEM_TYPE_TEXT: - case ITEM_TYPE_EDITFIELD: - case ITEM_TYPE_NUMERICFIELD: - case ITEM_TYPE_SLIDER: - case ITEM_TYPE_YESNO: - case ITEM_TYPE_BIND: - case ITEM_TYPE_VALIDFILEFIELD: - case ITEM_TYPE_DECIMALFIELD: - case ITEM_TYPE_UPREDITFIELD: - case ITEM_TYPE_EMAILFIELD: - case ITEM_TYPE_PASSWORDFIELD: - break; - - default: - return; - } - - if (item->typeData.editField == nullptr) - return; - - const auto* editField = item->typeData.editField; - if(std::fabs(-1.0f - editField->defVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() - || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) - { - WriteDvarFloatProperty("dvarFloat", item, editField); - } - else - { - WriteStringProperty("dvar", item->dvar); - } - 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 - { - Indent(); - if (multiDef->strDef) - WriteKey("dvarStrList"); - else - WriteKey("dvarFloatList"); - - m_stream << "{"; - for (auto i = 0; i < multiDef->count; i++) - { - if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) - continue; - - m_stream << " \"" << multiDef->dvarList[i] << "\""; - - if (multiDef->strDef) - m_stream << " \"" << multiDef->dvarStr[i] << "\""; - else - m_stream << " " << multiDef->dvarValue[i] << ""; - } - m_stream << " }\n"; - } - - void WriteMultiProperties(const itemDef_s* item) const - { - if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) - return; - - const auto* multiDef = item->typeData.multi; - - if (multiDef->count <= 0) - return; - - WriteStringProperty("dvar", item->dvar); - WriteMultiValueProperty(multiDef); - } - - void WriteEnumDvarProperties(const itemDef_s* item) const - { - if (item->type != ITEM_TYPE_DVARENUM) - return; - - WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); - } - - void WriteTickerProperties(const itemDef_s* item) const - { - if (item->type != ITEM_TYPE_NEWS_TICKER) - return; - - 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) - { - 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.rect); - 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); - WriteStatementProperty("visible", item->visibleExp, true); - WriteStatementProperty("disabled", item->disabledExp, true); - WriteIntProperty("ownerDraw", item->window.ownerDraw, 0); - 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); - WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); - WriteStringProperty("dvarTest", item->dvarTest); - - if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) - WriteStringProperty("enableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) - WriteStringProperty("disableDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) - WriteStringProperty("showDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) - WriteStringProperty("hideDvar", item->enableDvar); - else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) - WriteStringProperty("focusDvar", item->enableDvar); - - WriteItemKeyHandlerProperty(item->onKey); - WriteStatementProperty("exp text", item->textExp, false); - WriteStatementProperty("exp material", item->materialExp, false); - WriteStatementProperty("exp disabled", item->disabledExp, false); - WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); - WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); - WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); - WriteDecodeEffectProperty("decodeEffect", item); - - WriteListBoxProperties(item); - WriteEditFieldProperties(item); - WriteMultiProperties(item); - WriteEnumDvarProperties(item); - WriteTickerProperties(item); - } - - void WriteItemDefs(const itemDef_s* const* itemDefs, const size_t itemCount) - { - for (auto i = 0u; i < itemCount; i++) - { - Indent(); - m_stream << "itemDef\n"; - Indent(); - m_stream << "{\n"; - IncIndent(); - - WriteItemData(itemDefs[i]); - - DecIndent(); - Indent(); - m_stream << "}\n"; - } - } - - 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); - 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); - WriteStatementProperty("visible", menu->visibleExp, true); - 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); - } - -public: - explicit MenuDumperIw4(std::ostream& stream) - : MenuDumper(stream) - { - } - - void WriteMenu(const menuDef_t* menu) - { - Indent(); - m_stream << "menuDef\n"; - Indent(); - m_stream << "{\n"; - IncIndent(); - - WriteMenuData(menu); - - DecIndent(); - Indent(); - m_stream << "}\n"; - } -}; - const MenuList* AssetDumperMenuDef::GetParentMenuList(XAssetInfo* asset) { const auto* menu = asset->Asset(); @@ -1244,7 +58,7 @@ void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo #include -#include "Menu/MenuDumper.h" +#include "Game/IW4/Menu/MenuDumperIW4.h" +#include "Menu/AbstractMenuDumper.h" namespace fs = std::filesystem; diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp new file mode 100644 index 00000000..c97b4606 --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp @@ -0,0 +1,786 @@ +#include "MenuDumperIW4.h" + +#include +#include +#include + +#include "ObjWriting.h" +#include "Game/IW4/MenuConstantsIW4.h" + +using namespace IW4; + +size_t MenuDumper::FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition) +{ + assert(statement->numEntries >= 0); + assert(openingParenthesisPosition < static_cast(statement->numEntries)); + + const auto statementEnd = static_cast(statement->numEntries); + + // The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though. + // We will pretend it does since the game does sometimes leave out opening parenthesis from the entries. + auto currentParenthesisDepth = 1; + for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++) + { + const auto& expEntry = statement->entries[currentSearchPosition]; + if (expEntry.type != EET_OPERATOR) + continue; + + // Any function means a "left out" left paren + if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT) + { + currentParenthesisDepth++; + } + else if (expEntry.data.op == OP_RIGHTPAREN) + { + if (currentParenthesisDepth > 0) + currentParenthesisDepth--; + if (currentParenthesisDepth == 0) + return currentSearchPosition; + } + } + + return statementEnd; +} + +void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const +{ + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext && expEntry.data.op != OP_COMMA) + m_stream << " "; + + if (expEntry.data.op == OP_LEFTPAREN) + { + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + + currentPos = closingParenPos + 1; + spaceNext = true; + } + else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING) + { + switch (expEntry.data.op) + { + case EXP_FUNC_STATIC_DVAR_INT: + m_stream << "dvarint"; + break; + + case EXP_FUNC_STATIC_DVAR_BOOL: + m_stream << "dvarbool"; + break; + + case EXP_FUNC_STATIC_DVAR_FLOAT: + m_stream << "dvarfloat"; + break; + + case EXP_FUNC_STATIC_DVAR_STRING: + m_stream << "dvarstring"; + break; + + default: + break; + } + + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + + if (closingParenPos - currentPos + 1 >= 1) + { + const auto& staticDvarEntry = statement->entries[currentPos + 1]; + if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT) + { + if (statement->supportingData + && statement->supportingData->staticDvarList.staticDvars + && staticDvarEntry.data.operand.internals.intVal >= 0 + && staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars) + { + const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal]; + if (staticDvar && staticDvar->dvarName) + m_stream << staticDvar->dvarName; + } + else + { + m_stream << "#INVALID_DVAR_INDEX"; + } + } + else + { + m_stream << "#INVALID_DVAR_OPERAND"; + } + } + + m_stream << ")"; + currentPos = closingParenPos + 1; + spaceNext = true; + } + else + { + if (expEntry.data.op >= 0 && static_cast(expEntry.data.op) < std::extent_v) + m_stream << g_expFunctionNames[expEntry.data.op]; + + if (expEntry.data.op >= OP_COUNT) + { + // Functions do not have opening parenthesis in the entries. We can just pretend they do though + const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos); + m_stream << "("; + WriteStatementEntryRange(statement, currentPos + 1, closingParenPos); + m_stream << ")"; + currentPos = closingParenPos + 1; + } + else + currentPos++; + + spaceNext = expEntry.data.op != OP_NEG; + } +} + +void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const +{ + const auto& operand = statement->entries[currentPos].data.operand; + + if (operand.internals.function == nullptr) + return; + + if (!ObjWriting::Configuration.MenuLegacyMode) + { + int functionIndex = -1; + if (statement->supportingData && statement->supportingData->uifunctions.functions) + { + for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++) + { + if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function) + { + functionIndex = supportingFunctionIndex; + break; + } + } + } + + if (functionIndex >= 0) + m_stream << "FUNC_" << functionIndex; + else + m_stream << "INVALID_FUNC"; + } + else + { + m_stream << "("; + WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function); + m_stream << ")"; + } +} + +void MenuDumper::WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const +{ + const auto& expEntry = statement->entries[currentPos]; + + if (spaceNext) + m_stream << " "; + + const auto& operand = expEntry.data.operand; + + switch (operand.dataType) + { + case VAL_FLOAT: + m_stream << operand.internals.floatVal; + break; + + case VAL_INT: + m_stream << operand.internals.intVal; + break; + + case VAL_STRING: + m_stream << "\"" << operand.internals.stringVal.string << "\""; + break; + + case VAL_FUNCTION: + WriteStatementOperandFunction(statement, currentPos); + break; + + default: + break; + } + + currentPos++; + spaceNext = true; +} + +void MenuDumper::WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const +{ + assert(startOffset <= endOffset); + assert(endOffset <= static_cast(statement->numEntries)); + + auto currentPos = startOffset; + auto spaceNext = false; + while (currentPos < endOffset) + { + const auto& expEntry = statement->entries[currentPos]; + + if (expEntry.type == EET_OPERATOR) + { + WriteStatementOperator(statement, currentPos, spaceNext); + } + else + { + WriteStatementOperand(statement, currentPos, spaceNext); + } + } +} + +void MenuDumper::WriteStatement(const Statement_s* statement) const +{ + if (statement == nullptr || statement->numEntries < 0) + return; + + WriteStatementEntryRange(statement, 0, static_cast(statement->numEntries)); +} + +void MenuDumper::WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const +{ + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + const auto statementEnd = static_cast(statementValue->numEntries); + + if (statementValue->numEntries >= 1 + && statementValue->entries[0].type == EET_OPERATOR + && statementValue->entries[0].data.op == OP_LEFTPAREN) + { + const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0); + + if (parenthesisEnd >= statementEnd) + WriteStatementEntryRange(statementValue, 1, statementEnd); + else if (parenthesisEnd == statementEnd - 1) + WriteStatementEntryRange(statementValue, 1, statementEnd - 1); + else + WriteStatementEntryRange(statementValue, 0, statementEnd); + } + else + { + WriteStatementEntryRange(statementValue, 0, statementEnd); + } +} + +void MenuDumper::WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const +{ + if (statementValue == nullptr || statementValue->numEntries < 0) + return; + + Indent(); + WriteKey(propertyKey); + + if (isBooleanStatement) + { + m_stream << "when("; + WriteStatementSkipInitialUnnecessaryParenthesis(statementValue); + m_stream << ");\n"; + } + else + { + WriteStatementSkipInitialUnnecessaryParenthesis(statementValue); + m_stream << ";\n"; + } +} + +void MenuDumper::WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const +{ + if (setLocalVarData == nullptr) + return; + + Indent(); + m_stream << setFunction << " " << setLocalVarData->localVarName << " "; + WriteStatement(setLocalVarData->expression); + m_stream << ";\n"; +} + +//#define WRITE_ORIGINAL_SCRIPT + +void MenuDumper::WriteUnconditionalScript(const char* script) const +{ +#ifdef WRITE_ORIGINAL_SCRIPT + Indent(); + m_stream << script << "\n"; + return; +#endif + + const auto tokenList = CreateScriptTokenList(script); + + auto isNewStatement = true; + for (const auto& token : tokenList) + { + if (isNewStatement) + { + if (token == ";") + continue; + + Indent(); + } + + if (token == ";") + { + m_stream << ";\n"; + isNewStatement = true; + continue; + } + + if (!isNewStatement) + m_stream << " "; + else + isNewStatement = false; + + if (DoesTokenNeedQuotationMarks(token)) + m_stream << "\"" << token << "\""; + else + m_stream << token; + } + + if (!isNewStatement) + m_stream << ";\n"; +} + +void MenuDumper::WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet) +{ + Indent(); + m_stream << "{\n"; + IncIndent(); + + for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++) + { + const auto* eventHandler = eventHandlerSet->eventHandlers[i]; + if (eventHandler == nullptr) + continue; + + switch (eventHandler->eventType) + { + case EVENT_UNCONDITIONAL: + WriteUnconditionalScript(eventHandler->eventData.unconditionalScript); + break; + + case EVENT_IF: + if (eventHandler->eventData.conditionalScript == nullptr + || eventHandler->eventData.conditionalScript->eventExpression == nullptr + || eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr) + { + continue; + } + + Indent(); + m_stream << "if ("; + WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression); + m_stream << ")\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet); + break; + + case EVENT_ELSE: + if (eventHandler->eventData.elseScript == nullptr) + continue; + + Indent(); + m_stream << "else\n"; + WriteMenuEventHandlerSet(eventHandler->eventData.elseScript); + break; + + case EVENT_SET_LOCAL_VAR_BOOL: + WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_INT: + WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_FLOAT: + WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData); + break; + + case EVENT_SET_LOCAL_VAR_STRING: + WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData); + break; + + default: + break; + } + } + + DecIndent(); + Indent(); + m_stream << "}\n"; +} + +void MenuDumper::WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue) +{ + if (eventHandlerSetValue == nullptr) + return; + + Indent(); + m_stream << propertyKey << "\n"; + WriteMenuEventHandlerSet(eventHandlerSetValue); +} + +void MenuDumper::WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const +{ + Indent(); + WriteKey(propertyKey); + m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast(rect.horzAlign) << " " << static_cast(rect.vertAlign) << "\n"; +} + +void MenuDumper::WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const +{ + if (materialValue == nullptr || materialValue->info.name == nullptr) + return; + + if (materialValue->info.name[0] == ',') + WriteStringProperty(propertyKey, &materialValue->info.name[1]); + else + WriteStringProperty(propertyKey, materialValue->info.name); +} + +void MenuDumper::WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const +{ + if (soundAliasValue == nullptr) + return; + + WriteStringProperty(propertyKey, soundAliasValue->aliasName); +} + +void MenuDumper::WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const +{ + if (!item->decayActive) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n"; +} + +void MenuDumper::WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue) +{ + for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next) + { + if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"') + { + std::ostringstream ss; + ss << "execKey \"" << static_cast(currentHandler->key) << "\""; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + else + { + std::ostringstream ss; + ss << "execKeyInt " << currentHandler->key; + WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action); + } + } +} + +void MenuDumper::WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const +{ + if (!floatExpressions) + return; + + for (int i = 0; i < floatExpressionCount; i++) + { + const auto& floatExpression = floatExpressions[i]; + + if (floatExpression.target < 0 || floatExpression.target >= ITEM_FLOATEXP_TGT_COUNT) + continue; + + std::string propertyName = std::string("exp ") + floatExpressionTargetBindings[floatExpression.target].name + std::string(" ") + + floatExpressionTargetBindings[floatExpression.target].componentName; + + WriteStatementProperty(propertyName, floatExpression.expression, false); + } +} + +void MenuDumper::WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const +{ + if (listBox->numColumns <= 0) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << listBox->numColumns << "\n"; + + for (auto col = 0; col < listBox->numColumns; col++) + { + 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"; + } +} + +void MenuDumper::WriteListBoxProperties(const itemDef_s* item) +{ + if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr) + 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); +} + +void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const +{ + if (item->dvar == nullptr) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n"; +} + +void MenuDumper::WriteEditFieldProperties(const itemDef_s* item) const +{ + switch (item->type) + { + case ITEM_TYPE_TEXT: + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: + case ITEM_TYPE_SLIDER: + case ITEM_TYPE_YESNO: + case ITEM_TYPE_BIND: + case ITEM_TYPE_VALIDFILEFIELD: + case ITEM_TYPE_DECIMALFIELD: + case ITEM_TYPE_UPREDITFIELD: + case ITEM_TYPE_EMAILFIELD: + case ITEM_TYPE_PASSWORDFIELD: + break; + + default: + return; + } + + if (item->typeData.editField == nullptr) + return; + + const auto* editField = item->typeData.editField; + if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->minVal) >= std::numeric_limits::epsilon() + || std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits::epsilon()) + { + WriteDvarFloatProperty("dvarFloat", item, editField); + } + else + { + WriteStringProperty("dvar", item->dvar); + } + WriteStringProperty("localvar", item->localVar); + WriteIntProperty("maxChars", editField->maxChars, 0); + WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0); + WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0); +} + +void MenuDumper::WriteMultiValueProperty(const multiDef_s* multiDef) const +{ + Indent(); + if (multiDef->strDef) + WriteKey("dvarStrList"); + else + WriteKey("dvarFloatList"); + + m_stream << "{"; + for (auto i = 0; i < multiDef->count; i++) + { + if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr) + continue; + + m_stream << " \"" << multiDef->dvarList[i] << "\""; + + if (multiDef->strDef) + m_stream << " \"" << multiDef->dvarStr[i] << "\""; + else + m_stream << " " << multiDef->dvarValue[i] << ""; + } + m_stream << " }\n"; +} + +void MenuDumper::WriteMultiProperties(const itemDef_s* item) const +{ + if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr) + return; + + const auto* multiDef = item->typeData.multi; + + if (multiDef->count <= 0) + return; + + WriteStringProperty("dvar", item->dvar); + WriteMultiValueProperty(multiDef); +} + +void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const +{ + if (item->type != ITEM_TYPE_DVARENUM) + return; + + WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); +} + +void MenuDumper::WriteTickerProperties(const itemDef_s* item) const +{ + if (item->type != ITEM_TYPE_NEWS_TICKER) + return; + + const auto* newsTickerDef = item->typeData.ticker; + WriteIntProperty("spacing", newsTickerDef->spacing, 0); + WriteIntProperty("speed", newsTickerDef->speed, 0); + WriteIntProperty("newsfeed", newsTickerDef->feedId, 0); +} + +void MenuDumper::WriteItemData(const itemDef_s* item) +{ + 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.rect); + 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); + WriteStatementProperty("visible", item->visibleExp, true); + WriteStatementProperty("disabled", item->disabledExp, true); + WriteIntProperty("ownerDraw", item->window.ownerDraw, 0); + 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); + WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags); + WriteStringProperty("dvarTest", item->dvarTest); + + if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) + WriteStringProperty("enableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) + WriteStringProperty("disableDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) + WriteStringProperty("showDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) + WriteStringProperty("hideDvar", item->enableDvar); + else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) + WriteStringProperty("focusDvar", item->enableDvar); + + WriteItemKeyHandlerProperty(item->onKey); + WriteStatementProperty("exp text", item->textExp, false); + WriteStatementProperty("exp material", item->materialExp, false); + WriteStatementProperty("exp disabled", item->disabledExp, false); + WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount); + WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0); + WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0); + WriteDecodeEffectProperty("decodeEffect", item); + + WriteListBoxProperties(item); + WriteEditFieldProperties(item); + WriteMultiProperties(item); + WriteEnumDvarProperties(item); + WriteTickerProperties(item); +} + +void MenuDumper::WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount) +{ + for (auto i = 0u; i < itemCount; i++) + { + StartItemDefScope(); + + WriteItemData(itemDefs[i]); + + EndScope(); + } +} + +void MenuDumper::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); + 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); + WriteStatementProperty("visible", menu->visibleExp, true); + 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); +} + +MenuDumper::MenuDumper(std::ostream& stream) + : AbstractMenuDumper(stream) +{ +} + +void MenuDumper::WriteMenu(const menuDef_t* menu) +{ + StartMenuDefScope(); + + WriteMenuData(menu); + + EndScope(); +} diff --git a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h new file mode 100644 index 00000000..ff0ac99d --- /dev/null +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include "Menu/AbstractMenuDumper.h" +#include "Game/IW4/IW4.h" + +namespace IW4 +{ + class MenuDumper : public AbstractMenuDumper + { + static size_t FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition); + + void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; + void WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const; + void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const; + void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const; + void WriteStatement(const Statement_s* statement) const; + void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const; + void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const; + + void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const; + void WriteUnconditionalScript(const char* script) const; + void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet); + void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue); + + void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const; + void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const; + void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const; + void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const; + void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue); + void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const; + void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const; + + void WriteListBoxProperties(const itemDef_s* item); + void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const; + void WriteEditFieldProperties(const itemDef_s* item) const; + void WriteMultiValueProperty(const multiDef_s* multiDef) const; + void WriteMultiProperties(const itemDef_s* item) const; + void WriteEnumDvarProperties(const itemDef_s* item) const; + void WriteTickerProperties(const itemDef_s* item) const; + + void WriteItemData(const itemDef_s* item); + void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount); + + void WriteMenuData(const menuDef_t* menu); + + public: + explicit MenuDumper(std::ostream& stream); + + void WriteMenu(const menuDef_t* menu); + }; +} diff --git a/src/ObjWriting/Menu/AbstractMenuDumper.cpp b/src/ObjWriting/Menu/AbstractMenuDumper.cpp new file mode 100644 index 00000000..14a2764f --- /dev/null +++ b/src/ObjWriting/Menu/AbstractMenuDumper.cpp @@ -0,0 +1,250 @@ +#include "AbstractMenuDumper.h" + +#include +#include +#include + +#include "Parsing/Impl/ParserInputStream.h" +#include "Parsing/Simple/SimpleLexer.h" + +AbstractMenuDumper::AbstractMenuDumper(std::ostream& stream) + : m_stream(stream), + m_indent(0u) +{ +} + +void AbstractMenuDumper::IncIndent() +{ + m_indent++; +} + +void AbstractMenuDumper::DecIndent() +{ + if (m_indent > 0) + m_indent--; +} + +void AbstractMenuDumper::Indent() const +{ + for (auto i = 0u; i < m_indent; i++) + m_stream << " "; +} + +void AbstractMenuDumper::StartScope(const std::string& scopeName) +{ + Indent(); + m_stream << scopeName << "\n"; + Indent(); + m_stream << "{\n"; + IncIndent(); +} + +void AbstractMenuDumper::StartMenuDefScope() +{ + StartScope("menuDef"); +} + +void AbstractMenuDumper::StartItemDefScope() +{ + StartScope("itemDef"); +} + +void AbstractMenuDumper::StartFunctionDefScope() +{ + StartScope("functionDef"); +} + +void AbstractMenuDumper::EndScope() +{ + DecIndent(); + Indent(); + m_stream << "}\n"; +} + +std::vector AbstractMenuDumper::CreateScriptTokenList(const char* script) +{ + const std::string scriptString(script); + std::istringstream stringStream(scriptString); + ParserInputStream inputStream(stringStream, "MenuScript"); + SimpleLexer lexer(&inputStream, SimpleLexer::Config{false, true, false}); + + std::vector result; + auto hasLexerTokens = true; + while (hasLexerTokens) + { + const auto& token = lexer.GetToken(0); + switch (token.m_type) + { + case SimpleParserValueType::IDENTIFIER: + result.emplace_back(token.IdentifierValue()); + break; + + case SimpleParserValueType::STRING: + result.emplace_back(token.StringValue()); + break; + + case SimpleParserValueType::CHARACTER: + result.emplace_back(1, token.CharacterValue()); + break; + + case SimpleParserValueType::INVALID: + case SimpleParserValueType::END_OF_FILE: + hasLexerTokens = false; + break; + + default: + assert(false); + break; + } + + lexer.PopTokens(1); + } + + return result; +} + +bool AbstractMenuDumper::DoesTokenNeedQuotationMarks(const std::string& token) +{ + const auto hasAlNumCharacter = std::any_of(token.begin(), token.end(), [](const char& c) + { + return isalnum(c); + }); + + if (!hasAlNumCharacter) + return false; + + const auto hasNonIdentifierCharacter = std::any_of(token.begin(), token.end(), [](const char& c) + { + return !isalnum(c) && c != '_'; + }); + + return hasNonIdentifierCharacter; +} + +const std::string& AbstractMenuDumper::BoolValue(const bool value) +{ + return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE; +} + +void AbstractMenuDumper::WriteKey(const std::string& keyName) const +{ + m_stream << keyName; + + if (keyName.size() < MENU_KEY_SPACING) + { + const auto spacingLength = MENU_KEY_SPACING - keyName.size(); + for (auto i = 0u; i < spacingLength; i++) + m_stream << " "; + } +} + +void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const +{ + if (propertyValue.empty()) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\"" << propertyValue << "\"\n"; +} + +void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const +{ + if (propertyValue == nullptr || propertyValue[0] == '\0') + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\"" << propertyValue << "\"\n"; +} + +void AbstractMenuDumper::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const +{ + if (propertyValue == defaultValue) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << BoolValue(propertyValue) << "\n"; +} + +void AbstractMenuDumper::WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const +{ + if (propertyValue == defaultValue) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue << "\n"; +} + +void AbstractMenuDumper::WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const +{ + if (std::fabs(propertyValue - defaultValue) < std::numeric_limits::epsilon()) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue << "\n"; +} + +void AbstractMenuDumper::WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const +{ + if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits::epsilon() + && std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits::epsilon()) + { + return; + } + + Indent(); + WriteKey(propertyKey); + m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n"; +} + +void AbstractMenuDumper::WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const +{ + if (!shouldWrite) + return; + + Indent(); + WriteKey(propertyKey); + m_stream << "\n"; +} + +void AbstractMenuDumper::WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const +{ + for (auto i = 0u; i < sizeof(flagsValue) * 8; i++) + { + if (flagsValue & (1 << i)) + { + Indent(); + WriteKey(propertyKey); + m_stream << i << "\n"; + } + } +} + +void AbstractMenuDumper::Start() +{ + Indent(); + m_stream << "{\n"; + IncIndent(); +} + +void AbstractMenuDumper::End() +{ + for (auto i = 0u; i < m_indent; i++) + { + DecIndent(); + Indent(); + m_stream << "}\n"; + } +} + +void AbstractMenuDumper::IncludeMenu(const std::string& menuPath) const +{ + Indent(); + m_stream << "loadMenu { \"" << menuPath << "\" }\n"; +} diff --git a/src/ObjWriting/Menu/AbstractMenuDumper.h b/src/ObjWriting/Menu/AbstractMenuDumper.h new file mode 100644 index 00000000..669586e6 --- /dev/null +++ b/src/ObjWriting/Menu/AbstractMenuDumper.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +class AbstractMenuDumper +{ +protected: + static constexpr auto MENU_KEY_SPACING = 28u; + static const inline std::string BOOL_VALUE_TRUE = "1"; + static const inline std::string BOOL_VALUE_FALSE = "0"; + static constexpr inline float COLOR_0000[4]{ 0.0f, 0.0f, 0.0f, 0.0f }; + static constexpr inline float COLOR_1111[4]{ 1.0f, 1.0f, 1.0f, 1.0f }; + + std::ostream& m_stream; + size_t m_indent; + + void IncIndent(); + void DecIndent(); + void Indent() const; + + void StartScope(const std::string& scopeName); + void StartMenuDefScope(); + void StartItemDefScope(); + void StartFunctionDefScope(); + void EndScope(); + + static std::vector CreateScriptTokenList(const char* script); + static bool DoesTokenNeedQuotationMarks(const std::string& token); + + static const std::string& BoolValue(bool value); + void WriteKey(const std::string& keyName) const; + void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const; + void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const; + void WriteBoolProperty(const std::string& propertyKey, bool propertyValue, bool defaultValue) const; + void WriteIntProperty(const std::string& propertyKey, int propertyValue, int defaultValue) const; + void WriteFloatProperty(const std::string& propertyKey, float propertyValue, float defaultValue) const; + void WriteColorProperty(const std::string& propertyKey, const float(&propertyValue)[4], const float(&defaultValue)[4]) const; + void WriteKeywordProperty(const std::string& propertyKey, bool shouldWrite) const; + void WriteFlagsProperty(const std::string& propertyKey, int flagsValue) const; + + explicit AbstractMenuDumper(std::ostream& stream); + +public: + void Start(); + void End(); + + void IncludeMenu(const std::string& menuPath) const; +}; \ No newline at end of file diff --git a/src/ObjWriting/Menu/MenuDumper.cpp b/src/ObjWriting/Menu/MenuDumper.cpp deleted file mode 100644 index a1936394..00000000 --- a/src/ObjWriting/Menu/MenuDumper.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "MenuDumper.h" - -MenuDumper::MenuDumper(std::ostream& stream) - : m_stream(stream), - m_indent(0u) -{ -} - -void MenuDumper::IncIndent() -{ - m_indent++; -} - -void MenuDumper::DecIndent() -{ - if (m_indent > 0) - m_indent--; -} - -void MenuDumper::Indent() const -{ - for (auto i = 0u; i < m_indent; i++) - m_stream << " "; -} - -void MenuDumper::Start() -{ - Indent(); - m_stream << "{\n"; - IncIndent(); -} - -void MenuDumper::End() -{ - for (auto i = 0u; i < m_indent; i++) - { - DecIndent(); - Indent(); - m_stream << "}\n"; - } -} - -void MenuDumper::IncludeMenu(const std::string& menuPath) const -{ - Indent(); - m_stream << "loadMenu { \"" << menuPath << "\" }\n"; -} diff --git a/src/ObjWriting/Menu/MenuDumper.h b/src/ObjWriting/Menu/MenuDumper.h deleted file mode 100644 index c471bb03..00000000 --- a/src/ObjWriting/Menu/MenuDumper.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -class MenuDumper -{ -protected: - std::ostream& m_stream; - size_t m_indent; - - void IncIndent(); - void DecIndent(); - void Indent() const; - -public: - explicit MenuDumper(std::ostream& stream); - - void Start(); - void End(); - - void IncludeMenu(const std::string& menuPath) const; -}; \ No newline at end of file