mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-09-01 06:27:26 +00:00
refactor: streamline menu dumping
This commit is contained in:
@@ -1,53 +0,0 @@
|
|||||||
#include "AssetDumperMenuDef.h"
|
|
||||||
|
|
||||||
#include "AssetDumperMenuList.h"
|
|
||||||
#include "Game/IW4/GameAssetPoolIW4.h"
|
|
||||||
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
|
||||||
#include "ObjWriting.h"
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace IW4;
|
|
||||||
|
|
||||||
std::string AssetDumperMenuDef::GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo<menuDef_t>* asset)
|
|
||||||
{
|
|
||||||
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->Asset());
|
|
||||||
|
|
||||||
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
|
|
||||||
return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu";
|
|
||||||
|
|
||||||
return menuDumpingState->second.m_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssetDumperMenuDef::ShouldDump(XAssetInfo<menuDef_t>* asset)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset)
|
|
||||||
{
|
|
||||||
const auto* menu = asset->Asset();
|
|
||||||
auto* zoneState = context.GetZoneAssetDumperState<menu::MenuDumpingZoneState>();
|
|
||||||
|
|
||||||
if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
|
|
||||||
{
|
|
||||||
// Make sure menu paths based on menu lists are created
|
|
||||||
const auto* gameAssetPool = dynamic_cast<GameAssetPoolIW4*>(asset->m_zone->m_pools.get());
|
|
||||||
for (auto* menuListAsset : *gameAssetPool->m_menu_list)
|
|
||||||
AssetDumperMenuList::CreateDumpingStateForMenuList(zoneState, menuListAsset->Asset());
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto menuFilePath = GetPathForMenu(zoneState, asset);
|
|
||||||
const auto assetFile = context.OpenAssetFile(menuFilePath);
|
|
||||||
|
|
||||||
if (!assetFile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MenuDumper menuDumper(*assetFile);
|
|
||||||
|
|
||||||
menuDumper.Start();
|
|
||||||
menuDumper.WriteMenu(menu);
|
|
||||||
menuDumper.End();
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Dumping/AbstractAssetDumper.h"
|
|
||||||
#include "Game/IW4/IW4.h"
|
|
||||||
#include "Menu/MenuDumpingZoneState.h"
|
|
||||||
|
|
||||||
namespace IW4
|
|
||||||
{
|
|
||||||
class AssetDumperMenuDef final : public AbstractAssetDumper<menuDef_t>
|
|
||||||
{
|
|
||||||
static std::string GetPathForMenu(menu::MenuDumpingZoneState* zoneState, XAssetInfo<menuDef_t>* asset);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool ShouldDump(XAssetInfo<menuDef_t>* asset) override;
|
|
||||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset) override;
|
|
||||||
};
|
|
||||||
} // namespace IW4
|
|
@@ -1,179 +0,0 @@
|
|||||||
#include "AssetDumperMenuList.h"
|
|
||||||
|
|
||||||
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
|
||||||
#include "ObjWriting.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <set>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
using namespace IW4;
|
|
||||||
|
|
||||||
std::vector<const ExpressionSupportingData*> AssetDumperMenuList::GetAllUniqueExpressionSupportingData(const MenuList* menuList)
|
|
||||||
{
|
|
||||||
std::vector<const ExpressionSupportingData*> result;
|
|
||||||
std::set<const ExpressionSupportingData*> 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->expressionData == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (alreadyAddedSupportingData.find(menu->expressionData) == alreadyAddedSupportingData.end())
|
|
||||||
{
|
|
||||||
result.push_back(menu->expressionData);
|
|
||||||
alreadyAddedSupportingData.emplace(menu->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)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "FUNC_" << functionIndex;
|
|
||||||
|
|
||||||
menuDumper.WriteFunctionDef(ss.str(), function);
|
|
||||||
}
|
|
||||||
|
|
||||||
functionIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuList::DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList)
|
|
||||||
{
|
|
||||||
for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++)
|
|
||||||
{
|
|
||||||
const auto* menu = menuList->menus[menuNum];
|
|
||||||
|
|
||||||
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu);
|
|
||||||
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If the menu was embedded directly as menu list write its data in the menu list file
|
|
||||||
if (menuDumpingState->second.m_alias_menu_list == menuList)
|
|
||||||
menuDumper.WriteMenu(menu);
|
|
||||||
else
|
|
||||||
menuDumper.IncludeMenu(menuDumpingState->second.m_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssetDumperMenuList::ShouldDump(XAssetInfo<MenuList>* asset)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset)
|
|
||||||
{
|
|
||||||
const auto* menuList = asset->Asset();
|
|
||||||
const auto assetFile = context.OpenAssetFile(asset->m_name);
|
|
||||||
|
|
||||||
if (!assetFile)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto* zoneState = context.GetZoneAssetDumperState<menu::MenuDumpingZoneState>();
|
|
||||||
|
|
||||||
MenuDumper menuDumper(*assetFile);
|
|
||||||
|
|
||||||
menuDumper.Start();
|
|
||||||
|
|
||||||
if (!ObjWriting::Configuration.MenuLegacyMode)
|
|
||||||
DumpFunctions(menuDumper, menuList);
|
|
||||||
|
|
||||||
DumpMenus(menuDumper, zoneState, menuList);
|
|
||||||
|
|
||||||
menuDumper.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AssetDumperMenuList::PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu)
|
|
||||||
{
|
|
||||||
const auto* menuAssetName = menu->window.name;
|
|
||||||
|
|
||||||
if (!menuAssetName)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
if (menuAssetName[0] == ',')
|
|
||||||
menuAssetName = &menuAssetName[1];
|
|
||||||
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << menuListParentPath << menuAssetName << ".menu";
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuList::CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList)
|
|
||||||
{
|
|
||||||
if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const std::string menuListName(menuList->name);
|
|
||||||
const fs::path p(menuListName);
|
|
||||||
std::string parentPath;
|
|
||||||
if (p.has_parent_path())
|
|
||||||
parentPath = p.parent_path().string() + "/";
|
|
||||||
|
|
||||||
for (auto i = 0; i < menuList->menuCount; i++)
|
|
||||||
{
|
|
||||||
auto* menu = menuList->menus[i];
|
|
||||||
|
|
||||||
if (menu == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto existingState = zoneState->m_menu_dumping_state_map.find(menu);
|
|
||||||
if (existingState == zoneState->m_menu_dumping_state_map.end())
|
|
||||||
{
|
|
||||||
auto menuPath = PathForMenu(parentPath, menu);
|
|
||||||
const auto isTheSameAsMenuList = menuPath == menuListName;
|
|
||||||
zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr);
|
|
||||||
}
|
|
||||||
else if (existingState->second.m_alias_menu_list == nullptr)
|
|
||||||
{
|
|
||||||
auto menuPath = PathForMenu(parentPath, menu);
|
|
||||||
const auto isTheSameAsMenuList = menuPath == menuListName;
|
|
||||||
if (isTheSameAsMenuList)
|
|
||||||
{
|
|
||||||
existingState->second.m_alias_menu_list = menuList;
|
|
||||||
existingState->second.m_path = std::move(menuPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuList::DumpPool(AssetDumpingContext& context, AssetPool<MenuList>* pool)
|
|
||||||
{
|
|
||||||
auto* zoneState = context.GetZoneAssetDumperState<menu::MenuDumpingZoneState>();
|
|
||||||
|
|
||||||
for (auto* asset : *pool)
|
|
||||||
CreateDumpingStateForMenuList(zoneState, asset->Asset());
|
|
||||||
|
|
||||||
AbstractAssetDumper<MenuList>::DumpPool(context, pool);
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Dumping/AbstractAssetDumper.h"
|
|
||||||
#include "Game/IW4/IW4.h"
|
|
||||||
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
|
||||||
#include "Menu/MenuDumpingZoneState.h"
|
|
||||||
|
|
||||||
namespace IW4
|
|
||||||
{
|
|
||||||
class AssetDumperMenuList final : public AbstractAssetDumper<MenuList>
|
|
||||||
{
|
|
||||||
static std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList);
|
|
||||||
|
|
||||||
static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList);
|
|
||||||
static void DumpMenus(MenuDumper& menuDumper, menu::MenuDumpingZoneState* zoneState, const MenuList* menuList);
|
|
||||||
|
|
||||||
static std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool ShouldDump(XAssetInfo<MenuList>* asset) override;
|
|
||||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void CreateDumpingStateForMenuList(menu::MenuDumpingZoneState* zoneState, const MenuList* menuList);
|
|
||||||
void DumpPool(AssetDumpingContext& context, AssetPool<MenuList>* pool) override;
|
|
||||||
};
|
|
||||||
} // namespace IW4
|
|
@@ -1,923 +1,60 @@
|
|||||||
#include "MenuDumperIW4.h"
|
#include "MenuDumperIW4.h"
|
||||||
|
|
||||||
#include "Game/IW4/MenuConstantsIW4.h"
|
#include "Game/IW4/GameAssetPoolIW4.h"
|
||||||
|
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
||||||
|
#include "MenuListDumperIW4.h"
|
||||||
|
#include "MenuWriterIW4.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <filesystem>
|
||||||
#include <cmath>
|
#include <string>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using namespace IW4;
|
using namespace IW4;
|
||||||
|
using namespace ::menu;
|
||||||
|
|
||||||
// Uncomment this macro to skip interpretative expression dumping
|
namespace
|
||||||
// #define DUMP_NAIVE
|
|
||||||
|
|
||||||
#ifdef DUMP_NAIVE
|
|
||||||
#define DUMP_FUNC WriteStatementNaive
|
|
||||||
#else
|
|
||||||
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t MenuDumper::FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
|
|
||||||
{
|
{
|
||||||
assert(statement->numEntries >= 0);
|
std::string GetPathForMenu(MenuDumpingZoneState* zoneState, XAssetInfo<menuDef_t>* asset)
|
||||||
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
|
|
||||||
|
|
||||||
const auto statementEnd = static_cast<size_t>(statement->numEntries);
|
|
||||||
|
|
||||||
// The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though.
|
|
||||||
// We will pretend it does since the game does sometimes leave out opening parenthesis from the entries.
|
|
||||||
auto currentParenthesisDepth = 1;
|
|
||||||
for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++)
|
|
||||||
{
|
{
|
||||||
const auto& expEntry = statement->entries[currentSearchPosition];
|
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(asset->Asset());
|
||||||
if (expEntry.type != EET_OPERATOR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Any function means a "left out" left paren
|
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
|
||||||
if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT)
|
return "ui_mp/" + std::string(asset->Asset()->window.name) + ".menu";
|
||||||
{
|
|
||||||
currentParenthesisDepth++;
|
return menuDumpingState->second.m_path;
|
||||||
}
|
}
|
||||||
else if (expEntry.data.op == OP_RIGHTPAREN)
|
} // namespace
|
||||||
{
|
|
||||||
if (currentParenthesisDepth > 0)
|
namespace IW4::menu
|
||||||
currentParenthesisDepth--;
|
{
|
||||||
if (currentParenthesisDepth == 0)
|
bool MenuDumper::ShouldDump(XAssetInfo<menuDef_t>* asset)
|
||||||
return currentSearchPosition;
|
{
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return statementEnd;
|
void MenuDumper::DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
const auto* menu = asset->Asset();
|
||||||
m_stream << "(";
|
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
|
||||||
WriteStatementEntryRange(statement, currentPos + 1, closingParenPos);
|
|
||||||
m_stream << ")";
|
|
||||||
|
|
||||||
currentPos = closingParenPos + 1;
|
if (!ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
|
||||||
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:
|
// Make sure menu paths based on menu lists are created
|
||||||
m_stream << "dvarint";
|
const auto* gameAssetPool = dynamic_cast<GameAssetPoolIW4*>(asset->m_zone->m_pools.get());
|
||||||
break;
|
for (auto* menuListAsset : *gameAssetPool->m_menu_list)
|
||||||
|
CreateDumpingStateForMenuList(zoneState, menuListAsset->Asset());
|
||||||
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 menuFilePath = GetPathForMenu(zoneState, asset);
|
||||||
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
const auto assetFile = context.OpenAssetFile(menuFilePath);
|
||||||
m_stream << "(";
|
|
||||||
|
|
||||||
if (closingParenPos - currentPos + 1 >= 1)
|
if (!assetFile)
|
||||||
{
|
return;
|
||||||
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 << ")";
|
auto menuWriter = CreateMenuWriter(*assetFile);
|
||||||
currentPos = closingParenPos + 1;
|
|
||||||
spaceNext = true;
|
menuWriter->Start();
|
||||||
|
menuWriter->WriteMenu(*menu);
|
||||||
|
menuWriter->End();
|
||||||
}
|
}
|
||||||
else
|
} // namespace IW4::menu
|
||||||
{
|
|
||||||
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
|
||||||
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, const size_t currentPos) const
|
|
||||||
{
|
|
||||||
const auto& operand = statement->entries[currentPos].data.operand;
|
|
||||||
|
|
||||||
if (operand.internals.function == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!ObjWriting::Configuration.MenuLegacyMode)
|
|
||||||
{
|
|
||||||
int functionIndex = -1;
|
|
||||||
if (statement->supportingData && statement->supportingData->uifunctions.functions)
|
|
||||||
{
|
|
||||||
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++)
|
|
||||||
{
|
|
||||||
if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
|
|
||||||
{
|
|
||||||
functionIndex = supportingFunctionIndex;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (functionIndex >= 0)
|
|
||||||
m_stream << "FUNC_" << functionIndex;
|
|
||||||
else
|
|
||||||
m_stream << "INVALID_FUNC";
|
|
||||||
m_stream << "()";
|
|
||||||
}
|
|
||||||
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:
|
|
||||||
WriteEscapedString(operand.internals.stringVal.string);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VAL_FUNCTION:
|
|
||||||
WriteStatementOperandFunction(statement, currentPos);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPos++;
|
|
||||||
spaceNext = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteStatementEntryRange(const Statement_s* statement, const size_t startOffset, const size_t endOffset) const
|
|
||||||
{
|
|
||||||
assert(startOffset <= endOffset);
|
|
||||||
assert(endOffset <= static_cast<size_t>(statement->numEntries));
|
|
||||||
|
|
||||||
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<size_t>(statement->numEntries));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
|
|
||||||
{
|
|
||||||
if (statementValue == nullptr || statementValue->numEntries < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto statementEnd = static_cast<size_t>(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::WriteStatementNaive(const Statement_s* statement) const
|
|
||||||
{
|
|
||||||
const auto entryCount = static_cast<size_t>(statement->numEntries);
|
|
||||||
|
|
||||||
const auto missingClosingParenthesis = statement->numEntries > 0 && statement->entries[0].type == EET_OPERATOR
|
|
||||||
&& statement->entries[0].data.op == OP_LEFTPAREN
|
|
||||||
&& FindStatementClosingParenthesis(statement, 0) >= static_cast<size_t>(statement->numEntries);
|
|
||||||
|
|
||||||
for (auto i = 0uz; i < entryCount; i++)
|
|
||||||
{
|
|
||||||
const auto& entry = statement->entries[i];
|
|
||||||
if (entry.type == EET_OPERAND)
|
|
||||||
{
|
|
||||||
size_t pos = i;
|
|
||||||
bool discard = false;
|
|
||||||
WriteStatementOperand(statement, pos, discard);
|
|
||||||
}
|
|
||||||
else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
|
||||||
{
|
|
||||||
switch (entry.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, i);
|
|
||||||
m_stream << "(";
|
|
||||||
|
|
||||||
if (closingParenPos - i + 1u >= 1u)
|
|
||||||
{
|
|
||||||
const auto& staticDvarEntry = statement->entries[i + 1];
|
|
||||||
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
|
||||||
{
|
|
||||||
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && 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 << ")";
|
|
||||||
i = closingParenPos;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>);
|
|
||||||
if (entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
|
||||||
m_stream << g_expFunctionNames[entry.data.op];
|
|
||||||
if (entry.data.op >= OP_COUNT)
|
|
||||||
m_stream << "(";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (missingClosingParenthesis)
|
|
||||||
m_stream << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
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(";
|
|
||||||
DUMP_FUNC(statementValue);
|
|
||||||
m_stream << ");\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DUMP_FUNC(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<int>(rect.horzAlign) << " " << static_cast<int>(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<char>(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::WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const
|
|
||||||
{
|
|
||||||
if (!value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
|
|
||||||
const auto tokenList = CreateScriptTokenList(value);
|
|
||||||
|
|
||||||
auto firstToken = true;
|
|
||||||
m_stream << "{ ";
|
|
||||||
for (const auto& token : tokenList)
|
|
||||||
{
|
|
||||||
if (firstToken)
|
|
||||||
firstToken = false;
|
|
||||||
else
|
|
||||||
m_stream << ";";
|
|
||||||
m_stream << "\"" << token << "\"";
|
|
||||||
}
|
|
||||||
if (!firstToken)
|
|
||||||
m_stream << " ";
|
|
||||||
m_stream << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const
|
|
||||||
{
|
|
||||||
if (listBox->numColumns <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << listBox->numColumns << "\n";
|
|
||||||
|
|
||||||
for (auto col = 0; col < listBox->numColumns; col++)
|
|
||||||
{
|
|
||||||
Indent();
|
|
||||||
for (auto i = 0u; i < MENU_KEY_SPACING; i++)
|
|
||||||
m_stream << " ";
|
|
||||||
|
|
||||||
m_stream << listBox->columnInfo[col].pos << " " << listBox->columnInfo[col].width << " " << listBox->columnInfo[col].maxChars << " "
|
|
||||||
<< listBox->columnInfo[col].alignment << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteListBoxProperties(const itemDef_s* item)
|
|
||||||
{
|
|
||||||
if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto* listBox = item->typeData.listBox;
|
|
||||||
WriteKeywordProperty("notselectable", listBox->notselectable != 0);
|
|
||||||
WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0);
|
|
||||||
WriteKeywordProperty("usepaging", listBox->usePaging != 0);
|
|
||||||
WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f);
|
|
||||||
WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f);
|
|
||||||
WriteFloatProperty("feeder", item->special, 0.0f);
|
|
||||||
WriteIntProperty("elementtype", listBox->elementStyle, 0);
|
|
||||||
WriteColumnProperty("columns", listBox);
|
|
||||||
WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick);
|
|
||||||
WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000);
|
|
||||||
WriteMaterialProperty("selectIcon", listBox->selectIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
|
|
||||||
{
|
|
||||||
if (item->dvar == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteEditFieldProperties(const itemDef_s* item) const
|
|
||||||
{
|
|
||||||
switch (item->type)
|
|
||||||
{
|
|
||||||
case ITEM_TYPE_TEXT:
|
|
||||||
case ITEM_TYPE_EDITFIELD:
|
|
||||||
case ITEM_TYPE_NUMERICFIELD:
|
|
||||||
case ITEM_TYPE_SLIDER:
|
|
||||||
case ITEM_TYPE_YESNO:
|
|
||||||
case ITEM_TYPE_BIND:
|
|
||||||
case ITEM_TYPE_VALIDFILEFIELD:
|
|
||||||
case ITEM_TYPE_DECIMALFIELD:
|
|
||||||
case ITEM_TYPE_UPREDITFIELD:
|
|
||||||
case ITEM_TYPE_EMAILFIELD:
|
|
||||||
case ITEM_TYPE_PASSWORDFIELD:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->typeData.editField == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto* editField = item->typeData.editField;
|
|
||||||
if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits<float>::epsilon()
|
|
||||||
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|
|
||||||
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
|
|
||||||
{
|
|
||||||
WriteDvarFloatProperty("dvarFloat", item, editField);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
WriteStringProperty("localvar", item->localVar);
|
|
||||||
WriteMultiValueProperty(multiDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const
|
|
||||||
{
|
|
||||||
if (item->type != ITEM_TYPE_DVARENUM)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WriteStringProperty("dvar", item->dvar);
|
|
||||||
WriteStringProperty("localvar", item->localVar);
|
|
||||||
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.rectClient);
|
|
||||||
WriteIntProperty("style", item->window.style, 0);
|
|
||||||
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
|
|
||||||
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
|
|
||||||
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
|
|
||||||
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
|
|
||||||
WriteIntProperty("border", item->window.border, 0);
|
|
||||||
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
|
|
||||||
|
|
||||||
if (item->visibleExp)
|
|
||||||
WriteStatementProperty("visible", item->visibleExp, true);
|
|
||||||
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
|
||||||
WriteIntProperty("visible", 1, 0);
|
|
||||||
|
|
||||||
WriteStatementProperty("disabled", item->disabledExp, true);
|
|
||||||
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
|
|
||||||
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
|
|
||||||
WriteIntProperty("align", item->alignment, 0);
|
|
||||||
WriteIntProperty("textalign", item->textAlignMode, 0);
|
|
||||||
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
|
|
||||||
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
|
|
||||||
WriteFloatProperty("textscale", item->textscale, 0.0f);
|
|
||||||
WriteIntProperty("textstyle", item->textStyle, 0);
|
|
||||||
WriteIntProperty("textfont", item->fontEnum, 0);
|
|
||||||
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
|
|
||||||
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
|
|
||||||
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
|
|
||||||
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
|
|
||||||
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
|
|
||||||
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
|
|
||||||
WriteMaterialProperty("background", item->window.background);
|
|
||||||
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
|
|
||||||
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
|
|
||||||
WriteMenuEventHandlerSetProperty("action", item->action);
|
|
||||||
WriteMenuEventHandlerSetProperty("accept", item->accept);
|
|
||||||
// WriteFloatProperty("special", item->special, 0.0f);
|
|
||||||
WriteSoundAliasProperty("focusSound", item->focusSound);
|
|
||||||
WriteStringProperty("dvarTest", item->dvarTest);
|
|
||||||
|
|
||||||
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
|
|
||||||
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
|
|
||||||
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
|
|
||||||
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
|
|
||||||
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
|
|
||||||
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
|
|
||||||
|
|
||||||
WriteItemKeyHandlerProperty(item->onKey);
|
|
||||||
WriteStatementProperty("exp text", item->textExp, false);
|
|
||||||
WriteStatementProperty("exp material", item->materialExp, false);
|
|
||||||
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
|
|
||||||
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
|
|
||||||
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
|
|
||||||
WriteDecodeEffectProperty("decodeEffect", item);
|
|
||||||
|
|
||||||
WriteListBoxProperties(item);
|
|
||||||
WriteEditFieldProperties(item);
|
|
||||||
WriteMultiProperties(item);
|
|
||||||
WriteEnumDvarProperties(item);
|
|
||||||
WriteTickerProperties(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount)
|
|
||||||
{
|
|
||||||
for (auto i = 0u; i < itemCount; i++)
|
|
||||||
{
|
|
||||||
StartItemDefScope();
|
|
||||||
|
|
||||||
WriteItemData(itemDefs[i]);
|
|
||||||
|
|
||||||
EndScope();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteMenuData(const menuDef_t* menu)
|
|
||||||
{
|
|
||||||
WriteStringProperty("name", menu->window.name);
|
|
||||||
WriteBoolProperty("fullscreen", menu->fullScreen, false);
|
|
||||||
WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
|
|
||||||
WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION);
|
|
||||||
WriteRectProperty("rect", menu->window.rect);
|
|
||||||
WriteIntProperty("style", menu->window.style, 0);
|
|
||||||
WriteIntProperty("border", menu->window.border, 0);
|
|
||||||
WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f);
|
|
||||||
WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000);
|
|
||||||
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
|
|
||||||
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
|
|
||||||
WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000);
|
|
||||||
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
|
|
||||||
WriteMaterialProperty("background", menu->window.background);
|
|
||||||
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
|
|
||||||
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
|
|
||||||
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
|
|
||||||
WriteStringProperty("soundLoop", menu->soundName);
|
|
||||||
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
|
|
||||||
WriteFloatProperty("fadeClamp", menu->fadeClamp, 0.0f);
|
|
||||||
WriteIntProperty("fadeCycle", menu->fadeCycle, 0);
|
|
||||||
WriteFloatProperty("fadeAmount", menu->fadeAmount, 0.0f);
|
|
||||||
WriteFloatProperty("fadeInAmount", menu->fadeInAmount, 0.0f);
|
|
||||||
WriteFloatProperty("blurWorld", menu->blurRadius, 0.0f);
|
|
||||||
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
|
|
||||||
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
|
|
||||||
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
|
|
||||||
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
|
|
||||||
WriteStringProperty("allowedBinding", menu->allowedBinding);
|
|
||||||
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
|
|
||||||
|
|
||||||
if (menu->visibleExp)
|
|
||||||
WriteStatementProperty("visible", menu->visibleExp, true);
|
|
||||||
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
|
||||||
WriteIntProperty("visible", 1, 0);
|
|
||||||
|
|
||||||
WriteStatementProperty("exp rect X", menu->rectXExp, false);
|
|
||||||
WriteStatementProperty("exp rect Y", menu->rectYExp, false);
|
|
||||||
WriteStatementProperty("exp rect W", menu->rectWExp, false);
|
|
||||||
WriteStatementProperty("exp rect H", menu->rectHExp, false);
|
|
||||||
WriteStatementProperty("exp openSound", menu->openSoundExp, false);
|
|
||||||
WriteStatementProperty("exp closeSound", menu->closeSoundExp, false);
|
|
||||||
WriteMenuEventHandlerSetProperty("onOpen", menu->onOpen);
|
|
||||||
WriteMenuEventHandlerSetProperty("onClose", menu->onClose);
|
|
||||||
WriteMenuEventHandlerSetProperty("onRequestClose", menu->onCloseRequest);
|
|
||||||
WriteMenuEventHandlerSetProperty("onESC", menu->onESC);
|
|
||||||
WriteItemKeyHandlerProperty(menu->onKey);
|
|
||||||
WriteItemDefs(menu->items, menu->itemCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
@@ -1,57 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/IW4/IW4.h"
|
#include "Game/IW4/IW4.h"
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
#include "Menu/MenuDumpingZoneState.h"
|
||||||
|
|
||||||
#include <string>
|
namespace IW4::menu
|
||||||
|
|
||||||
namespace IW4
|
|
||||||
{
|
{
|
||||||
class MenuDumper : public AbstractMenuDumper
|
class MenuDumper final : public AbstractAssetDumper<menuDef_t>
|
||||||
{
|
{
|
||||||
static size_t FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition);
|
protected:
|
||||||
|
bool ShouldDump(XAssetInfo<menuDef_t>* asset) override;
|
||||||
void WriteStatementNaive(const Statement_s* statement) const;
|
void DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset) override;
|
||||||
|
|
||||||
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 WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const;
|
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
} // namespace IW4
|
} // namespace IW4::menu
|
||||||
|
187
src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp
Normal file
187
src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.cpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#include "MenuListDumperIW4.h"
|
||||||
|
|
||||||
|
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
||||||
|
#include "Menu/AbstractMenuWriter.h"
|
||||||
|
#include "MenuWriterIW4.h"
|
||||||
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
using namespace ::menu;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList)
|
||||||
|
{
|
||||||
|
std::vector<const ExpressionSupportingData*> result;
|
||||||
|
std::set<const ExpressionSupportingData*> 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->expressionData == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (alreadyAddedSupportingData.find(menu->expressionData) == alreadyAddedSupportingData.end())
|
||||||
|
{
|
||||||
|
result.push_back(menu->expressionData);
|
||||||
|
alreadyAddedSupportingData.emplace(menu->expressionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpFunctions(IW4::menu::IWriterIW4& 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)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "FUNC_" << functionIndex;
|
||||||
|
|
||||||
|
menuDumper.WriteFunctionDef(ss.str(), function);
|
||||||
|
}
|
||||||
|
|
||||||
|
functionIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpMenus(IW4::menu::IWriterIW4& menuDumper, ::menu::MenuDumpingZoneState* zoneState, const MenuList* menuList)
|
||||||
|
{
|
||||||
|
for (auto menuNum = 0; menuNum < menuList->menuCount; menuNum++)
|
||||||
|
{
|
||||||
|
const auto* menu = menuList->menus[menuNum];
|
||||||
|
|
||||||
|
const auto menuDumpingState = zoneState->m_menu_dumping_state_map.find(menu);
|
||||||
|
if (menuDumpingState == zoneState->m_menu_dumping_state_map.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the menu was embedded directly as menu list write its data in the menu list file
|
||||||
|
if (menuDumpingState->second.m_alias_menu_list == menuList)
|
||||||
|
menuDumper.WriteMenu(*menu);
|
||||||
|
else
|
||||||
|
menuDumper.IncludeMenu(menuDumpingState->second.m_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PathForMenu(const std::string& menuListParentPath, const menuDef_t* menu)
|
||||||
|
{
|
||||||
|
const auto* menuAssetName = menu->window.name;
|
||||||
|
|
||||||
|
if (!menuAssetName)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (menuAssetName[0] == ',')
|
||||||
|
menuAssetName = &menuAssetName[1];
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << menuListParentPath << menuAssetName << ".menu";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace IW4::menu
|
||||||
|
{
|
||||||
|
void CreateDumpingStateForMenuList(::menu::MenuDumpingZoneState* zoneState, const MenuList* menuList)
|
||||||
|
{
|
||||||
|
if (menuList->menuCount <= 0 || menuList->menus == nullptr || menuList->name == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string menuListName(menuList->name);
|
||||||
|
const fs::path p(menuListName);
|
||||||
|
std::string parentPath;
|
||||||
|
if (p.has_parent_path())
|
||||||
|
parentPath = p.parent_path().string() + "/";
|
||||||
|
|
||||||
|
for (auto i = 0; i < menuList->menuCount; i++)
|
||||||
|
{
|
||||||
|
auto* menu = menuList->menus[i];
|
||||||
|
|
||||||
|
if (menu == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto existingState = zoneState->m_menu_dumping_state_map.find(menu);
|
||||||
|
if (existingState == zoneState->m_menu_dumping_state_map.end())
|
||||||
|
{
|
||||||
|
auto menuPath = PathForMenu(parentPath, menu);
|
||||||
|
const auto isTheSameAsMenuList = menuPath == menuListName;
|
||||||
|
zoneState->CreateMenuDumpingState(menu, std::move(menuPath), isTheSameAsMenuList ? menuList : nullptr);
|
||||||
|
}
|
||||||
|
else if (existingState->second.m_alias_menu_list == nullptr)
|
||||||
|
{
|
||||||
|
auto menuPath = PathForMenu(parentPath, menu);
|
||||||
|
const auto isTheSameAsMenuList = menuPath == menuListName;
|
||||||
|
if (isTheSameAsMenuList)
|
||||||
|
{
|
||||||
|
existingState->second.m_alias_menu_list = menuList;
|
||||||
|
existingState->second.m_path = std::move(menuPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuListDumper::ShouldDump(XAssetInfo<MenuList>* asset)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuListDumper::DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset)
|
||||||
|
{
|
||||||
|
const auto* menuList = asset->Asset();
|
||||||
|
const auto assetFile = context.OpenAssetFile(asset->m_name);
|
||||||
|
|
||||||
|
if (!assetFile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
|
||||||
|
|
||||||
|
auto menuWriter = CreateMenuWriter(*assetFile);
|
||||||
|
|
||||||
|
menuWriter->Start();
|
||||||
|
|
||||||
|
if (!ObjWriting::Configuration.MenuLegacyMode)
|
||||||
|
DumpFunctions(*menuWriter, menuList);
|
||||||
|
|
||||||
|
DumpMenus(*menuWriter, zoneState, menuList);
|
||||||
|
|
||||||
|
menuWriter->End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuListDumper::DumpPool(AssetDumpingContext& context, AssetPool<MenuList>* pool)
|
||||||
|
{
|
||||||
|
auto* zoneState = context.GetZoneAssetDumperState<MenuDumpingZoneState>();
|
||||||
|
|
||||||
|
for (auto* asset : *pool)
|
||||||
|
CreateDumpingStateForMenuList(zoneState, asset->Asset());
|
||||||
|
|
||||||
|
AbstractAssetDumper<MenuList>::DumpPool(context, pool);
|
||||||
|
}
|
||||||
|
} // namespace IW4::menu
|
21
src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h
Normal file
21
src/ObjWriting/Game/IW4/Menu/MenuListDumperIW4.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "Game/IW4/Menu/MenuDumperIW4.h"
|
||||||
|
#include "Menu/MenuDumpingZoneState.h"
|
||||||
|
|
||||||
|
namespace IW4::menu
|
||||||
|
{
|
||||||
|
void CreateDumpingStateForMenuList(::menu::MenuDumpingZoneState* zoneState, const MenuList* menuList);
|
||||||
|
|
||||||
|
class MenuListDumper final : public AbstractAssetDumper<MenuList>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool ShouldDump(XAssetInfo<MenuList>* asset) override;
|
||||||
|
void DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void DumpPool(AssetDumpingContext& context, AssetPool<MenuList>* pool) override;
|
||||||
|
};
|
||||||
|
} // namespace IW4::menu
|
957
src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.cpp
Normal file
957
src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.cpp
Normal file
@@ -0,0 +1,957 @@
|
|||||||
|
#include "MenuWriterIW4.h"
|
||||||
|
|
||||||
|
#include "Game/IW4/MenuConstantsIW4.h"
|
||||||
|
#include "Menu/AbstractMenuWriter.h"
|
||||||
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
|
||||||
|
// Uncomment this macro to skip interpretative expression dumping
|
||||||
|
// #define DUMP_NAIVE
|
||||||
|
|
||||||
|
#ifdef DUMP_NAIVE
|
||||||
|
#define DUMP_FUNC WriteStatementNaive
|
||||||
|
#else
|
||||||
|
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
|
||||||
|
{
|
||||||
|
assert(statement->numEntries >= 0);
|
||||||
|
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statement->numEntries);
|
||||||
|
|
||||||
|
// The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though.
|
||||||
|
// We will pretend it does since the game does sometimes leave out opening parenthesis from the entries.
|
||||||
|
auto currentParenthesisDepth = 1;
|
||||||
|
for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++)
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentSearchPosition];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MenuWriter final : public ::menu::AbstractBaseWriter, public IW4::menu::IWriterIW4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MenuWriter(std::ostream& stream)
|
||||||
|
: AbstractBaseWriter(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) override
|
||||||
|
{
|
||||||
|
StartFunctionDefScope();
|
||||||
|
|
||||||
|
WriteStringProperty("name", functionName);
|
||||||
|
WriteStatementProperty("value", statement, false);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenu(const menuDef_t& menu) override
|
||||||
|
{
|
||||||
|
StartMenuDefScope();
|
||||||
|
|
||||||
|
WriteMenuData(&menu);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncludeMenu(const std::string& menuPath) const override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::IncludeMenu(menuPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WriteStatementNaive(const Statement_s* statement) const
|
||||||
|
{
|
||||||
|
const auto entryCount = static_cast<size_t>(statement->numEntries);
|
||||||
|
|
||||||
|
const auto missingClosingParenthesis = statement->numEntries > 0 && statement->entries[0].type == EET_OPERATOR
|
||||||
|
&& statement->entries[0].data.op == OP_LEFTPAREN
|
||||||
|
&& FindStatementClosingParenthesis(statement, 0) >= static_cast<size_t>(statement->numEntries);
|
||||||
|
|
||||||
|
for (auto i = 0uz; i < entryCount; i++)
|
||||||
|
{
|
||||||
|
const auto& entry = statement->entries[i];
|
||||||
|
if (entry.type == EET_OPERAND)
|
||||||
|
{
|
||||||
|
size_t pos = i;
|
||||||
|
bool discard = false;
|
||||||
|
WriteStatementOperand(statement, pos, discard);
|
||||||
|
}
|
||||||
|
else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
||||||
|
{
|
||||||
|
switch (entry.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, i);
|
||||||
|
m_stream << "(";
|
||||||
|
|
||||||
|
if (closingParenPos - i + 1u >= 1u)
|
||||||
|
{
|
||||||
|
const auto& staticDvarEntry = statement->entries[i + 1];
|
||||||
|
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
||||||
|
{
|
||||||
|
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
|
||||||
|
&& 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 << ")";
|
||||||
|
i = closingParenPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>);
|
||||||
|
if (entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
||||||
|
m_stream << g_expFunctionNames[entry.data.op];
|
||||||
|
if (entry.data.op >= OP_COUNT)
|
||||||
|
m_stream << "(";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingClosingParenthesis)
|
||||||
|
m_stream << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (spaceNext && expEntry.data.op != OP_COMMA)
|
||||||
|
m_stream << " ";
|
||||||
|
|
||||||
|
if (expEntry.data.op == OP_LEFTPAREN)
|
||||||
|
{
|
||||||
|
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
||||||
|
m_stream << "(";
|
||||||
|
WriteStatementEntryRange(statement, currentPos + 1, closingParenPos);
|
||||||
|
m_stream << ")";
|
||||||
|
|
||||||
|
currentPos = closingParenPos + 1;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
||||||
|
{
|
||||||
|
switch (expEntry.data.op)
|
||||||
|
{
|
||||||
|
case EXP_FUNC_STATIC_DVAR_INT:
|
||||||
|
m_stream << "dvarint";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_BOOL:
|
||||||
|
m_stream << "dvarbool";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_FLOAT:
|
||||||
|
m_stream << "dvarfloat";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_STRING:
|
||||||
|
m_stream << "dvarstring";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions do not have opening parenthesis in the entries. We can just pretend they do though
|
||||||
|
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
||||||
|
m_stream << "(";
|
||||||
|
|
||||||
|
if (closingParenPos - currentPos + 1 >= 1)
|
||||||
|
{
|
||||||
|
const auto& staticDvarEntry = statement->entries[currentPos + 1];
|
||||||
|
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
||||||
|
{
|
||||||
|
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
|
||||||
|
&& staticDvarEntry.data.operand.internals.intVal >= 0
|
||||||
|
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
|
||||||
|
{
|
||||||
|
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
|
||||||
|
if (staticDvar && staticDvar->dvarName)
|
||||||
|
m_stream << staticDvar->dvarName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "#INVALID_DVAR_INDEX";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "#INVALID_DVAR_OPERAND";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stream << ")";
|
||||||
|
currentPos = closingParenPos + 1;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
||||||
|
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 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";
|
||||||
|
m_stream << "()";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "(";
|
||||||
|
WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function);
|
||||||
|
m_stream << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (spaceNext)
|
||||||
|
m_stream << " ";
|
||||||
|
|
||||||
|
const auto& operand = expEntry.data.operand;
|
||||||
|
|
||||||
|
switch (operand.dataType)
|
||||||
|
{
|
||||||
|
case VAL_FLOAT:
|
||||||
|
m_stream << operand.internals.floatVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_INT:
|
||||||
|
m_stream << operand.internals.intVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_STRING:
|
||||||
|
WriteEscapedString(operand.internals.stringVal.string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_FUNCTION:
|
||||||
|
WriteStatementOperandFunction(statement, currentPos);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos++;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const
|
||||||
|
{
|
||||||
|
assert(startOffset <= endOffset);
|
||||||
|
assert(endOffset <= static_cast<size_t>(statement->numEntries));
|
||||||
|
|
||||||
|
auto currentPos = startOffset;
|
||||||
|
auto spaceNext = false;
|
||||||
|
while (currentPos < endOffset)
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (expEntry.type == EET_OPERATOR)
|
||||||
|
{
|
||||||
|
WriteStatementOperator(statement, currentPos, spaceNext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatementOperand(statement, currentPos, spaceNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatement(const Statement_s* statement) const
|
||||||
|
{
|
||||||
|
if (statement == nullptr || statement->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement->numEntries));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
|
||||||
|
{
|
||||||
|
if (statementValue == nullptr || statementValue->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
|
||||||
|
|
||||||
|
if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN)
|
||||||
|
{
|
||||||
|
const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0);
|
||||||
|
|
||||||
|
if (parenthesisEnd >= statementEnd)
|
||||||
|
WriteStatementEntryRange(statementValue, 1, statementEnd);
|
||||||
|
else if (parenthesisEnd == statementEnd - 1)
|
||||||
|
WriteStatementEntryRange(statementValue, 1, statementEnd - 1);
|
||||||
|
else
|
||||||
|
WriteStatementEntryRange(statementValue, 0, statementEnd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatementEntryRange(statementValue, 0, statementEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const
|
||||||
|
{
|
||||||
|
if (statementValue == nullptr || statementValue->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
if (isBooleanStatement)
|
||||||
|
{
|
||||||
|
m_stream << "when(";
|
||||||
|
DUMP_FUNC(statementValue);
|
||||||
|
m_stream << ");\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DUMP_FUNC(statementValue);
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const
|
||||||
|
{
|
||||||
|
if (setLocalVarData == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << setFunction << " " << setLocalVarData->localVarName << " ";
|
||||||
|
WriteStatement(setLocalVarData->expression);
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// #define WRITE_ORIGINAL_SCRIPT
|
||||||
|
void WriteUnconditionalScript(const char* script) const
|
||||||
|
{
|
||||||
|
#ifdef WRITE_ORIGINAL_SCRIPT
|
||||||
|
Indent();
|
||||||
|
m_stream << script << "\n";
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto tokenList = CreateScriptTokenList(script);
|
||||||
|
|
||||||
|
auto isNewStatement = true;
|
||||||
|
for (const auto& token : tokenList)
|
||||||
|
{
|
||||||
|
if (isNewStatement)
|
||||||
|
{
|
||||||
|
if (token == ";")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == ";")
|
||||||
|
{
|
||||||
|
m_stream << ";\n";
|
||||||
|
isNewStatement = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNewStatement)
|
||||||
|
m_stream << " ";
|
||||||
|
else
|
||||||
|
isNewStatement = false;
|
||||||
|
|
||||||
|
if (DoesTokenNeedQuotationMarks(token))
|
||||||
|
m_stream << "\"" << token << "\"";
|
||||||
|
else
|
||||||
|
m_stream << token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNewStatement)
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet)
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
m_stream << "{\n";
|
||||||
|
IncIndent();
|
||||||
|
|
||||||
|
for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++)
|
||||||
|
{
|
||||||
|
const auto* eventHandler = eventHandlerSet->eventHandlers[i];
|
||||||
|
if (eventHandler == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (eventHandler->eventType)
|
||||||
|
{
|
||||||
|
case EVENT_UNCONDITIONAL:
|
||||||
|
WriteUnconditionalScript(eventHandler->eventData.unconditionalScript);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_IF:
|
||||||
|
if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr
|
||||||
|
|| eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << "if (";
|
||||||
|
WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression);
|
||||||
|
m_stream << ")\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_ELSE:
|
||||||
|
if (eventHandler->eventData.elseScript == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << "else\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandler->eventData.elseScript);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_BOOL:
|
||||||
|
WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_INT:
|
||||||
|
WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_FLOAT:
|
||||||
|
WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_STRING:
|
||||||
|
WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DecIndent();
|
||||||
|
Indent();
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue)
|
||||||
|
{
|
||||||
|
if (eventHandlerSetValue == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << propertyKey << "\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandlerSetValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast<int>(rect.horzAlign) << " "
|
||||||
|
<< static_cast<int>(rect.vertAlign) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const
|
||||||
|
{
|
||||||
|
if (materialValue == nullptr || materialValue->info.name == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (materialValue->info.name[0] == ',')
|
||||||
|
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
|
||||||
|
else
|
||||||
|
WriteStringProperty(propertyKey, materialValue->info.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
|
||||||
|
{
|
||||||
|
if (soundAliasValue == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (!item->decayActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue)
|
||||||
|
{
|
||||||
|
for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next)
|
||||||
|
{
|
||||||
|
if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"')
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "execKey \"" << static_cast<char>(currentHandler->key) << "\"";
|
||||||
|
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "execKeyInt " << currentHandler->key;
|
||||||
|
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
const auto tokenList = CreateScriptTokenList(value);
|
||||||
|
|
||||||
|
auto firstToken = true;
|
||||||
|
m_stream << "{ ";
|
||||||
|
for (const auto& token : tokenList)
|
||||||
|
{
|
||||||
|
if (firstToken)
|
||||||
|
firstToken = false;
|
||||||
|
else
|
||||||
|
m_stream << ";";
|
||||||
|
m_stream << "\"" << token << "\"";
|
||||||
|
}
|
||||||
|
if (!firstToken)
|
||||||
|
m_stream << " ";
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const
|
||||||
|
{
|
||||||
|
if (listBox->numColumns <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << listBox->numColumns << "\n";
|
||||||
|
|
||||||
|
for (auto col = 0; col < listBox->numColumns; col++)
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
for (auto i = 0u; i < MENU_KEY_SPACING; i++)
|
||||||
|
m_stream << " ";
|
||||||
|
|
||||||
|
m_stream << listBox->columnInfo[col].pos << " " << listBox->columnInfo[col].width << " " << listBox->columnInfo[col].maxChars << " "
|
||||||
|
<< listBox->columnInfo[col].alignment << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteListBoxProperties(const itemDef_s* item)
|
||||||
|
{
|
||||||
|
if (item->type != ITEM_TYPE_LISTBOX || item->typeData.listBox == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* listBox = item->typeData.listBox;
|
||||||
|
WriteKeywordProperty("notselectable", listBox->notselectable != 0);
|
||||||
|
WriteKeywordProperty("noscrollbars", listBox->noScrollBars != 0);
|
||||||
|
WriteKeywordProperty("usepaging", listBox->usePaging != 0);
|
||||||
|
WriteFloatProperty("elementwidth", listBox->elementWidth, 0.0f);
|
||||||
|
WriteFloatProperty("elementheight", listBox->elementHeight, 0.0f);
|
||||||
|
WriteFloatProperty("feeder", item->special, 0.0f);
|
||||||
|
WriteIntProperty("elementtype", listBox->elementStyle, 0);
|
||||||
|
WriteColumnProperty("columns", listBox);
|
||||||
|
WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick);
|
||||||
|
WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000);
|
||||||
|
WriteMaterialProperty("selectIcon", listBox->selectIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
|
||||||
|
{
|
||||||
|
if (item->dvar == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << "\"" << item->dvar << "\" " << editField->defVal << " " << editField->minVal << " " << editField->maxVal << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteEditFieldProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
switch (item->type)
|
||||||
|
{
|
||||||
|
case ITEM_TYPE_TEXT:
|
||||||
|
case ITEM_TYPE_EDITFIELD:
|
||||||
|
case ITEM_TYPE_NUMERICFIELD:
|
||||||
|
case ITEM_TYPE_SLIDER:
|
||||||
|
case ITEM_TYPE_YESNO:
|
||||||
|
case ITEM_TYPE_BIND:
|
||||||
|
case ITEM_TYPE_VALIDFILEFIELD:
|
||||||
|
case ITEM_TYPE_DECIMALFIELD:
|
||||||
|
case ITEM_TYPE_UPREDITFIELD:
|
||||||
|
case ITEM_TYPE_EMAILFIELD:
|
||||||
|
case ITEM_TYPE_PASSWORDFIELD:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->typeData.editField == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* editField = item->typeData.editField;
|
||||||
|
if (std::fabs(-1.0f - editField->defVal) >= std::numeric_limits<float>::epsilon()
|
||||||
|
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|
||||||
|
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
|
||||||
|
{
|
||||||
|
WriteDvarFloatProperty("dvarFloat", item, editField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
}
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteIntProperty("maxChars", editField->maxChars, 0);
|
||||||
|
WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0);
|
||||||
|
WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiValueProperty(const multiDef_s* multiDef) const
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
if (multiDef->strDef)
|
||||||
|
WriteKey("dvarStrList");
|
||||||
|
else
|
||||||
|
WriteKey("dvarFloatList");
|
||||||
|
|
||||||
|
m_stream << "{";
|
||||||
|
for (auto i = 0; i < multiDef->count; i++)
|
||||||
|
{
|
||||||
|
if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_stream << " \"" << multiDef->dvarList[i] << "\"";
|
||||||
|
|
||||||
|
if (multiDef->strDef)
|
||||||
|
m_stream << " \"" << multiDef->dvarStr[i] << "\"";
|
||||||
|
else
|
||||||
|
m_stream << " " << multiDef->dvarValue[i] << "";
|
||||||
|
}
|
||||||
|
m_stream << " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* multiDef = item->typeData.multi;
|
||||||
|
|
||||||
|
if (multiDef->count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteMultiValueProperty(multiDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteEnumDvarProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (item->type != ITEM_TYPE_DVARENUM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 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.rectClient);
|
||||||
|
WriteIntProperty("style", item->window.style, 0);
|
||||||
|
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
|
||||||
|
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
|
||||||
|
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
|
||||||
|
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
|
||||||
|
WriteIntProperty("border", item->window.border, 0);
|
||||||
|
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
|
||||||
|
|
||||||
|
if (item->visibleExp)
|
||||||
|
WriteStatementProperty("visible", item->visibleExp, true);
|
||||||
|
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
||||||
|
WriteIntProperty("visible", 1, 0);
|
||||||
|
|
||||||
|
WriteStatementProperty("disabled", item->disabledExp, true);
|
||||||
|
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
|
||||||
|
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
|
||||||
|
WriteIntProperty("align", item->alignment, 0);
|
||||||
|
WriteIntProperty("textalign", item->textAlignMode, 0);
|
||||||
|
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
|
||||||
|
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
|
||||||
|
WriteFloatProperty("textscale", item->textscale, 0.0f);
|
||||||
|
WriteIntProperty("textstyle", item->textStyle, 0);
|
||||||
|
WriteIntProperty("textfont", item->fontEnum, 0);
|
||||||
|
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
|
||||||
|
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
|
||||||
|
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
|
||||||
|
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
|
||||||
|
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
|
||||||
|
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
|
||||||
|
WriteMaterialProperty("background", item->window.background);
|
||||||
|
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
|
||||||
|
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
|
||||||
|
WriteMenuEventHandlerSetProperty("action", item->action);
|
||||||
|
WriteMenuEventHandlerSetProperty("accept", item->accept);
|
||||||
|
// WriteFloatProperty("special", item->special, 0.0f);
|
||||||
|
WriteSoundAliasProperty("focusSound", item->focusSound);
|
||||||
|
WriteStringProperty("dvarTest", item->dvarTest);
|
||||||
|
|
||||||
|
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
|
||||||
|
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
|
||||||
|
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
|
||||||
|
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
|
||||||
|
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
|
||||||
|
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
|
||||||
|
|
||||||
|
WriteItemKeyHandlerProperty(item->onKey);
|
||||||
|
WriteStatementProperty("exp text", item->textExp, false);
|
||||||
|
WriteStatementProperty("exp material", item->materialExp, false);
|
||||||
|
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
|
||||||
|
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
|
||||||
|
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
|
||||||
|
WriteDecodeEffectProperty("decodeEffect", item);
|
||||||
|
|
||||||
|
WriteListBoxProperties(item);
|
||||||
|
WriteEditFieldProperties(item);
|
||||||
|
WriteMultiProperties(item);
|
||||||
|
WriteEnumDvarProperties(item);
|
||||||
|
WriteTickerProperties(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < itemCount; i++)
|
||||||
|
{
|
||||||
|
StartItemDefScope();
|
||||||
|
|
||||||
|
WriteItemData(itemDefs[i]);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuData(const menuDef_t* menu)
|
||||||
|
{
|
||||||
|
WriteStringProperty("name", menu->window.name);
|
||||||
|
WriteBoolProperty("fullscreen", menu->fullScreen, false);
|
||||||
|
WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
|
||||||
|
WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION);
|
||||||
|
WriteRectProperty("rect", menu->window.rect);
|
||||||
|
WriteIntProperty("style", menu->window.style, 0);
|
||||||
|
WriteIntProperty("border", menu->window.border, 0);
|
||||||
|
WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f);
|
||||||
|
WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000);
|
||||||
|
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
|
||||||
|
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
|
||||||
|
WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000);
|
||||||
|
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
|
||||||
|
WriteMaterialProperty("background", menu->window.background);
|
||||||
|
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
|
||||||
|
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
|
||||||
|
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
|
||||||
|
WriteStringProperty("soundLoop", menu->soundName);
|
||||||
|
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
|
||||||
|
WriteFloatProperty("fadeClamp", menu->fadeClamp, 0.0f);
|
||||||
|
WriteIntProperty("fadeCycle", menu->fadeCycle, 0);
|
||||||
|
WriteFloatProperty("fadeAmount", menu->fadeAmount, 0.0f);
|
||||||
|
WriteFloatProperty("fadeInAmount", menu->fadeInAmount, 0.0f);
|
||||||
|
WriteFloatProperty("blurWorld", menu->blurRadius, 0.0f);
|
||||||
|
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
|
||||||
|
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
|
||||||
|
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
|
||||||
|
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
|
||||||
|
WriteStringProperty("allowedBinding", menu->allowedBinding);
|
||||||
|
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
|
||||||
|
|
||||||
|
if (menu->visibleExp)
|
||||||
|
WriteStatementProperty("visible", menu->visibleExp, true);
|
||||||
|
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
||||||
|
WriteIntProperty("visible", 1, 0);
|
||||||
|
|
||||||
|
WriteStatementProperty("exp rect X", menu->rectXExp, false);
|
||||||
|
WriteStatementProperty("exp rect Y", menu->rectYExp, false);
|
||||||
|
WriteStatementProperty("exp rect W", menu->rectWExp, false);
|
||||||
|
WriteStatementProperty("exp rect H", menu->rectHExp, false);
|
||||||
|
WriteStatementProperty("exp openSound", menu->openSoundExp, false);
|
||||||
|
WriteStatementProperty("exp closeSound", menu->closeSoundExp, false);
|
||||||
|
WriteMenuEventHandlerSetProperty("onOpen", menu->onOpen);
|
||||||
|
WriteMenuEventHandlerSetProperty("onClose", menu->onClose);
|
||||||
|
WriteMenuEventHandlerSetProperty("onRequestClose", menu->onCloseRequest);
|
||||||
|
WriteMenuEventHandlerSetProperty("onESC", menu->onESC);
|
||||||
|
WriteItemKeyHandlerProperty(menu->onKey);
|
||||||
|
WriteItemDefs(menu->items, menu->itemCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace IW4::menu
|
||||||
|
{
|
||||||
|
std::unique_ptr<IWriterIW4> CreateMenuWriter(std::ostream& stream)
|
||||||
|
{
|
||||||
|
return std::make_unique<MenuWriter>(stream);
|
||||||
|
}
|
||||||
|
} // namespace IW4::menu
|
19
src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.h
Normal file
19
src/ObjWriting/Game/IW4/Menu/MenuWriterIW4.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "Menu/IMenuWriter.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace IW4::menu
|
||||||
|
{
|
||||||
|
class IWriterIW4 : public ::menu::IWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) = 0;
|
||||||
|
virtual void WriteMenu(const menuDef_t& menu) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IWriterIW4> CreateMenuWriter(std::ostream& stream);
|
||||||
|
} // namespace IW4::menu
|
@@ -9,8 +9,8 @@
|
|||||||
#include "Localize/LocalizeDumperIW4.h"
|
#include "Localize/LocalizeDumperIW4.h"
|
||||||
#include "Maps/AddonMapEntsDumperIW4.h"
|
#include "Maps/AddonMapEntsDumperIW4.h"
|
||||||
#include "Material/MaterialDecompilingDumperIW4.h"
|
#include "Material/MaterialDecompilingDumperIW4.h"
|
||||||
#include "Menu/AssetDumperMenuDef.h"
|
#include "Menu/MenuDumperIW4.h"
|
||||||
#include "Menu/AssetDumperMenuList.h"
|
#include "Menu/MenuListDumperIW4.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
#include "PhysCollmap/AssetDumperPhysCollmap.h"
|
#include "PhysCollmap/AssetDumperPhysCollmap.h"
|
||||||
#include "PhysPreset/PhysPresetInfoStringDumperIW4.h"
|
#include "PhysPreset/PhysPresetInfoStringDumperIW4.h"
|
||||||
@@ -63,8 +63,8 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const
|
|||||||
// DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD)
|
// DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD)
|
||||||
DUMP_ASSET_POOL(light_def::Dumper, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF)
|
DUMP_ASSET_POOL(light_def::Dumper, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF)
|
||||||
// DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT)
|
// DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT)
|
||||||
DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST)
|
DUMP_ASSET_POOL(menu::MenuListDumper, m_menu_list, ASSET_TYPE_MENULIST)
|
||||||
DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU)
|
DUMP_ASSET_POOL(menu::MenuDumper, m_menu_def, ASSET_TYPE_MENU)
|
||||||
DUMP_ASSET_POOL(localize::Dumper, m_localize, ASSET_TYPE_LOCALIZE_ENTRY)
|
DUMP_ASSET_POOL(localize::Dumper, m_localize, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||||
DUMP_ASSET_POOL(weapon::Dumper, m_weapon, ASSET_TYPE_WEAPON)
|
DUMP_ASSET_POOL(weapon::Dumper, m_weapon, ASSET_TYPE_WEAPON)
|
||||||
// DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
// DUMP_ASSET_POOL(AssetDumperSndDriverGlobals, m_snd_driver_globals, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||||
|
@@ -1,76 +0,0 @@
|
|||||||
#include "AssetDumperMenuDef.h"
|
|
||||||
|
|
||||||
#include "Game/IW5/GameAssetPoolIW5.h"
|
|
||||||
#include "Game/IW5/Menu/MenuDumperIW5.h"
|
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
|
||||||
#include "ObjWriting.h"
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
using namespace IW5;
|
|
||||||
|
|
||||||
const MenuList* AssetDumperMenuDef::GetParentMenuList(XAssetInfo<menuDef_t>* asset)
|
|
||||||
{
|
|
||||||
const auto* menu = asset->Asset();
|
|
||||||
const auto* gameAssetPool = dynamic_cast<GameAssetPoolIW5*>(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<menuDef_t>* 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<menuDef_t>* asset)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuDef::DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* 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();
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Dumping/AbstractAssetDumper.h"
|
|
||||||
#include "Game/IW5/IW5.h"
|
|
||||||
|
|
||||||
namespace IW5
|
|
||||||
{
|
|
||||||
class AssetDumperMenuDef final : public AbstractAssetDumper<menuDef_t>
|
|
||||||
{
|
|
||||||
static const MenuList* GetParentMenuList(XAssetInfo<menuDef_t>* asset);
|
|
||||||
static std::string GetPathForMenu(XAssetInfo<menuDef_t>* asset);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool ShouldDump(XAssetInfo<menuDef_t>* asset) override;
|
|
||||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset) override;
|
|
||||||
};
|
|
||||||
} // namespace IW5
|
|
@@ -1,128 +0,0 @@
|
|||||||
#include "AssetDumperMenuList.h"
|
|
||||||
|
|
||||||
#include "Game/IW5/Menu/MenuDumperIW5.h"
|
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
|
||||||
#include "ObjWriting.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <set>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
using namespace IW5;
|
|
||||||
|
|
||||||
std::vector<const ExpressionSupportingData*> AssetDumperMenuList::GetAllUniqueExpressionSupportingData(const MenuList* menuList)
|
|
||||||
{
|
|
||||||
std::vector<const ExpressionSupportingData*> result;
|
|
||||||
std::set<const ExpressionSupportingData*> 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];
|
|
||||||
const auto* menuAssetName = menu->window.name;
|
|
||||||
|
|
||||||
bool isReference = false;
|
|
||||||
if (menuAssetName && menuAssetName[0] == ',')
|
|
||||||
{
|
|
||||||
menuAssetName = &menuAssetName[1];
|
|
||||||
isReference = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << parentPath << menuAssetName << ".menu";
|
|
||||||
|
|
||||||
const auto menuName = ss.str();
|
|
||||||
|
|
||||||
// If the menu was embedded directly as menu list write its data in the menu list file
|
|
||||||
if (!isReference && menuName == menuList->name)
|
|
||||||
menuDumper.WriteMenu(menu);
|
|
||||||
else
|
|
||||||
menuDumper.IncludeMenu(ss.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AssetDumperMenuList::ShouldDump(XAssetInfo<MenuList>* asset)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetDumperMenuList::DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* 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();
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Dumping/AbstractAssetDumper.h"
|
|
||||||
#include "Game/IW5/IW5.h"
|
|
||||||
#include "Game/IW5/Menu/MenuDumperIW5.h"
|
|
||||||
|
|
||||||
namespace IW5
|
|
||||||
{
|
|
||||||
class AssetDumperMenuList final : public AbstractAssetDumper<MenuList>
|
|
||||||
{
|
|
||||||
static std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList);
|
|
||||||
|
|
||||||
static void DumpFunctions(MenuDumper& menuDumper, const MenuList* menuList);
|
|
||||||
static void DumpMenus(MenuDumper& menuDumper, const MenuList* menuList);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool ShouldDump(XAssetInfo<MenuList>* asset) override;
|
|
||||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset) override;
|
|
||||||
};
|
|
||||||
} // namespace IW5
|
|
@@ -1,920 +1,83 @@
|
|||||||
#include "MenuDumperIW5.h"
|
#include "MenuDumperIW5.h"
|
||||||
|
|
||||||
#include "Game/IW5/MenuConstantsIW5.h"
|
#include "Game/IW5/GameAssetPoolIW5.h"
|
||||||
|
#include "Game/IW5/Menu/MenuDumperIW5.h"
|
||||||
|
#include "MenuWriterIW5.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <filesystem>
|
||||||
#include <cmath>
|
#include <format>
|
||||||
#include <sstream>
|
#include <string>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
using namespace IW5;
|
using namespace IW5;
|
||||||
|
|
||||||
// Uncomment this macro to skip interpretative expression dumping
|
namespace
|
||||||
// #define DUMP_NAIVE
|
|
||||||
|
|
||||||
#ifdef DUMP_NAIVE
|
|
||||||
#define DUMP_FUNC WriteStatementNaive
|
|
||||||
#else
|
|
||||||
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t MenuDumper::FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition)
|
|
||||||
{
|
{
|
||||||
assert(statement->numEntries >= 0);
|
const MenuList* GetParentMenuList(XAssetInfo<menuDef_t>* asset)
|
||||||
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
|
|
||||||
|
|
||||||
const auto statementEnd = static_cast<size_t>(statement->numEntries);
|
|
||||||
|
|
||||||
// The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though.
|
|
||||||
// We will pretend it does since the game does sometimes leave out opening parenthesis from the entries.
|
|
||||||
auto currentParenthesisDepth = 1;
|
|
||||||
for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++)
|
|
||||||
{
|
{
|
||||||
const auto& expEntry = statement->entries[currentSearchPosition];
|
const auto* menu = asset->Asset();
|
||||||
if (expEntry.type != EET_OPERATOR)
|
const auto* gameAssetPool = dynamic_cast<GameAssetPoolIW5*>(asset->m_zone->m_pools.get());
|
||||||
continue;
|
for (const auto* menuList : *gameAssetPool->m_menu_list)
|
||||||
|
|
||||||
// Any function means a "left out" left paren
|
|
||||||
if (expEntry.data.op == OP_LEFTPAREN || expEntry.data.op >= OP_COUNT)
|
|
||||||
{
|
{
|
||||||
currentParenthesisDepth++;
|
const auto* menuListAsset = menuList->Asset();
|
||||||
}
|
|
||||||
else if (expEntry.data.op == OP_RIGHTPAREN)
|
|
||||||
{
|
|
||||||
if (currentParenthesisDepth > 0)
|
|
||||||
currentParenthesisDepth--;
|
|
||||||
if (currentParenthesisDepth == 0)
|
|
||||||
return currentSearchPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return statementEnd;
|
for (auto menuIndex = 0; menuIndex < menuListAsset->menuCount; menuIndex++)
|
||||||
}
|
|
||||||
|
|
||||||
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
|
if (menuListAsset->menus[menuIndex] == menu)
|
||||||
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
|
return menuListAsset;
|
||||||
{
|
|
||||||
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 << ")";
|
return nullptr;
|
||||||
currentPos = closingParenPos + 1;
|
|
||||||
spaceNext = true;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
std::string GetPathForMenu(XAssetInfo<menuDef_t>* asset)
|
||||||
{
|
{
|
||||||
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
const auto* list = GetParentMenuList(asset);
|
||||||
m_stream << g_expFunctionNames[expEntry.data.op];
|
|
||||||
|
|
||||||
if (expEntry.data.op >= OP_COUNT)
|
if (!list)
|
||||||
{
|
return std::format("ui_mp/{}.menu", asset->Asset()->window.name);
|
||||||
// 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;
|
const fs::path p(list->name);
|
||||||
|
std::string parentPath;
|
||||||
|
if (p.has_parent_path())
|
||||||
|
parentPath = p.parent_path().string() + "/";
|
||||||
|
|
||||||
|
return std::format("{}{}.menu", parentPath, asset->Asset()->window.name);
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const
|
namespace IW5::menu
|
||||||
{
|
{
|
||||||
const auto& operand = statement->entries[currentPos].data.operand;
|
bool MenuDumper::ShouldDump(XAssetInfo<menuDef_t>* asset)
|
||||||
|
|
||||||
if (operand.internals.function == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!ObjWriting::Configuration.MenuLegacyMode)
|
|
||||||
{
|
{
|
||||||
int functionIndex = -1;
|
return true;
|
||||||
if (statement->supportingData && statement->supportingData->uifunctions.functions)
|
}
|
||||||
|
|
||||||
|
void MenuDumper::DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset)
|
||||||
|
{
|
||||||
|
const auto* menu = asset->Asset();
|
||||||
|
const auto menuFilePath = GetPathForMenu(asset);
|
||||||
|
|
||||||
|
if (ObjWriting::ShouldHandleAssetType(ASSET_TYPE_MENULIST))
|
||||||
{
|
{
|
||||||
for (auto supportingFunctionIndex = 0; supportingFunctionIndex < statement->supportingData->uifunctions.totalFunctions; supportingFunctionIndex++)
|
// Don't dump menu file separately if the name matches the menu list
|
||||||
{
|
const auto* menuListParent = GetParentMenuList(asset);
|
||||||
if (statement->supportingData->uifunctions.functions[supportingFunctionIndex] == operand.internals.function)
|
if (menuListParent && menuFilePath == menuListParent->name)
|
||||||
{
|
return;
|
||||||
functionIndex = supportingFunctionIndex;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionIndex >= 0)
|
const auto assetFile = context.OpenAssetFile(menuFilePath);
|
||||||
m_stream << "FUNC_" << functionIndex;
|
|
||||||
else
|
if (!assetFile)
|
||||||
m_stream << "INVALID_FUNC";
|
return;
|
||||||
m_stream << "()";
|
|
||||||
|
auto menuWriter = CreateMenuWriter(*assetFile);
|
||||||
|
|
||||||
|
menuWriter->Start();
|
||||||
|
menuWriter->WriteMenu(*menu);
|
||||||
|
menuWriter->End();
|
||||||
}
|
}
|
||||||
else
|
} // namespace IW5::menu
|
||||||
{
|
|
||||||
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:
|
|
||||||
WriteEscapedString(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<size_t>(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::WriteStatementNaive(const Statement_s* statement) const
|
|
||||||
{
|
|
||||||
const auto entryCount = static_cast<unsigned>(statement->numEntries);
|
|
||||||
for (auto i = 0uz; i < entryCount; i++)
|
|
||||||
{
|
|
||||||
const auto& entry = statement->entries[i];
|
|
||||||
if (entry.type == EET_OPERAND)
|
|
||||||
{
|
|
||||||
size_t pos = i;
|
|
||||||
bool discard = false;
|
|
||||||
WriteStatementOperand(statement, pos, discard);
|
|
||||||
}
|
|
||||||
else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
|
||||||
{
|
|
||||||
switch (entry.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, i);
|
|
||||||
m_stream << "(";
|
|
||||||
|
|
||||||
if (closingParenPos - i + 1u >= 1u)
|
|
||||||
{
|
|
||||||
const auto& staticDvarEntry = statement->entries[i + 1u];
|
|
||||||
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
|
||||||
{
|
|
||||||
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars && 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 << ")";
|
|
||||||
i = closingParenPos;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>);
|
|
||||||
if (entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
|
||||||
m_stream << g_expFunctionNames[entry.data.op];
|
|
||||||
if (entry.data.op >= OP_COUNT)
|
|
||||||
m_stream << "(";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteStatement(const Statement_s* statement) const
|
|
||||||
{
|
|
||||||
if (statement == nullptr || statement->numEntries < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement->numEntries));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
|
|
||||||
{
|
|
||||||
if (statementValue == nullptr || statementValue->numEntries < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto statementEnd = static_cast<size_t>(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(";
|
|
||||||
DUMP_FUNC(statementValue);
|
|
||||||
m_stream << ");\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DUMP_FUNC(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<int>(rect.horzAlign) << " " << static_cast<int>(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<char>(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::WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const
|
|
||||||
{
|
|
||||||
if (!value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
|
|
||||||
const auto tokenList = CreateScriptTokenList(value);
|
|
||||||
|
|
||||||
auto firstToken = true;
|
|
||||||
m_stream << "{ ";
|
|
||||||
for (const auto& token : tokenList)
|
|
||||||
{
|
|
||||||
if (firstToken)
|
|
||||||
firstToken = false;
|
|
||||||
else
|
|
||||||
m_stream << ";";
|
|
||||||
m_stream << "\"" << token << "\"";
|
|
||||||
}
|
|
||||||
if (!firstToken)
|
|
||||||
m_stream << " ";
|
|
||||||
m_stream << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<float>::epsilon()
|
|
||||||
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|
|
||||||
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
|
|
||||||
{
|
|
||||||
WriteDvarFloatProperty("dvarFloat", item, editField);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
WriteStringProperty("localvar", item->localVar);
|
|
||||||
WriteMultiValueProperty(multiDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const
|
|
||||||
{
|
|
||||||
if (item->type != ITEM_TYPE_DVARENUM)
|
|
||||||
return;
|
|
||||||
|
|
||||||
WriteStringProperty("dvar", item->dvar);
|
|
||||||
WriteStringProperty("localvar", item->localVar);
|
|
||||||
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.rectClient);
|
|
||||||
WriteIntProperty("style", item->window.style, 0);
|
|
||||||
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
|
|
||||||
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
|
|
||||||
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
|
|
||||||
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
|
|
||||||
WriteIntProperty("border", item->window.border, 0);
|
|
||||||
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
|
|
||||||
|
|
||||||
if (item->visibleExp)
|
|
||||||
WriteStatementProperty("visible", item->visibleExp, true);
|
|
||||||
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
|
||||||
WriteIntProperty("visible", 1, 0);
|
|
||||||
|
|
||||||
WriteStatementProperty("disabled", item->disabledExp, true);
|
|
||||||
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
|
|
||||||
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
|
|
||||||
WriteIntProperty("align", item->alignment, 0);
|
|
||||||
WriteIntProperty("textalign", item->textAlignMode, 0);
|
|
||||||
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
|
|
||||||
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
|
|
||||||
WriteFloatProperty("textscale", item->textscale, 0.0f);
|
|
||||||
WriteIntProperty("textstyle", item->textStyle, 0);
|
|
||||||
WriteIntProperty("textfont", item->fontEnum, 0);
|
|
||||||
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
|
|
||||||
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
|
|
||||||
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
|
|
||||||
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
|
|
||||||
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
|
|
||||||
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
|
|
||||||
WriteMaterialProperty("background", item->window.background);
|
|
||||||
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
|
|
||||||
WriteMenuEventHandlerSetProperty("hasFocus", item->hasFocus);
|
|
||||||
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
|
|
||||||
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
|
|
||||||
WriteMenuEventHandlerSetProperty("action", item->action);
|
|
||||||
WriteMenuEventHandlerSetProperty("accept", item->accept);
|
|
||||||
// WriteFloatProperty("special", item->special, 0.0f);
|
|
||||||
WriteSoundAliasProperty("focusSound", item->focusSound);
|
|
||||||
WriteStringProperty("dvarTest", item->dvarTest);
|
|
||||||
|
|
||||||
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
|
|
||||||
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
|
|
||||||
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
|
|
||||||
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
|
|
||||||
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
|
|
||||||
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
|
|
||||||
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
|
|
||||||
|
|
||||||
WriteItemKeyHandlerProperty(item->onKey);
|
|
||||||
WriteStatementProperty("exp text", item->textExp, false);
|
|
||||||
WriteStatementProperty("exp textaligny", item->textAlignYExp, false);
|
|
||||||
WriteStatementProperty("exp material", item->materialExp, false);
|
|
||||||
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
|
|
||||||
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
|
|
||||||
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
|
|
||||||
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);
|
|
||||||
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
|
|
||||||
WriteMaterialProperty("background", menu->window.background);
|
|
||||||
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
|
|
||||||
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
|
|
||||||
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
|
|
||||||
WriteStringProperty("soundLoop", menu->data->soundName);
|
|
||||||
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
|
|
||||||
WriteFloatProperty("fadeClamp", menu->data->fadeClamp, 0.0f);
|
|
||||||
WriteIntProperty("fadeCycle", menu->data->fadeCycle, 0);
|
|
||||||
WriteFloatProperty("fadeAmount", menu->data->fadeAmount, 0.0f);
|
|
||||||
WriteFloatProperty("fadeInAmount", menu->data->fadeInAmount, 0.0f);
|
|
||||||
WriteFloatProperty("blurWorld", menu->data->blurRadius, 0.0f);
|
|
||||||
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
|
|
||||||
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
|
|
||||||
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
|
|
||||||
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
|
|
||||||
WriteStringProperty("allowedBinding", menu->data->allowedBinding);
|
|
||||||
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
|
|
||||||
|
|
||||||
if (menu->data->visibleExp)
|
|
||||||
WriteStatementProperty("visible", menu->data->visibleExp, true);
|
|
||||||
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
|
||||||
WriteIntProperty("visible", 1, 0);
|
|
||||||
|
|
||||||
WriteStatementProperty("exp rect X", menu->data->rectXExp, false);
|
|
||||||
WriteStatementProperty("exp rect Y", menu->data->rectYExp, false);
|
|
||||||
WriteStatementProperty("exp rect W", menu->data->rectWExp, false);
|
|
||||||
WriteStatementProperty("exp rect H", menu->data->rectHExp, false);
|
|
||||||
WriteStatementProperty("exp openSound", menu->data->openSoundExp, false);
|
|
||||||
WriteStatementProperty("exp closeSound", menu->data->closeSoundExp, false);
|
|
||||||
WriteStatementProperty("exp soundLoop", menu->data->soundLoopExp, false);
|
|
||||||
WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen);
|
|
||||||
WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose);
|
|
||||||
WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest);
|
|
||||||
WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC);
|
|
||||||
WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu->data->onFocusDueToClose);
|
|
||||||
WriteItemKeyHandlerProperty(menu->data->onKey);
|
|
||||||
WriteItemDefs(menu->items, menu->itemCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
@@ -1,57 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/IW5/IW5.h"
|
#include "Game/IW5/IW5.h"
|
||||||
#include "Menu/AbstractMenuDumper.h"
|
#include "Game/IW5/Menu/MenuDumperIW5.h"
|
||||||
|
|
||||||
#include <string>
|
namespace IW5::menu
|
||||||
|
|
||||||
namespace IW5
|
|
||||||
{
|
{
|
||||||
class MenuDumper : public AbstractMenuDumper
|
class MenuDumper final : public AbstractAssetDumper<menuDef_t>
|
||||||
{
|
{
|
||||||
static size_t FindStatementClosingParenthesis(const Statement_s* statement, size_t openingParenthesisPosition);
|
protected:
|
||||||
|
bool ShouldDump(XAssetInfo<menuDef_t>* asset) override;
|
||||||
void WriteStatementNaive(const Statement_s* statement) const;
|
void DumpAsset(AssetDumpingContext& context, XAssetInfo<menuDef_t>* asset) override;
|
||||||
|
|
||||||
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 WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const;
|
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
} // namespace IW5
|
} // namespace IW5::menu
|
||||||
|
133
src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp
Normal file
133
src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include "MenuListDumperIW5.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/Menu/MenuWriterIW5.h"
|
||||||
|
#include "Menu/AbstractMenuWriter.h"
|
||||||
|
#include "MenuWriterIW5.h"
|
||||||
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::vector<const ExpressionSupportingData*> GetAllUniqueExpressionSupportingData(const MenuList* menuList)
|
||||||
|
{
|
||||||
|
std::vector<const ExpressionSupportingData*> result;
|
||||||
|
std::set<const ExpressionSupportingData*> 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 DumpFunctions(IW5::menu::IWriterIW5& 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;
|
||||||
|
|
||||||
|
menuDumper.WriteFunctionDef(std::format("FUNC_{}", functionIndex), function);
|
||||||
|
|
||||||
|
functionIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpMenus(IW5::menu::IWriterIW5& 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];
|
||||||
|
const auto* menuAssetName = menu->window.name;
|
||||||
|
|
||||||
|
bool isReference = false;
|
||||||
|
if (menuAssetName && menuAssetName[0] == ',')
|
||||||
|
{
|
||||||
|
menuAssetName = &menuAssetName[1];
|
||||||
|
isReference = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << parentPath << menuAssetName << ".menu";
|
||||||
|
|
||||||
|
const auto menuName = ss.str();
|
||||||
|
|
||||||
|
// If the menu was embedded directly as menu list write its data in the menu list file
|
||||||
|
if (!isReference && menuName == menuList->name)
|
||||||
|
menuDumper.WriteMenu(*menu);
|
||||||
|
else
|
||||||
|
menuDumper.IncludeMenu(ss.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace IW5::menu
|
||||||
|
{
|
||||||
|
bool MenuListDumper::ShouldDump(XAssetInfo<MenuList>* asset)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuListDumper::DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset)
|
||||||
|
{
|
||||||
|
const auto* menuList = asset->Asset();
|
||||||
|
const auto assetFile = context.OpenAssetFile(asset->m_name);
|
||||||
|
|
||||||
|
if (!assetFile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto menuWriter = CreateMenuWriter(*assetFile);
|
||||||
|
|
||||||
|
menuWriter->Start();
|
||||||
|
|
||||||
|
if (!ObjWriting::Configuration.MenuLegacyMode)
|
||||||
|
DumpFunctions(*menuWriter, menuList);
|
||||||
|
|
||||||
|
DumpMenus(*menuWriter, menuList);
|
||||||
|
|
||||||
|
menuWriter->End();
|
||||||
|
}
|
||||||
|
} // namespace IW5::menu
|
14
src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h
Normal file
14
src/ObjWriting/Game/IW5/Menu/MenuListDumperIW5.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
|
||||||
|
namespace IW5::menu
|
||||||
|
{
|
||||||
|
class MenuListDumper final : public AbstractAssetDumper<MenuList>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool ShouldDump(XAssetInfo<MenuList>* asset) override;
|
||||||
|
void DumpAsset(AssetDumpingContext& context, XAssetInfo<MenuList>* asset) override;
|
||||||
|
};
|
||||||
|
} // namespace IW5::menu
|
957
src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.cpp
Normal file
957
src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.cpp
Normal file
@@ -0,0 +1,957 @@
|
|||||||
|
#include "MenuWriterIW5.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/MenuConstantsIW5.h"
|
||||||
|
#include "Menu/AbstractMenuWriter.h"
|
||||||
|
#include "ObjWriting.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
|
||||||
|
// Uncomment this macro to skip interpretative expression dumping
|
||||||
|
// #define DUMP_NAIVE
|
||||||
|
|
||||||
|
#ifdef DUMP_NAIVE
|
||||||
|
#define DUMP_FUNC WriteStatementNaive
|
||||||
|
#else
|
||||||
|
#define DUMP_FUNC WriteStatementSkipInitialUnnecessaryParenthesis
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
size_t FindStatementClosingParenthesis(const Statement_s* statement, const size_t openingParenthesisPosition)
|
||||||
|
{
|
||||||
|
assert(statement->numEntries >= 0);
|
||||||
|
assert(openingParenthesisPosition < static_cast<size_t>(statement->numEntries));
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statement->numEntries);
|
||||||
|
|
||||||
|
// The openingParenthesisPosition does not necessarily point to an actual opening parenthesis operator. That's fine though.
|
||||||
|
// We will pretend it does since the game does sometimes leave out opening parenthesis from the entries.
|
||||||
|
auto currentParenthesisDepth = 1;
|
||||||
|
for (auto currentSearchPosition = openingParenthesisPosition + 1; currentSearchPosition < statementEnd; currentSearchPosition++)
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentSearchPosition];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class MenuWriter final : public ::menu::AbstractBaseWriter, public IW5::menu::IWriterIW5
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MenuWriter(std::ostream& stream)
|
||||||
|
: AbstractBaseWriter(stream)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) override
|
||||||
|
{
|
||||||
|
StartFunctionDefScope();
|
||||||
|
|
||||||
|
WriteStringProperty("name", functionName);
|
||||||
|
WriteStatementProperty("value", statement, false);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenu(const menuDef_t& menu) override
|
||||||
|
{
|
||||||
|
StartMenuDefScope();
|
||||||
|
|
||||||
|
WriteMenuData(&menu);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncludeMenu(const std::string& menuPath) const override
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::IncludeMenu(menuPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WriteStatementNaive(const Statement_s* statement) const
|
||||||
|
{
|
||||||
|
const auto entryCount = static_cast<unsigned>(statement->numEntries);
|
||||||
|
for (auto i = 0uz; i < entryCount; i++)
|
||||||
|
{
|
||||||
|
const auto& entry = statement->entries[i];
|
||||||
|
if (entry.type == EET_OPERAND)
|
||||||
|
{
|
||||||
|
size_t pos = i;
|
||||||
|
bool discard = false;
|
||||||
|
WriteStatementOperand(statement, pos, discard);
|
||||||
|
}
|
||||||
|
else if (entry.data.op >= EXP_FUNC_STATIC_DVAR_INT && entry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
||||||
|
{
|
||||||
|
switch (entry.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, i);
|
||||||
|
m_stream << "(";
|
||||||
|
|
||||||
|
if (closingParenPos - i + 1u >= 1u)
|
||||||
|
{
|
||||||
|
const auto& staticDvarEntry = statement->entries[i + 1u];
|
||||||
|
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
||||||
|
{
|
||||||
|
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
|
||||||
|
&& 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 << ")";
|
||||||
|
i = closingParenPos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>);
|
||||||
|
if (entry.data.op >= 0 && static_cast<unsigned>(entry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
||||||
|
m_stream << g_expFunctionNames[entry.data.op];
|
||||||
|
if (entry.data.op >= OP_COUNT)
|
||||||
|
m_stream << "(";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementOperator(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (spaceNext && expEntry.data.op != OP_COMMA)
|
||||||
|
m_stream << " ";
|
||||||
|
|
||||||
|
if (expEntry.data.op == OP_LEFTPAREN)
|
||||||
|
{
|
||||||
|
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
||||||
|
m_stream << "(";
|
||||||
|
WriteStatementEntryRange(statement, currentPos + 1, closingParenPos);
|
||||||
|
m_stream << ")";
|
||||||
|
|
||||||
|
currentPos = closingParenPos + 1;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
else if (expEntry.data.op >= EXP_FUNC_STATIC_DVAR_INT && expEntry.data.op <= EXP_FUNC_STATIC_DVAR_STRING)
|
||||||
|
{
|
||||||
|
switch (expEntry.data.op)
|
||||||
|
{
|
||||||
|
case EXP_FUNC_STATIC_DVAR_INT:
|
||||||
|
m_stream << "dvarint";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_BOOL:
|
||||||
|
m_stream << "dvarbool";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_FLOAT:
|
||||||
|
m_stream << "dvarfloat";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXP_FUNC_STATIC_DVAR_STRING:
|
||||||
|
m_stream << "dvarstring";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions do not have opening parenthesis in the entries. We can just pretend they do though
|
||||||
|
const auto closingParenPos = FindStatementClosingParenthesis(statement, currentPos);
|
||||||
|
m_stream << "(";
|
||||||
|
|
||||||
|
if (closingParenPos - currentPos + 1 >= 1)
|
||||||
|
{
|
||||||
|
const auto& staticDvarEntry = statement->entries[currentPos + 1];
|
||||||
|
if (staticDvarEntry.type == EET_OPERAND && staticDvarEntry.data.operand.dataType == VAL_INT)
|
||||||
|
{
|
||||||
|
if (statement->supportingData && statement->supportingData->staticDvarList.staticDvars
|
||||||
|
&& staticDvarEntry.data.operand.internals.intVal >= 0
|
||||||
|
&& staticDvarEntry.data.operand.internals.intVal < statement->supportingData->staticDvarList.numStaticDvars)
|
||||||
|
{
|
||||||
|
const auto* staticDvar = statement->supportingData->staticDvarList.staticDvars[staticDvarEntry.data.operand.internals.intVal];
|
||||||
|
if (staticDvar && staticDvar->dvarName)
|
||||||
|
m_stream << staticDvar->dvarName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "#INVALID_DVAR_INDEX";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "#INVALID_DVAR_OPERAND";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stream << ")";
|
||||||
|
currentPos = closingParenPos + 1;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (expEntry.data.op >= 0 && static_cast<unsigned>(expEntry.data.op) < std::extent_v<decltype(g_expFunctionNames)>)
|
||||||
|
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 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";
|
||||||
|
m_stream << "()";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_stream << "(";
|
||||||
|
WriteStatementSkipInitialUnnecessaryParenthesis(operand.internals.function);
|
||||||
|
m_stream << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementOperand(const Statement_s* statement, size_t& currentPos, bool& spaceNext) const
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (spaceNext)
|
||||||
|
m_stream << " ";
|
||||||
|
|
||||||
|
const auto& operand = expEntry.data.operand;
|
||||||
|
|
||||||
|
switch (operand.dataType)
|
||||||
|
{
|
||||||
|
case VAL_FLOAT:
|
||||||
|
m_stream << operand.internals.floatVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_INT:
|
||||||
|
m_stream << operand.internals.intVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_STRING:
|
||||||
|
WriteEscapedString(operand.internals.stringVal.string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VAL_FUNCTION:
|
||||||
|
WriteStatementOperandFunction(statement, currentPos);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPos++;
|
||||||
|
spaceNext = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementEntryRange(const Statement_s* statement, size_t startOffset, size_t endOffset) const
|
||||||
|
{
|
||||||
|
assert(startOffset <= endOffset);
|
||||||
|
assert(endOffset <= static_cast<size_t>(statement->numEntries));
|
||||||
|
|
||||||
|
auto currentPos = startOffset;
|
||||||
|
auto spaceNext = false;
|
||||||
|
while (currentPos < endOffset)
|
||||||
|
{
|
||||||
|
const auto& expEntry = statement->entries[currentPos];
|
||||||
|
|
||||||
|
if (expEntry.type == EET_OPERATOR)
|
||||||
|
{
|
||||||
|
WriteStatementOperator(statement, currentPos, spaceNext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatementOperand(statement, currentPos, spaceNext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatement(const Statement_s* statement) const
|
||||||
|
{
|
||||||
|
if (statement == nullptr || statement->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStatementEntryRange(statement, 0, static_cast<size_t>(statement->numEntries));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementSkipInitialUnnecessaryParenthesis(const Statement_s* statementValue) const
|
||||||
|
{
|
||||||
|
if (statementValue == nullptr || statementValue->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto statementEnd = static_cast<size_t>(statementValue->numEntries);
|
||||||
|
|
||||||
|
if (statementValue->numEntries >= 1 && statementValue->entries[0].type == EET_OPERATOR && statementValue->entries[0].data.op == OP_LEFTPAREN)
|
||||||
|
{
|
||||||
|
const auto parenthesisEnd = FindStatementClosingParenthesis(statementValue, 0);
|
||||||
|
|
||||||
|
if (parenthesisEnd >= statementEnd)
|
||||||
|
WriteStatementEntryRange(statementValue, 1, statementEnd);
|
||||||
|
else if (parenthesisEnd == statementEnd - 1)
|
||||||
|
WriteStatementEntryRange(statementValue, 1, statementEnd - 1);
|
||||||
|
else
|
||||||
|
WriteStatementEntryRange(statementValue, 0, statementEnd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatementEntryRange(statementValue, 0, statementEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStatementProperty(const std::string& propertyKey, const Statement_s* statementValue, bool isBooleanStatement) const
|
||||||
|
{
|
||||||
|
if (statementValue == nullptr || statementValue->numEntries < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
if (isBooleanStatement)
|
||||||
|
{
|
||||||
|
m_stream << "when(";
|
||||||
|
DUMP_FUNC(statementValue);
|
||||||
|
m_stream << ");\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DUMP_FUNC(statementValue);
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSetLocalVarData(const std::string& setFunction, const SetLocalVarData* setLocalVarData) const
|
||||||
|
{
|
||||||
|
if (setLocalVarData == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << setFunction << " " << setLocalVarData->localVarName << " ";
|
||||||
|
WriteStatement(setLocalVarData->expression);
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// #define WRITE_ORIGINAL_SCRIPT
|
||||||
|
void WriteUnconditionalScript(const char* script) const
|
||||||
|
{
|
||||||
|
#ifdef WRITE_ORIGINAL_SCRIPT
|
||||||
|
Indent();
|
||||||
|
m_stream << script << "\n";
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto tokenList = CreateScriptTokenList(script);
|
||||||
|
|
||||||
|
auto isNewStatement = true;
|
||||||
|
for (const auto& token : tokenList)
|
||||||
|
{
|
||||||
|
if (isNewStatement)
|
||||||
|
{
|
||||||
|
if (token == ";")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == ";")
|
||||||
|
{
|
||||||
|
m_stream << ";\n";
|
||||||
|
isNewStatement = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNewStatement)
|
||||||
|
m_stream << " ";
|
||||||
|
else
|
||||||
|
isNewStatement = false;
|
||||||
|
|
||||||
|
if (DoesTokenNeedQuotationMarks(token))
|
||||||
|
m_stream << "\"" << token << "\"";
|
||||||
|
else
|
||||||
|
m_stream << token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNewStatement)
|
||||||
|
m_stream << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuEventHandlerSet(const MenuEventHandlerSet* eventHandlerSet)
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
m_stream << "{\n";
|
||||||
|
IncIndent();
|
||||||
|
|
||||||
|
for (auto i = 0; i < eventHandlerSet->eventHandlerCount; i++)
|
||||||
|
{
|
||||||
|
const auto* eventHandler = eventHandlerSet->eventHandlers[i];
|
||||||
|
if (eventHandler == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (eventHandler->eventType)
|
||||||
|
{
|
||||||
|
case EVENT_UNCONDITIONAL:
|
||||||
|
WriteUnconditionalScript(eventHandler->eventData.unconditionalScript);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_IF:
|
||||||
|
if (eventHandler->eventData.conditionalScript == nullptr || eventHandler->eventData.conditionalScript->eventExpression == nullptr
|
||||||
|
|| eventHandler->eventData.conditionalScript->eventHandlerSet == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << "if (";
|
||||||
|
WriteStatementSkipInitialUnnecessaryParenthesis(eventHandler->eventData.conditionalScript->eventExpression);
|
||||||
|
m_stream << ")\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandler->eventData.conditionalScript->eventHandlerSet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_ELSE:
|
||||||
|
if (eventHandler->eventData.elseScript == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << "else\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandler->eventData.elseScript);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_BOOL:
|
||||||
|
WriteSetLocalVarData("setLocalVarBool", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_INT:
|
||||||
|
WriteSetLocalVarData("setLocalVarInt", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_FLOAT:
|
||||||
|
WriteSetLocalVarData("setLocalVarFloat", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVENT_SET_LOCAL_VAR_STRING:
|
||||||
|
WriteSetLocalVarData("setLocalVarString", eventHandler->eventData.setLocalVarData);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DecIndent();
|
||||||
|
Indent();
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuEventHandlerSetProperty(const std::string& propertyKey, const MenuEventHandlerSet* eventHandlerSetValue)
|
||||||
|
{
|
||||||
|
if (eventHandlerSetValue == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
m_stream << propertyKey << "\n";
|
||||||
|
WriteMenuEventHandlerSet(eventHandlerSetValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteRectProperty(const std::string& propertyKey, const rectDef_s& rect) const
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << rect.x << " " << rect.y << " " << rect.w << " " << rect.h << " " << static_cast<int>(rect.horzAlign) << " "
|
||||||
|
<< static_cast<int>(rect.vertAlign) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMaterialProperty(const std::string& propertyKey, const Material* materialValue) const
|
||||||
|
{
|
||||||
|
if (materialValue == nullptr || materialValue->info.name == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (materialValue->info.name[0] == ',')
|
||||||
|
WriteStringProperty(propertyKey, &materialValue->info.name[1]);
|
||||||
|
else
|
||||||
|
WriteStringProperty(propertyKey, materialValue->info.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) const
|
||||||
|
{
|
||||||
|
if (soundAliasValue == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty(propertyKey, soundAliasValue->aliasName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (!item->decayActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << item->fxLetterTime << " " << item->fxDecayStartTime << " " << item->fxDecayDuration << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue)
|
||||||
|
{
|
||||||
|
for (const auto* currentHandler = itemKeyHandlerValue; currentHandler; currentHandler = currentHandler->next)
|
||||||
|
{
|
||||||
|
if (currentHandler->key >= '!' && currentHandler->key <= '~' && currentHandler->key != '"')
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "execKey \"" << static_cast<char>(currentHandler->key) << "\"";
|
||||||
|
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "execKeyInt " << currentHandler->key;
|
||||||
|
WriteMenuEventHandlerSetProperty(ss.str(), currentHandler->action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
const auto tokenList = CreateScriptTokenList(value);
|
||||||
|
|
||||||
|
auto firstToken = true;
|
||||||
|
m_stream << "{ ";
|
||||||
|
for (const auto& token : tokenList)
|
||||||
|
{
|
||||||
|
if (firstToken)
|
||||||
|
firstToken = false;
|
||||||
|
else
|
||||||
|
m_stream << ";";
|
||||||
|
m_stream << "\"" << token << "\"";
|
||||||
|
}
|
||||||
|
if (!firstToken)
|
||||||
|
m_stream << " ";
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 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 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);
|
||||||
|
WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
|
||||||
|
{
|
||||||
|
if (item->dvar == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << "\"" << item->dvar << "\" " << editField->stepVal << " " << editField->minVal << " " << editField->maxVal << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteEditFieldProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
switch (item->type)
|
||||||
|
{
|
||||||
|
case ITEM_TYPE_TEXT:
|
||||||
|
case ITEM_TYPE_EDITFIELD:
|
||||||
|
case ITEM_TYPE_NUMERICFIELD:
|
||||||
|
case ITEM_TYPE_SLIDER:
|
||||||
|
case ITEM_TYPE_YESNO:
|
||||||
|
case ITEM_TYPE_BIND:
|
||||||
|
case ITEM_TYPE_VALIDFILEFIELD:
|
||||||
|
case ITEM_TYPE_DECIMALFIELD:
|
||||||
|
case ITEM_TYPE_UPREDITFIELD:
|
||||||
|
case ITEM_TYPE_EMAILFIELD:
|
||||||
|
case ITEM_TYPE_PASSWORDFIELD:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item->typeData.editField == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* editField = item->typeData.editField;
|
||||||
|
if (std::fabs(-1.0f - editField->stepVal) >= std::numeric_limits<float>::epsilon()
|
||||||
|
|| std::fabs(-1.0f - editField->minVal) >= std::numeric_limits<float>::epsilon()
|
||||||
|
|| std::fabs(-1.0f - editField->maxVal) >= std::numeric_limits<float>::epsilon())
|
||||||
|
{
|
||||||
|
WriteDvarFloatProperty("dvarFloat", item, editField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
}
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteIntProperty("maxChars", editField->maxChars, 0);
|
||||||
|
WriteKeywordProperty("maxCharsGotoNext", editField->maxCharsGotoNext != 0);
|
||||||
|
WriteIntProperty("maxPaintChars", editField->maxPaintChars, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiValueProperty(const multiDef_s* multiDef) const
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
if (multiDef->strDef)
|
||||||
|
WriteKey("dvarStrList");
|
||||||
|
else
|
||||||
|
WriteKey("dvarFloatList");
|
||||||
|
|
||||||
|
m_stream << "{";
|
||||||
|
for (auto i = 0; i < multiDef->count; i++)
|
||||||
|
{
|
||||||
|
if (multiDef->dvarList[i] == nullptr || multiDef->strDef && multiDef->dvarStr[i] == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_stream << " \"" << multiDef->dvarList[i] << "\"";
|
||||||
|
|
||||||
|
if (multiDef->strDef)
|
||||||
|
m_stream << " \"" << multiDef->dvarStr[i] << "\"";
|
||||||
|
else
|
||||||
|
m_stream << " " << multiDef->dvarValue[i] << "";
|
||||||
|
}
|
||||||
|
m_stream << " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMultiProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (item->type != ITEM_TYPE_MULTI || item->typeData.multi == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto* multiDef = item->typeData.multi;
|
||||||
|
|
||||||
|
if (multiDef->count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteMultiValueProperty(multiDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteEnumDvarProperties(const itemDef_s* item) const
|
||||||
|
{
|
||||||
|
if (item->type != ITEM_TYPE_DVARENUM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WriteStringProperty("dvar", item->dvar);
|
||||||
|
WriteStringProperty("localvar", item->localVar);
|
||||||
|
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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 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.rectClient);
|
||||||
|
WriteIntProperty("style", item->window.style, 0);
|
||||||
|
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
|
||||||
|
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
|
||||||
|
WriteKeywordProperty("horizontalscroll", item->window.staticFlags & WINDOW_FLAG_HORIZONTAL_SCROLL);
|
||||||
|
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
|
||||||
|
WriteIntProperty("border", item->window.border, 0);
|
||||||
|
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
|
||||||
|
|
||||||
|
if (item->visibleExp)
|
||||||
|
WriteStatementProperty("visible", item->visibleExp, true);
|
||||||
|
else if (item->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
||||||
|
WriteIntProperty("visible", 1, 0);
|
||||||
|
|
||||||
|
WriteStatementProperty("disabled", item->disabledExp, true);
|
||||||
|
WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
|
||||||
|
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
|
||||||
|
WriteIntProperty("align", item->alignment, 0);
|
||||||
|
WriteIntProperty("textalign", item->textAlignMode, 0);
|
||||||
|
WriteFloatProperty("textalignx", item->textalignx, 0.0f);
|
||||||
|
WriteFloatProperty("textaligny", item->textaligny, 0.0f);
|
||||||
|
WriteFloatProperty("textscale", item->textscale, 0.0f);
|
||||||
|
WriteIntProperty("textstyle", item->textStyle, 0);
|
||||||
|
WriteIntProperty("textfont", item->fontEnum, 0);
|
||||||
|
WriteColorProperty("backcolor", item->window.backColor, COLOR_0000);
|
||||||
|
WriteColorProperty("forecolor", item->window.foreColor, COLOR_1111);
|
||||||
|
WriteColorProperty("bordercolor", item->window.borderColor, COLOR_0000);
|
||||||
|
WriteColorProperty("outlinecolor", item->window.outlineColor, COLOR_0000);
|
||||||
|
WriteColorProperty("disablecolor", item->window.disableColor, COLOR_0000);
|
||||||
|
WriteColorProperty("glowcolor", item->glowColor, COLOR_0000);
|
||||||
|
WriteMaterialProperty("background", item->window.background);
|
||||||
|
WriteMenuEventHandlerSetProperty("onFocus", item->onFocus);
|
||||||
|
WriteMenuEventHandlerSetProperty("hasFocus", item->hasFocus);
|
||||||
|
WriteMenuEventHandlerSetProperty("leaveFocus", item->leaveFocus);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseEnter", item->mouseEnter);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseExit", item->mouseExit);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseEnterText", item->mouseEnterText);
|
||||||
|
WriteMenuEventHandlerSetProperty("mouseExitText", item->mouseExitText);
|
||||||
|
WriteMenuEventHandlerSetProperty("action", item->action);
|
||||||
|
WriteMenuEventHandlerSetProperty("accept", item->accept);
|
||||||
|
// WriteFloatProperty("special", item->special, 0.0f);
|
||||||
|
WriteSoundAliasProperty("focusSound", item->focusSound);
|
||||||
|
WriteStringProperty("dvarTest", item->dvarTest);
|
||||||
|
|
||||||
|
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
|
||||||
|
WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
|
||||||
|
WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
|
||||||
|
WriteMultiTokenStringProperty("showDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
|
||||||
|
WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
|
||||||
|
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
|
||||||
|
WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
|
||||||
|
|
||||||
|
WriteItemKeyHandlerProperty(item->onKey);
|
||||||
|
WriteStatementProperty("exp text", item->textExp, false);
|
||||||
|
WriteStatementProperty("exp textaligny", item->textAlignYExp, false);
|
||||||
|
WriteStatementProperty("exp material", item->materialExp, false);
|
||||||
|
WriteFloatExpressionsProperty(item->floatExpressions, item->floatExpressionCount);
|
||||||
|
WriteIntProperty("gamemsgwindowindex", item->gameMsgWindowIndex, 0);
|
||||||
|
WriteIntProperty("gamemsgwindowmode", item->gameMsgWindowMode, 0);
|
||||||
|
WriteDecodeEffectProperty("decodeEffect", item);
|
||||||
|
|
||||||
|
WriteListBoxProperties(item);
|
||||||
|
WriteEditFieldProperties(item);
|
||||||
|
WriteMultiProperties(item);
|
||||||
|
WriteEnumDvarProperties(item);
|
||||||
|
WriteTickerProperties(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteItemDefs(const itemDef_s* const* itemDefs, size_t itemCount)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < itemCount; i++)
|
||||||
|
{
|
||||||
|
StartItemDefScope();
|
||||||
|
|
||||||
|
WriteItemData(itemDefs[i]);
|
||||||
|
|
||||||
|
EndScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMenuData(const menuDef_t* menu)
|
||||||
|
{
|
||||||
|
WriteStringProperty("name", menu->window.name);
|
||||||
|
WriteBoolProperty("fullscreen", menu->data->fullScreen, false);
|
||||||
|
WriteKeywordProperty("screenSpace", menu->window.staticFlags & WINDOW_FLAG_SCREEN_SPACE);
|
||||||
|
WriteKeywordProperty("decoration", menu->window.staticFlags & WINDOW_FLAG_DECORATION);
|
||||||
|
WriteRectProperty("rect", menu->window.rect);
|
||||||
|
WriteIntProperty("style", menu->window.style, 0);
|
||||||
|
WriteIntProperty("border", menu->window.border, 0);
|
||||||
|
WriteFloatProperty("borderSize", menu->window.borderSize, 0.0f);
|
||||||
|
WriteColorProperty("backcolor", menu->window.backColor, COLOR_0000);
|
||||||
|
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
|
||||||
|
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
|
||||||
|
WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000);
|
||||||
|
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
|
||||||
|
WriteMaterialProperty("background", menu->window.background);
|
||||||
|
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
|
||||||
|
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
|
||||||
|
WriteKeywordProperty("outOfBoundsClick", menu->window.staticFlags & WINDOW_FLAG_OUT_OF_BOUNDS_CLICK);
|
||||||
|
WriteStringProperty("soundLoop", menu->data->soundName);
|
||||||
|
WriteKeywordProperty("popup", menu->window.staticFlags & WINDOW_FLAG_POPUP);
|
||||||
|
WriteFloatProperty("fadeClamp", menu->data->fadeClamp, 0.0f);
|
||||||
|
WriteIntProperty("fadeCycle", menu->data->fadeCycle, 0);
|
||||||
|
WriteFloatProperty("fadeAmount", menu->data->fadeAmount, 0.0f);
|
||||||
|
WriteFloatProperty("fadeInAmount", menu->data->fadeInAmount, 0.0f);
|
||||||
|
WriteFloatProperty("blurWorld", menu->data->blurRadius, 0.0f);
|
||||||
|
WriteKeywordProperty("legacySplitScreenScale", menu->window.staticFlags & WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE);
|
||||||
|
WriteKeywordProperty("hiddenDuringScope", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_SCOPE);
|
||||||
|
WriteKeywordProperty("hiddenDuringFlashbang", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG);
|
||||||
|
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
|
||||||
|
WriteStringProperty("allowedBinding", menu->data->allowedBinding);
|
||||||
|
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
|
||||||
|
|
||||||
|
if (menu->data->visibleExp)
|
||||||
|
WriteStatementProperty("visible", menu->data->visibleExp, true);
|
||||||
|
else if (menu->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE)
|
||||||
|
WriteIntProperty("visible", 1, 0);
|
||||||
|
|
||||||
|
WriteStatementProperty("exp rect X", menu->data->rectXExp, false);
|
||||||
|
WriteStatementProperty("exp rect Y", menu->data->rectYExp, false);
|
||||||
|
WriteStatementProperty("exp rect W", menu->data->rectWExp, false);
|
||||||
|
WriteStatementProperty("exp rect H", menu->data->rectHExp, false);
|
||||||
|
WriteStatementProperty("exp openSound", menu->data->openSoundExp, false);
|
||||||
|
WriteStatementProperty("exp closeSound", menu->data->closeSoundExp, false);
|
||||||
|
WriteStatementProperty("exp soundLoop", menu->data->soundLoopExp, false);
|
||||||
|
WriteMenuEventHandlerSetProperty("onOpen", menu->data->onOpen);
|
||||||
|
WriteMenuEventHandlerSetProperty("onClose", menu->data->onClose);
|
||||||
|
WriteMenuEventHandlerSetProperty("onRequestClose", menu->data->onCloseRequest);
|
||||||
|
WriteMenuEventHandlerSetProperty("onESC", menu->data->onESC);
|
||||||
|
WriteMenuEventHandlerSetProperty("onFocusDueToClose", menu->data->onFocusDueToClose);
|
||||||
|
WriteItemKeyHandlerProperty(menu->data->onKey);
|
||||||
|
WriteItemDefs(menu->items, menu->itemCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace IW5::menu
|
||||||
|
{
|
||||||
|
std::unique_ptr<IWriterIW5> CreateMenuWriter(std::ostream& stream)
|
||||||
|
{
|
||||||
|
return std::make_unique<MenuWriter>(stream);
|
||||||
|
}
|
||||||
|
} // namespace IW5::menu
|
19
src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.h
Normal file
19
src/ObjWriting/Game/IW5/Menu/MenuWriterIW5.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Menu/IMenuWriter.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace IW5::menu
|
||||||
|
{
|
||||||
|
class IWriterIW5 : public ::menu::IWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void WriteFunctionDef(const std::string& functionName, const Statement_s* statement) = 0;
|
||||||
|
virtual void WriteMenu(const menuDef_t& menu) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IWriterIW5> CreateMenuWriter(std::ostream& stream);
|
||||||
|
} // namespace IW5::menu
|
@@ -7,8 +7,8 @@
|
|||||||
#include "Leaderboard/LeaderboardJsonDumperIW5.h"
|
#include "Leaderboard/LeaderboardJsonDumperIW5.h"
|
||||||
#include "Localize/LocalizeDumperIW5.h"
|
#include "Localize/LocalizeDumperIW5.h"
|
||||||
#include "Maps/AddonMapEntsDumperIW5.h"
|
#include "Maps/AddonMapEntsDumperIW5.h"
|
||||||
#include "Menu/AssetDumperMenuDef.h"
|
#include "Menu/MenuDumperIW5.h"
|
||||||
#include "Menu/AssetDumperMenuList.h"
|
#include "Menu/MenuListDumperIW5.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
#include "RawFile/RawFileDumperIW5.h"
|
#include "RawFile/RawFileDumperIW5.h"
|
||||||
#include "Script/ScriptDumperIW5.h"
|
#include "Script/ScriptDumperIW5.h"
|
||||||
@@ -53,8 +53,8 @@ bool ObjWriter::DumpZone(AssetDumpingContext& context) const
|
|||||||
// DUMP_ASSET_POOL(AssetDumperGfxWorld, m_gfx_world, ASSET_TYPE_GFXWORLD)
|
// 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(AssetDumperGfxLightDef, m_gfx_light_def, ASSET_TYPE_LIGHT_DEF)
|
||||||
// DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT)
|
// DUMP_ASSET_POOL(AssetDumperFont_s, m_font, ASSET_TYPE_FONT)
|
||||||
DUMP_ASSET_POOL(AssetDumperMenuList, m_menu_list, ASSET_TYPE_MENULIST)
|
DUMP_ASSET_POOL(menu::MenuListDumper, m_menu_list, ASSET_TYPE_MENULIST)
|
||||||
DUMP_ASSET_POOL(AssetDumperMenuDef, m_menu_def, ASSET_TYPE_MENU)
|
DUMP_ASSET_POOL(menu::MenuDumper, m_menu_def, ASSET_TYPE_MENU)
|
||||||
DUMP_ASSET_POOL(localize::Dumper, m_localize, ASSET_TYPE_LOCALIZE_ENTRY)
|
DUMP_ASSET_POOL(localize::Dumper, m_localize, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||||
DUMP_ASSET_POOL(attachment::JsonDumper, m_attachment, ASSET_TYPE_ATTACHMENT)
|
DUMP_ASSET_POOL(attachment::JsonDumper, m_attachment, ASSET_TYPE_ATTACHMENT)
|
||||||
DUMP_ASSET_POOL(weapon::Dumper, m_weapon, ASSET_TYPE_WEAPON)
|
DUMP_ASSET_POOL(weapon::Dumper, m_weapon, ASSET_TYPE_WEAPON)
|
||||||
|
@@ -1,298 +0,0 @@
|
|||||||
#include "AbstractMenuDumper.h"
|
|
||||||
|
|
||||||
#include "Parsing/Impl/ParserSingleInputStream.h"
|
|
||||||
#include "Parsing/Simple/SimpleLexer.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
AbstractMenuDumper::AbstractMenuDumper(std::ostream& stream)
|
|
||||||
: m_stream(stream),
|
|
||||||
m_indent(0u)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::IncIndent()
|
|
||||||
{
|
|
||||||
m_indent++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::DecIndent()
|
|
||||||
{
|
|
||||||
if (m_indent > 0)
|
|
||||||
m_indent--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::Indent() const
|
|
||||||
{
|
|
||||||
for (auto i = 0u; i < m_indent; i++)
|
|
||||||
m_stream << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::StartScope(const std::string& scopeName)
|
|
||||||
{
|
|
||||||
Indent();
|
|
||||||
m_stream << scopeName << "\n";
|
|
||||||
Indent();
|
|
||||||
m_stream << "{\n";
|
|
||||||
IncIndent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::StartMenuDefScope()
|
|
||||||
{
|
|
||||||
StartScope("menuDef");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::StartItemDefScope()
|
|
||||||
{
|
|
||||||
StartScope("itemDef");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::StartFunctionDefScope()
|
|
||||||
{
|
|
||||||
StartScope("functionDef");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::EndScope()
|
|
||||||
{
|
|
||||||
DecIndent();
|
|
||||||
Indent();
|
|
||||||
m_stream << "}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> AbstractMenuDumper::CreateScriptTokenList(const char* script)
|
|
||||||
{
|
|
||||||
const std::string scriptString(script);
|
|
||||||
std::istringstream stringStream(scriptString);
|
|
||||||
ParserSingleInputStream inputStream(stringStream, "MenuScript");
|
|
||||||
|
|
||||||
SimpleLexer::Config lexerConfig;
|
|
||||||
lexerConfig.m_emit_new_line_tokens = false;
|
|
||||||
lexerConfig.m_read_strings = true;
|
|
||||||
lexerConfig.m_string_escape_sequences = true;
|
|
||||||
lexerConfig.m_read_integer_numbers = false;
|
|
||||||
lexerConfig.m_read_floating_point_numbers = false;
|
|
||||||
SimpleLexer lexer(&inputStream, std::move(lexerConfig));
|
|
||||||
|
|
||||||
std::vector<std::string> result;
|
|
||||||
auto hasLexerTokens = true;
|
|
||||||
while (hasLexerTokens)
|
|
||||||
{
|
|
||||||
const auto& token = lexer.GetToken(0);
|
|
||||||
switch (token.m_type)
|
|
||||||
{
|
|
||||||
case SimpleParserValueType::IDENTIFIER:
|
|
||||||
result.emplace_back(token.IdentifierValue());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SimpleParserValueType::STRING:
|
|
||||||
result.emplace_back(token.StringValue());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SimpleParserValueType::CHARACTER:
|
|
||||||
result.emplace_back(1, token.CharacterValue());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SimpleParserValueType::INVALID:
|
|
||||||
case SimpleParserValueType::END_OF_FILE:
|
|
||||||
hasLexerTokens = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer.PopTokens(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AbstractMenuDumper::DoesTokenNeedQuotationMarks(const std::string& token)
|
|
||||||
{
|
|
||||||
if (token.empty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const auto hasAlNumCharacter = std::ranges::any_of(token,
|
|
||||||
[](const char& c)
|
|
||||||
{
|
|
||||||
return isalnum(c);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasAlNumCharacter)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto hasNonIdentifierCharacter = std::ranges::any_of(token,
|
|
||||||
[](const char& c)
|
|
||||||
{
|
|
||||||
return !isalnum(c) && c != '_';
|
|
||||||
});
|
|
||||||
|
|
||||||
return hasNonIdentifierCharacter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteEscapedString(const std::string_view& str) const
|
|
||||||
{
|
|
||||||
m_stream << "\"";
|
|
||||||
|
|
||||||
for (const auto& c : str)
|
|
||||||
{
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\r':
|
|
||||||
m_stream << "\\r";
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
m_stream << "\\n";
|
|
||||||
break;
|
|
||||||
case '\t':
|
|
||||||
m_stream << "\\t";
|
|
||||||
break;
|
|
||||||
case '\f':
|
|
||||||
m_stream << "\\f";
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
m_stream << "\\\"";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
m_stream << c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stream << "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& AbstractMenuDumper::BoolValue(const bool value)
|
|
||||||
{
|
|
||||||
return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteKey(const std::string& keyName) const
|
|
||||||
{
|
|
||||||
m_stream << keyName;
|
|
||||||
|
|
||||||
if (keyName.size() < MENU_KEY_SPACING)
|
|
||||||
{
|
|
||||||
const auto spacingLength = MENU_KEY_SPACING - keyName.size();
|
|
||||||
for (auto i = 0u; i < spacingLength; i++)
|
|
||||||
m_stream << " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const
|
|
||||||
{
|
|
||||||
if (propertyValue.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
|
|
||||||
WriteEscapedString(propertyValue);
|
|
||||||
m_stream << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const
|
|
||||||
{
|
|
||||||
if (propertyValue == nullptr || propertyValue[0] == '\0')
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
|
|
||||||
WriteEscapedString(propertyValue);
|
|
||||||
m_stream << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const
|
|
||||||
{
|
|
||||||
if (propertyValue == defaultValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << BoolValue(propertyValue) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const
|
|
||||||
{
|
|
||||||
if (propertyValue == defaultValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << propertyValue << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const
|
|
||||||
{
|
|
||||||
if (std::fabs(propertyValue - defaultValue) < std::numeric_limits<float>::epsilon())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << propertyValue << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const
|
|
||||||
{
|
|
||||||
if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits<float>::epsilon()
|
|
||||||
&& std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits<float>::epsilon()
|
|
||||||
&& std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits<float>::epsilon()
|
|
||||||
&& std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits<float>::epsilon())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const
|
|
||||||
{
|
|
||||||
if (!shouldWrite)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const
|
|
||||||
{
|
|
||||||
for (auto i = 0u; i < sizeof(flagsValue) * 8; i++)
|
|
||||||
{
|
|
||||||
if (flagsValue & (1 << i))
|
|
||||||
{
|
|
||||||
Indent();
|
|
||||||
WriteKey(propertyKey);
|
|
||||||
m_stream << i << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::Start()
|
|
||||||
{
|
|
||||||
Indent();
|
|
||||||
m_stream << "{\n";
|
|
||||||
IncIndent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::End()
|
|
||||||
{
|
|
||||||
for (auto i = 0u; i < m_indent; i++)
|
|
||||||
{
|
|
||||||
DecIndent();
|
|
||||||
Indent();
|
|
||||||
m_stream << "}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractMenuDumper::IncludeMenu(const std::string& menuPath) const
|
|
||||||
{
|
|
||||||
Indent();
|
|
||||||
m_stream << "loadMenu { \"" << menuPath << "\" }\n";
|
|
||||||
}
|
|
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class AbstractMenuDumper
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
static constexpr auto MENU_KEY_SPACING = 28u;
|
|
||||||
static const inline std::string BOOL_VALUE_TRUE = "1";
|
|
||||||
static const inline std::string BOOL_VALUE_FALSE = "0";
|
|
||||||
static constexpr inline float COLOR_0000[4]{0.0f, 0.0f, 0.0f, 0.0f};
|
|
||||||
static constexpr inline float COLOR_1111[4]{1.0f, 1.0f, 1.0f, 1.0f};
|
|
||||||
|
|
||||||
std::ostream& m_stream;
|
|
||||||
size_t m_indent;
|
|
||||||
|
|
||||||
void IncIndent();
|
|
||||||
void DecIndent();
|
|
||||||
void Indent() const;
|
|
||||||
|
|
||||||
void StartScope(const std::string& scopeName);
|
|
||||||
void StartMenuDefScope();
|
|
||||||
void StartItemDefScope();
|
|
||||||
void StartFunctionDefScope();
|
|
||||||
void EndScope();
|
|
||||||
|
|
||||||
static std::vector<std::string> CreateScriptTokenList(const char* script);
|
|
||||||
static bool DoesTokenNeedQuotationMarks(const std::string& token);
|
|
||||||
|
|
||||||
void WriteEscapedString(const std::string_view& str) const;
|
|
||||||
|
|
||||||
static const std::string& BoolValue(bool value);
|
|
||||||
void WriteKey(const std::string& keyName) const;
|
|
||||||
void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const;
|
|
||||||
void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const;
|
|
||||||
void WriteBoolProperty(const std::string& propertyKey, bool propertyValue, bool defaultValue) const;
|
|
||||||
void WriteIntProperty(const std::string& propertyKey, int propertyValue, int defaultValue) const;
|
|
||||||
void WriteFloatProperty(const std::string& propertyKey, float propertyValue, float defaultValue) const;
|
|
||||||
void WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const;
|
|
||||||
void WriteKeywordProperty(const std::string& propertyKey, bool shouldWrite) const;
|
|
||||||
void WriteFlagsProperty(const std::string& propertyKey, int flagsValue) const;
|
|
||||||
|
|
||||||
explicit AbstractMenuDumper(std::ostream& stream);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void Start();
|
|
||||||
void End();
|
|
||||||
|
|
||||||
void IncludeMenu(const std::string& menuPath) const;
|
|
||||||
};
|
|
301
src/ObjWriting/Menu/AbstractMenuWriter.cpp
Normal file
301
src/ObjWriting/Menu/AbstractMenuWriter.cpp
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
#include "AbstractMenuWriter.h"
|
||||||
|
|
||||||
|
#include "Parsing/Impl/ParserSingleInputStream.h"
|
||||||
|
#include "Parsing/Simple/SimpleLexer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace menu
|
||||||
|
{
|
||||||
|
AbstractBaseWriter::AbstractBaseWriter(std::ostream& stream)
|
||||||
|
: m_stream(stream),
|
||||||
|
m_indent(0u)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::IncIndent()
|
||||||
|
{
|
||||||
|
m_indent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::DecIndent()
|
||||||
|
{
|
||||||
|
if (m_indent > 0)
|
||||||
|
m_indent--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::Indent() const
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < m_indent; i++)
|
||||||
|
m_stream << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::StartScope(const std::string& scopeName)
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
m_stream << scopeName << "\n";
|
||||||
|
Indent();
|
||||||
|
m_stream << "{\n";
|
||||||
|
IncIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::StartMenuDefScope()
|
||||||
|
{
|
||||||
|
StartScope("menuDef");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::StartItemDefScope()
|
||||||
|
{
|
||||||
|
StartScope("itemDef");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::StartFunctionDefScope()
|
||||||
|
{
|
||||||
|
StartScope("functionDef");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::EndScope()
|
||||||
|
{
|
||||||
|
DecIndent();
|
||||||
|
Indent();
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AbstractBaseWriter::CreateScriptTokenList(const char* script)
|
||||||
|
{
|
||||||
|
const std::string scriptString(script);
|
||||||
|
std::istringstream stringStream(scriptString);
|
||||||
|
ParserSingleInputStream inputStream(stringStream, "MenuScript");
|
||||||
|
|
||||||
|
SimpleLexer::Config lexerConfig;
|
||||||
|
lexerConfig.m_emit_new_line_tokens = false;
|
||||||
|
lexerConfig.m_read_strings = true;
|
||||||
|
lexerConfig.m_string_escape_sequences = true;
|
||||||
|
lexerConfig.m_read_integer_numbers = false;
|
||||||
|
lexerConfig.m_read_floating_point_numbers = false;
|
||||||
|
SimpleLexer lexer(&inputStream, std::move(lexerConfig));
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
auto hasLexerTokens = true;
|
||||||
|
while (hasLexerTokens)
|
||||||
|
{
|
||||||
|
const auto& token = lexer.GetToken(0);
|
||||||
|
switch (token.m_type)
|
||||||
|
{
|
||||||
|
case SimpleParserValueType::IDENTIFIER:
|
||||||
|
result.emplace_back(token.IdentifierValue());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SimpleParserValueType::STRING:
|
||||||
|
result.emplace_back(token.StringValue());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SimpleParserValueType::CHARACTER:
|
||||||
|
result.emplace_back(1, token.CharacterValue());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SimpleParserValueType::INVALID:
|
||||||
|
case SimpleParserValueType::END_OF_FILE:
|
||||||
|
hasLexerTokens = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer.PopTokens(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractBaseWriter::DoesTokenNeedQuotationMarks(const std::string& token)
|
||||||
|
{
|
||||||
|
if (token.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto hasAlNumCharacter = std::ranges::any_of(token,
|
||||||
|
[](const char& c)
|
||||||
|
{
|
||||||
|
return isalnum(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasAlNumCharacter)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto hasNonIdentifierCharacter = std::ranges::any_of(token,
|
||||||
|
[](const char& c)
|
||||||
|
{
|
||||||
|
return !isalnum(c) && c != '_';
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasNonIdentifierCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteEscapedString(const std::string_view& str) const
|
||||||
|
{
|
||||||
|
m_stream << "\"";
|
||||||
|
|
||||||
|
for (const auto& c : str)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '\r':
|
||||||
|
m_stream << "\\r";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
m_stream << "\\n";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
m_stream << "\\t";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
m_stream << "\\f";
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
m_stream << "\\\"";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_stream << c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stream << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& AbstractBaseWriter::BoolValue(const bool value)
|
||||||
|
{
|
||||||
|
return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteKey(const std::string& keyName) const
|
||||||
|
{
|
||||||
|
m_stream << keyName;
|
||||||
|
|
||||||
|
if (keyName.size() < MENU_KEY_SPACING)
|
||||||
|
{
|
||||||
|
const auto spacingLength = MENU_KEY_SPACING - keyName.size();
|
||||||
|
for (auto i = 0u; i < spacingLength; i++)
|
||||||
|
m_stream << " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const
|
||||||
|
{
|
||||||
|
if (propertyValue.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
WriteEscapedString(propertyValue);
|
||||||
|
m_stream << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const
|
||||||
|
{
|
||||||
|
if (propertyValue == nullptr || propertyValue[0] == '\0')
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
|
||||||
|
WriteEscapedString(propertyValue);
|
||||||
|
m_stream << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const
|
||||||
|
{
|
||||||
|
if (propertyValue == defaultValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << BoolValue(propertyValue) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteIntProperty(const std::string& propertyKey, const int propertyValue, const int defaultValue) const
|
||||||
|
{
|
||||||
|
if (propertyValue == defaultValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << propertyValue << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteFloatProperty(const std::string& propertyKey, const float propertyValue, const float defaultValue) const
|
||||||
|
{
|
||||||
|
if (std::fabs(propertyValue - defaultValue) < std::numeric_limits<float>::epsilon())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << propertyValue << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const
|
||||||
|
{
|
||||||
|
if (std::fabs(propertyValue[0] - defaultValue[0]) < std::numeric_limits<float>::epsilon()
|
||||||
|
&& std::fabs(propertyValue[1] - defaultValue[1]) < std::numeric_limits<float>::epsilon()
|
||||||
|
&& std::fabs(propertyValue[2] - defaultValue[2]) < std::numeric_limits<float>::epsilon()
|
||||||
|
&& std::fabs(propertyValue[3] - defaultValue[3]) < std::numeric_limits<float>::epsilon())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << propertyValue[0] << " " << propertyValue[1] << " " << propertyValue[2] << " " << propertyValue[3] << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteKeywordProperty(const std::string& propertyKey, const bool shouldWrite) const
|
||||||
|
{
|
||||||
|
if (!shouldWrite)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::WriteFlagsProperty(const std::string& propertyKey, const int flagsValue) const
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < sizeof(flagsValue) * 8; i++)
|
||||||
|
{
|
||||||
|
if (flagsValue & (1 << i))
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
WriteKey(propertyKey);
|
||||||
|
m_stream << i << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::Start()
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
m_stream << "{\n";
|
||||||
|
IncIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::End()
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < m_indent; i++)
|
||||||
|
{
|
||||||
|
DecIndent();
|
||||||
|
Indent();
|
||||||
|
m_stream << "}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBaseWriter::IncludeMenu(const std::string& menuPath) const
|
||||||
|
{
|
||||||
|
Indent();
|
||||||
|
m_stream << "loadMenu { \"" << menuPath << "\" }\n";
|
||||||
|
}
|
||||||
|
} // namespace menu
|
59
src/ObjWriting/Menu/AbstractMenuWriter.h
Normal file
59
src/ObjWriting/Menu/AbstractMenuWriter.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IMenuWriter.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace menu
|
||||||
|
{
|
||||||
|
class AbstractBaseWriter : public IWriter
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
static constexpr auto MENU_KEY_SPACING = 28u;
|
||||||
|
static const inline std::string BOOL_VALUE_TRUE = "1";
|
||||||
|
static const inline std::string BOOL_VALUE_FALSE = "0";
|
||||||
|
static constexpr inline float COLOR_0000[4]{0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
static constexpr inline float COLOR_1111[4]{1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Start() override;
|
||||||
|
void End() override;
|
||||||
|
|
||||||
|
void IncludeMenu(const std::string& menuPath) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit AbstractBaseWriter(std::ostream& stream);
|
||||||
|
|
||||||
|
void IncIndent();
|
||||||
|
void DecIndent();
|
||||||
|
void Indent() const;
|
||||||
|
|
||||||
|
void StartScope(const std::string& scopeName);
|
||||||
|
void StartMenuDefScope();
|
||||||
|
void StartItemDefScope();
|
||||||
|
void StartFunctionDefScope();
|
||||||
|
void EndScope();
|
||||||
|
|
||||||
|
static std::vector<std::string> CreateScriptTokenList(const char* script);
|
||||||
|
static bool DoesTokenNeedQuotationMarks(const std::string& token);
|
||||||
|
|
||||||
|
void WriteEscapedString(const std::string_view& str) const;
|
||||||
|
|
||||||
|
static const std::string& BoolValue(bool value);
|
||||||
|
void WriteKey(const std::string& keyName) const;
|
||||||
|
void WriteStringProperty(const std::string& propertyKey, const std::string& propertyValue) const;
|
||||||
|
void WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const;
|
||||||
|
void WriteBoolProperty(const std::string& propertyKey, bool propertyValue, bool defaultValue) const;
|
||||||
|
void WriteIntProperty(const std::string& propertyKey, int propertyValue, int defaultValue) const;
|
||||||
|
void WriteFloatProperty(const std::string& propertyKey, float propertyValue, float defaultValue) const;
|
||||||
|
void WriteColorProperty(const std::string& propertyKey, const float (&propertyValue)[4], const float (&defaultValue)[4]) const;
|
||||||
|
void WriteKeywordProperty(const std::string& propertyKey, bool shouldWrite) const;
|
||||||
|
void WriteFlagsProperty(const std::string& propertyKey, int flagsValue) const;
|
||||||
|
|
||||||
|
std::ostream& m_stream;
|
||||||
|
size_t m_indent;
|
||||||
|
};
|
||||||
|
} // namespace menu
|
18
src/ObjWriting/Menu/IMenuWriter.h
Normal file
18
src/ObjWriting/Menu/IMenuWriter.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace menu
|
||||||
|
{
|
||||||
|
class IWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IWriter() = default;
|
||||||
|
virtual ~IWriter() = default;
|
||||||
|
|
||||||
|
virtual void Start() = 0;
|
||||||
|
virtual void End() = 0;
|
||||||
|
|
||||||
|
virtual void IncludeMenu(const std::string& menuPath) const = 0;
|
||||||
|
};
|
||||||
|
} // namespace menu
|
Reference in New Issue
Block a user