#include #include "ImenuDef_t.hpp" namespace Assets { std::unordered_map ImenuDef_t::LoadedMenus; void ImenuDef_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) { // load from disk auto menus = Components::Menus::LoadMenu(Utils::String::VA("ui_mp/%s.menu", name.data())); if (menus.empty()) return; if (menus.size() > 1) Components::Logger::Print("Menu '{}' on disk has more than one menudef in it. Only saving the first one\n", name); header->menu = menus[0].second; } void ImenuDef_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { auto* asset = header.menu; if (asset->window.background) { builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->window.background); } // mark items for (int i = 0; i < asset->itemCount; ++i) { if (asset->items[i]->window.background) { builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->items[i]->window.background); } if (asset->items[i]->focusSound) { builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->items[i]->focusSound); } if (asset->items[i]->type == 6 && asset->items[i]->typeData.listBox && asset->items[i]->typeData.listBox->selectIcon) { builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->items[i]->typeData.listBox->selectIcon); } } } void ImenuDef_t::save_ExpressionSupportingData(Game::ExpressionSupportingData* asset, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::ExpressionSupportingData, 24); Utils::Stream* buffer = builder->getBuffer(); #ifdef WRITE_LOGS buffer->enterStruct("ExpressionSupportingData"); #endif buffer->align(Utils::Stream::ALIGN_4); auto* dest = buffer->dest(); buffer->save(asset); if (asset->uifunctions.functions) { buffer->align(Utils::Stream::ALIGN_4); auto** destStatement = buffer->dest(); buffer->saveArray(asset->uifunctions.functions, asset->uifunctions.totalFunctions); for (int i = 0; i < asset->uifunctions.totalFunctions; ++i) { if (asset->uifunctions.functions[i]) { Utils::Stream::ClearPointer(&destStatement[i]); buffer->align(Utils::Stream::ALIGN_4); this->save_Statement_s(asset->uifunctions.functions[i], builder); } } Utils::Stream::ClearPointer(&dest->uifunctions.functions); } if (asset->staticDvarList.staticDvars) { buffer->align(Utils::Stream::ALIGN_4); auto** destStaticDvars = buffer->dest(); buffer->saveArray(asset->staticDvarList.staticDvars, asset->staticDvarList.numStaticDvars); for (auto i = 0; i < asset->staticDvarList.numStaticDvars; ++i) { if (asset->staticDvarList.staticDvars[i]) { Utils::Stream::ClearPointer(&destStaticDvars[i]); buffer->align(Utils::Stream::ALIGN_4); auto* destStaticDvar = buffer->dest(); buffer->save(asset->staticDvarList.staticDvars[i]); if (asset->staticDvarList.staticDvars[i]->dvarName) { buffer->saveString(asset->staticDvarList.staticDvars[i]->dvarName); Utils::Stream::ClearPointer(&destStaticDvar->dvarName); } } } Utils::Stream::ClearPointer(&dest->staticDvarList.staticDvars); } if (asset->uiStrings.strings) { buffer->align(Utils::Stream::ALIGN_4); const auto** destUIStrings = buffer->dest(); buffer->saveArray(asset->uiStrings.strings, asset->uiStrings.totalStrings); for (int i = 0; i < asset->uiStrings.totalStrings; ++i) { if (asset->uiStrings.strings[i]) { buffer->saveString(asset->uiStrings.strings[i]); Utils::Stream::ClearPointer(&destUIStrings[i]); } } } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } void ImenuDef_t::save_Statement_s(Game::Statement_s* asset, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::Statement_s, 24); AssertSize(Game::expressionEntry, 12); Utils::Stream* buffer = builder->getBuffer(); #ifdef WRITE_LOGS buffer->enterStruct("Statement_s"); #endif // Write header data auto* dest = buffer->dest(); buffer->save(asset); // Write statement entries if (asset->entries) { #ifdef WRITE_LOGS buffer->enterStruct("statement entries"); #endif buffer->align(Utils::Stream::ALIGN_4); // Write entries auto* destEntries = buffer->dest(); buffer->save(asset->entries, sizeof(Game::expressionEntry), asset->numEntries); // Loop through entries for (int i = 0; i < asset->numEntries; ++i) { #ifdef WRITE_LOGS buffer->enterStruct("entry"); #endif if (asset->entries[i].type) { switch (asset->entries[i].data.operand.dataType) { // Those types do not require additional data case 0: case 1: break; // Expression string case 2: if (asset->entries[i].data.operand.internals.stringVal.string) { buffer->saveString(asset->entries[i].data.operand.internals.stringVal.string); Utils::Stream::ClearPointer(&destEntries[i].data.operand.internals.stringVal.string); } break; // Function case 3: if (asset->entries[i].data.operand.internals.function) { buffer->align(Utils::Stream::ALIGN_4); this->save_Statement_s(asset->entries[i].data.operand.internals.function, builder); Utils::Stream::ClearPointer(&destEntries[i].data.operand.internals.function); } break; } } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } if (asset->supportingData) { this->save_ExpressionSupportingData(asset->supportingData, builder); Utils::Stream::ClearPointer(&dest->supportingData); } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } void ImenuDef_t::save_MenuEventHandlerSet(Game::MenuEventHandlerSet* asset, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::MenuEventHandlerSet, 8); Utils::Stream* buffer = builder->getBuffer(); #ifdef WRITE_LOGS buffer->enterStruct("MenuEventHandlerSet"); #endif // Write header data auto* destset = buffer->dest(); buffer->save(asset); // Event handlers if (asset->eventHandlers) { buffer->align(Utils::Stream::ALIGN_4); // Write pointers to zone buffer->save(asset->eventHandlers, sizeof(Game::MenuEventHandler*), asset->eventHandlerCount); // Loop through eventHandlers for (auto i = 0; i < asset->eventHandlerCount; ++i) { if (asset->eventHandlers[i]) { buffer->align(Utils::Stream::ALIGN_4); #ifdef WRITE_LOGS buffer->enterStruct("MenuEventHandler"); #endif // Write menu event handler auto* dest = buffer->dest(); buffer->save(asset->eventHandlers[i]); // Write additional data based on type switch (asset->eventHandlers[i]->eventType) { // unconditional scripts case 0: if (asset->eventHandlers[i]->eventData.unconditionalScript) { buffer->saveString(asset->eventHandlers[i]->eventData.unconditionalScript); Utils::Stream::ClearPointer(&dest->eventData.unconditionalScript); } break; // ConditionalScript case 1: if (asset->eventHandlers[i]->eventData.conditionalScript) { buffer->align(Utils::Stream::ALIGN_4); auto* destConditionalScript = buffer->dest(); buffer->save(asset->eventHandlers[i]->eventData.conditionalScript); // eventExpression if (asset->eventHandlers[i]->eventData.conditionalScript->eventExpression) { buffer->align(Utils::Stream::ALIGN_4); this->save_Statement_s(asset->eventHandlers[i]->eventData.conditionalScript->eventExpression, builder); Utils::Stream::ClearPointer(&destConditionalScript->eventExpression); } // eventHandlerSet if (asset->eventHandlers[i]->eventData.conditionalScript->eventHandlerSet) { buffer->align(Utils::Stream::ALIGN_4); this->save_MenuEventHandlerSet(asset->eventHandlers[i]->eventData.conditionalScript->eventHandlerSet, builder); Utils::Stream::ClearPointer(&destConditionalScript->eventHandlerSet); } Utils::Stream::ClearPointer(&dest->eventData.conditionalScript); } break; // elseScript case 2: if (asset->eventHandlers[i]->eventData.elseScript) { buffer->align(Utils::Stream::ALIGN_4); this->save_MenuEventHandlerSet(asset->eventHandlers[i]->eventData.elseScript, builder); Utils::Stream::ClearPointer(&dest->eventData.elseScript); } break; // localVarData expressions case 3: case 4: case 5: case 6: if (asset->eventHandlers[i]->eventData.setLocalVarData) { buffer->align(Utils::Stream::ALIGN_4); // header data auto* destLocalVarData = buffer->dest(); buffer->save(asset->eventHandlers[i]->eventData.setLocalVarData); // localVarName if (asset->eventHandlers[i]->eventData.setLocalVarData->localVarName) { buffer->saveString(asset->eventHandlers[i]->eventData.setLocalVarData->localVarName); Utils::Stream::ClearPointer(&destLocalVarData->localVarName); } // statement if (asset->eventHandlers[i]->eventData.setLocalVarData->expression) { buffer->align(Utils::Stream::ALIGN_4); this->save_Statement_s(asset->eventHandlers[i]->eventData.setLocalVarData->expression, builder); Utils::Stream::ClearPointer(&destLocalVarData->expression); } Utils::Stream::ClearPointer(&dest->eventData.setLocalVarData); } break; } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } } Utils::Stream::ClearPointer(&destset->eventHandlers); } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } void ImenuDef_t::save_ItemKeyHandler(Game::ItemKeyHandler* asset, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::ItemKeyHandler, 12); Utils::Stream* buffer = builder->getBuffer(); #ifdef WRITE_LOGS buffer->enterStruct("ItemKeyHandler"); #endif while (asset) { // Write header auto* dest = buffer->dest(); buffer->save(asset); // MenuEventHandlerSet if (asset->action) { buffer->align(Utils::Stream::ALIGN_4); this->save_MenuEventHandlerSet(asset->action, builder); Utils::Stream::ClearPointer(&dest->action); } if (asset->next) { // align every index, besides the first one? buffer->align(Utils::Stream::ALIGN_4); } // Next key handler asset = asset->next; } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } #define EVENTHANDLERSET(__index) \ if (asset->__index) \ { \ buffer->align(Utils::Stream::ALIGN_4); \ this->save_MenuEventHandlerSet(asset->__index, builder); \ Utils::Stream::ClearPointer(&dest->__index); \ } #define STATEMENT(__index) \ if (asset->__index) \ { \ buffer->align(Utils::Stream::ALIGN_4); \ this->save_Statement_s(asset->__index, builder); \ Utils::Stream::ClearPointer(&dest->__index); \ } void ImenuDef_t::save_itemDefData_t(Game::itemDefData_t* asset, int type, Game::itemDef_s* dest, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::newsTickerDef_s, 28); AssertSize(Game::listBoxDef_s, 324); AssertSize(Game::editFieldDef_s, 32); AssertSize(Game::multiDef_s, 392); Utils::Stream* buffer = builder->getBuffer(); #ifdef WRITE_LOGS buffer->enterStruct("itemDefData_t"); #endif // feeder if (type == 6) { buffer->align(Utils::Stream::ALIGN_4); auto* destlb = buffer->dest(); buffer->save(asset->listBox); if (asset->listBox->onDoubleClick) { buffer->align(Utils::Stream::ALIGN_4); this->save_MenuEventHandlerSet(asset->listBox->onDoubleClick, builder); } if (asset->listBox->selectIcon) { destlb->selectIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->listBox->selectIcon).material; } } // HexRays spaghetti else if (type != 4 && type != 9 && type != 16 && type != 18 && type != 11 && type != 14 && type != 10 && type != 17 && type != 22 && type != 23 && type != 0) { switch (type) { // enum dvar case 13: buffer->saveString(asset->enumDvarName); break; // newsticker case 20: buffer->align(Utils::Stream::ALIGN_4); buffer->save(asset->ticker); break; // textScrollDef case 21: buffer->align(Utils::Stream::ALIGN_4); buffer->save(asset->scroll); break; case 12: buffer->align(Utils::Stream::ALIGN_4); auto* destdef = buffer->dest(); buffer->save(asset->multi); for (int i = 0; i < 32; ++i) { if (asset->multi->dvarList[i]) { buffer->saveString(asset->multi->dvarList[i]); Utils::Stream::ClearPointer(&destdef->dvarList[i]); } } for (int i = 0; i < 32; ++i) { if (asset->multi->dvarStr[i]) { buffer->saveString(asset->multi->dvarStr[i]); Utils::Stream::ClearPointer(&destdef->dvarStr[i]); } } break; } } // editFieldDef else { buffer->align(Utils::Stream::ALIGN_4); buffer->save(asset->editField); } Utils::Stream::ClearPointer(&dest->typeData.data); #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } void ImenuDef_t::save_itemDef_s(Game::itemDef_s *asset, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::itemDef_s, 380); Utils::Stream* buffer = builder->getBuffer(); auto* dest = buffer->dest(); #ifdef WRITE_LOGS if (asset->window.name) buffer->enterStruct(Utils::String::VA("itemDef_s: name = '%s'", asset->window.name)); else if (asset->window.background) buffer->enterStruct(Utils::String::VA("itemDef_s: bg = '%s'", asset->window.background->info.name)); else buffer->enterStruct("itemDef_s"); #endif buffer->save(asset); // window data save_windowDef_t(&asset->window, dest, builder); // text if (asset->text) { buffer->saveString(asset->text); Utils::Stream::ClearPointer(&dest->text); } // MenuEventHandlerSets EVENTHANDLERSET(mouseEnterText); EVENTHANDLERSET(mouseExitText); EVENTHANDLERSET(mouseEnter); EVENTHANDLERSET(mouseExit); EVENTHANDLERSET(action); EVENTHANDLERSET(accept); EVENTHANDLERSET(onFocus); EVENTHANDLERSET(leaveFocus); // Dvar strings if (asset->dvar) { buffer->saveString(asset->dvar); Utils::Stream::ClearPointer(&dest->dvar); } if (asset->dvarTest) { buffer->saveString(asset->dvarTest); Utils::Stream::ClearPointer(&dest->dvarTest); } // ItemKeyHandler if (asset->onKey) { buffer->align(Utils::Stream::ALIGN_4); this->save_ItemKeyHandler(asset->onKey, builder); Utils::Stream::ClearPointer(&dest->onKey); } // Dvar strings if (asset->enableDvar) { buffer->saveString(asset->enableDvar); Utils::Stream::ClearPointer(&dest->enableDvar); } if (asset->localVar) { buffer->saveString(asset->localVar); Utils::Stream::ClearPointer(&dest->localVar); } // Focus sound if (asset->focusSound) { dest->focusSound = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->focusSound).sound; } // itemDefData if (asset->typeData.data) { this->save_itemDefData_t(&asset->typeData, asset->type, dest, builder); } // floatExpressions if (asset->floatExpressions) { buffer->align(Utils::Stream::ALIGN_4); #ifdef WRITE_LOGS buffer->enterStruct("floatExpressions"); #endif auto* destExp = buffer->dest(); buffer->saveArray(asset->floatExpressions, asset->floatExpressionCount); for (int i = 0; i < asset->floatExpressionCount; ++i) { buffer->align(Utils::Stream::ALIGN_4); this->save_Statement_s(asset->floatExpressions[i].expression, builder); Utils::Stream::ClearPointer(&destExp[i].expression); } Utils::Stream::ClearPointer(&dest->floatExpressions); #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } // Statements STATEMENT(visibleExp); STATEMENT(disabledExp); STATEMENT(textExp); STATEMENT(materialExp); #ifdef WRITE_LOGS buffer->leaveStruct(); #endif } void ImenuDef_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::menuDef_t, 400); #ifdef WRITE_LOGS buffer->enterStruct("ImenuDef_t"); #endif Utils::Stream* buffer = builder->getBuffer(); auto* asset = header.menu; auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); // ExpressionSupportingData if (asset->expressionData) { // dest->expressionData = nullptr; this->save_ExpressionSupportingData(asset->expressionData, builder); Utils::Stream::ClearPointer(&dest->expressionData); } // Window data save_windowDef_t(&asset->window, dest, builder); // Font if (asset->font) { buffer->saveString(asset->font); Utils::Stream::ClearPointer(&dest->font); } // MenuEventHandlerSets EVENTHANDLERSET(onOpen); EVENTHANDLERSET(onCloseRequest); EVENTHANDLERSET(onClose); EVENTHANDLERSET(onESC); // ItemKeyHandler if (asset->onKey) { buffer->align(Utils::Stream::ALIGN_4); this->save_ItemKeyHandler(asset->onKey, builder); Utils::Stream::ClearPointer(&dest->onKey); } // Statement STATEMENT(visibleExp); // Strings if (asset->allowedBinding) { buffer->saveString(asset->allowedBinding); Utils::Stream::ClearPointer(&dest->allowedBinding); } if (asset->soundName) { buffer->saveString(asset->soundName); Utils::Stream::ClearPointer(&dest->soundName); } // Statements STATEMENT(rectXExp); STATEMENT(rectYExp); STATEMENT(rectHExp); STATEMENT(rectWExp); STATEMENT(openSoundExp); STATEMENT(closeSoundExp); // Items if (asset->items) { buffer->align(Utils::Stream::ALIGN_4); buffer->saveArray(asset->items, asset->itemCount); for (int i = 0; i < asset->itemCount; ++i) { if (asset->items[i]) { buffer->align(Utils::Stream::ALIGN_4); this->save_itemDef_s(asset->items[i], builder); } } } #ifdef WRITE_LOGS buffer->leaveStruct(); #endif buffer->popBlock(); } }