diff --git a/src/IW4/Assets/MenuDef.cpp b/src/IW4/Assets/MenuDef.cpp new file mode 100644 index 0000000..2189898 --- /dev/null +++ b/src/IW4/Assets/MenuDef.cpp @@ -0,0 +1,994 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#include "stdafx.hpp" + +namespace ZoneTool +{ + namespace IW4 + { + void IMenuDef::init(const std::string& name, ZoneMemory* mem) + { + this->name_ = name; + this->asset_ = DB_FindXAssetHeader(this->type(), this->name().data()).menu; + } + + std::string IMenuDef::name() + { + return this->name_; + } + + std::int32_t IMenuDef::type() + { + return menu; + } + + int IMenuDef::indentCounter = 0; + FILE* IMenuDef::fp = nullptr; + + static const char* g_expOperatorNames[] + { + "NOOP", + ")", + "*", + "/", + "%", + "+", + "-", + "!", + "<", + "<=", + ">", + ">=", + "==", + "!=", + "&&", + "||", + "(", + ",", + "&", + "|", + "~", + "<<", + ">>", + "dvarint(static)", + "dvarbool(static)", + "dvarfloat(static)", + "dvarstring(static)", + "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 char* g_commandList[] = + { + "fadein", + "fadeout", + "show", + "hide", + "showMenu", + "hideMenu", + "setcolor", + "open", + "close", + "closeForAllPlayers", + "ingameopen", + "ingameclose", + "setbackground", + "setItemColor", + "focusfirst", + "setfocus", + "setfocusbydvar", + "setdvar", + "exec", + "execnow", + "execOnDvarStringValue", + "execOnDvarIntValue", + "execOnDvarFloatValue", + "execNowOnDvarStringValue", + "execNowOnDvarIntValue", + "execNowOnDvarFloatValue", + "play", + "scriptmenuresponse", + "scriptMenuRespondOnDvarStringValue", + "scriptMenuRespondOnDvarIntValue", + "scriptMenuRespondOnDvarFloatValue", + "setPlayerData", + "setPlayerDataSplitscreen", + "updateMail", + "openMail", + "deleteMail", + "doMailLottery", + "resetStatsConfirm", + "resetStatsCancel", + "setGameMode", + "setLocalVarBool", + "setLocalVarInt", + "setLocalVarFloat", + "setLocalVarString", + "feederTop", + "feederBottom", + "showGamerCard", + "openforgametype", + "closeforgametype", + "statclearperknew", + "statsetusingtable", + "statclearbitmask", + "kickPlayer", + "getKickPlayerQuestion", + "partyUpdateMissingMapPackDvar", + "getHostMigrateQuestion", + "makehost", + "togglePlayerMute", + "addFriendFromId", + "resolveError", + "lerp", + // added by me + "uiScript" + }; + + struct ItemFloatExpressionEntry + { + int target; + const char* s1; + const char* s2; + }; + + ItemFloatExpressionEntry g_itemFloatExpressions[19] = + { + { 0, "rect", "x" }, + { 1, "rect", "y" }, + { 2, "rect", "w" }, + { 3, "rect", "h" }, + { 4, "forecolor", "r" }, + { 5, "forecolor", "g" }, + { 6, "forecolor", "b" }, + { 7, "forecolor", "rgb" }, + { 8, "forecolor", "a" }, + { 9, "glowcolor", "r" }, + { 10, "glowcolor", "g" }, + { 11, "glowcolor", "b" }, + { 12, "glowcolor", "rgb" }, + { 13, "glowcolor", "a" }, + { 14, "backcolor", "r" }, + { 15, "backcolor", "g" }, + { 16, "backcolor", "b" }, + { 17, "backcolor", "rgb" }, + { 18, "backcolor", "a" } + }; + + void IMenuDef::dump(menuDef_t* asset) + { + ZONETOOL_INFO("Dumping menu %s", asset->window.name); + + fp = FileSystem::FileOpen("menus\\"s + asset->window.name + ".menu"s, "w"); + indentCounter = 1; + + if (fp) + { + emit_menu_def(asset); + } + + FileSystem::FileClose(fp); + } + + void IMenuDef::emit_open_brace() + { + fprintf(fp, "%s{\n", get_tabs()); + } + + void IMenuDef::emit_closing_brace() + { + fprintf(fp, "%s}\n", get_tabs()); + } + + void IMenuDef::push_indent() + { + emit_open_brace(); + IMenuDef::indentCounter++; + } + + void IMenuDef::pop_indent() + { + IMenuDef::indentCounter--; + emit_closing_brace(); + } + + void IMenuDef::emit_menu_def(menuDef_t* asset) + { + fputs("{\n\tmenuDef\n", fp); + push_indent(); + + emit_window_def(&asset->window, false); + emit_color("focuscolor", asset->focusColor); + emit_int("fullscreen", asset->fullscreen); + emit_float("fadeCycle", asset->fadeCycle); + emit_float("fadeClamp", asset->fadeClamp); + emit_float("fadeAmount", asset->fadeAmount); + emit_float("fadeInAmount", asset->fadeInAmount); + emit_float("blurWorld", asset->blurRadius); + emit_string("allowedBinding", asset->allowedBinding); + emit_statement("visible when", asset->visibleExp); + emit_statement("exp rect x", asset->rectXExp); + emit_statement("exp rect y", asset->rectYExp); + emit_statement("exp rect h", asset->rectHExp); + emit_statement("exp rect w", asset->rectWExp); + emit_statement("exp openSound", asset->openSoundExp); + emit_statement("exp closeSound", asset->closeSoundExp); + emit_item_key_handler("execKeyInt", asset->onKey); + emit_menu_event_handler_set("onOpen", asset->onOpen); + emit_menu_event_handler_set("onRequestClose", asset->onRequestClose); + emit_menu_event_handler_set("onClose", asset->onClose); + emit_menu_event_handler_set("onEsc", asset->onEsc); + + for (auto i = 0; i < asset->itemCount; i++) + { + emit_item_def(asset->items[i]); + } + + pop_indent(); + fputs("}\n", fp); + } + + void IMenuDef::emit_item_def(itemDef_t* item) + { + fputs("\t\titemDef\n", fp); + push_indent(); + + emit_window_def(&item->window, true); + emit_int("type", item->type); + emit_int("align", item->alignment); + emit_int("textfont", item->fontEnum); + emit_int("textalign", item->textAlignMode); + emit_float("textalignx", item->textAlignX); + emit_float("textaligny", item->textAlignY); + emit_float("textscale", item->textScale); + emit_color("glowColor", item->glowColor); + emit_bool("decodeEffect", item->decayActive); + + if (item->type == ITEM_TYPE_GAME_MESSAGE_WINDOW) + { + emit_int("gamemsgwindowindex", item->gameMsgWindowIndex); + emit_int("gamemsgwindowmode", item->gameMsgWindowMode); + } + + emit_string("text", item->text); + emit_bool("textsavegame", item->textSaveGameInfo & 1); + emit_bool("textcinematicsubtitle", item->textSaveGameInfo & 2); + emit_float("feeder", item->special); + emit_string("focusSound", item->focusSound); + + if (item->type != ITEM_TYPE_SLIDER && item->type != ITEM_TYPE_DVARENUM) + { + emit_string("dvar", item->dvar); + emit_item_def_data(&item->typeData, item->type); + } + else + { + emit_item_def_data(&item->typeData, item->type); + } + + emit_dvar_flags(item->dvarFlags, item->dvarTest, item->enableDvar); + emit_item_float_expressions(item->floatExpressions, item->floatExpressionCount); + emit_statement("visible when", item->visibleExp); + emit_statement("disabled when", item->disabledExp); + emit_statement("exp text", item->textExp); + emit_statement("exp material", item->materialExp); + emit_item_key_handler("execKeyInt", item->onKey); + emit_menu_event_handler_set("mouseEnterText", item->mouseEnterText); + emit_menu_event_handler_set("mouseExitText", item->mouseExitText); + emit_menu_event_handler_set("mouseEnter", item->mouseEnter); + emit_menu_event_handler_set("mouseExit", item->mouseExit); + emit_menu_event_handler_set("action", item->action); + emit_menu_event_handler_set("accept", item->accept); + emit_menu_event_handler_set("onFocus", item->onFocus); + emit_menu_event_handler_set("leaveFocus", item->leaveFocus); + + pop_indent(); + } + + void IMenuDef::emit_window_def(windowDef_t* window, bool is_item) + { + emit_string("name", window->name); + if (is_item) + { + emit_rect("rect", window->rectClient); + } + else + { + emit_rect("rect", window->rect); + } + emit_static_flags(window->staticFlags); + emit_dynamic_flags(window->dynamicFlags); + emit_string("group", window->group); + emit_int("style", window->style); + emit_int("border", window->border); + emit_float("borderSize", window->borderSize); + emit_int("ownerdraw", window->ownerDraw); + emit_int("ownerdrawFlag", window->ownerDrawFlags); + emit_color("forecolor", window->foreColor); + emit_color("backcolor", window->backColor); + emit_color("bordercolor", window->borderColor); + emit_color("outlinecolor", window->outlineColor); + emit_color("disablecolor", window->disableColor); + + if (window->background) + { + emit_string("background", window->background->name); + } + } + + void IMenuDef::emit_set_local_var_data(SetLocalVarData* data, EventType type) + { + if (data) + { + std::string cmd; + switch (type) + { + case EVENT_SET_LOCAL_VAR_BOOL: + cmd = "setLocalVarBool"; + break; + case EVENT_SET_LOCAL_VAR_INT: + cmd = "setLocalVarInt"; + break; + case EVENT_SET_LOCAL_VAR_FLOAT: + cmd = "setLocalVarFloat"; + break; + case EVENT_SET_LOCAL_VAR_STRING: + cmd = "setLocalVarString"; + break; + } + + fprintf(fp, "%s%s \"%s\"", get_tabs(), cmd.c_str(), data->localVarName); + emit_statement(nullptr, data->expression, true); + } + } + + void IMenuDef::emit_dynamic_flags(int flags) + { + emit_bool("visible 1", flags & WINDOWDYNAMIC_VISIBLE); + } + + void IMenuDef::emit_static_flags(int flags) + { + emit_bool("decoration", flags & WINDOWSTATIC_DECORATION); + emit_bool("horizontalscroll", flags & WINDOWSTATIC_HORIZONTALSCROLL); + emit_bool("screenSpace", flags & WINDOWSTATIC_SCREENSPACE); + emit_bool("autowrapped", flags & WINDOWSTATIC_AUTOWRAPPED); + emit_bool("popup", flags & WINDOWSTATIC_POPUP); + emit_bool("outOfBoundsClick", flags & WINDOWSTATIC_OUTOFBOUNDSCLICK); + emit_bool("legacySplitScreenScale", flags & WINDOWSTATIC_LEGACYSPLITSCREENSCALE); + emit_bool("hiddenDuringFlashbang", flags & WINDOWSTATIC_HIDDENDURINGFLASH); + emit_bool("hiddenDuringScope", flags & WINDOWSTATIC_HIDDENDURINGSCOPE); + emit_bool("hiddenDuringUI", flags & WINDOWSTATIC_HIDDENDURINGUI); + emit_bool("textOnlyFocus", flags & WINDOWSTATIC_TEXTONLYFOCUS); + } + + void IMenuDef::emit_dvar_flags(int dvarFlags, const char* dvarTest, const char* enableDvar) + { + std::string command; + switch (dvarFlags) + { + case 0x01: + command = "enableDvar"; + break; + case 0x02: + command = "disableDvar"; + break; + case 0x04: + command = "showDvar"; + break; + case 0x08: + command = "hideDvar"; + break; + case 0x10: + command = "focusDvar"; + break; + } + + emit_string("dvarTest", dvarTest); + + if (enableDvar) + { + fprintf(fp, "%s%s { %s}\n", get_tabs(), command.c_str(), enableDvar); + } + } + + void IMenuDef::emit_item_def_data(itemDefData_t* data, int type) + { + if (data->data) + { + switch (type) + { + case ITEM_TYPE_LISTBOX: + emit_list_box(data->listBox); + break; + case ITEM_TYPE_MULTI: + emit_multi_def(data->multiDef); + break; + case ITEM_TYPE_DVARENUM: + fprintf(fp, "%s%s %s %s", get_tabs(), "dvar", "dvarEnumList", data->enumDvarName); + break; + case ITEM_TYPE_NEWSTICKER: + emit_int("newsfeed", data->ticker->feedId); + emit_int("speed", data->ticker->speed); + emit_int("spacing", data->ticker->spacing); + break; + case ITEM_TYPE_EDITFIELD: + case ITEM_TYPE_NUMERICFIELD: + case ITEM_TYPE_VALIDFILEFIELD: + case ITEM_TYPE_UPREDITFIELD: + case ITEM_TYPE_YESNO: + case ITEM_TYPE_BIND: + case ITEM_TYPE_TEXT: + case ITEM_TYPE_DECIMALFIELD: + case ITEM_TYPE_EMAILFIELD: + case ITEM_TYPE_PASSWORDFIELD: + emit_int("maxChars", data->editField->maxChars); + emit_int("maxCharsGotoNext", data->editField->maxCharsGotoNext); + emit_int("maxPaintChars", data->editField->maxPaintChars); + break; + case ITEM_TYPE_TEXTSCROLL: + default: + break; + } + } + } + + void IMenuDef::emit_multi_def(multiDef_s* multiDef) + { + if (multiDef->strDef) + { + fprintf(fp, "%s%s {", get_tabs(), "dvarStrList"); + } + else + { + fprintf(fp, "%s%s {", get_tabs(), "dvarFloatList"); + } + + for (auto i = 0; i < multiDef->count; i++) + { + fprintf(fp, " \"%s\"", multiDef->dvarList[i]); + if (multiDef->strDef) + { + fprintf(fp, " \"%s\"", multiDef->dvarStr[i]); + } + else + { + fprintf(fp, " %g", multiDef->dvarValue[i]); + } + } + + fputs(" }\n", fp); + } + + void IMenuDef::emit_list_box(listBoxDef_s* listBox) + { + emit_color("selectBorder", listBox->selectBorder); + + if (listBox->selectIcon) + { + emit_string("selectIcon", listBox->selectIcon->name); + } + + emit_float("elementWidth", listBox->elementWidth); + emit_float("elementHeight", listBox->elementHeight); + emit_int("elementtype", listBox->elementStyle); + emit_column_info(listBox->columnInfo, listBox->numColumns); + emit_bool("noscrollbars", listBox->noscrollbars); + emit_bool("notselectable", listBox->notselectable); + emit_bool("usepaging", listBox->usepaging); + emit_menu_event_handler_set("doubleClick", listBox->doubleClick); + } + + void IMenuDef::emit_column_info(columnInfo_s* columns, int count) + { + fprintf(fp, "//%snumcol\t\t\txpos\txwidth\ttextlen\t alignment\n", get_tabs()); + fprintf(fp, "%s%s %i\t\t%i\t\t%i\t\t%i\t\t %i\n", get_tabs(), "columns", count, columns[0].xpos, columns[0].width, columns[0].maxChars, columns[0].alignment); + for (int i = 1; i < count; i++) + { + fprintf(fp, "%s\t\t\t\t%i\t\t%i\t\t%i\t\t %i\n", get_tabs(), columns[i].xpos, columns[i].width, columns[i].maxChars, columns[i].alignment); + } + } + + void IMenuDef::emit_item_float_expressions(ItemFloatExpression* expressions, int count) + { + if (expressions) + { + for (auto i = 0; i < count; i++) + { + std::string name; + name = "exp "; + name += g_itemFloatExpressions[expressions[i].target].s1; + name += " "; + name += g_itemFloatExpressions[expressions[i].target].s2; + emit_statement(name.c_str(), expressions[i].expression); + } + } + } + + void IMenuDef::emit_item_key_handler(const char* name, ItemKeyHandler* handler) + { + if (handler) + { + emit_menu_event_handler_set("", handler->action); + emit_item_key_handler("execKeyInt", handler->next); + } + } + + void IMenuDef::emit_menu_event_handler_set(const char* name, MenuEventHandlerSet* set) + { + if (set) + { + const bool formatStatement = true; + + if (name) + { + fprintf(fp, "%s%s\n", get_tabs(), name); + push_indent(); + } + + for (auto i = 0; i < set->eventHandlerCount; i++) + { + switch (set->eventHandlers[i]->eventType) + { + case EVENT_UNCONDITIONAL: + fprintf(fp, "%s%s", get_tabs(), format_script(set->eventHandlers[i]->eventData.unconditionalScript).c_str()); + break; + case EVENT_IF: + emit_conditional_script(set->eventHandlers[i]->eventData.conditionalScript); + break; + case EVENT_ELSE: + emit_menu_event_handler_set("else", set->eventHandlers[i]->eventData.elseScript); + break; + case EVENT_SET_LOCAL_VAR_BOOL: + case EVENT_SET_LOCAL_VAR_INT: + case EVENT_SET_LOCAL_VAR_FLOAT: + case EVENT_SET_LOCAL_VAR_STRING: + emit_set_local_var_data(set->eventHandlers[i]->eventData.setLocalVarData, set->eventHandlers[i]->eventType); + break; + default: + break; + } + } + + if (name) + { + pop_indent(); + } + } + } + + void IMenuDef::emit_conditional_script(ConditionalScript* script) + { + if (script) + { + fprintf(fp, "%sif", get_tabs()); + emit_statement(nullptr, script->eventExpression); + push_indent(); + emit_menu_event_handler_set(nullptr, script->eventHandlerSet); + pop_indent(); + } + } + + void IMenuDef::emit_statement(const char* name, Statement_s* statement, bool semiColon) + { + if (statement) + { + const bool clean_statements = true; + bool needs_closing_parenthesis = false; + + if (name) + { + fprintf(fp, "%s%s", get_tabs(), name); + } + + if (clean_statements) + { + if (statement->entries[0].data.op != OP_LEFTPAREN) + { + fputs(" (", fp); + } + } + + for (auto i = 0; i < statement->numEntries; i++) + { + int type = statement->entries[i].type; + if (type == OPERATOR) + { + needs_closing_parenthesis = true; + auto op = statement->entries[i].data.op; + if (op < OP_FIRSTFUNCTIONCALL) + { + if (statement->entries[i - 1].data.op == OP_RIGHTPAREN && op == OP_LEFTPAREN) + { + ++i; + continue; + } + + if (statement->entries[i - 1].data.op == OP_NOT) + { + fprintf(fp, "%s", g_expOperatorNames[op]); + } + else + { + fprintf(fp, " %s", g_expOperatorNames[op]); + } + } + else if (op >= OP_STATICDVARINT && op <= OP_STATICDVARSTRING) + { + i++; + + std::string command; + switch (op) + { + case OP_STATICDVARINT: + command = "dvarstring"; + break; + case OP_STATICDVARBOOL: + command = "dvarbool"; + break; + case OP_STATICDVARFLOAT: + command = "dvarfloat"; + break; + case OP_STATICDVARSTRING: + command = "dvarstring"; + break; + } + + command += "( \""; + command += statement->supportingData->staticDvarList.staticDvars[statement->entries[i].data.operand.internals.intVal]->dvarName; + command += "\" )"; + + if (statement->entries[i - 2].data.op == OP_NOT) + { + fprintf(fp, "%s", command.c_str()); + } + else + { + fprintf(fp, " %s", command.c_str()); + } + + i++; + } + else + { + if (statement->entries[i - 1].data.op == OP_NOT) + fprintf(fp, "%s(", g_expOperatorNames[op]); + else + fprintf(fp, " %s(", g_expOperatorNames[op]); + } + } + else if (type == OPERAND) + { + switch (statement->entries[i].data.operand.dataType) + { + case VAL_INT: + fprintf(fp, " %i", statement->entries[i].data.operand.internals.intVal); + break; + case VAL_FLOAT: + fprintf(fp, " %g", statement->entries[i].data.operand.internals.floatVal); + break; + case VAL_STRING: + fprintf(fp, " \"%s\"", escape_string(statement->entries[i].data.operand.internals.stringVal.string).c_str()); + break; + case VAL_FUNCTION: + emit_statement(nullptr, statement->entries[i].data.operand.internals.function); + break; + } + } + } + + if (clean_statements) + { + if (needs_closing_parenthesis) + { + fputs(" )", fp); + } + + if (statement->entries[0].data.op != OP_LEFTPAREN) + { + fputs(" )", fp); + } + + if (semiColon) + { + fputs(";", fp); + } + } + + fputs("\n", fp); + } + } + + void IMenuDef::emit_string(const char* name, const char* value) + { + if (value && *value) + { + fprintf(fp, "%s%s \"%s\"\n", get_tabs(), name, value); + } + } + + void IMenuDef::emit_bool(const char* name, bool value) + { + if (value) + { + fprintf(fp, "%s%s\n", get_tabs(), name); + } + } + + void IMenuDef::emit_int(const char* name, int value) + { + if (value) + { + fprintf(fp, "%s%s %i\n", get_tabs(), name, value); + } + } + + void IMenuDef::emit_float(const char* name, float value) + { + if (value) + { + fprintf(fp, "%s%s %g\n", get_tabs(), name, value); + } + } + + void IMenuDef::emit_rect(const char* name, rectDef_s& rect) + { + // TODO: Some pre-processing may be needed + fprintf(fp, "%s%s %g %g %g %g %i %i\n", get_tabs(), name, rect.x, rect.y, rect.w, rect.h, rect.horzAlign, rect.vertAlign); + } + + void IMenuDef::emit_color(const char* name, vec4_t& color) + { + if (color[0] > 0.0f || color[1] > 0.0f || color[2] > 0.0f || color[3] > 0.0f) + { + fprintf(fp, "%s%s %g %g %g %g\n", get_tabs(), name, color[0], color[1], color[2], color[3]); + } + } + + const char* IMenuDef::get_tabs() + { + static char tabs[10]; + + for (auto i = 0; i < indentCounter && i < 10; i++) + { + tabs[i] = '\t'; + } + + tabs[indentCounter] = 0; + return tabs; + } + + void IMenuDef::replace_all(std::string& str, std::string from, std::string to, bool case_insensitive) + { + if (case_insensitive) + { + auto replace = [&](std::string from, std::string to) -> bool + { + std::string lowered(str); + std::transform(str.begin(), str.end(), lowered.begin(), ::tolower); + + auto pos = lowered.find(from); + if (pos == std::string::npos) + { + return false; + } + + str.replace(pos, from.length(), to); + return true; + }; + + std::transform(from.begin(), from.end(), from.begin(), ::tolower); + while (replace(from, to)); + } + else + { + std::size_t pos = 0; + while ((pos = str.find(from, pos)) != std::string::npos) + { + str.replace(pos, from.length(), to); + pos += to.length(); + } + } + } + + std::string IMenuDef::escape_string(const char* value) + { + std::string out(value); + replace_all(out, "\t", "\\t"); + replace_all(out, "\n", "\\n"); + return out; + } + + std::string IMenuDef::format_script(const char* value) + { + std::string out = escape_string(value); + const std::string tabs = get_tabs(); + + // clean indentation and semi colons + out += "\n"; + replace_all(out, "\t;", "\t"); + replace_all(out, tabs + "\n", ""); + replace_all(out, " ; \n", ";\n"); + replace_all(out, " \n", ";\n"); + replace_all(out, " ; ", ";\n" + tabs); + replace_all(out, "; ", ";\n" + tabs); + + // remove quotes from keywords + replace_all(out, "\"self\"", "self"); + replace_all(out, "\"forecolor\"", "forecolor"); + replace_all(out, "\"backcolor\"", "backcolor"); + replace_all(out, "\"bordercolor\"", "bordercolor"); + + // remove quotes from commands + for (auto i = 0; i < 62; i++) + { + replace_all(out, "\""s + g_commandList[i] + "\""s, g_commandList[i], true); + } + + return out; + } + } +} \ No newline at end of file diff --git a/src/IW4/Assets/MenuDef.hpp b/src/IW4/Assets/MenuDef.hpp new file mode 100644 index 0000000..b90880b --- /dev/null +++ b/src/IW4/Assets/MenuDef.hpp @@ -0,0 +1,65 @@ +// ======================= ZoneTool ======================= +// zonetool, a fastfile linker for various +// Call of Duty titles. +// +// Project: https://github.com/ZoneTool/zonetool +// Author: RektInator (https://github.com/RektInator) +// License: GNU GPL v3.0 +// ======================================================== +#pragma once + +namespace ZoneTool +{ + namespace IW4 + { + class IMenuDef : public IAsset + { + private: + std::string name_; + menuDef_t* asset_ = nullptr; + + public: + void init(const std::string& name, ZoneMemory* mem) override; + + std::string name() override; + std::int32_t type() override; + + static int indentCounter; + static FILE* fp; + static void dump(menuDef_t* asset); + + static void emit_menu_def(menuDef_t* asset); + static void emit_item_def(itemDef_t* item); + static void emit_window_def(windowDef_t* window, bool is_item); + static void emit_set_local_var_data(SetLocalVarData* data, EventType type); + static void emit_conditional_script(ConditionalScript* script); + static void emit_statement(const char* name, Statement_s* statement, bool semiColon = false); + static void emit_menu_event_handler_set(const char* name, MenuEventHandlerSet* set); + static void emit_item_key_handler(const char* name, ItemKeyHandler* handler); + static void emit_item_float_expressions(ItemFloatExpression* expression, int count); + static void emit_column_info(columnInfo_s* columns, int count); + static void emit_list_box(listBoxDef_s* listBox); + static void emit_multi_def(multiDef_s* multiDef); + static void emit_item_def_data(itemDefData_t* data, int type); + static void emit_dvar_flags(int dvarFlags, const char* dvarTest, const char* enableDvar); + static void emit_static_flags(int flags); + static void emit_dynamic_flags(int flags); + static void emit_color(const char* name, vec4_t& color); + static void emit_rect(const char* name, rectDef_s& rect); + static void emit_string(const char* name, const char* value); + static void emit_float(const char* name, float value); + static void emit_int(const char* name, int value); + static void emit_bool(const char* name, bool value); + + static void emit_open_brace(); + static void emit_closing_brace(); + + static void push_indent(); + static void pop_indent(); + static const char* get_tabs(); + static void replace_all(std::string& output, std::string from, std::string to, bool case_insensitive = false); + static std::string escape_string(const char* value); + static std::string format_script(const char* value); + }; + } +} \ No newline at end of file diff --git a/src/IW4/IW4.cpp b/src/IW4/IW4.cpp index 7fa9cbb..81102b7 100644 --- a/src/IW4/IW4.cpp +++ b/src/IW4/IW4.cpp @@ -294,6 +294,7 @@ char**>(0x00799278)[type]); DECLARE_ASSET(weapon, IWeaponDef); DECLARE_ASSET(addon_map_ents, IAddonMapEnts); DECLARE_ASSET(font, IFontDef); + DECLARE_ASSET(menu, IMenuDef); } } } diff --git a/src/IW4/Structs.hpp b/src/IW4/Structs.hpp index e846684..a3cc586 100644 --- a/src/IW4/Structs.hpp +++ b/src/IW4/Structs.hpp @@ -2160,6 +2160,636 @@ namespace ZoneTool }; #pragma pack(pop) +#pragma pack(push, 4) + struct rectDef_s + { + float x; + float y; + float w; + float h; + char horzAlign; + char vertAlign; + }; +#pragma pack(pop) + + struct expression_s; + struct statement_s; + struct menuDef_t; + //enum operationEnum; + struct MenuEventHandlerSet; + struct Statement_s; + + enum operationEnum + { + OP_NOOP = 0x0, + OP_RIGHTPAREN = 0x1, + 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_BITWISEAND = 0x12, + OP_BITWISEOR = 0x13, + OP_BITWISENOT = 0x14, + OP_BITSHIFTLEFT = 0x15, + OP_BITSHIFTRIGHT = 0x16, + OP_FIRSTFUNCTIONCALL = 0x17, + OP_STATICDVARINT = 0x17, + OP_STATICDVARBOOL = 0x18, + OP_STATICDVARFLOAT = 0x19, + OP_STATICDVARSTRING = 0x1A, + OP_INT = 0x1B, + OP_STRING = 0x1C, + OP_FLOAT = 0x1D, + OP_SIN = 0x1E, + OP_COS = 0x1F, + OP_MIN = 0x20, + OP_MAX = 0x21, + OP_MILLISECONDS = 0x22, + OP_DVARINT = 0x23, + OP_DVARBOOL = 0x24, + OP_DVARFLOAT = 0x25, + OP_DVARSTRING = 0x26, + OP_STAT = 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_INKILLCAMNPC = 0x30, + OP_PLAYERFIELD = 0x31, + OP_GETPERK = 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_INPRIVATEPARTY = 0x3C, + OP_PRIVATEPARTYHOST = 0x3D, + OP_PRIVATEPARTYHOSTINLOBBY = 0x3E, + OP_ALONEINPARTY = 0x3F, + OP_ADSJAVELIN = 0x40, + OP_WEAPLOCKBLINK = 0x41, + OP_WEAPATTACKTOP = 0x42, + OP_WEAPATTACKDIRECT = 0x43, + OP_WEAPLOCKING = 0x44, + OP_WEAPLOCKED = 0x45, + OP_WEAPLOCKTOOCLOSE = 0x46, + OP_WEAPLOCKSCREENPOSX = 0x47, + OP_WEAPLOCKSCREENPOSY = 0x48, + OP_SECONDSASTIME = 0x49, + OP_TABLELOOKUP = 0x4A, + OP_TABLELOOKUPBYROW = 0x4B, + OP_TABLEGETROWNUM = 0x4C, + OP_LOCALIZESTRING = 0x4D, + OP_LOCALVARINT = 0x4E, + OP_LOCALVARBOOL = 0x4F, + OP_LOCALVARFLOAT = 0x50, + OP_LOCALVARSTRING = 0x51, + OP_TIMELEFT = 0x52, + OP_SECONDSASCOUNTDOWN = 0x53, + OP_GAMEMSGWNDACTIVE = 0x54, + OP_GAMETYPENAME = 0x55, + OP_GAMETYPE = 0x56, + OP_GAMETYPEDESCRIPTION = 0x57, + OP_SCORE = 0x58, + OP_FRIENDSONLINE = 0x59, + OP_SPECTATINGCLIENT = 0x5A, + OP_SPECTATINGFREE = 0x5B, + OP_STATRANGEBITSSET = 0x5C, + OP_KEYBINDING = 0x5D, + OP_ACTIONSLOTUSABLE = 0x5E, + OP_HUDFADE = 0x5F, + OP_MAXRECOMMENDEDPLAYERS = 0x60, + OP_ACCEPTINGINVITE = 0x61, + OP_ISINTERMISSION = 0x62, + OP_GAMEHOST = 0x63, + OP_PARTYISMISSINGMAPPACK = 0x64, + OP_PARTYMISSINGMAPPACKERROR = 0x65, + OP_ANYNEWMAPPACKS = 0x66, + OP_AMISELECTED = 0x67, + OP_PARTYSTATUSSTRING = 0x68, + OP_ATTACHEDCONTROLLERCOUNT = 0x69, + OP_ISSPLITSCREENONLINEPOSSIBLE = 0x6A, + OP_SPLITSCREENPLAYERCOUNT = 0x6B, + OP_GETPLAYERDATA = 0x6C, + OP_GETPLAYERDATASPLITSCREEN = 0x6D, + OP_EXPERIENCEFORLEVEL = 0x6E, + OP_LEVELFOREXPERIENCE = 0x6F, + OP_ISITEMUNLOCKED = 0x70, + OP_ISITEMUNLOCKEDSPLITSCREEN = 0x71, + OP_DEBUGPRINT = 0x72, + OP_GETPLAYERDATAANYBOOLTRUE = 0x73, + OP_WEAPONCLASSNEW = 0x74, + OP_WEAPONNAME = 0x75, + OP_ISRELOADING = 0x76, + OP_SAVEGAMEAVAILABLE = 0x77, + OP_UNLOCKEDITEMCOUNT = 0x78, + OP_UNLOCKEDITEMCOUNTSPLITSCREEN = 0x79, + OP_UNLOCKEDITEM = 0x7A, + OP_UNLOCKEDITEMSPLITSCREEN = 0x7B, + OP_MAILSUBJECT = 0x7C, + OP_MAILFROM = 0x7D, + OP_MAILRECEIVED = 0x7E, + OP_MAILBODY = 0x7F, + OP_MAILLOOTLOCALIZED = 0x80, + OP_MAILGIVESLOOT = 0x81, + OP_ANYNEWMAIL = 0x82, + OP_MAILTIMETOFOLLOWUP = 0x83, + OP_MAILLOOTTYPE = 0x84, + OP_MAILRANLOTTERY = 0x85, + OP_LOTTERYLOOTLOCALIZED = 0x86, + OP_RADARISJAMMED = 0x87, + OP_RADARJAMINTENSITY = 0x88, + OP_RADARISENABLED = 0x89, + OP_ISEMPJAMMED = 0x8A, + OP_PLAYERADS = 0x8B, + OP_WEAPONHEATACTIVE = 0x8C, + OP_WEAPONHEATVALUE = 0x8D, + OP_WEAPONHEATOVERHEATED = 0x8E, + OP_GETSPLASHTEXT = 0x8F, + OP_GETSPLASHDESCRIPTION = 0x90, + OP_GETSPLASHMATERIAL = 0x91, + OP_SPLASHHASICON = 0x92, + OP_SPLASHROWNUM = 0x93, + OP_GETFOCUSEDITEMNAME = 0x94, + OP_GETFOCUSEDITEMX = 0x95, + OP_GETFOCUSEDITEMY = 0x96, + OP_GETFOCUSEDITEMWIDTH = 0x97, + OP_GETFOCUSEDITEMHEIGHT = 0x98, + OP_GETITEMX = 0x99, + OP_GETITEMY = 0x9A, + OP_GETITEMWIDTH = 0x9B, + OP_GETITEMHEIGHT = 0x9C, + OP_PLAYLIST = 0x9D, + OP_SCOREBOARDEXTERNALMUTENOTICE = 0x9E, + OP_GETCLIENTMATCHDATA = 0x9F, + OP_GETCLIENTMATCHDATADEF = 0xA0, + OP_GETMAPNAME = 0xA1, + OP_GETMAPIMAGE = 0xA2, + OP_GETMAPCUSTOM = 0xA3, + OP_GETMIGRATIONSTATUS = 0xA4, + OP_GETPLAYERCARDINFO = 0xA5, + OP_ISOFFLINEPROFILESELECTED = 0xA6, + OP_COOPPLAYER = 0xA7, + OP_ISCOOP = 0xA8, + OP_GETPARTYSTATUS = 0xA9, + OP_GETSEARCHPARAMS = 0xAA, + OP_GETTIMEPLAYED = 0xAB, + OP_ISSELECTEDPLAYERFRIEND = 0xAC, + OP_GETCHARBYINDEX = 0xAD, + OP_GETPROFILEDATA = 0xAE, + OP_ISPROFILESIGNEDIN = 0xAF, + OP_GETWAITPOPUPSTATUS = 0xB0, + OP_GETNATTYPE = 0xB1, + OP_GETLOCALIZEDNATTYPE = 0xB2, + OP_GETADJUSTEDSAFEAREAHORIZONTAL = 0xB3, + OP_GETADJUSTEDSAFEAREAVERTICAL = 0xB4, + OP_CONNECTIONINFO = 0xB5, + OP_OFFLINEPROFILECANSAVE = 0xB6, + OP_ALLSPLITSCREENPROFILESCANSAVE = 0xB7, + OP_ALLSPLITSCREENPROFILESARESIGNEDIN = 0xB8, + OP_DOWEHAVEMAPPACK = 0xB9, + }; + + /* windowDef_t->dynamicFlags */ + // 0x1 + #define WINDOWDYNAMIC_HASFOCUS 0x00000002 + #define WINDOWDYNAMIC_VISIBLE 0x00000004 + #define WINDOWDYNAMIC_FADEOUT 0x00000010 + #define WINDOWDYNAMIC_FADEIN 0x00000020 + // 0x40 + // 0x80 + #define WINDOWDYNAMIC_CLOSED 0x00000800 + // 0x2000 + #define WINDOWDYNAMIC_BACKCOLOR 0x00008000 + #define WINDOWDYNAMIC_FORECOLOR 0x00010000 + + /* windowDef_t->staticFlags */ + #define WINDOWSTATIC_DECORATION 0x00100000 + #define WINDOWSTATIC_HORIZONTALSCROLL 0x00200000 + #define WINDOWSTATIC_SCREENSPACE 0x00400000 + #define WINDOWSTATIC_AUTOWRAPPED 0x00800000 + #define WINDOWSTATIC_POPUP 0x01000000 + #define WINDOWSTATIC_OUTOFBOUNDSCLICK 0x02000000 + #define WINDOWSTATIC_LEGACYSPLITSCREENSCALE 0x04000000 + #define WINDOWSTATIC_HIDDENDURINGFLASH 0x10000000 + #define WINDOWSTATIC_HIDDENDURINGSCOPE 0x20000000 + #define WINDOWSTATIC_HIDDENDURINGUI 0x40000000 + #define WINDOWSTATIC_TEXTONLYFOCUS 0x80000000 + + #define ITEM_TYPE_TEXT 0 // simple text + #define ITEM_TYPE_BUTTON 1 // button, basically text with a border + #define ITEM_TYPE_RADIOBUTTON 2 // toggle button, may be grouped + #define ITEM_TYPE_CHECKBOX 3 // check box + #define ITEM_TYPE_EDITFIELD 4 // editable text, associated with a dvar + #define ITEM_TYPE_COMBO 5 // drop down list + #define ITEM_TYPE_LISTBOX 6 // scrollable list + #define ITEM_TYPE_MODEL 7 // model + #define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is + #define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a dvar + #define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. + #define ITEM_TYPE_YESNO 11 // yes no dvar setting + #define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated + #define ITEM_TYPE_DVARENUM 13 // multiple list setting, enumerated from a dvar + #define ITEM_TYPE_BIND 14 // bind + #define ITEM_TYPE_MENUMODEL 15 // special menu model + #define ITEM_TYPE_VALIDFILEFIELD 16 // text must be valid for use in a dos filename + #define ITEM_TYPE_DECIMALFIELD 17 // editable text, associated with a dvar, which allows decimal input + #define ITEM_TYPE_UPREDITFIELD 18 // editable text, associated with a dvar + #define ITEM_TYPE_GAME_MESSAGE_WINDOW 19 // game message window + #define ITEM_TYPE_NEWSTICKER 20 // horizontal scrollbox + #define ITEM_TYPE_TEXTSCROLL 21 // vertical scrollbox + #define ITEM_TYPE_EMAILFIELD 22 + #define ITEM_TYPE_PASSWORDFIELD 23 + + struct UIFunctionList + { + int totalFunctions; + Statement_s** functions; + }; + + struct StaticDvar + { + /*dvar_t*/ + void* dvar; + const char* dvarName; + }; + + struct StaticDvarList + { + int numStaticDvars; + StaticDvar** staticDvars; + }; + + struct StringList + { + int totalStrings; + const char** strings; + }; + + struct ExpressionSupportingData + { + UIFunctionList uifunctions; + StaticDvarList staticDvarList; + StringList uiStrings; + }; + + enum expDataType : int + { + VAL_INT = 0x0, + VAL_FLOAT = 0x1, + VAL_STRING = 0x2, + VAL_FUNCTION = 0x3, + }; + + struct ExpressionString + { + const char* string; + }; + + union operandInternalDataUnion + { + int intVal; + float floatVal; + ExpressionString stringVal; + Statement_s* function; + }; + + struct Operand + { + expDataType dataType; + operandInternalDataUnion internals; + }; + + union entryInternalData + { + operationEnum op; /* operationEnum */ + Operand operand; + }; + + /* expressionEntry->type */ + #define OPERATOR 0 + #define OPERAND 1 + + struct expressionEntry // 0xC + { + int type; + entryInternalData data; + }; + + struct Statement_s // 0x18 + { + int numEntries; + expressionEntry* entries; + ExpressionSupportingData* supportingData; + int lastExecuteTime; + Operand lastResult; + }; + + struct SetLocalVarData + { + const char* localVarName; + Statement_s* expression; + }; + + struct ConditionalScript + { + MenuEventHandlerSet* eventHandlerSet; + Statement_s* eventExpression; // loads this first + }; + + union EventData + { + const char* unconditionalScript; + ConditionalScript* conditionalScript; + MenuEventHandlerSet* elseScript; + SetLocalVarData* setLocalVarData; + }; + + enum EventType + { + EVENT_UNCONDITIONAL = 0x0, + EVENT_IF = 0x1, + EVENT_ELSE = 0x2, + EVENT_SET_LOCAL_VAR_BOOL = 0x3, + EVENT_SET_LOCAL_VAR_INT = 0x4, + EVENT_SET_LOCAL_VAR_FLOAT = 0x5, + EVENT_SET_LOCAL_VAR_STRING = 0x6, + EVENT_COUNT = 0x7, + }; + + struct MenuEventHandler + { + EventData eventData; + EventType eventType; + }; + + struct MenuEventHandlerSet + { + int eventHandlerCount; + MenuEventHandler** eventHandlers; + }; + + struct ItemKeyHandler + { + int key; + MenuEventHandlerSet* action; + ItemKeyHandler* next; + }; + + struct windowDef_t // 0xA4 + { + const char* name; // 0x00 + rectDef_s rect; + rectDef_s rectClient; + char* group; // 0x2C + int style; // 0x30 + int border; // 0x34 + int ownerDraw; // 0x38 + int ownerDrawFlags; // 0x3C + float borderSize; // 0x40 + int staticFlags; // 0x44 + int dynamicFlags; // 0x48 + int nextTime; // 0x4C + float foreColor[4]; // 0x50 + float backColor[4]; // 0x60 + float borderColor[4];// 0x70 + float outlineColor[4];// 0x80 + float disableColor[4];// 0x90 + Material* background; // 0xA0 + }; + + 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 = 0x13, + }; + + struct ItemFloatExpression + { + ItemFloatExpressionTarget target; + Statement_s* expression; + }; + + struct editFieldDef_s + { + float minVal; + float maxVal; + float defVal; + float range; + int maxChars; + int maxCharsGotoNext; + int maxPaintChars; + int paintOffset; + }; + + struct multiDef_s // 0x188 + { + const char* dvarList[32]; + const char* dvarStr[32]; + float dvarValue[32]; + int count; + int strDef; + }; + + struct columnInfo_s + { + int xpos; + int width; + int maxChars; + int alignment; + }; + + struct listBoxDef_s // 0x144 + { + int mousePos; + int startPos[1]; + int endPos[1]; + int drawPadding; + float elementWidth; + float elementHeight; + int elementStyle; + int numColumns; + columnInfo_s columnInfo[16]; + MenuEventHandlerSet* doubleClick; // 0xC8 + int notselectable; + int noscrollbars; + int usepaging; + float selectBorder[4]; + Material* selectIcon; + }; + + struct newsTickerDef_s + { + int feedId; + int speed; + int spacing; + int lastTime; + int start; + int end; + float x; + }; + + struct textScrollDef_s + { + int startTime; + }; + + union itemDefData_t + { + listBoxDef_s* listBox; + editFieldDef_s* editField; + newsTickerDef_s* ticker; + multiDef_s* multiDef; + const char* enumDvarName; + textScrollDef_s* scroll; + void* data; + }; + + struct itemDef_t + { + windowDef_t window; + rectDef_s textRect[1]; + int type; + int dataType; + int alignment; + int fontEnum; + int textAlignMode; + float textAlignX; + float textAlignY; + float textScale; + int textStyle; + int gameMsgWindowIndex; + int gameMsgWindowMode; + const char* text; + int textSaveGameInfo; + int parent; + MenuEventHandlerSet* mouseEnterText; + MenuEventHandlerSet* mouseExitText; + MenuEventHandlerSet* mouseEnter; + MenuEventHandlerSet* mouseExit; + MenuEventHandlerSet* action; + MenuEventHandlerSet* accept; + MenuEventHandlerSet* onFocus; + MenuEventHandlerSet* leaveFocus; + const char* dvar; + const char* dvarTest; + ItemKeyHandler* onKey; + const char* enableDvar; + const char* localVar; + int dvarFlags; + const char* focusSound; + float special; + int cursorPos[1]; + itemDefData_t typeData; + int imageTrack; + int floatExpressionCount; + ItemFloatExpression* floatExpressions; + Statement_s* visibleExp; + Statement_s* disabledExp; + Statement_s* textExp; + Statement_s* materialExp; + float glowColor[4]; + bool decayActive; + int fxBirthTime; + int fxLetterTime; + int fxDecayStartTime; + int fxDecayDuration; + int lastSoundPlayedTime; + }; + + struct menuTransition // 0x1C + { + int transitionType; + int targetField; + int startTime; + float startVal; + float endVal; + float time; + int endTriggerType; + }; + + struct menuDef_t + { + windowDef_t window; + int font; + int fullscreen; + int itemCount; + int fontIndex; + int cursorItems; + int fadeCycle; + float fadeClamp; + float fadeAmount; + float fadeInAmount; + float blurRadius; + MenuEventHandlerSet* onOpen; + MenuEventHandlerSet* onRequestClose; + MenuEventHandlerSet* onClose; + MenuEventHandlerSet* onEsc; + ItemKeyHandler* onKey; + Statement_s* visibleExp; + const char* allowedBinding; + const char* soundLoop; + int imageTrack; + float focusColor[4]; + Statement_s* rectXExp; + Statement_s* rectYExp; + Statement_s* rectHExp; + Statement_s* rectWExp; + Statement_s* openSoundExp; + Statement_s* closeSoundExp; + itemDef_t** items; + menuTransition scaleTransition[1]; + menuTransition alphaTransition[1]; + menuTransition xTransition[1]; + menuTransition yTransition[1]; + ExpressionSupportingData* expressionData; + }; + + struct MenuList + { + const char* name; + int menuCount; + menuDef_t** menus; + }; + // Localized Strings struct LocalizeEntry { @@ -3482,6 +4112,7 @@ namespace ZoneTool StringTable* stringtable; ComWorld* comworld; ComWorld* com_map; + menuDef_t* menu; LocalizeEntry* localize; SndCurve* soundcurve; SndCurve* sndcurve; diff --git a/src/IW4/Zone.cpp b/src/IW4/Zone.cpp index a212d91..4f808c0 100644 --- a/src/IW4/Zone.cpp +++ b/src/IW4/Zone.cpp @@ -128,6 +128,7 @@ namespace ZoneTool DECLARE_ASSET(lightdef, ILightDef); DECLARE_ASSET(structureddatadef, IStructuredDataDef); DECLARE_ASSET(addon_map_ents, IAddonMapEnts); + DECLARE_ASSET(menu, IMenuDef); } void Zone::add_asset_of_type(const std::string& type, const std::string& name) diff --git a/src/IW4/Zone.hpp b/src/IW4/Zone.hpp index f85962f..ae15c06 100644 --- a/src/IW4/Zone.hpp +++ b/src/IW4/Zone.hpp @@ -41,6 +41,7 @@ extern std::string currentzone; #include "Assets/LightDef.hpp" #include "Assets/StructuredDataDef.hpp" #include "Assets/AddonMapEnts.hpp" +#include "Assets/MenuDef.hpp" namespace ZoneTool {