From 568095f57e11c0b89e22bbc328c61f4d45f9ecca Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 24 Oct 2021 22:00:31 +0200 Subject: [PATCH] IW5 menu dumping initial commit --- src/Common/Game/IW4/IW4_Assets.h | 38 +- src/Common/Game/IW5/IW5_Assets.h | 387 ++------- src/ObjCommon/Game/IW5/MenuConstantsIW5.h | 386 +++++++++ src/ObjCommon/Game/IW5/ObjConstantsIW5.h | 20 + .../Game/IW4/Menu/MenuDumperIW4.cpp | 2 +- .../IW5/AssetDumpers/AssetDumperMenuDef.cpp | 76 ++ .../IW5/AssetDumpers/AssetDumperMenuDef.h | 17 + .../IW5/AssetDumpers/AssetDumperMenuList.cpp | 120 +++ .../IW5/AssetDumpers/AssetDumperMenuList.h | 20 + .../Game/IW5/Menu/MenuDumperIW5.cpp | 798 ++++++++++++++++++ src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h | 54 ++ src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp | 6 +- 12 files changed, 1592 insertions(+), 332 deletions(-) create mode 100644 src/ObjCommon/Game/IW5/MenuConstantsIW5.h create mode 100644 src/ObjCommon/Game/IW5/ObjConstantsIW5.h create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp create mode 100644 src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h create mode 100644 src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp create mode 100644 src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 9d94e496..b9be7141 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -1120,27 +1120,27 @@ namespace IW4 { OP_NOOP = 0x0, OP_RIGHTPAREN = 0x1, - OP_MUL = 0x2, - OP_DIV = 0x3, - OP_MOD = 0x4, - OP_PLUS = 0x5, - OP_MINUS = 0x6, - OP_NEG = 0x7, - OP_SMALLER = 0x8, - OP_SMALLEREQ = 0x9, - OP_GREATER = 0xA, - OP_GREATEREQ = 0xB, - OP_EQ = 0xC, - OP_NOTEQ = 0xD, - OP_LOGAND = 0xE, - OP_LOGOR = 0xF, + OP_MULTIPLY = 0x2, + OP_DIVIDE = 0x3, + OP_MODULUS = 0x4, + OP_ADD = 0x5, + OP_SUBTRACT = 0x6, + OP_NOT = 0x7, + OP_LESSTHAN = 0x8, + OP_LESSTHANEQUALTO = 0x9, + OP_GREATERTHAN = 0xA, + OP_GREATERTHANEQUALTO = 0xB, + OP_EQUALS = 0xC, + OP_NOTEQUAL = 0xD, + OP_AND = 0xE, + OP_OR = 0xF, OP_LEFTPAREN = 0x10, OP_COMMA = 0x11, - OP_BITAND = 0x12, - OP_BITOR = 0x13, - OP_BITNEG = 0x14, - OP_SHIFTLEFT = 0x15, - OP_SHIFTRIGHT = 0x16, + OP_BITWISEAND = 0x12, + OP_BITWISEOR = 0x13, + OP_BITWISENOT = 0x14, + OP_BITSHIFTLEFT = 0x15, + OP_BITSHIFTRIGHT = 0x16, OP_COUNT }; diff --git a/src/Common/Game/IW5/IW5_Assets.h b/src/Common/Game/IW5/IW5_Assets.h index 29bb554e..d6876e47 100644 --- a/src/Common/Game/IW5/IW5_Assets.h +++ b/src/Common/Game/IW5/IW5_Assets.h @@ -2258,7 +2258,7 @@ namespace IW5 VAL_STRING = 0x2, VAL_FUNCTION = 0x3, - NUM_DATATYPES, + NUM_DATATYPES }; struct ExpressionString @@ -2280,7 +2280,7 @@ namespace IW5 operandInternalDataUnion internals; }; - enum operationEnum + enum expressionOperatorType_e { OP_NOOP = 0x0, OP_RIGHTPAREN = 0x1, @@ -2305,314 +2305,16 @@ namespace IW5 OP_BITWISENOT = 0x14, OP_BITSHIFTLEFT = 0x15, OP_BITSHIFTRIGHT = 0x16, - OP_STATICDVARINT = 0x17, - OP_FIRSTFUNCTIONCALL = 0x17, - OP_STATICDVARBOOL = 0x18, - OP_STATICDVARFLOAT = 0x19, - OP_STATICDVARSTRING = 0x1A, - OP_TOINT = 0x1B, - OP_TOSTRING = 0x1C, - OP_TOFLOAT = 0x1D, - LAST_COMMONLY_CALLED_FUNCTION = 0x1D, - OP_SIN = 0x1E, - OP_COS = 0x1F, - OP_MIN = 0x20, - OP_MAX = 0x21, - OP_MILLISECONDS = 0x22, - OP_LOCALCLIENTUIMILLISECONDS = 0x23, - OP_DVARINT = 0x24, - OP_DVARBOOL = 0x25, - OP_DVARFLOAT = 0x26, - OP_DVARSTRING = 0x27, - OP_UIACTIVE = 0x28, - OP_FLASHBANGED = 0x29, - OP_USINGVEHICLE = 0x2A, - OP_MISSILECAM = 0x2B, - OP_SCOPED = 0x2C, - OP_SCOPEDTHERMAL = 0x2D, - OP_SCOREBOARDVISIBLE = 0x2E, - OP_INKILLCAM = 0x2F, - OP_INKILLCAM_NPC = 0x30, - OP_PLAYERFIELD = 0x31, - OP_GET_PLAYER_PERK = 0x32, - OP_SELECTINGLOCATION = 0x33, - OP_SELECTINGDIRECTION = 0x34, - OP_TEAMFIELD = 0x35, - OP_OTHERTEAMFIELD = 0x36, - OP_MARINESFIELD = 0x37, - OP_OPFORFIELD = 0x38, - OP_MENUISOPEN = 0x39, - OP_WRITINGDATA = 0x3A, - OP_INLOBBY = 0x3B, - OP_INGAMELOBBY = 0x3C, - OP_INPRIVATEPARTY = 0x3D, - OP_PRIVATEPARTYHOST = 0x3E, - OP_PRIVATEPARTYHOSTINLOBBY = 0x3F, - OP_ALONEINPARTY = 0x40, - OP_ADSJAVELIN = 0x41, - OP_WEAPLOCKBLINK = 0x42, - OP_WEAPATTACKTOP = 0x43, - OP_WEAPATTACKDIRECT = 0x44, - OP_WEAPLOCKING = 0x45, - OP_WEAPLOCKED = 0x46, - OP_WEAPLOCKTOOCLOSE = 0x47, - OP_WEAPLOCKSCREENPOSX = 0x48, - OP_WEAPLOCKSCREENPOSY = 0x49, - OP_SECONDSASTIME = 0x4A, - OP_TABLELOOKUP = 0x4B, - OP_TABLELOOKUPBYROW = 0x4C, - OP_TABLEGETROWNUM = 0x4D, - OP_LOCALIZESTRING = 0x4E, - OP_LOCALVARINT = 0x4F, - OP_LOCALVARBOOL = 0x50, - OP_LOCALVARFLOAT = 0x51, - OP_LOCALVARSTRING = 0x52, - OP_TIMELEFT = 0x53, - OP_SECONDSASCOUNTDOWN = 0x54, - OP_GAMEMSGWNDACTIVE = 0x55, - OP_GAMETYPENAME = 0x56, - OP_GAMETYPE = 0x57, - OP_GAMETYPEDESCRIPTION = 0x58, - OP_SCORE = 0x59, - OP_FOLLOWING = 0x5A, - OP_SPECTATINGFREE = 0x5B, - OP_KEYBINDING = 0x5C, - OP_ACTIONSLOTUSABLE = 0x5D, - OP_HUDFADE = 0x5E, - OP_MAXPLAYERS = 0x5F, - OP_ACCEPTINGINVITE = 0x60, - OP_ISINTERMISSION = 0x61, - OP_GAMEHOST = 0x62, - OP_PARTYHASMISSINGMAPPACK = 0x63, - OP_PARTYMISSINGMAPPACKERROR = 0x64, - OP_ANYNEWMAPPACKS = 0x65, - OP_AMISELECTED = 0x66, - OP_PARTYSTATUSSTRING = 0x67, - OP_ATTACHED_CONTROLLER_COUNT = 0x68, - OP_IS_SPLIT_SCREEN_ONLINE_POSSIBLE = 0x69, - OP_SPLITSCREENPLAYERCOUNT = 0x6A, - OP_GETPLAYERDATA = 0x6B, - OP_GETPLAYERDATASPLITSCREEN = 0x6C, - OP_GET_MATCHRULES_DATA = 0x6D, - OP_GET_SAVED_MATCHRULES_METADATA = 0x6E, - OP_LEVEL_FOR_EXPERIENCE_MP = 0x6F, - OP_LEVEL_FOR_EXPERIENCE_SO = 0x70, - OP_IS_ITEM_UNLOCKED = 0x71, - OP_IS_ITEM_UNLOCKEDSPLITSCREEN = 0x72, - OP_IS_CARDICON_UNLOCKED = 0x73, - OP_IS_CARDTITLE_UNLOCKED = 0x74, - OP_IS_CARDICON_NEW = 0x75, - OP_IS_CARDTITLE_NEW = 0x76, - OP_IS_CARDICON_UNLOCKED_SPLITSCREEN = 0x77, - OP_IS_CARDTITLE_UNLOCKED_SPLITSCREEN = 0x78, - OP_IS_CARDICON_NEW_SPLITSCREEN = 0x79, - OP_IS_CARDTITLE_NEW_SPLITSCREEN = 0x7A, - OP_IS_PROFILEITEM_UNLOCKED = 0x7B, - OP_IS_PROFILEITEM_UNLOCKED_SPLITSCREEN = 0x7C, - OP_IS_PROFILEITEM_NEW = 0x7D, - OP_IS_PROFILEITEM_NEW_SPLITSCREEN = 0x7E, - OP_DEBUG_PRINT = 0x7F, - OP_GETPLAYERDATA_ANYBOOLTRUE = 0x80, - OP_GETPROFILE_ANYBOOLTRUE = 0x81, - OP_WEAPON_CLASS_NEW = 0x82, - OP_WEAPONNAME = 0x83, - OP_ISRELOADING = 0x84, - OP_SAVE_GAME_AVAILABLE = 0x85, - OP_UNLOCKED_ITEM_COUNT = 0x86, - OP_UNLOCKED_ITEM_COUNT_SPLITSCREEN = 0x87, - OP_UNLOCKED_ITEM = 0x88, - OP_UNLOCKED_ITEM_SPLITSCREEN = 0x89, - OP_RADAR_IS_JAMMED = 0x8A, - OP_RADAR_JAM_INTENSITY = 0x8B, - OP_RADAR_IS_ENABLED = 0x8C, - OP_EMP_JAMMED = 0x8D, - OP_PLAYERADS = 0x8E, - OP_WEAPON_HEAT_ACTIVE = 0x8F, - OP_WEAPON_HEAT_VALUE = 0x90, - OP_WEAPON_HEAT_OVERHEATED = 0x91, - OP_SPLASH_TEXT = 0x92, - OP_SPLASH_DESCRIPTION = 0x93, - OP_SPLASH_MATERIAL = 0x94, - OP_SPLASH_HAS_ICON = 0x95, - OP_SPLASH_ROWNUM = 0x96, - OP_GETFOCUSED_NAME = 0x97, - OP_GETFOCUSED_X = 0x98, - OP_GETFOCUSED_Y = 0x99, - OP_GETFOCUSED_W = 0x9A, - OP_GETFOCUSED_H = 0x9B, - OP_GETITEMDEF_X = 0x9C, - OP_GETITEMDEF_Y = 0x9D, - OP_GETITEMDEF_W = 0x9E, - OP_GETITEMDEF_H = 0x9F, - OP_PLAYLISTFIELD = 0xA0, - OP_SCOREBOARD_EXTERNALMUTE_NOTICE = 0xA1, - OP_CLIENT_MATCH_DATA = 0xA2, - OP_CLIENT_MATCH_DATA_DEF = 0xA3, - OP_GET_MAP_NAME = 0xA4, - OP_GET_MAP_IMAGE = 0xA5, - OP_GET_MAP_CUSTOM = 0xA6, - OP_GET_MIGRATION_STATUS = 0xA7, - OP_GET_PLAYERCARD_INFO = 0xA8, - OP_IS_OFFLINE_PROFILE_SELECTED = 0xA9, - OP_COOP_PLAYERFIELD = 0xAA, - OP_IS_COOP = 0xAB, - OP_GETPARTYSTATUS = 0xAC, - OP_GETSEARCHPARAMS = 0xAD, - OP_GETTIMEPLAYED = 0xAE, - OP_IS_SELECTED_PLAYER_FRIEND = 0xAF, - OP_GETCHARBYINDEX = 0xB0, - OP_GETPLAYERPROFILEDATA = 0xB1, - OP_GETPLAYERPROFILEDATASPLITSCREEN = 0xB2, - OP_IS_PROFILE_SIGNED_IN = 0xB3, - OP_GET_WAIT_POPUP_STATUS = 0xB4, - OP_GETNATTYPE = 0xB5, - OP_GETLOCALIZEDNATTYPE = 0xB6, - OP_GET_ADJUSTED_SAFEAREA_HORIZONTAL = 0xB7, - OP_GET_ADJUSTED_SAFEAREA_VERTICAL = 0xB8, - OP_CONNECTION_INFO = 0xB9, - OP_OFFLINE_PROFILE_CAN_SAVE = 0xBA, - OP_USER_WITHOUT_OFFLINE_PROFILE = 0xBB, - OP_ALL_SPLITSCREEN_PROFILES_CAN_SAVE = 0xBC, - OP_ALL_SPLITSCREEN_PROFILES_ARE_SIGNED_IN = 0xBD, - OP_DO_WE_HAVE_MAP_PACK = 0xBE, - OP_MAY_INVITE_PLAYER_TO_PARTY = 0xBF, - OP_GETPATCHNOTES = 0xC0, - OP_GETGAMEINFOS = 0xC1, - OP_COOP_READY = 0xC2, - OP_VOTE_CAST = 0xC3, - OP_VOTE_PASSED = 0xC4, - OP_GET_MAP_VOTE_MAP_IMAGE = 0xC5, - OP_GET_MAP_VOTE_MAP_NAME = 0xC6, - OP_GET_MAP_VOTE_GAME_TYPE_NAME = 0xC7, - OP_IS_FRIEND_INVITABLE = 0xC8, - OP_IS_FRIEND_JOINABLE = 0xC9, - OP_GET_SORTED_CHALLENGE_INDEX = 0xCA, - OP_GET_SORTED_CHALLENGE_NAME = 0xCB, - OP_GET_SORTED_CHALLENGE_COUNT = 0xCC, - OP_GET_FILTER_CHALLENGE_COUNT = 0xCD, - OP_GET_FILTER_CHALLENGE_LOCKED_COUNT = 0xCE, - OP_GET_FILTER_CHALLENGE_COMPLETE_COUNT = 0xCF, - OP_IS_SORTED_CHALLENGE_TIERED = 0xD0, - OP_GET_CHALLENGE_FILTER_CACHE_COUNT = 0xD1, - OP_GET_CHALLENGE_FILTER_CACHE_COMPLETE_COUNT = 0xD2, - OP_IS_COOP_SEARCHING = 0xD3, - OP_IS_COOP_PUBLIC = 0xD4, - OP_GET_COOP_DISPLAYABLE_GROUP_NUM = 0xD5, - OP_COOP_HAS_REQUIRED_ONLINE_FILES = 0xD6, - OP_GET_TEXTWIDTH = 0xD7, - OP_GET_TEXTHEIGHT = 0xD8, - OP_DEVELOPER = 0xD9, - OP_IS_USING_AIRBURST_WEAPON = 0xDA, - OP_GET_AIRBURST_METERS = 0xDB, - OP_GET_CROSSHAIR_TRACE_METERS = 0xDC, - OP_GET_FACEBOOK_STATUS_TEXT = 0xDD, - OP_IS_FACEBOOK_LOGGED_IN = 0xDE, - OP_IS_FACEBOOK_CHECKING = 0xDF, - OP_IS_FACEBOOK_ALLOWED = 0xE0, - OP_GETPRIVATELOBBYSTATUS = 0xE1, - OP_INCLUDEDINMAPROTATION = 0xE2, - OP_SELECT = 0xE3, - OP_IS_DEMO_PLAYING = 0xE4, - OP_GET_USER_GROUP_TEXT = 0xE5, - OP_GET_USER_GROUP_COMMON_INTEREST_TOTAL = 0xE6, - OP_IS_DEMO_FOLLOW_CAMERA = 0xE7, - OP_IS_DEMO_FREE_CAMERA = 0xE8, - OP_IS_DEMO_CAPTURING_SCREENSHOT = 0xE9, - OP_PARTY_HOST_WAITING_ON_MEMBERS = 0xEA, - OP_POPUP_PARTY_MEMBER_AWAY = 0xEB, - OP_SELECTED_PARTY_MEMBER_AWAY = 0xEC, - OP_GAMETIME = 0xED, - OP_GAMEENDTIME = 0xEE, - OP_HAS_FOCUS = 0xEF, - OP_MENU_HAS_FOCUS = 0xF0, - OP_GET_DEMO_SEGMENT_COUNT = 0xF1, - OP_GET_DEMO_SEGMENT_INFORMATION = 0xF2, - OP_IS_CLIP_MODIFIED = 0xF3, - OP_IS_USING_RECIPE = 0xF4, - OP_IS_GUEST = 0xF5, - OP_GET_FACEBOOK_HELP_TEXT = 0xF6, - OP_IS_ELITE_CLAN_ALLOWED = 0xF7, - OP_IS_ENTITLEMENTS_ALLOWED = 0xF8, - OP_IS_USERGROUPS_ALLOWED = 0xF9, - OP_IS_WAITING_FOR_ONLINE_SERVICES = 0xFA, - OP_GET_TEXTWIDTHMODCASE = 0xFB, - OP_GET_SAVE_SCREEN_TITLE = 0xFC, - OP_GET_SAVE_SCREEN_DESCRIPTION = 0xFD, - OP_GET_ONLINEVAULT_SELECTEDITEM_DATA = 0xFE, - OP_ONLINEVAULT_IS_RESTRICTED = 0xFF, - OP_IS_CONTENTSERVER_TASK_IN_PROGRESS = 0x100, - OP_IS_CONTENTSERVER_GET_TASK_PROGRESS = 0x101, - OP_GET_RECENTGAMES_SELECTEDITEM_DATA = 0x102, - OP_GAMETYPENAME_ABBREVIATED = 0x103, - OP_GET_MAP_VOTE_GAME_TYPE_NAME_ABBREVIATED = 0x104, - OP_IS_USER_SIGNED_IN_TO_LIVE = 0x105, - OP_USER_CAN_PLAY_ONLINE = 0x106, - OP_GET_PAST_TITLE_RANK = 0x107, - OP_GET_FEEDER_DATA = 0x108, - OP_PARTY_CLIENTS_UP_TO_DATE = 0x109, - OP_TRUNCATETEXTWITHELLIPSIS = 0x10A, - OP_UI_STARTED = 0x10B, - OP_CAN_RENDER_CLIP = 0x10C, - OP_GET_PREVIEW_MAP_CUSTOM = 0x10D, - OP_GET_DLC_MAPS_AVAILABLE_COUNT = 0x10E, - OP_IS_USER_SIGNED_IN = 0x10F, - OP_USINGINTERMISSIONTIMER = 0x110, - OP_ISUSINGCUSTOMMAPROTATION = 0x111, - OP_MENU_IS_TOPMOST = 0x112, - OP_FACEBOOK_IS_PLATFORM_FRIEND = 0x113, - OP_ELITE_CLAN_IS_PLATFORM_FRIEND = 0x114, - OP_ELITE_CLAN_IS_ME = 0x115, - OP_ELITE_CLAN_IS_LEADER = 0x116, - OP_IS_USER_SIGNED_IN_FOR_VAULT = 0x117, - OP_GET_USING_MATCHRULES_DATA = 0x118, - OP_CAN_USER_ACCESS_ONLINEVAULT = 0x119, - OP_FRIEND_GET_GAMERTAG = 0x11A, - OP_RECENTPLAYER_GET_GAMERTAG = 0x11B, - OP_LIVEPARTY_GET_GAMERTAG = 0x11C, - OP_FACEBOOK_GET_GAMERTAG = 0x11D, - OP_ELITECLAN_GET_GAMERTAG = 0x11E, - OP_LIVEPARTY_IS_ME = 0x11F, - OP_LIVEPARTY_IS_LOCAL = 0x120, - OP_DOUBLECLICK_WAS_RIGHT_CLICK = 0x121, - OP_IS_DEMO_CLIP_RECORDING = 0x122, - OP_GET_INDEX_FROM_STRING = 0x123, - OP_GET_STRING_WIHTOUT_INDEX = 0x124, - OP_ELITECLAN_GET_NAME = 0x125, - OP_ELITECLAN_GET_HELP = 0x126, - OP_ELITECLAN_GET_MOTD = 0x127, - OP_ELITECLAN_IS_MEMBER = 0x128, - OP_ELITECLAN_IS_EMBLEM_OK = 0x129, - OP_FACEBOOKFRIENDS_SHOW_NEXT = 0x12A, - OP_FACEBOOKFRIENDS_SHOW_PREV = 0x12B, - OP_GET_ONLINEVAULT_FRIEND_GAMERTAG = 0x12C, - OP_GET_OBJECTIVE_LIST_HEIGHT = 0x12D, - OP_IS_CLIENT_DEMO_ENABLED = 0x12E, - OP_IS_USER_SIGNED_IN_TO_DEMONWARE = 0x12F, - OP_IS_CUSTOM_CLASS_RESTRICTED = 0x130, - OP_IS_WEAPON_RESTRICTED = 0x131, - OP_ANY_SPLITSCREEN_PROFILES_ARE_SIGNED_IN = 0x132, - OP_IS_GUEST_SPLITSCREEN = 0x133, - OP_IS_ITEM_UNLOCKED_BY_CLIENT = 0x134, - OP_IS_ANY_USER_SIGNED_IN_TO_LIVE = 0x135, - OP_GET_PAST_TITLE_PRESTIGE = 0x136, - OP_SPLITSCREENACTIVEGAMEPADCOUNT = 0x137, - OP_SHOW_FRIEND_PLAYERCARD = 0x138, - OP_GET_FRIEND_PLAYERCARD_PRESENCE = 0x139, - OP_SHOW_RECENT_PLAYERS_GROUP_ICON = 0x13A, - OP_GET_WRAPPED_TEXT_HEIGHT = 0x13B, - OP_CAN_SAVE = 0x13C, - OP_GET_GAME_INVITES_COUNT = 0x13D, - OP_IS_SPLITSCREEN_GAMER_LIVE_ENABLED = 0x13E, - OP_SO_COOP_SHOW_COMMON_GROUP_ICON = 0x13F, - OP_STRIP_COLORS_FROM_STRING = 0x140, - OP_CAN_USER_ACCESS_THEATRE = 0x141, - OP_IS_CHALLENGE_PERIODIC = 0x142, - OP_GET_CHALLENGE_DATA = 0x143, - OP_IS_ELITE_APP_PRESENT = 0x144, - OP_ELITE_CLAN_SELECTED_IS_ME = 0x145, - OP_ENOUGH_STORAGE_SPACE_FOR_CLIENT_DEMO = 0x146, - NUM_OPERATORS + OP_COUNT + }; + + enum expressionFunction_e + { + EXP_FUNC_STATIC_DVAR_INT = OP_COUNT, + EXP_FUNC_STATIC_DVAR_BOOL, + EXP_FUNC_STATIC_DVAR_FLOAT, + EXP_FUNC_STATIC_DVAR_STRING, }; enum expressionEntryType : int @@ -2623,7 +2325,7 @@ namespace IW5 union entryInternalData { - operationEnum op; + int op; Operand operand; }; @@ -2840,6 +2542,22 @@ namespace IW5 unsigned char vertAlign; }; + // This is data from IW4, could be different for IW5, to be investigated + enum WindowDefStaticFlag : unsigned int + { + WINDOW_FLAG_DECORATION = 0x100000, + WINDOW_FLAG_HORIZONTAL_SCROLL = 0x200000, + WINDOW_FLAG_OUT_OF_BOUNDS_CLICK = 0x2000000, + WINDOW_FLAG_SCREEN_SPACE = 0x400000, + WINDOW_FLAG_AUTO_WRAPPED = 0x800000, + WINDOW_FLAG_POPUP = 0x1000000, + WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE = 0x4000000, + WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG = 0x10000000, + WINDOW_FLAG_HIDDEN_DURING_SCOPE = 0x20000000, + WINDOW_FLAG_HIDDEN_DURING_UI = 0x40000000, + WINDOW_FLAG_TEXT_ONLY_FOCUS = 0x80000000, + }; + struct windowDef_t { const char* name; @@ -2862,6 +2580,23 @@ namespace IW5 Material* background; }; + // This is data from IW4, could be different for IW5, to be investigated + enum ItemDefFlag : unsigned int + { + ITEM_FLAG_SAVE_GAME_INFO = 0x1, + ITEM_FLAG_CINEMATIC_SUBTITLE = 0x2, + }; + + // This is data from IW4, could be different for IW5, to be investigated + enum ItemDefDvarFlag + { + ITEM_DVAR_FLAG_ENABLE = 0x1, + ITEM_DVAR_FLAG_DISABLE = 0x2, + ITEM_DVAR_FLAG_SHOW = 0x4, + ITEM_DVAR_FLAG_HIDE = 0x8, + ITEM_DVAR_FLAG_FOCUS = 0x10, + }; + struct columnInfo_s { int xpos; @@ -2936,6 +2671,38 @@ namespace IW5 void* data; }; + enum ItemFloatExpressionTarget + { + ITEM_FLOATEXP_TGT_RECT_X = 0x0, + ITEM_FLOATEXP_TGT_RECT_Y = 0x1, + ITEM_FLOATEXP_TGT_RECT_W = 0x2, + ITEM_FLOATEXP_TGT_RECT_H = 0x3, + ITEM_FLOATEXP_TGT_FORECOLOR_R = 0x4, + ITEM_FLOATEXP_TGT_FORECOLOR_G = 0x5, + ITEM_FLOATEXP_TGT_FORECOLOR_B = 0x6, + ITEM_FLOATEXP_TGT_FORECOLOR_RGB = 0x7, + ITEM_FLOATEXP_TGT_FORECOLOR_A = 0x8, + ITEM_FLOATEXP_TGT_GLOWCOLOR_R = 0x9, + ITEM_FLOATEXP_TGT_GLOWCOLOR_G = 0xA, + ITEM_FLOATEXP_TGT_GLOWCOLOR_B = 0xB, + ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB = 0xC, + ITEM_FLOATEXP_TGT_GLOWCOLOR_A = 0xD, + ITEM_FLOATEXP_TGT_BACKCOLOR_R = 0xE, + ITEM_FLOATEXP_TGT_BACKCOLOR_G = 0xF, + ITEM_FLOATEXP_TGT_BACKCOLOR_B = 0x10, + ITEM_FLOATEXP_TGT_BACKCOLOR_RGB = 0x11, + ITEM_FLOATEXP_TGT_BACKCOLOR_A = 0x12, + + ITEM_FLOATEXP_TGT_COUNT + }; + + struct ItemExpressionTargetBinding + { + int target; + const char* name; + const char* componentName; + }; + struct ItemFloatExpression { int target; diff --git a/src/ObjCommon/Game/IW5/MenuConstantsIW5.h b/src/ObjCommon/Game/IW5/MenuConstantsIW5.h new file mode 100644 index 00000000..390d47c9 --- /dev/null +++ b/src/ObjCommon/Game/IW5/MenuConstantsIW5.h @@ -0,0 +1,386 @@ +#pragma once + +#include "Game/IW5/IW5.h" + +namespace IW5 +{ + inline const char* g_expFunctionNames[] + { + "NOOP", + ")", + "*", + "/", + "%", + "+", + "-", + "!", + "<", + "<=", + ">", + ">=", + "==", + "!=", + "&&", + "||", + "(", + ",", + "&", + "|", + "~", + "<<", + ">>", + "dvarint(static)", + "dvarbool(static)", + "dvarfloat(static)", + "dvarstring(static)", + "int", + "string", + "float", + "sin", + "cos", + "min", + "max", + "milliseconds", + "localclientuimilliseconds", + "dvarint", + "dvarbool", + "dvarfloat", + "dvarstring", + "ui_active", + "flashbanged", + "usingvehicle", + "missilecam", + "scoped", + "scopedthermal", + "scoreboard_visible", + "inkillcam", + "inkillcamnpc", + "player", + "getperk", + "selecting_location", + "selecting_direction", + "team", + "otherteam", + "marinesfield", + "opforfield", + "menuisopen", + "writingdata", + "inlobby", + "ingamelobby", + "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", + "spectatingclient", + "spectatingfree", + "keybinding", + "actionslotusable", + "hudfade", + "maxrecommendedplayers", + "acceptinginvite", + "isintermission", + "gamehost", + "partyismissingmappack", + "partymissingmappackerror", + "anynewmappacks", + "amiselected", + "partystatusstring", + "attachedcontrollercount", + "issplitscreenonlinepossible", + "splitscreenplayercount", + "getplayerdata", + "getplayerdatasplitscreen", + "getmatchrulesdata", + "getsavedmatchrulesmetadata", + "levelforexperiencemp", + "levelforexperienceso", + "isitemunlocked", + "isitemunlockedsplitscreen", + "iscardiconunlocked", + "iscardtitleunlocked", + "iscardiconnew", + "iscardtitlenew", + "iscardiconunlockedsplitscreen", + "iscardtitleunlockedsplitscreen", + "iscardiconnewsplitscreen", + "iscardtitlenewsplitscreen", + "isprofileitemunlocked", + "isprofileitemunlockedsplitscreen", + "isprofileitemnew", + "isprofileitemnewsplitscreen", + "debugprint", + "getplayerdataanybooltrue", + "getprofileanybooltrue", + "weaponclassnew", + "weaponname", + "isreloading", + "savegameavailable", + "unlockeditemcount", + "unlockeditemcountsplitscreen", + "unlockeditem", + "unlockeditemsplitscreen", + "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", + "getprofiledatasplitscreen", + "isprofilesignedin", + "getwaitpopupstatus", + "getnattype", + "getlocalizednattype", + "getadjustedsafeareahorizontal", + "getadjustedsafeareavertical", + "connectioninfo", + "offlineprofilecansave", + "userwithoutofflineprofilewarning", + "allsplitscreenprofilescansave", + "allsplitscreenprofilesaresignedin", + "dowehavemappack", + "mayinviteplayertoparty", + "getpatchnotes", + "getgameinfos", + "coopready", + "votecast", + "votepassed", + "getmapvotemapimage", + "getmapvotemapname", + "mapvotegametypename", + "isfriendinvitable", + "isfriendjoinable", + "getsortedchallengeindex", + "getsortedchallengename", + "getsortedchallengecount", + "getfilterchallengecount", + "getfilterchallengelockedcount", + "getfilterchallengecompletecount", + "issortedchallengetiered", + "getchallengefiltercachecount", + "getchallengefiltercachecompletecount", + "iscoopsearching", + "coopshowpublictype", + "coopdisplayablegroupnum", + "coophasrequiredonlinefiles", + "getTextWidth", + "getTextHeight", + "isdeveloper", + "isusingairburst", + "getairburstmeters", + "getcrosshairtracemeters", + "getfacebookstatustext", + "isfacebookloggedin", + "isfacebookchecking", + "isfacebookallowed", + "getprivatepartystatus", + "includedinmaprotation", + "select", + "isdemoplaying", + "getusergrouptext", + "getusergroupcommoninteresttotal", + "isdemofollowcamera", + "isdemofreecamera", + "isdemocapturingscreenshot", + "ispartyhostwaitingonmembers", + "ispopuppartymemberaway", + "isselectedpartymemberaway", + "gettime", + "gameendtime", + "hasfocus", + "menuhasfocus", + "getdemosegmentcount", + "getdemosegmentinformation", + "isclipmodified", + "isusingmatchrulesdata", + "isguest", + "getfacebookhelptext", + "iseliteclanallowed", + "isentitlementsallowed", + "isusergroupsallowed", + "iswaitingforonlineservices", + "getTextWidthModCase", + "getSaveScreenTitle", + "getSaveScreenDescription", + "getOnlineVaultSelectedItemData", + "isOnlineVaultRestricted", + "isContentServerTaskInProgress", + "getContentServerTaskProgress", + "getRecentGamesSelectedItemData", + "gametypenameAbbreviated", + "mapvotegametypenameAbbreviated", + "isusersignedintolive", + "usercanplayonline", + "getPastTitleRank", + "getFeederData", + "partyclientsuptodate", + "truncateTextWithEllipsis", + "uistarted", + "canRenderClip", + "getpreviewmapcustom", + "getdlcmapsavailablecount", + "isusersignedin", + "isUsingIntermissionTimer", + "isUsingCustomMapRotation", + "menuistopmost", + "facebook_isplatfromfriend", + "eliteclan_isplatfromfriend", + "eliteclan_isme", + "eliteclan_isleader", + "isusersignedinforvault", + "getusingmatchrulesdata", + "canuseraccessonlinevault", + "friend_getgamertag", + "recentplayer_getgamertag", + "liveparty_getgamertag", + "facebook_getgamertag", + "eliteclan_getgamertag", + "liveparty_isme", + "liveparty_islocal", + "doubleclickwasrightclick", + "isdemocliprecording", + "getIndexFromString", + "getStringWithoutIndex", + "eliteclan_getname", + "eliteclan_gethelp", + "eliteclan_getmotd", + "eliteclan_ismember", + "eliteclan_isemblem_ok", + "facebook_friends_show_next", + "facebook_friends_show_prev", + "getOnlineVaultFriendGamerTag", + "getObjectiveListHeight", + "isClientDemoEnabled", + "isusersignedintodemonware", + "customClassIsRestricted", + "weaponIsRestricted", + "anysplitscreenprofilesaresignedin", + "isguestsplitscreen", + "isitemunlockedbyclient", + "isanyusersignedintolive", + "getPastTitlePrestige", + "splitscreenactivegamepadcount", + "showFriendPlayercard", + "getFriendPlayercardPresence", + "showRecentPlayerGroupIcon", + "getwrappedtextheight", + "canClientSave", + "getgameinvitescount", + "issplitscreengamerliveenabled", + "so_coopShowCommonGroupIcon", + "stripColorsFromString", + "DEPRECATED", + "ischallengeperiodic", + "getchallengedata", + "iseliteapppresent", + "eliteclan_selectedisme", + "enoughStorageSpaceForClientDemo", + "isusersignedinforcommerce", + "getfacebookmenutext", + "getfacebookisposting", + "meetplayer_isplatformfriend", + "isselectedplayerguest", + "getsplitscreencontrollerclientnum", + "isClientDemoEnabledSplitScreen", + "ItemCanTakeFocus", + "getTimeSinceLastDoubleClick", + "isServerListRefreshing", + "isRecipeNameValid", + "recipeExists", + "getfacebookoptionshelptext", + "dowehaveallavailablemappacks", + "isThereNewEliteItems", + "isPayingSubscriber", + "localuser_ismissingmappack", + "localuser_missingmappackerror", + "getFirstSpecOpsDLCMap", + "localuser_missingmapname", + "showStoreNew", + "commerce_getstatus", + "isManifestDownloaded", + "areAllItemsUnlocked", + "commerce_getstatuscond", + "doWeHaveMissingOwnedContent", + }; + + 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"}, + }; +} diff --git a/src/ObjCommon/Game/IW5/ObjConstantsIW5.h b/src/ObjCommon/Game/IW5/ObjConstantsIW5.h new file mode 100644 index 00000000..d6aa3645 --- /dev/null +++ b/src/ObjCommon/Game/IW5/ObjConstantsIW5.h @@ -0,0 +1,20 @@ +#pragma once + +namespace IW5 +{ + 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/Menu/MenuDumperIW4.cpp b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp index 8762f62f..0f62f87a 100644 --- a/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp +++ b/src/ObjWriting/Game/IW4/Menu/MenuDumperIW4.cpp @@ -133,7 +133,7 @@ void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& cu else currentPos++; - spaceNext = expEntry.data.op != OP_NEG; + spaceNext = expEntry.data.op != OP_NOT; } } diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp new file mode 100644 index 00000000..47154ffa --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.cpp @@ -0,0 +1,76 @@ +#include "AssetDumperMenuDef.h" + +#include +#include + +#include "ObjWriting.h" +#include "Game/IW5/GameAssetPoolIW5.h" +#include "Game/IW5/Menu/MenuDumperIW5.h" +#include "Menu/AbstractMenuDumper.h" + +namespace fs = std::filesystem; + +using namespace IW5; + +const MenuList* AssetDumperMenuDef::GetParentMenuList(XAssetInfo* asset) +{ + const auto* menu = asset->Asset(); + const auto* gameAssetPool = dynamic_cast(asset->m_zone->m_pools.get()); + for (const auto* menuList : *gameAssetPool->m_menu_list) + { + const auto* menuListAsset = menuList->Asset(); + + for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++) + { + if (menuListAsset->menus[menuIndex] == menu) + return menuListAsset; + } + } + + return nullptr; +} + +std::string AssetDumperMenuDef::GetPathForMenu(XAssetInfo* asset) +{ + const auto* list = GetParentMenuList(asset); + + if (!list) + return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu"; + + const fs::path p(list->name); + std::string parentPath; + if (p.has_parent_path()) + parentPath = p.parent_path().string() + "/"; + + return parentPath + std::string(asset->Asset()->window.name) + ".menu"; +} + +bool AssetDumperMenuDef::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* menu = asset->Asset(); + const auto menuFilePath = GetPathForMenu(asset); + + 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; + } + + const auto assetFile = context.OpenAssetFile(menuFilePath); + + if (!assetFile) + return; + + MenuDumper menuDumper(*assetFile); + + menuDumper.Start(); + menuDumper.WriteMenu(menu); + menuDumper.End(); +} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h new file mode 100644 index 00000000..d965cc91 --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuDef.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" + +namespace IW5 +{ + class AssetDumperMenuDef final : public AbstractAssetDumper + { + static const MenuList* GetParentMenuList(XAssetInfo* asset); + static std::string GetPathForMenu(XAssetInfo* asset); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp new file mode 100644 index 00000000..85b758be --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.cpp @@ -0,0 +1,120 @@ +#include "AssetDumperMenuList.h" + +#include +#include +#include +#include + +#include "ObjWriting.h" +#include "Game/IW5/Menu/MenuDumperIW5.h" +#include "Menu/AbstractMenuDumper.h" + +namespace fs = std::filesystem; + +using namespace IW5; + +std::vector AssetDumperMenuList::GetAllUniqueExpressionSupportingData(const MenuList* menuList) +{ + std::vector result; + std::set alreadyAddedSupportingData; + + if (menuList->menus == nullptr) + return result; + + for(auto i = 0; i < menuList->menuCount; i++) + { + if(menuList->menus[i] == nullptr) + continue; + + const auto* menu = menuList->menus[i]; + + if(menu->data == nullptr || menu->data->expressionData == nullptr) + continue; + + if(alreadyAddedSupportingData.find(menu->data->expressionData) == alreadyAddedSupportingData.end()) + { + result.push_back(menu->data->expressionData); + alreadyAddedSupportingData.emplace(menu->data->expressionData); + } + } + + return result; +} + +void AssetDumperMenuList::DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList) +{ + const auto allSupportingData = GetAllUniqueExpressionSupportingData(menuList); + auto functionIndex = 0u; + + assert(allSupportingData.size() <= 1); + + for (const auto* supportingData : allSupportingData) + { + if (supportingData->uifunctions.functions == nullptr) + continue; + + for(auto i = 0; i < supportingData->uifunctions.totalFunctions; i++) + { + const auto* function = supportingData->uifunctions.functions[i]; + if(function == nullptr) + continue; + + std::stringstream ss; + ss << "FUNC_" << functionIndex; + + menuDumper.WriteFunctionDef(ss.str(), function); + + functionIndex++; + } + } +} + +void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, 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]; + + std::ostringstream ss; + ss << parentPath << menu->window.name << ".menu"; + + const auto menuName = ss.str(); + + // If the menu was embedded directly as menu list write its data in the menu list file + if (menuName == menuList->name) + menuDumper.WriteMenu(menu); + else + menuDumper.IncludeMenu(ss.str()); + } +} + +bool AssetDumperMenuList::ShouldDump(XAssetInfo* asset) +{ + return true; +} + +void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* menuList = asset->Asset(); + const auto assetFile = context.OpenAssetFile(asset->m_name); + + if (!assetFile) + return; + + MenuDumper menuDumper(*assetFile); + + menuDumper.Start(); + + if(!ObjWriting::Configuration.MenuLegacyMode) + DumpFunctions(menuDumper, menuList); + + DumpMenus(menuDumper, menuList); + + menuDumper.End(); +} diff --git a/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h new file mode 100644 index 00000000..6f12aaf3 --- /dev/null +++ b/src/ObjWriting/Game/IW5/AssetDumpers/AssetDumperMenuList.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Dumping/AbstractAssetDumper.h" +#include "Game/IW5/IW5.h" +#include "Game/IW5/Menu/MenuDumperIW5.h" + +namespace IW5 +{ + class AssetDumperMenuList final : public AbstractAssetDumper + { + static std::vector GetAllUniqueExpressionSupportingData(const MenuList* menuList); + + static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList); + static void DumpMenus(MenuDumper& menuDumper, const MenuList* menuList); + + protected: + bool ShouldDump(XAssetInfo* asset) override; + void DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) override; + }; +} diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp new file mode 100644 index 00000000..a8d6b34f --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.cpp @@ -0,0 +1,798 @@ +#include "MenuDumperIW5.h" + +#include +#include +#include + +#include "ObjWriting.h" +#include "Game/IW5/MenuConstantsIW5.h" + +using namespace IW5; + +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_NOT; + } +} + +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].xpos + << " " << listBox->columnInfo[col].ypos + << " " << listBox->columnInfo[col].width + << " " << listBox->columnInfo[col].height + << " " << 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->stepVal << " " << 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->stepVal) >= 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 || item->typeData.ticker == nullptr) + 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->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); + 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); + WriteStatementProperty("visible", menu->data->visibleExp, true); + 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); + WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen); + WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose); + WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest); + WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC); + WriteItemKeyHandlerProperty(menu->data->onKey); + WriteItemDefs(menu->items, menu->itemCount); +} + +MenuDumper::MenuDumper(std::ostream& stream) + : AbstractMenuDumper(stream) +{ +} + +void MenuDumper::WriteFunctionDef(const std::string& functionName, const Statement_s* statement) +{ + StartFunctionDefScope(); + + WriteStringProperty("name", functionName); + WriteStatementProperty("value", statement, false); + + EndScope(); +} + +void MenuDumper::WriteMenu(const menuDef_t* menu) +{ + StartMenuDefScope(); + + WriteMenuData(menu); + + EndScope(); +} diff --git a/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h new file mode 100644 index 00000000..1ead711f --- /dev/null +++ b/src/ObjWriting/Game/IW5/Menu/MenuDumperIW5.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "Menu/AbstractMenuDumper.h" +#include "Game/IW5/IW5.h" + +namespace IW5 +{ + 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 WriteFunctionDef(const std::string& functionName, const Statement_s* statement); + void WriteMenu(const menuDef_t* menu); + }; +} diff --git a/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp b/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp index 5b9f22e2..32f4579b 100644 --- a/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp +++ b/src/ObjWriting/Game/IW5/ZoneDumperIW5.cpp @@ -8,6 +8,8 @@ #include "AssetDumpers/AssetDumperGfxImage.h" #include "AssetDumpers/AssetDumperLoadedSound.h" #include "AssetDumpers/AssetDumperLocalizeEntry.h" +#include "AssetDumpers/AssetDumperMenuDef.h" +#include "AssetDumpers/AssetDumperMenuList.h" #include "AssetDumpers/AssetDumperRawFile.h" #include "AssetDumpers/AssetDumperStringTable.h" #include "AssetDumpers/AssetDumperXModel.h" @@ -53,8 +55,8 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const // DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD) // DUMP_ASSET_POOL(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF) // DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT) - // DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) - // DUMP_ASSET_POOL(AssetDumpermenuDef_t, m_menu_def, ASSET_TYPE_MENU) + DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST) + DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU) DUMP_ASSET_POOL(AssetDumperLocalizeEntry, m_localize, ASSET_TYPE_LOCALIZE_ENTRY) // DUMP_ASSET_POOL(AssetDumperWeaponAttachment, m_attachment, ASSET_TYPE_ATTACHMENT) // DUMP_ASSET_POOL(AssetDumperWeaponCompleteDef, m_weapon, ASSET_TYPE_WEAPON)