Merge pull request #7 from Laupetin/feature/iw5-menus

IW5 Menu writing & dumping improvements
This commit is contained in:
Jan 2023-09-24 16:20:31 +02:00 committed by GitHub
commit 6802b1c23b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2189 additions and 181 deletions

View File

@ -2084,8 +2084,8 @@ namespace IW4
float y; float y;
float w; float w;
float h; float h;
char horzAlign; unsigned char horzAlign;
char vertAlign; unsigned char vertAlign;
}; };
enum WindowDefStaticFlag : unsigned int enum WindowDefStaticFlag : unsigned int

View File

@ -2316,7 +2316,21 @@ namespace IW5
EXP_FUNC_STATIC_DVAR_FLOAT, EXP_FUNC_STATIC_DVAR_FLOAT,
EXP_FUNC_STATIC_DVAR_STRING, EXP_FUNC_STATIC_DVAR_STRING,
EXP_FUNC_DYN_START EXP_FUNC_DYN_START,
EXP_FUNC_INT = EXP_FUNC_DYN_START,
EXP_FUNC_STRING,
EXP_FUNC_FLOAT,
EXP_FUNC_SIN,
EXP_FUNC_COS,
EXP_FUNC_MIN,
EXP_FUNC_MAX,
EXP_FUNC_MILLISECONDS,
EXP_FUNC_LOCAL_CLIENT_UI_MILLISECONDS,
EXP_FUNC_DVAR_INT,
EXP_FUNC_DVAR_BOOL,
EXP_FUNC_DVAR_FLOAT,
EXP_FUNC_DVAR_STRING
}; };
enum expressionEntryType : int enum expressionEntryType : int
@ -2554,12 +2568,25 @@ namespace IW5
WINDOW_FLAG_AUTO_WRAPPED = 0x800000, WINDOW_FLAG_AUTO_WRAPPED = 0x800000,
WINDOW_FLAG_POPUP = 0x1000000, WINDOW_FLAG_POPUP = 0x1000000,
WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE = 0x4000000, WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE = 0x4000000,
WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG = 0x10000000, WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG = 0x10000000, // confirmed
WINDOW_FLAG_HIDDEN_DURING_SCOPE = 0x20000000, WINDOW_FLAG_HIDDEN_DURING_SCOPE = 0x20000000, // confirmed
WINDOW_FLAG_HIDDEN_DURING_UI = 0x40000000, WINDOW_FLAG_HIDDEN_DURING_UI = 0x40000000, // confirmed
WINDOW_FLAG_TEXT_ONLY_FOCUS = 0x80000000, WINDOW_FLAG_TEXT_ONLY_FOCUS = 0x80000000,
}; };
// This is data from IW4, could be different for IW5, to be investigated
enum WindowDefDynamicFlag : unsigned int
{
WINDOW_FLAG_HOVERED = 0x1, // guessed
WINDOW_FLAG_FOCUSED = 0x2,
WINDOW_FLAG_VISIBLE = 0x4, // confirmed
WINDOW_FLAG_FADING_OUT = 0x10,
WINDOW_FLAG_FADING_IN = 0x20,
WINDOW_FLAG_80 = 0x80,
WINDOW_FLAG_NON_DEFAULT_BACKCOLOR = 0x8000,
WINDOW_FLAG_NON_DEFAULT_FORECOLOR = 0x10000
};
struct windowDef_t struct windowDef_t
{ {
const char* name; const char* name;

View File

@ -34,8 +34,8 @@ namespace IW4
static_cast<float>(rect.y), static_cast<float>(rect.y),
static_cast<float>(rect.w), static_cast<float>(rect.w),
static_cast<float>(rect.h), static_cast<float>(rect.h),
static_cast<char>(rect.horizontalAlign), static_cast<unsigned char>(rect.horizontalAlign),
static_cast<char>(rect.verticalAlign) static_cast<unsigned char>(rect.verticalAlign)
}; };
} }
@ -46,8 +46,8 @@ namespace IW4
static_cast<float>(rectRelativeTo.y + rect.y), static_cast<float>(rectRelativeTo.y + rect.y),
static_cast<float>(rect.w), static_cast<float>(rect.w),
static_cast<float>(rect.h), static_cast<float>(rect.h),
static_cast<char>(rect.horizontalAlign), static_cast<unsigned char>(rect.horizontalAlign),
static_cast<char>(rect.verticalAlign) static_cast<unsigned char>(rect.verticalAlign)
}; };
} }
@ -502,7 +502,7 @@ namespace IW4
{ {
const auto* staticValue = dynamic_cast<const SimpleExpressionValue*>(expression); const auto* staticValue = dynamic_cast<const SimpleExpressionValue*>(expression);
isStatic = staticValue != nullptr; isStatic = staticValue != nullptr;
isTruthy = isStatic && staticValue->IsTruthy(); isTruthy = isStatic && (staticValue->m_type == SimpleExpressionValue::Type::INT || staticValue->m_type == SimpleExpressionValue::Type::DOUBLE) && staticValue->IsTruthy();
} }
else else
{ {
@ -661,7 +661,7 @@ namespace IW4
return outputSet; return outputSet;
} }
_NODISCARD ItemKeyHandler* ConvertKeyHandler(const std::map<int, std::unique_ptr<CommonEventHandlerSet>>& keyHandlers, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const _NODISCARD ItemKeyHandler* ConvertKeyHandler(const std::multimap<int, std::unique_ptr<CommonEventHandlerSet>>& keyHandlers, const CommonMenuDef* menu, const CommonItemDef* item = nullptr) const
{ {
if (keyHandlers.empty()) if (keyHandlers.empty())
return nullptr; return nullptr;
@ -751,7 +751,8 @@ namespace IW4
continue; continue;
} }
assert(false); // Do not consider this a mistake since the games menus do this by mistake and it should be able to compile them anyway
// But the game should also not know what to do with this i guess
expressionIsStatic = false; expressionIsStatic = false;
} }
@ -943,6 +944,7 @@ namespace IW4
ApplyFlag(item->window.staticFlags, commonItem.m_auto_wrapped, WINDOW_FLAG_AUTO_WRAPPED); ApplyFlag(item->window.staticFlags, commonItem.m_auto_wrapped, WINDOW_FLAG_AUTO_WRAPPED);
ApplyFlag(item->window.staticFlags, commonItem.m_horizontal_scroll, WINDOW_FLAG_HORIZONTAL_SCROLL); ApplyFlag(item->window.staticFlags, commonItem.m_horizontal_scroll, WINDOW_FLAG_HORIZONTAL_SCROLL);
item->type = ConvertItemType(commonItem.m_type); item->type = ConvertItemType(commonItem.m_type);
item->dataType = item->type;
item->window.border = commonItem.m_border; item->window.border = commonItem.m_border;
item->window.borderSize = static_cast<float>(commonItem.m_border_size); item->window.borderSize = static_cast<float>(commonItem.m_border_size);
item->visibleExp = ConvertVisibleExpression(&item->window, commonItem.m_visible_expression.get(), &parentMenu, &commonItem); item->visibleExp = ConvertVisibleExpression(&item->window, commonItem.m_visible_expression.get(), &parentMenu, &commonItem);

View File

@ -0,0 +1,17 @@
#include "AssetLoaderMenuDef.h"
#include <cstring>
#include "ObjLoading.h"
#include "Game/IW5/IW5.h"
#include "Pool/GlobalAssetPool.h"
using namespace IW5;
void* AssetLoaderMenuDef::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
{
auto* menu = memory->Create<menuDef_t>();
memset(menu, 0, sizeof(menuDef_t));
menu->window.name = memory->Dup(assetName.c_str());
return menu;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Game/IW5/IW5.h"
#include "AssetLoading/BasicAssetLoader.h"
#include "SearchPath/ISearchPath.h"
namespace IW5
{
class AssetLoaderMenuDef final : public BasicAssetLoader<ASSET_TYPE_MENU, menuDef_t>
{
public:
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
};
}

View File

@ -0,0 +1,207 @@
#include "AssetLoaderMenuList.h"
#include <cstring>
#include <iostream>
#include "ObjLoading.h"
#include "Game/IW5/IW5.h"
#include "Game/IW5/Menu/MenuConversionZoneStateIW5.h"
#include "Game/IW5/Menu/MenuConverterIW5.h"
#include "Parsing/Menu/MenuFileReader.h"
#include "Pool/GlobalAssetPool.h"
using namespace IW5;
namespace IW5
{
class MenuLoader
{
public:
static bool ProcessParsedResults(const std::string& fileName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, menu::ParsingResult* parsingResult,
menu::MenuAssetZoneState* zoneState, MenuConversionZoneState* conversionState, std::vector<menuDef_t*>& menus,
std::vector<XAssetInfoGeneric*>& menuListDependencies)
{
const auto menuCount = parsingResult->m_menus.size();
const auto functionCount = parsingResult->m_functions.size();
const auto menuLoadCount = parsingResult->m_menus_to_load.size();
auto totalItemCount = 0u;
for (const auto& menu : parsingResult->m_menus)
totalItemCount += menu->m_items.size();
std::cout << "Successfully read menu file \"" << fileName << "\" (" << menuLoadCount << " loads, " << menuCount << " menus, " << functionCount << " functions, " << totalItemCount <<
" items)\n";
// Add all functions to the zone state to make them available for all menus to be converted
for (auto& function : parsingResult->m_functions)
zoneState->AddFunction(std::move(function));
// Prepare a list of all menus of this file
std::vector<XAssetInfo<menuDef_t>*> allMenusOfFile;
allMenusOfFile.reserve(parsingResult->m_menus.size());
// Convert all menus and add them as assets
for (auto& menu : parsingResult->m_menus)
{
MenuConverter converter(ObjLoading::Configuration.MenuNoOptimization, searchPath, memory, manager);
auto* menuAsset = converter.ConvertMenu(*menu);
if (menuAsset == nullptr)
{
std::cout << "Failed to convert menu file \"" << menu->m_name << "\"\n";
return false;
}
menus.push_back(menuAsset);
auto* menuAssetInfo = manager->AddAsset(ASSET_TYPE_MENU, menu->m_name, menuAsset, std::move(converter.GetDependencies()), std::vector<scr_string_t>());
if (menuAssetInfo)
{
allMenusOfFile.push_back(reinterpret_cast<XAssetInfo<menuDef_t>*>(menuAssetInfo));
menuListDependencies.push_back(menuAssetInfo);
}
zoneState->AddMenu(std::move(menu));
}
// Register this file with all loaded menus
conversionState->AddLoadedFile(fileName, std::move(allMenusOfFile));
return true;
}
static MenuList* CreateMenuListAsset(const std::string& assetName, MemoryManager* memory, const std::vector<menuDef_t*>& menus)
{
auto* menuListAsset = memory->Create<MenuList>();
menuListAsset->name = memory->Dup(assetName.c_str());
menuListAsset->menuCount = static_cast<int>(menus.size());
if (menuListAsset->menuCount > 0)
{
menuListAsset->menus = static_cast<menuDef_t**>(memory->Alloc(sizeof(uintptr_t) * menuListAsset->menuCount));
for (auto i = 0; i < menuListAsset->menuCount; i++)
menuListAsset->menus[i] = menus[i];
}
else
menuListAsset->menus = nullptr;
return menuListAsset;
}
static std::unique_ptr<menu::ParsingResult> ParseMenuFile(const std::string& menuFileName, ISearchPath* searchPath, const menu::MenuAssetZoneState* zoneState)
{
const auto file = searchPath->Open(menuFileName);
if (!file.IsOpen())
return nullptr;
menu::MenuFileReader reader(*file.m_stream, menuFileName, menu::FeatureLevel::IW5, [searchPath](const std::string& filename, const std::string& sourceFile) -> std::unique_ptr<std::istream>
{
auto foundFileToInclude = searchPath->Open(filename);
if (!foundFileToInclude.IsOpen() || !foundFileToInclude.m_stream)
return nullptr;
return std::move(foundFileToInclude.m_stream);
});
reader.IncludeZoneState(zoneState);
reader.SetPermissiveMode(ObjLoading::Configuration.MenuPermissiveParsing);
return reader.ReadMenuFile();
}
};
}
void* AssetLoaderMenuList::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
{
auto* menuList = memory->Create<MenuList>();
memset(menuList, 0, sizeof(MenuList));
menuList->name = memory->Dup(assetName.c_str());
return menuList;
}
bool AssetLoaderMenuList::CanLoadFromRaw() const
{
return true;
}
bool BuildMenuFileQueue(std::deque<std::string>& menuLoadQueue, const std::string& menuListAssetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, menu::MenuAssetZoneState* zoneState,
MenuConversionZoneState* conversionState, std::vector<menuDef_t*>& menus, std::vector<XAssetInfoGeneric*>& menuListDependencies)
{
const auto alreadyLoadedMenuListFileMenus = conversionState->m_menus_by_filename.find(menuListAssetName);
if (alreadyLoadedMenuListFileMenus == conversionState->m_menus_by_filename.end())
{
const auto menuListResult = MenuLoader::ParseMenuFile(menuListAssetName, searchPath, zoneState);
if (menuListResult)
{
MenuLoader::ProcessParsedResults(menuListAssetName, searchPath, memory, manager, menuListResult.get(), zoneState, conversionState, menus, menuListDependencies);
for (const auto& menuToLoad : menuListResult->m_menus_to_load)
menuLoadQueue.push_back(menuToLoad);
zoneState->AddMenusToLoad(menuListAssetName, std::move(menuListResult->m_menus_to_load));
}
else
return false;
}
return true;
}
void LoadMenuFileFromQueue(const std::string& menuFilePath, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, menu::MenuAssetZoneState* zoneState,
MenuConversionZoneState* conversionState, std::vector<menuDef_t*>& menus, std::vector<XAssetInfoGeneric*>& menuListDependencies)
{
const auto alreadyLoadedMenuFile = conversionState->m_menus_by_filename.find(menuFilePath);
if (alreadyLoadedMenuFile != conversionState->m_menus_by_filename.end())
{
std::cout << "Already loaded \"" << menuFilePath << "\", skipping\n";
for (auto* menu : alreadyLoadedMenuFile->second)
{
menus.push_back(menu->Asset());
menuListDependencies.push_back(menu);
}
return;
}
const auto menuFileResult = MenuLoader::ParseMenuFile(menuFilePath, searchPath, zoneState);
if (menuFileResult)
{
MenuLoader::ProcessParsedResults(menuFilePath, searchPath, memory, manager, menuFileResult.get(), zoneState, conversionState, menus, menuListDependencies);
if (!menuFileResult->m_menus_to_load.empty())
std::cout << "WARNING: Menu file has menus to load even though it is not a menu list, ignoring: \"" << menuFilePath << "\"\n";
}
else
std::cerr << "Could not read menu file \"" << menuFilePath << "\"\n";
}
bool AssetLoaderMenuList::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
{
std::vector<menuDef_t*> menus;
std::vector<XAssetInfoGeneric*> menuListDependencies;
auto* zoneState = manager->GetAssetLoadingContext()->GetZoneAssetLoaderState<menu::MenuAssetZoneState>();
auto* conversionState = manager->GetAssetLoadingContext()->GetZoneAssetLoaderState<MenuConversionZoneState>();
std::deque<std::string> menuLoadQueue;
if (!BuildMenuFileQueue(menuLoadQueue, assetName, searchPath, memory, manager, zoneState, conversionState, menus, menuListDependencies))
return false;
while(!menuLoadQueue.empty())
{
const auto& menuFileToLoad = menuLoadQueue.front();
LoadMenuFileFromQueue(menuFileToLoad, searchPath, memory, manager, zoneState, conversionState, menus, menuListDependencies);
menuLoadQueue.pop_front();
}
auto* menuListAsset = MenuLoader::CreateMenuListAsset(assetName, memory, menus);
if (menuListAsset)
manager->AddAsset(ASSET_TYPE_MENULIST, assetName, menuListAsset, menuListDependencies, std::vector<scr_string_t>());
return true;
}
void AssetLoaderMenuList::FinalizeAssetsForZone(AssetLoadingContext* context) const
{
context->GetZoneAssetLoaderState<MenuConversionZoneState>()->FinalizeSupportingData();
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "Game/IW5/IW5.h"
#include "AssetLoading/BasicAssetLoader.h"
#include "AssetLoading/IAssetLoadingManager.h"
#include "SearchPath/ISearchPath.h"
namespace IW5
{
class AssetLoaderMenuList final : public BasicAssetLoader<ASSET_TYPE_MENULIST, MenuList>
{
public:
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
_NODISCARD bool CanLoadFromRaw() const override;
bool LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
void FinalizeAssetsForZone(AssetLoadingContext* context) const override;
};
}

View File

@ -0,0 +1,121 @@
#include "MenuConversionZoneStateIW5.h"
#include <cstring>
using namespace IW5;
MenuConversionZoneState::MenuConversionZoneState()
: m_zone(nullptr),
m_supporting_data(nullptr)
{
}
void MenuConversionZoneState::SetZone(Zone* zone)
{
auto* memory = zone->GetMemory();
m_zone = zone;
m_supporting_data = memory->Create<ExpressionSupportingData>();
memset(m_supporting_data, 0, sizeof(ExpressionSupportingData));
}
Statement_s* MenuConversionZoneState::FindFunction(const std::string& functionName)
{
const auto foundFunction = m_function_by_name.find(functionName);
if (foundFunction != m_function_by_name.end())
return foundFunction->second;
return nullptr;
}
Statement_s* MenuConversionZoneState::AddFunction(const std::string& functionName, Statement_s* function)
{
m_functions.push_back(function);
m_function_by_name.emplace(std::make_pair(functionName, function));
return function;
}
size_t MenuConversionZoneState::AddStaticDvar(const std::string& dvarName)
{
const auto foundDvar = m_dvars_by_name.find(dvarName);
if (foundDvar != m_dvars_by_name.end())
return foundDvar->second;
auto* memory = m_zone->GetMemory();
auto* staticDvar = static_cast<StaticDvar*>(memory->Alloc(sizeof(StaticDvar)));
staticDvar->dvarName = memory->Dup(dvarName.c_str());
staticDvar->dvar = nullptr;
const auto staticDvarIndex = m_static_dvars.size();
m_static_dvars.push_back(staticDvar);
m_dvars_by_name.emplace(std::make_pair(dvarName, staticDvarIndex));
return staticDvarIndex;
}
const char* MenuConversionZoneState::AddString(const std::string& str)
{
const auto foundString = m_strings_by_value.find(str);
if (foundString != m_strings_by_value.end())
return foundString->second;
auto* memory = m_zone->GetMemory();
const auto* strDuped = memory->Dup(str.c_str());
m_strings.push_back(strDuped);
m_strings_by_value.emplace(std::make_pair(str, strDuped));
return strDuped;
}
void MenuConversionZoneState::AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile)
{
m_menus_by_filename.emplace(std::make_pair(std::move(loadedFileName), std::move(menusOfFile)));
}
void MenuConversionZoneState::FinalizeSupportingData() const
{
auto* memory = m_zone->GetMemory();
m_supporting_data->uifunctions.totalFunctions = static_cast<int>(m_functions.size());
m_supporting_data->staticDvarList.numStaticDvars = static_cast<int>(m_static_dvars.size());
m_supporting_data->uiStrings.totalStrings = static_cast<int>(m_strings.size());
if (m_supporting_data->uifunctions.functions)
memory->Free(m_supporting_data->uifunctions.functions);
if (m_supporting_data->staticDvarList.staticDvars)
memory->Free(m_supporting_data->staticDvarList.staticDvars);
if (m_supporting_data->uiStrings.strings)
memory->Free(m_supporting_data->uiStrings.strings);
if (!m_functions.empty())
{
m_supporting_data->uifunctions.functions = static_cast<Statement_s**>(memory->Alloc(sizeof(void*) * m_functions.size()));
memcpy(m_supporting_data->uifunctions.functions, &m_functions[0], sizeof(void*) * m_functions.size());
}
else
m_supporting_data->uifunctions.functions = nullptr;
if (!m_static_dvars.empty())
{
m_supporting_data->staticDvarList.staticDvars = static_cast<StaticDvar**>(memory->Alloc(sizeof(void*) * m_static_dvars.size()));
memcpy(m_supporting_data->staticDvarList.staticDvars, &m_static_dvars[0], sizeof(void*) * m_static_dvars.size());
}
else
m_supporting_data->staticDvarList.staticDvars = nullptr;
if (!m_strings.empty())
{
m_supporting_data->uiStrings.strings = static_cast<const char**>(memory->Alloc(sizeof(void*) * m_strings.size()));
memcpy(m_supporting_data->uiStrings.strings, &m_strings[0], sizeof(void*) * m_strings.size());
}
else
m_supporting_data->uiStrings.strings = nullptr;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <map>
#include "AssetLoading/IZoneAssetLoaderState.h"
#include "Game/IW5/IW5.h"
namespace IW5
{
class MenuConversionZoneState final : public IZoneAssetLoaderState
{
Zone* m_zone;
std::vector<Statement_s*> m_functions;
std::map<std::string, Statement_s*> m_function_by_name;
std::vector<StaticDvar*> m_static_dvars;
std::map<std::string, size_t> m_dvars_by_name;
std::vector<const char*> m_strings;
std::map<std::string, const char*> m_strings_by_value;
public:
std::map<std::string, std::vector<XAssetInfo<menuDef_t>*>> m_menus_by_filename;
ExpressionSupportingData* m_supporting_data;
MenuConversionZoneState();
void SetZone(Zone* zone) override;
Statement_s* FindFunction(const std::string& functionName);
Statement_s* AddFunction(const std::string& functionName, Statement_s* function);
size_t AddStaticDvar(const std::string& dvarName);
const char* AddString(const std::string& str);
void AddLoadedFile(std::string loadedFileName, std::vector<XAssetInfo<menuDef_t>*> menusOfFile);
void FinalizeSupportingData() const;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
#pragma once
#include "Utils/ClassUtils.h"
#include "AssetLoading/IAssetLoadingManager.h"
#include "Game/IW5/IW5.h"
#include "Parsing/Menu/Domain/CommonMenuDef.h"
#include "Utils/MemoryManager.h"
#include "SearchPath/ISearchPath.h"
namespace IW5
{
class MenuConverter
{
bool m_disable_optimizations;
ISearchPath* m_search_path;
MemoryManager* m_memory;
IAssetLoadingManager* m_manager;
std::vector<XAssetInfoGeneric*> m_dependencies;
public:
MenuConverter(bool disableOptimizations, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager);
std::vector<XAssetInfoGeneric*>& GetDependencies();
_NODISCARD menuDef_t* ConvertMenu(const menu::CommonMenuDef& commonMenu);
};
}

View File

@ -5,6 +5,8 @@
#include "ObjContainer/IPak/IPak.h" #include "ObjContainer/IPak/IPak.h"
#include "ObjLoading.h" #include "ObjLoading.h"
#include "AssetLoaders/AssetLoaderLocalizeEntry.h" #include "AssetLoaders/AssetLoaderLocalizeEntry.h"
#include "AssetLoaders/AssetLoaderMenuDef.h"
#include "AssetLoaders/AssetLoaderMenuList.h"
#include "AssetLoaders/AssetLoaderRawFile.h" #include "AssetLoaders/AssetLoaderRawFile.h"
#include "AssetLoaders/AssetLoaderStringTable.h" #include "AssetLoaders/AssetLoaderStringTable.h"
#include "AssetLoading/AssetLoadingManager.h" #include "AssetLoading/AssetLoadingManager.h"
@ -44,8 +46,8 @@ ObjLoader::ObjLoader()
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_GFXWORLD, GfxWorld)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_GFXWORLD, GfxWorld))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_LIGHT_DEF, GfxLightDef)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_LIGHT_DEF, GfxLightDef))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FONT, Font_s)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FONT, Font_s))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MENULIST, MenuList)) REGISTER_ASSET_LOADER(AssetLoaderMenuList)
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MENU, menuDef_t)) REGISTER_ASSET_LOADER(AssetLoaderMenuDef)
REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry) REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry)
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_ATTACHMENT, WeaponAttachment)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_ATTACHMENT, WeaponAttachment))
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_WEAPON, WeaponCompleteDef)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_WEAPON, WeaponCompleteDef))

View File

@ -32,6 +32,7 @@ std::vector<LocalizeFileEntry> LocalizeFileReader::ReadLocalizeFile()
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = true; lexerConfig.m_emit_new_line_tokens = true;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
lexerConfig.m_read_integer_numbers = false; lexerConfig.m_read_integer_numbers = false;
lexerConfig.m_read_floating_point_numbers = false; lexerConfig.m_read_floating_point_numbers = false;
const auto lexer = std::make_unique<SimpleLexer>(m_stream, std::move(lexerConfig)); const auto lexer = std::make_unique<SimpleLexer>(m_stream, std::move(lexerConfig));

View File

@ -15,44 +15,6 @@ SequenceLocalizeFileLanguageValue::SequenceLocalizeFileLanguageValue()
}); });
} }
std::string SequenceLocalizeFileLanguageValue::UnescapeValue(const std::string& value)
{
std::ostringstream str;
auto isEscaped = false;
for(const auto c : value)
{
if(isEscaped)
{
switch(c)
{
case 'n':
str << '\n';
break;
case 'r':
str << '\r';
break;
default:
str << c;
break;
}
isEscaped = false;
}
else if(c == '\\')
{
isEscaped = true;
}
else
{
str << c;
}
}
return str.str();
}
void SequenceLocalizeFileLanguageValue::ProcessMatch(LocalizeFileParserState* state, SequenceResult<SimpleParserValue>& result) const void SequenceLocalizeFileLanguageValue::ProcessMatch(LocalizeFileParserState* state, SequenceResult<SimpleParserValue>& result) const
{ {
const auto& langToken = result.NextCapture(CAPTURE_LANGUAGE_NAME); const auto& langToken = result.NextCapture(CAPTURE_LANGUAGE_NAME);
@ -69,5 +31,5 @@ void SequenceLocalizeFileLanguageValue::ProcessMatch(LocalizeFileParserState* st
state->m_current_reference_languages.emplace(langName); state->m_current_reference_languages.emplace(langName);
if(langName == state->m_language_name_caps) if(langName == state->m_language_name_caps)
state->m_entries.emplace_back(state->m_current_reference, UnescapeValue(valueToken.StringValue())); state->m_entries.emplace_back(state->m_current_reference, valueToken.StringValue());
} }

View File

@ -7,8 +7,6 @@ class SequenceLocalizeFileLanguageValue final : public LocalizeFileParser::seque
static constexpr auto CAPTURE_LANGUAGE_NAME = 1; static constexpr auto CAPTURE_LANGUAGE_NAME = 1;
static constexpr auto CAPTURE_ENTRY_VALUE = 2; static constexpr auto CAPTURE_ENTRY_VALUE = 2;
static std::string UnescapeValue(const std::string& value);
protected: protected:
void ProcessMatch(LocalizeFileParserState* state, SequenceResult<SimpleParserValue>& result) const override; void ProcessMatch(LocalizeFileParserState* state, SequenceResult<SimpleParserValue>& result) const override;

View File

@ -8,7 +8,10 @@ SequenceLocalizeFileReference::SequenceLocalizeFileReference()
AddMatchers({ AddMatchers({
create.Keyword("REFERENCE"), create.Keyword("REFERENCE"),
create.Identifier().Capture(CAPTURE_REFERENCE_NAME), create.Or({
create.Identifier(),
create.String()
}).Capture(CAPTURE_REFERENCE_NAME),
create.Type(SimpleParserValueType::NEW_LINE) create.Type(SimpleParserValueType::NEW_LINE)
}); });
} }

View File

@ -44,6 +44,7 @@ namespace menu
std::string m_select_icon; std::string m_select_icon;
std::unique_ptr<CommonEventHandlerSet> m_on_double_click; std::unique_ptr<CommonEventHandlerSet> m_on_double_click;
std::unique_ptr<ISimpleExpression> m_element_height_expression;
std::vector<Column> m_columns; std::vector<Column> m_columns;
}; };
@ -136,6 +137,7 @@ namespace menu
std::unique_ptr<ISimpleExpression> m_visible_expression; std::unique_ptr<ISimpleExpression> m_visible_expression;
std::unique_ptr<ISimpleExpression> m_disabled_expression; std::unique_ptr<ISimpleExpression> m_disabled_expression;
std::unique_ptr<ISimpleExpression> m_text_expression; std::unique_ptr<ISimpleExpression> m_text_expression;
std::unique_ptr<ISimpleExpression> m_text_align_y_expression;
std::unique_ptr<ISimpleExpression> m_material_expression; std::unique_ptr<ISimpleExpression> m_material_expression;
std::unique_ptr<ISimpleExpression> m_rect_x_exp; std::unique_ptr<ISimpleExpression> m_rect_x_exp;
std::unique_ptr<ISimpleExpression> m_rect_y_exp; std::unique_ptr<ISimpleExpression> m_rect_y_exp;
@ -145,6 +147,7 @@ namespace menu
ColorExpressions m_glowcolor_expressions; ColorExpressions m_glowcolor_expressions;
ColorExpressions m_backcolor_expressions; ColorExpressions m_backcolor_expressions;
std::unique_ptr<CommonEventHandlerSet> m_on_focus; std::unique_ptr<CommonEventHandlerSet> m_on_focus;
std::unique_ptr<CommonEventHandlerSet> m_has_focus;
std::unique_ptr<CommonEventHandlerSet> m_on_leave_focus; std::unique_ptr<CommonEventHandlerSet> m_on_leave_focus;
std::unique_ptr<CommonEventHandlerSet> m_on_mouse_enter; std::unique_ptr<CommonEventHandlerSet> m_on_mouse_enter;
std::unique_ptr<CommonEventHandlerSet> m_on_mouse_exit; std::unique_ptr<CommonEventHandlerSet> m_on_mouse_exit;
@ -152,7 +155,7 @@ namespace menu
std::unique_ptr<CommonEventHandlerSet> m_on_mouse_exit_text; std::unique_ptr<CommonEventHandlerSet> m_on_mouse_exit_text;
std::unique_ptr<CommonEventHandlerSet> m_on_action; std::unique_ptr<CommonEventHandlerSet> m_on_action;
std::unique_ptr<CommonEventHandlerSet> m_on_accept; std::unique_ptr<CommonEventHandlerSet> m_on_accept;
std::map<int, std::unique_ptr<CommonEventHandlerSet>> m_key_handlers; std::multimap<int, std::unique_ptr<CommonEventHandlerSet>> m_key_handlers;
std::unique_ptr<CommonItemFeaturesListBox> m_list_box_features; std::unique_ptr<CommonItemFeaturesListBox> m_list_box_features;
std::unique_ptr<CommonItemFeaturesEditField> m_edit_field_features; std::unique_ptr<CommonItemFeaturesEditField> m_edit_field_features;

View File

@ -29,6 +29,7 @@ namespace menu
int m_owner_draw = 0; int m_owner_draw = 0;
int m_owner_draw_flags = 0; int m_owner_draw_flags = 0;
std::string m_sound_loop; std::string m_sound_loop;
std::unique_ptr<ISimpleExpression> m_sound_loop_exp;
double m_fade_clamp = 0; double m_fade_clamp = 0;
int m_fade_cycle = 0; int m_fade_cycle = 0;
double m_fade_amount = 0; double m_fade_amount = 0;
@ -46,7 +47,8 @@ namespace menu
std::unique_ptr<CommonEventHandlerSet> m_on_close; std::unique_ptr<CommonEventHandlerSet> m_on_close;
std::unique_ptr<CommonEventHandlerSet> m_on_request_close; std::unique_ptr<CommonEventHandlerSet> m_on_request_close;
std::unique_ptr<CommonEventHandlerSet> m_on_esc; std::unique_ptr<CommonEventHandlerSet> m_on_esc;
std::map<int, std::unique_ptr<CommonEventHandlerSet>> m_key_handlers; std::unique_ptr<CommonEventHandlerSet> m_on_focus_due_to_close;
std::multimap<int, std::unique_ptr<CommonEventHandlerSet>> m_key_handlers;
bool m_full_screen = false; bool m_full_screen = false;
bool m_screen_space = false; bool m_screen_space = false;

View File

@ -0,0 +1,27 @@
#include "MenuFileCommonOperations.h"
#include "Parsing/ParsingException.h"
using namespace menu;
void MenuFileCommonOperations::EnsureIsNumericExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression)
{
if (!state->m_permissive_mode && expression.IsStatic())
{
const auto staticValue = expression.EvaluateStatic();
if (staticValue.m_type != SimpleExpressionValue::Type::INT && staticValue.m_type != SimpleExpressionValue::Type::DOUBLE)
throw ParsingException(pos, "Expression is expected to be numeric. Use permissive mode to compile anyway.");
}
}
void MenuFileCommonOperations::EnsureIsStringExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression)
{
if (!state->m_permissive_mode && expression.IsStatic())
{
const auto staticValue = expression.EvaluateStatic();
if (staticValue.m_type != SimpleExpressionValue::Type::STRING)
throw ParsingException(pos, "Expression is expected to be string. Use permissive mode to compile anyway.");
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "MenuFileParserState.h"
#include "Parsing/TokenPos.h"
#include "Parsing/Simple/Expression/ISimpleExpression.h"
namespace menu
{
class MenuFileCommonOperations
{
public:
static void EnsureIsNumericExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression);
static void EnsureIsStringExpression(const MenuFileParserState* state, const TokenPos& pos, const ISimpleExpression& expression);
};
}

View File

@ -129,6 +129,7 @@ std::unique_ptr<ParsingResult> MenuFileReader::ReadMenuFile()
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
MenuExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig); MenuExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig);

View File

@ -235,7 +235,8 @@ namespace menu::event_handler_set_scope_sequences
AddMatchers({ AddMatchers({
create.Or({ create.Or({
create.Numeric(), create.Numeric(),
create.Text(), create.String(),
create.Identifier(),
create.Type(SimpleParserValueType::CHARACTER), create.Type(SimpleParserValueType::CHARACTER),
}).Capture(CAPTURE_SCRIPT_TOKEN) }).Capture(CAPTURE_SCRIPT_TOKEN)
}); });
@ -579,8 +580,13 @@ namespace menu::event_handler_set_scope_sequences
state->m_current_script << "\" ; "; state->m_current_script << "\" ; ";
} }
static void EmitDynamicSetLocalVar(const MenuFileParserState* state, const SetLocalVarType type, const std::string& varName, std::unique_ptr<ISimpleExpression> expression) static void EmitDynamicSetLocalVar(MenuFileParserState* state, const SetLocalVarType type, const std::string& varName, std::unique_ptr<ISimpleExpression> expression)
{ {
auto remainingScript = state->m_current_script.str();
if (!remainingScript.empty())
state->m_current_nested_event_handler_set->m_elements.emplace_back(std::make_unique<CommonEventHandlerScript>(std::move(remainingScript)));
state->m_current_script.str(std::string());
state->m_current_nested_event_handler_set->m_elements.emplace_back(std::make_unique<CommonEventHandlerSetLocalVar>(type, varName, std::move(expression))); state->m_current_nested_event_handler_set->m_elements.emplace_back(std::make_unique<CommonEventHandlerSetLocalVar>(type, varName, std::move(expression)));
} }

View File

@ -10,6 +10,7 @@
#include "Generic/GenericKeywordPropertySequence.h" #include "Generic/GenericKeywordPropertySequence.h"
#include "Generic/GenericMenuEventHandlerSetPropertySequence.h" #include "Generic/GenericMenuEventHandlerSetPropertySequence.h"
#include "Generic/GenericStringPropertySequence.h" #include "Generic/GenericStringPropertySequence.h"
#include "Parsing/Menu/MenuFileCommonOperations.h"
#include "Parsing/Menu/Matcher/MenuExpressionMatchers.h" #include "Parsing/Menu/Matcher/MenuExpressionMatchers.h"
#include "Parsing/Menu/Matcher/MenuMatcherFactory.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h"
@ -45,6 +46,34 @@ class ItemScopeOperations
CommonItemFeatureType::EDIT_FIELD // ITEM_TYPE_PASSWORDFIELD CommonItemFeatureType::EDIT_FIELD // ITEM_TYPE_PASSWORDFIELD
}; };
inline static const CommonItemFeatureType IW5_FEATURE_TYPE_BY_TYPE[0x18]
{
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_TEXT
CommonItemFeatureType::NONE, // ITEM_TYPE_BUTTON
CommonItemFeatureType::NONE, // ITEM_TYPE_RADIOBUTTON
CommonItemFeatureType::NONE, // ITEM_TYPE_CHECKBOX
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_EDITFIELD
CommonItemFeatureType::NONE, // ITEM_TYPE_COMBO
CommonItemFeatureType::LISTBOX, // ITEM_TYPE_LISTBOX
CommonItemFeatureType::NONE, // ITEM_TYPE_MODEL
CommonItemFeatureType::NONE, // ITEM_TYPE_OWNERDRAW
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_NUMERICFIELD
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_SLIDER
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_YESNO
CommonItemFeatureType::MULTI_VALUE, // ITEM_TYPE_MULTI
CommonItemFeatureType::ENUM_DVAR, // ITEM_TYPE_DVARENUM
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_BIND
CommonItemFeatureType::NONE, // ITEM_TYPE_MENUMODEL
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_VALIDFILEFIELD
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_DECIMALFIELD
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_UPREDITFIELD
CommonItemFeatureType::NONE, // ITEM_TYPE_GAME_MESSAGE_WINDOW
CommonItemFeatureType::NEWS_TICKER, // ITEM_TYPE_NEWS_TICKER
CommonItemFeatureType::NONE, // ITEM_TYPE_TEXT_SCROLL
CommonItemFeatureType::EDIT_FIELD, // ITEM_TYPE_EMAILFIELD
CommonItemFeatureType::EDIT_FIELD // ITEM_TYPE_PASSWORDFIELD
};
public: public:
static void SetItemType(CommonItemDef& item, const FeatureLevel featureLevel, const TokenPos& pos, const int type) static void SetItemType(CommonItemDef& item, const FeatureLevel featureLevel, const TokenPos& pos, const int type)
{ {
@ -66,8 +95,10 @@ public:
case FeatureLevel::IW5: case FeatureLevel::IW5:
default: default:
assert(false); if (static_cast<unsigned>(type) >= std::extent_v<decltype(IW5_FEATURE_TYPE_BY_TYPE)>)
throw ParsingException(pos, "Unimplemented item types for feature level"); throw ParsingException(pos, "Invalid item type");
item.m_feature_type = IW5_FEATURE_TYPE_BY_TYPE[static_cast<unsigned>(type)];
break;
} }
switch (item.m_feature_type) switch (item.m_feature_type)
@ -431,26 +462,46 @@ namespace menu::item_scope_sequences
{ {
static constexpr auto CAPTURE_FIRST_TOKEN = 1; static constexpr auto CAPTURE_FIRST_TOKEN = 1;
static constexpr auto CAPTURE_COLUMN_COUNT = 2; static constexpr auto CAPTURE_COLUMN_COUNT = 2;
static constexpr auto CAPTURE_POS = 3; static constexpr auto CAPTURE_X_POS = 3;
static constexpr auto CAPTURE_WIDTH = 4; static constexpr auto CAPTURE_Y_POS = 4;
static constexpr auto CAPTURE_MAX_CHARS = 5; static constexpr auto CAPTURE_WIDTH = 5;
static constexpr auto CAPTURE_ALIGNMENT = 6; static constexpr auto CAPTURE_HEIGHT = 6;
static constexpr auto CAPTURE_MAX_CHARS = 7;
static constexpr auto CAPTURE_ALIGNMENT = 8;
public: public:
SequenceColumns() explicit SequenceColumns(const FeatureLevel featureLevel)
{ {
const MenuMatcherFactory create(this); const MenuMatcherFactory create(this);
AddMatchers({ if (featureLevel == FeatureLevel::IW5)
create.KeywordIgnoreCase("columns").Capture(CAPTURE_FIRST_TOKEN), {
create.Integer().Capture(CAPTURE_COLUMN_COUNT), AddMatchers({
create.Loop(create.And({ create.KeywordIgnoreCase("columns").Capture(CAPTURE_FIRST_TOKEN),
create.Integer().Capture(CAPTURE_POS), create.Integer().Capture(CAPTURE_COLUMN_COUNT),
create.Integer().Capture(CAPTURE_WIDTH), create.Loop(create.And({
create.Integer().Capture(CAPTURE_MAX_CHARS), create.Integer().Capture(CAPTURE_X_POS),
create.Integer().Capture(CAPTURE_ALIGNMENT), create.Integer().Capture(CAPTURE_Y_POS),
})), create.Integer().Capture(CAPTURE_WIDTH),
}); create.Integer().Capture(CAPTURE_HEIGHT),
create.Integer().Capture(CAPTURE_MAX_CHARS),
create.Integer().Capture(CAPTURE_ALIGNMENT),
})),
});
}
else
{
AddMatchers({
create.KeywordIgnoreCase("columns").Capture(CAPTURE_FIRST_TOKEN),
create.Integer().Capture(CAPTURE_COLUMN_COUNT),
create.Loop(create.And({
create.Integer().Capture(CAPTURE_X_POS),
create.Integer().Capture(CAPTURE_WIDTH),
create.Integer().Capture(CAPTURE_MAX_CHARS),
create.Integer().Capture(CAPTURE_ALIGNMENT),
})),
});
}
} }
protected: protected:
@ -461,14 +512,14 @@ namespace menu::item_scope_sequences
ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos()); ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, result.NextCapture(CAPTURE_FIRST_TOKEN).GetPos());
const auto& listBoxFeatures = state->m_current_item->m_list_box_features; const auto& listBoxFeatures = state->m_current_item->m_list_box_features;
while (result.HasNextCapture(CAPTURE_POS)) while (result.HasNextCapture(CAPTURE_X_POS))
{ {
CommonItemFeaturesListBox::Column column CommonItemFeaturesListBox::Column column
{ {
result.NextCapture(CAPTURE_POS).IntegerValue(), result.NextCapture(CAPTURE_X_POS).IntegerValue(),
0, state->m_feature_level == FeatureLevel::IW5 ? result.NextCapture(CAPTURE_Y_POS).IntegerValue() : 0,
result.NextCapture(CAPTURE_WIDTH).IntegerValue(), result.NextCapture(CAPTURE_WIDTH).IntegerValue(),
0, state->m_feature_level == FeatureLevel::IW5 ? result.NextCapture(CAPTURE_HEIGHT).IntegerValue() : 0,
result.NextCapture(CAPTURE_MAX_CHARS).IntegerValue(), result.NextCapture(CAPTURE_MAX_CHARS).IntegerValue(),
result.NextCapture(CAPTURE_ALIGNMENT).IntegerValue() result.NextCapture(CAPTURE_ALIGNMENT).IntegerValue()
}; };
@ -613,12 +664,14 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
{ {
state->m_current_item->m_border_size = value; state->m_current_item->m_border_size = value;
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_visible_expression = std::move(value); state->m_current_item->m_visible_expression = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("disabled", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("disabled", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_disabled_expression = std::move(value); state->m_current_item->m_disabled_expression = std::move(value);
})); }));
AddSequence(std::make_unique<GenericIntPropertySequence>("ownerdraw", [](const MenuFileParserState* state, const TokenPos&, const int value) AddSequence(std::make_unique<GenericIntPropertySequence>("ownerdraw", [](const MenuFileParserState* state, const TokenPos&, const int value)
@ -681,36 +734,28 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
{ {
state->m_current_item->m_background = value; state->m_current_item->m_background = value;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_focus; return state->m_current_item->m_on_focus;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("leaveFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("leaveFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_leave_focus; return state->m_current_item->m_on_leave_focus;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseEnter", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseEnter", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_mouse_enter; return state->m_current_item->m_on_mouse_enter;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseExit", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseExit", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_mouse_exit; return state->m_current_item->m_on_mouse_exit;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseEnterText", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseEnterText", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_mouse_enter_text; return state->m_current_item->m_on_mouse_enter_text;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseExitText", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("mouseExitText", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_mouse_exit_text; return state->m_current_item->m_on_mouse_exit_text;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("action", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("action", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_action; return state->m_current_item->m_on_action;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("accept", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("accept", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_item->m_on_accept; return state->m_current_item->m_on_accept;
})); }));
// special // special
@ -761,101 +806,131 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
state->m_current_item->m_game_message_window_mode = value; state->m_current_item->m_game_message_window_mode = value;
})); }));
AddSequence(std::make_unique<SequenceDecodeEffect>()); AddSequence(std::make_unique<SequenceDecodeEffect>());
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "disabled"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "disabled"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_disabled_expression = std::move(value); state->m_current_item->m_disabled_expression = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "text"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "text"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value);
state->m_current_item->m_text_expression = std::move(value); state->m_current_item->m_text_expression = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value);
state->m_current_item->m_material_expression = std::move(value); state->m_current_item->m_material_expression = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "material"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{
state->m_current_item->m_material_expression = std::move(value);
}));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_rect_x_exp = std::move(value); state->m_current_item->m_rect_x_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_rect_y_exp = std::move(value); state->m_current_item->m_rect_y_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_rect_w_exp = std::move(value); state->m_current_item->m_rect_w_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_rect_h_exp = std::move(value); state->m_current_item->m_rect_h_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_forecolor_expressions.m_r_exp = std::move(value); state->m_current_item->m_forecolor_expressions.m_r_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_forecolor_expressions.m_g_exp = std::move(value); state->m_current_item->m_forecolor_expressions.m_g_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_forecolor_expressions.m_b_exp = std::move(value); state->m_current_item->m_forecolor_expressions.m_b_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_forecolor_expressions.m_a_exp = std::move(value); state->m_current_item->m_forecolor_expressions.m_a_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "forecolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_forecolor_expressions.m_rgb_exp = std::move(value); state->m_current_item->m_forecolor_expressions.m_rgb_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_glowcolor_expressions.m_r_exp = std::move(value); state->m_current_item->m_glowcolor_expressions.m_r_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_glowcolor_expressions.m_g_exp = std::move(value); state->m_current_item->m_glowcolor_expressions.m_g_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_glowcolor_expressions.m_b_exp = std::move(value); state->m_current_item->m_glowcolor_expressions.m_b_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_glowcolor_expressions.m_a_exp = std::move(value); state->m_current_item->m_glowcolor_expressions.m_a_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "glowcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_glowcolor_expressions.m_rgb_exp = std::move(value); state->m_current_item->m_glowcolor_expressions.m_rgb_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "R"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "R"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_backcolor_expressions.m_r_exp = std::move(value); state->m_current_item->m_backcolor_expressions.m_r_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "G"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "G"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_backcolor_expressions.m_g_exp = std::move(value); state->m_current_item->m_backcolor_expressions.m_g_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "B"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "B"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_backcolor_expressions.m_b_exp = std::move(value); state->m_current_item->m_backcolor_expressions.m_b_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "A"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "A"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_backcolor_expressions.m_a_exp = std::move(value); state->m_current_item->m_backcolor_expressions.m_a_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "backcolor", "RGB"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_backcolor_expressions.m_rgb_exp = std::move(value); state->m_current_item->m_backcolor_expressions.m_rgb_exp = std::move(value);
})); }));
if (featureLevel == FeatureLevel::IW5)
{
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("hasFocus", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
return state->m_current_item->m_has_focus;
}));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "textaligny"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_text_align_y_expression = std::move(value);
}));
}
// ============== ListBox ============== // ============== ListBox ==============
AddSequence(std::make_unique<SequenceColumns>()); AddSequence(std::make_unique<SequenceColumns>(featureLevel));
AddSequence(std::make_unique<GenericKeywordPropertySequence>("notselectable", [](const MenuFileParserState* state, const TokenPos& pos) AddSequence(std::make_unique<GenericKeywordPropertySequence>("notselectable", [](const MenuFileParserState* state, const TokenPos& pos)
{ {
ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos); ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos);
@ -891,8 +966,7 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos); ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos);
state->m_current_item->m_list_box_features->m_element_style = value; state->m_current_item->m_list_box_features->m_element_style = value;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("doubleclick", [](const MenuFileParserState* state, const TokenPos& pos) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("doubleclick", [](const MenuFileParserState* state, const TokenPos& pos) -> std::unique_ptr<CommonEventHandlerSet>& {
{
ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos); ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos);
return state->m_current_item->m_list_box_features->m_on_double_click; return state->m_current_item->m_list_box_features->m_on_double_click;
})); }));
@ -907,6 +981,16 @@ void ItemScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
state->m_current_item->m_list_box_features->m_select_icon = value; state->m_current_item->m_list_box_features->m_select_icon = value;
})); }));
if (featureLevel == FeatureLevel::IW5)
{
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "elementheight"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{
ItemScopeOperations::EnsureHasListboxFeatures(*state->m_current_item, pos);
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_item->m_list_box_features->m_element_height_expression = std::move(value);
}));
}
// ============== Edit Field ============== // ============== Edit Field ==============
AddSequence(std::make_unique<SequenceDvarFloat>()); AddSequence(std::make_unique<SequenceDvarFloat>());
AddSequence(std::make_unique<GenericStringPropertySequence>("localvar", [](const MenuFileParserState* state, const TokenPos& pos, const std::string& value) AddSequence(std::make_unique<GenericStringPropertySequence>("localvar", [](const MenuFileParserState* state, const TokenPos& pos, const std::string& value)

View File

@ -10,6 +10,7 @@
#include "Generic/GenericKeywordPropertySequence.h" #include "Generic/GenericKeywordPropertySequence.h"
#include "Generic/GenericMenuEventHandlerSetPropertySequence.h" #include "Generic/GenericMenuEventHandlerSetPropertySequence.h"
#include "Generic/GenericStringPropertySequence.h" #include "Generic/GenericStringPropertySequence.h"
#include "Parsing/Menu/MenuFileCommonOperations.h"
#include "Parsing/Menu/Matcher/MenuMatcherFactory.h" #include "Parsing/Menu/Matcher/MenuMatcherFactory.h"
#include "Parsing/Menu/Domain/CommonMenuTypes.h" #include "Parsing/Menu/Domain/CommonMenuTypes.h"
#include "Parsing/Menu/Matcher/MenuExpressionMatchers.h" #include "Parsing/Menu/Matcher/MenuExpressionMatchers.h"
@ -254,24 +255,21 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
{ {
state->m_current_menu->m_style = value; state->m_current_menu->m_style = value;
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywordAndBool("visible", [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_menu->m_visible_expression = std::move(value); state->m_current_menu->m_visible_expression = std::move(value);
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onOpen", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onOpen", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_menu->m_on_open; return state->m_current_menu->m_on_open;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_menu->m_on_close; return state->m_current_menu->m_on_close;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onRequestClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onRequestClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_menu->m_on_request_close; return state->m_current_menu->m_on_request_close;
})); }));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onESC", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onESC", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
{
return state->m_current_menu->m_on_esc; return state->m_current_menu->m_on_esc;
})); }));
AddSequence(std::make_unique<GenericIntPropertySequence>("border", [](const MenuFileParserState* state, const TokenPos&, const int value) AddSequence(std::make_unique<GenericIntPropertySequence>("border", [](const MenuFileParserState* state, const TokenPos&, const int value)
@ -322,28 +320,34 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
{ {
state->m_current_menu->m_sound_loop = value; state->m_current_menu->m_sound_loop = value;
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "X"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_menu->m_rect_x_exp = std::move(value); state->m_current_menu->m_rect_x_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "Y"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_menu->m_rect_y_exp = std::move(value); state->m_current_menu->m_rect_y_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "W"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_menu->m_rect_w_exp = std::move(value); state->m_current_menu->m_rect_w_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "rect", "H"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsNumericExpression(state, pos, *value);
state->m_current_menu->m_rect_h_exp = std::move(value); state->m_current_menu->m_rect_h_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "openSound"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "openSound"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value);
state->m_current_menu->m_open_sound_exp = std::move(value); state->m_current_menu->m_open_sound_exp = std::move(value);
})); }));
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "closeSound"}, [](const MenuFileParserState* state, const TokenPos&, std::unique_ptr<ISimpleExpression> value) AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "closeSound"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{ {
MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value);
state->m_current_menu->m_close_sound_exp = std::move(value); state->m_current_menu->m_close_sound_exp = std::move(value);
})); }));
AddSequence(std::make_unique<GenericKeywordPropertySequence>("popup", [](const MenuFileParserState* state, const TokenPos&) AddSequence(std::make_unique<GenericKeywordPropertySequence>("popup", [](const MenuFileParserState* state, const TokenPos&)
@ -396,5 +400,20 @@ void MenuScopeSequences::AddSequences(FeatureLevel featureLevel, bool permissive
{ {
state->m_current_menu->m_text_only_focus = true; state->m_current_menu->m_text_only_focus = true;
})); }));
if (featureLevel == FeatureLevel::IW5)
{
AddSequence(GenericExpressionPropertySequence::WithKeywords({"exp", "soundLoop"}, [](const MenuFileParserState* state, const TokenPos& pos, std::unique_ptr<ISimpleExpression> value)
{
MenuFileCommonOperations::EnsureIsStringExpression(state, pos, *value);
state->m_current_menu->m_sound_loop_exp = std::move(value);
}));
AddSequence(std::make_unique<GenericMenuEventHandlerSetPropertySequence>("onFocusDueToClose", [](const MenuFileParserState* state, const TokenPos&) -> std::unique_ptr<CommonEventHandlerSet>& {
return state->m_current_menu->m_on_focus_due_to_close;
}));
}
AddSequence(std::make_unique<SequenceConsumeSemicolons>()); AddSequence(std::make_unique<SequenceConsumeSemicolons>());
} }

View File

@ -47,6 +47,7 @@ std::vector<std::unique_ptr<CommonStructuredDataDef>> StructuredDataDefReader::R
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
const auto lexer = std::make_unique<SimpleLexer>(m_stream, std::move(lexerConfig)); const auto lexer = std::make_unique<SimpleLexer>(m_stream, std::move(lexerConfig));

View File

@ -22,6 +22,7 @@ bool TechniqueFileReader::ReadTechniqueDefinition() const
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = false;
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
const auto lexer = std::make_unique<SimpleLexer>(m_comment_proxy.get(), std::move(lexerConfig)); const auto lexer = std::make_unique<SimpleLexer>(m_comment_proxy.get(), std::move(lexerConfig));

View File

@ -22,6 +22,7 @@ std::unique_ptr<techset::TechsetDefinition> TechsetFileReader::ReadTechsetDefini
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = false;
lexerConfig.m_read_integer_numbers = false; lexerConfig.m_read_integer_numbers = false;
lexerConfig.m_read_floating_point_numbers = false; lexerConfig.m_read_floating_point_numbers = false;
const auto lexer = std::make_unique<SimpleLexer>(m_comment_proxy.get(), std::move(lexerConfig)); const auto lexer = std::make_unique<SimpleLexer>(m_comment_proxy.get(), std::move(lexerConfig));

View File

@ -1,6 +1,8 @@
#include "StringFileDumper.h" #include "StringFileDumper.h"
#include <regex> #include <regex>
#include "Utils/StringUtils.h"
StringFileDumper::StringFileDumper(Zone* zone, std::ostream& stream) StringFileDumper::StringFileDumper(Zone* zone, std::ostream& stream)
: AbstractTextDumper(stream), : AbstractTextDumper(stream),
m_zone(zone), m_zone(zone),
@ -36,19 +38,32 @@ void StringFileDumper::WriteHeader()
m_wrote_header = true; m_wrote_header = true;
} }
void StringFileDumper::WriteReference(const std::string& reference) const
{
if (reference.find_first_not_of(utils::LETTERS_AL_NUM_UNDERSCORE) != std::string::npos)
{
m_stream << "REFERENCE \"";
utils::EscapeStringForQuotationMarks(m_stream, reference);
m_stream << "\"\n";
}
else
m_stream << "REFERENCE " << reference << "\n";
}
void StringFileDumper::WriteLocalizeEntry(const std::string& reference, const std::string& value) void StringFileDumper::WriteLocalizeEntry(const std::string& reference, const std::string& value)
{ {
if (!m_wrote_header) if (!m_wrote_header)
WriteHeader(); WriteHeader();
m_stream << "\n"; m_stream << "\n";
m_stream << "REFERENCE " << reference << "\n"; WriteReference(reference);
auto escapedValue = std::regex_replace(value, std::regex("\n"), "\\n");
escapedValue = std::regex_replace(escapedValue, std::regex("\r"), "\\r");
const auto valueSpacing = std::string(15 - m_language_caps.length(), ' '); const auto valueSpacing = std::string(15 - m_language_caps.length(), ' ');
m_stream << "LANG_" << m_language_caps << valueSpacing << "\"" << escapedValue << "\"\n"; m_stream << "LANG_" << m_language_caps << valueSpacing << "\"";
utils::EscapeStringForQuotationMarks(m_stream, value);
m_stream << "\"\n";
} }
void StringFileDumper::Finalize() void StringFileDumper::Finalize()

View File

@ -14,6 +14,7 @@ class StringFileDumper : AbstractTextDumper
bool m_wrote_header; bool m_wrote_header;
void WriteHeader(); void WriteHeader();
void WriteReference(const std::string& reference) const;
public: public:
StringFileDumper(Zone* zone, std::ostream& stream); StringFileDumper(Zone* zone, std::ostream& stream);

View File

@ -25,7 +25,7 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool<
stringFileDumper.SetLanguageName(language); stringFileDumper.SetLanguageName(language);
// Magic string. Original string files do have this config file. The purpose of the config file is unknown though. // Magic string. Original string files do have this config file. The purpose of the config file is unknown though.
stringFileDumper.SetConfigFile(R"(C:\trees\cod3\cod3\bin\StringEd.cfg)"); stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)");
stringFileDumper.SetNotes(""); stringFileDumper.SetNotes("");

View File

@ -25,7 +25,7 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool<
stringFileDumper.SetLanguageName(language); stringFileDumper.SetLanguageName(language);
// Magic string. Original string files do have this config file. The purpose of the config file is unknown though. // Magic string. Original string files do have this config file. The purpose of the config file is unknown though.
stringFileDumper.SetConfigFile(R"(C:\trees\cod3\cod3\bin\StringEd.cfg)"); stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)");
stringFileDumper.SetNotes(""); stringFileDumper.SetNotes("");

View File

@ -137,7 +137,7 @@ void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& cu
} }
} }
void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const
{ {
const auto& operand = statement->entries[currentPos].data.operand; const auto& operand = statement->entries[currentPos].data.operand;
@ -160,7 +160,7 @@ void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, siz
} }
if (functionIndex >= 0) if (functionIndex >= 0)
m_stream << "FUNC_" << functionIndex; m_stream << "FUNC_" << functionIndex << "()";
else else
m_stream << "INVALID_FUNC"; m_stream << "INVALID_FUNC";
m_stream << "()"; m_stream << "()";
@ -193,7 +193,7 @@ void MenuDumper::WriteStatementOperand(const Statement_s* statement, size_t& cur
break; break;
case VAL_STRING: case VAL_STRING:
m_stream << "\"" << operand.internals.stringVal.string << "\""; WriteEscapedString(operand.internals.stringVal.string);
break; break;
case VAL_FUNCTION: case VAL_FUNCTION:
@ -645,6 +645,7 @@ void MenuDumper::WriteMultiProperties(const itemDef_s* item) const
return; return;
WriteStringProperty("dvar", item->dvar); WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteMultiValueProperty(multiDef); WriteMultiValueProperty(multiDef);
} }
@ -653,6 +654,8 @@ void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const
if (item->type != ITEM_TYPE_DVARENUM) if (item->type != ITEM_TYPE_DVARENUM)
return; return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
} }
@ -770,6 +773,7 @@ void MenuDumper::WriteMenuData(const menuDef_t* menu)
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000); WriteColorProperty("focuscolor", menu->focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu->window.background); WriteMaterialProperty("background", menu->window.background);
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);

View File

@ -25,7 +25,7 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool<
stringFileDumper.SetLanguageName(language); stringFileDumper.SetLanguageName(language);
// Magic string. Original string files do have this config file. The purpose of the config file is unknown though. // Magic string. Original string files do have this config file. The purpose of the config file is unknown though.
stringFileDumper.SetConfigFile(R"(C:\trees\cod3\cod3\bin\StringEd.cfg)"); stringFileDumper.SetConfigFile(R"(C:/trees/cod3/cod3/bin/StringEd.cfg)");
stringFileDumper.SetNotes(""); stringFileDumper.SetNotes("");

View File

@ -137,7 +137,7 @@ void MenuDumper::WriteStatementOperator(const Statement_s* statement, size_t& cu
} }
} }
void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, size_t currentPos) const void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, const size_t currentPos) const
{ {
const auto& operand = statement->entries[currentPos].data.operand; const auto& operand = statement->entries[currentPos].data.operand;
@ -160,7 +160,7 @@ void MenuDumper::WriteStatementOperandFunction(const Statement_s* statement, siz
} }
if (functionIndex >= 0) if (functionIndex >= 0)
m_stream << "FUNC_" << functionIndex; m_stream << "FUNC_" << functionIndex << "()";
else else
m_stream << "INVALID_FUNC"; m_stream << "INVALID_FUNC";
} }
@ -192,7 +192,7 @@ void MenuDumper::WriteStatementOperand(const Statement_s* statement, size_t& cur
break; break;
case VAL_STRING: case VAL_STRING:
m_stream << "\"" << operand.internals.stringVal.string << "\""; WriteEscapedString(operand.internals.stringVal.string);
break; break;
case VAL_FUNCTION: case VAL_FUNCTION:
@ -492,6 +492,31 @@ void MenuDumper::WriteFloatExpressionsProperty(const ItemFloatExpression* floatE
} }
} }
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 void MenuDumper::WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const
{ {
if (listBox->numColumns <= 0) if (listBox->numColumns <= 0)
@ -533,7 +558,7 @@ void MenuDumper::WriteListBoxProperties(const itemDef_s* item)
WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick); WriteMenuEventHandlerSetProperty("doubleclick", listBox->onDoubleClick);
WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000); WriteColorProperty("selectBorder", listBox->selectBorder, COLOR_0000);
WriteMaterialProperty("selectIcon", listBox->selectIcon); WriteMaterialProperty("selectIcon", listBox->selectIcon);
WriteStatementProperty("exp elementHeight", listBox->elementHeightExp, false); WriteStatementProperty("exp elementheight", listBox->elementHeightExp, false);
} }
void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const void MenuDumper::WriteDvarFloatProperty(const std::string& propertyKey, const itemDef_s* item, const editFieldDef_s* editField) const
@ -622,6 +647,7 @@ void MenuDumper::WriteMultiProperties(const itemDef_s* item) const
return; return;
WriteStringProperty("dvar", item->dvar); WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteMultiValueProperty(multiDef); WriteMultiValueProperty(multiDef);
} }
@ -630,6 +656,8 @@ void MenuDumper::WriteEnumDvarProperties(const itemDef_s* item) const
if (item->type != ITEM_TYPE_DVARENUM) if (item->type != ITEM_TYPE_DVARENUM)
return; return;
WriteStringProperty("dvar", item->dvar);
WriteStringProperty("localvar", item->localVar);
WriteStringProperty("dvarEnumList", item->typeData.enumDvarName); WriteStringProperty("dvarEnumList", item->typeData.enumDvarName);
} }
@ -651,7 +679,7 @@ void MenuDumper::WriteItemData(const itemDef_s* item)
WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO); WriteKeywordProperty("textsavegame", item->itemFlags & ITEM_FLAG_SAVE_GAME_INFO);
WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE); WriteKeywordProperty("textcinematicsubtitle", item->itemFlags & ITEM_FLAG_CINEMATIC_SUBTITLE);
WriteStringProperty("group", item->window.group); WriteStringProperty("group", item->window.group);
WriteRectProperty("rect", item->window.rect); WriteRectProperty("rect", item->window.rectClient);
WriteIntProperty("style", item->window.style, 0); WriteIntProperty("style", item->window.style, 0);
WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION); WriteKeywordProperty("decoration", item->window.staticFlags & WINDOW_FLAG_DECORATION);
WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED); WriteKeywordProperty("autowrapped", item->window.staticFlags & WINDOW_FLAG_AUTO_WRAPPED);
@ -659,9 +687,15 @@ void MenuDumper::WriteItemData(const itemDef_s* item)
WriteIntProperty("type", item->type, ITEM_TYPE_TEXT); WriteIntProperty("type", item->type, ITEM_TYPE_TEXT);
WriteIntProperty("border", item->window.border, 0); WriteIntProperty("border", item->window.border, 0);
WriteFloatProperty("borderSize", item->window.borderSize, 0.0f); WriteFloatProperty("borderSize", item->window.borderSize, 0.0f);
WriteStatementProperty("visible", item->visibleExp, true);
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); WriteStatementProperty("disabled", item->disabledExp, true);
WriteIntProperty("ownerDraw", item->window.ownerDraw, 0); WriteIntProperty("ownerdraw", item->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
WriteIntProperty("align", item->alignment, 0); WriteIntProperty("align", item->alignment, 0);
WriteIntProperty("textalign", item->textAlignMode, 0); WriteIntProperty("textalign", item->textAlignMode, 0);
WriteFloatProperty("textalignx", item->textalignx, 0.0f); WriteFloatProperty("textalignx", item->textalignx, 0.0f);
@ -687,19 +721,18 @@ void MenuDumper::WriteItemData(const itemDef_s* item)
WriteMenuEventHandlerSetProperty("accept", item->accept); WriteMenuEventHandlerSetProperty("accept", item->accept);
// WriteFloatProperty("special", item->special, 0.0f); // WriteFloatProperty("special", item->special, 0.0f);
WriteSoundAliasProperty("focusSound", item->focusSound); WriteSoundAliasProperty("focusSound", item->focusSound);
WriteFlagsProperty("ownerdrawFlag", item->window.ownerDrawFlags);
WriteStringProperty("dvarTest", item->dvarTest); WriteStringProperty("dvarTest", item->dvarTest);
if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE) if (item->dvarFlags & ITEM_DVAR_FLAG_ENABLE)
WriteStringProperty("enableDvar", item->enableDvar); WriteMultiTokenStringProperty("enableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE) else if (item->dvarFlags & ITEM_DVAR_FLAG_DISABLE)
WriteStringProperty("disableDvar", item->enableDvar); WriteMultiTokenStringProperty("disableDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW) else if (item->dvarFlags & ITEM_DVAR_FLAG_SHOW)
WriteStringProperty("showDvar", item->enableDvar); WriteMultiTokenStringProperty("showDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE) else if (item->dvarFlags & ITEM_DVAR_FLAG_HIDE)
WriteStringProperty("hideDvar", item->enableDvar); WriteMultiTokenStringProperty("hideDvar", item->enableDvar);
else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS) else if (item->dvarFlags & ITEM_DVAR_FLAG_FOCUS)
WriteStringProperty("focusDvar", item->enableDvar); WriteMultiTokenStringProperty("focusDvar", item->enableDvar);
WriteItemKeyHandlerProperty(item->onKey); WriteItemKeyHandlerProperty(item->onKey);
WriteStatementProperty("exp text", item->textExp, false); WriteStatementProperty("exp text", item->textExp, false);
@ -744,6 +777,7 @@ void MenuDumper::WriteMenuData(const menuDef_t* menu)
WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111); WriteColorProperty("forecolor", menu->window.foreColor, COLOR_1111);
WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000); WriteColorProperty("bordercolor", menu->window.borderColor, COLOR_0000);
WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000); WriteColorProperty("focuscolor", menu->data->focusColor, COLOR_0000);
WriteColorProperty("outlinecolor", menu->window.outlineColor, COLOR_0000);
WriteMaterialProperty("background", menu->window.background); WriteMaterialProperty("background", menu->window.background);
WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0); WriteIntProperty("ownerdraw", menu->window.ownerDraw, 0);
WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags); WriteFlagsProperty("ownerdrawFlag", menu->window.ownerDrawFlags);
@ -761,7 +795,12 @@ void MenuDumper::WriteMenuData(const menuDef_t* menu)
WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI); WriteKeywordProperty("hiddenDuringUI", menu->window.staticFlags & WINDOW_FLAG_HIDDEN_DURING_UI);
WriteStringProperty("allowedBinding", menu->data->allowedBinding); WriteStringProperty("allowedBinding", menu->data->allowedBinding);
WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS); WriteKeywordProperty("textOnlyFocus", menu->window.staticFlags & WINDOW_FLAG_TEXT_ONLY_FOCUS);
WriteStatementProperty("visible", menu->data->visibleExp, true);
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 X", menu->data->rectXExp, false);
WriteStatementProperty("exp rect Y", menu->data->rectYExp, false); WriteStatementProperty("exp rect Y", menu->data->rectYExp, false);
WriteStatementProperty("exp rect W", menu->data->rectWExp, false); WriteStatementProperty("exp rect W", menu->data->rectWExp, false);

View File

@ -29,6 +29,7 @@ namespace IW5
void WriteSoundAliasProperty(const std::string& propertyKey, const snd_alias_list_t* soundAliasValue) 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 WriteDecodeEffectProperty(const std::string& propertyKey, const itemDef_s* item) const;
void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue); void WriteItemKeyHandlerProperty(const ItemKeyHandler* itemKeyHandlerValue);
void WriteMultiTokenStringProperty(const std::string& propertyKey, const char* value) const;
void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const; void WriteFloatExpressionsProperty(const ItemFloatExpression* floatExpressions, int floatExpressionCount) const;
void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const; void WriteColumnProperty(const std::string& propertyKey, const listBoxDef_s* listBox) const;

View File

@ -25,7 +25,7 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool<
stringFileDumper.SetLanguageName(language); stringFileDumper.SetLanguageName(language);
// Magic string. Original string files do have this config file. The purpose of the config file is unknown though. // Magic string. Original string files do have this config file. The purpose of the config file is unknown though.
stringFileDumper.SetConfigFile(R"(C:\projects\cod\t5\bin\StringEd.cfg)"); stringFileDumper.SetConfigFile(R"(C:/projects/cod/t5/bin/StringEd.cfg)");
stringFileDumper.SetNotes(""); stringFileDumper.SetNotes("");

View File

@ -25,7 +25,7 @@ void AssetDumperLocalizeEntry::DumpPool(AssetDumpingContext& context, AssetPool<
stringFileDumper.SetLanguageName(language); stringFileDumper.SetLanguageName(language);
// Magic string. Original string files do have this config file. The purpose of the config file is unknown though. // Magic string. Original string files do have this config file. The purpose of the config file is unknown though.
stringFileDumper.SetConfigFile(R"(C:\projects\cod\t6\bin\StringEd.cfg)"); stringFileDumper.SetConfigFile(R"(C:/projects/cod/t6/bin/StringEd.cfg)");
stringFileDumper.SetNotes(""); stringFileDumper.SetNotes("");

View File

@ -70,6 +70,7 @@ std::vector<std::string> AbstractMenuDumper::CreateScriptTokenList(const char* s
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
lexerConfig.m_read_integer_numbers = false; lexerConfig.m_read_integer_numbers = false;
lexerConfig.m_read_floating_point_numbers = false; lexerConfig.m_read_floating_point_numbers = false;
SimpleLexer lexer(&inputStream, std::move(lexerConfig)); SimpleLexer lexer(&inputStream, std::move(lexerConfig));
@ -130,6 +131,38 @@ bool AbstractMenuDumper::DoesTokenNeedQuotationMarks(const std::string& token)
return hasNonIdentifierCharacter; 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) const std::string& AbstractMenuDumper::BoolValue(const bool value)
{ {
return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE; return value ? BOOL_VALUE_TRUE : BOOL_VALUE_FALSE;
@ -154,7 +187,9 @@ void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, con
Indent(); Indent();
WriteKey(propertyKey); WriteKey(propertyKey);
m_stream << "\"" << propertyValue << "\"\n";
WriteEscapedString(propertyValue);
m_stream << "\n";
} }
void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, const char* propertyValue) const
@ -164,7 +199,9 @@ void AbstractMenuDumper::WriteStringProperty(const std::string& propertyKey, con
Indent(); Indent();
WriteKey(propertyKey); WriteKey(propertyKey);
m_stream << "\"" << propertyValue << "\"\n";
WriteEscapedString(propertyValue);
m_stream << "\n";
} }
void AbstractMenuDumper::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const void AbstractMenuDumper::WriteBoolProperty(const std::string& propertyKey, const bool propertyValue, const bool defaultValue) const

View File

@ -30,6 +30,8 @@ protected:
static std::vector<std::string> CreateScriptTokenList(const char* script); static std::vector<std::string> CreateScriptTokenList(const char* script);
static bool DoesTokenNeedQuotationMarks(const std::string& token); static bool DoesTokenNeedQuotationMarks(const std::string& token);
void WriteEscapedString(const std::string_view& str) const;
static const std::string& BoolValue(bool value); static const std::string& BoolValue(bool value);
void WriteKey(const std::string& keyName) const; 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 std::string& propertyValue) const;

View File

@ -4,6 +4,7 @@
#include <sstream> #include <sstream>
#include "Utils/Alignment.h" #include "Utils/Alignment.h"
#include "Utils/StringUtils.h"
StructuredDataDefDumper::StructuredDataDefDumper(std::ostream& stream) StructuredDataDefDumper::StructuredDataDefDumper(std::ostream& stream)
: AbstractTextDumper(stream), : AbstractTextDumper(stream),
@ -37,7 +38,9 @@ void StructuredDataDefDumper::DumpEnum(const CommonStructuredDataEnum& _enum)
for (auto i = 0u; i < entryCount; i++) for (auto i = 0u; i < entryCount; i++)
{ {
Indent(); Indent();
m_stream << "\"" << _enum.m_entries[i].m_name << "\""; m_stream << "\"";
utils::EscapeStringForQuotationMarks(m_stream, _enum.m_entries[i].m_name);
m_stream << "\"";
if (i + 1 < entryCount) if (i + 1 < entryCount)
m_stream << ","; m_stream << ",";

View File

@ -2,11 +2,13 @@
#include <cassert> #include <cassert>
#include <deque> #include <deque>
#include <sstream>
#include "Utils/ClassUtils.h" #include "Utils/ClassUtils.h"
#include "Parsing/ILexer.h" #include "Parsing/ILexer.h"
#include "Parsing/IParserLineStream.h" #include "Parsing/IParserLineStream.h"
#include "Parsing/ParsingException.h" #include "Parsing/ParsingException.h"
#include "Utils/StringUtils.h"
template <typename TokenType> template <typename TokenType>
class AbstractLexer : public ILexer<TokenType> class AbstractLexer : public ILexer<TokenType>
@ -140,6 +142,52 @@ protected:
return std::string(currentLine.m_line, startPos, m_current_line_offset - startPos); return std::string(currentLine.m_line, startPos, m_current_line_offset - startPos);
} }
/**
* \brief Reads an identifier from the current position
* \return The value of the read identifier
*/
std::string ReadStringWithEscapeSequences()
{
const auto& currentLine = CurrentLine();
assert(m_current_line_offset >= 1);
assert(currentLine.m_line[m_current_line_offset - 1] == '"');
const auto startPos = m_current_line_offset;
const auto lineSize = currentLine.m_line.size();
auto isEscaped = false;
auto inEscape = false;
while (true)
{
if (m_current_line_offset >= lineSize)
throw ParsingException(TokenPos(*currentLine.m_filename, currentLine.m_line_number, m_current_line_offset), "Unclosed string");
const auto c = currentLine.m_line[m_current_line_offset];
if (c == '\"' && !inEscape)
break;
if (c == '\\' && !inEscape)
{
isEscaped = true;
inEscape = true;
}
else
{
inEscape = false;
}
m_current_line_offset++;
}
std::string str(currentLine.m_line, startPos, m_current_line_offset++ - startPos);
if (!isEscaped)
return str;
std::ostringstream ss;
utils::UnescapeStringFromQuotationMarks(ss, std::move(str));
return ss.str();
}
/** /**
* \brief Reads an identifier from the current position * \brief Reads an identifier from the current position
* \return The value of the read identifier * \return The value of the read identifier

View File

@ -67,6 +67,7 @@ std::unique_ptr<ISimpleExpression> SimpleExpressionInterpreter::Evaluate() const
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
SimpleExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig); SimpleExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig);
SimpleLexer lexer(m_input, std::move(lexerConfig)); SimpleLexer lexer(m_input, std::move(lexerConfig));

View File

@ -14,7 +14,7 @@ SimpleLexer::MultiCharacterTokenLookupEntry::MultiCharacterTokenLookupEntry(cons
SimpleLexer::SimpleLexer(IParserLineStream* stream) SimpleLexer::SimpleLexer(IParserLineStream* stream)
: AbstractLexer(stream), : AbstractLexer(stream),
m_config{false, true, true, true, {}}, m_config{false, true, false, true, true, {}},
m_check_for_multi_character_tokens(false), m_check_for_multi_character_tokens(false),
m_last_line(1) m_last_line(1)
{ {
@ -31,7 +31,7 @@ SimpleLexer::SimpleLexer(IParserLineStream* stream, Config config)
m_config.m_multi_character_tokens.clear(); m_config.m_multi_character_tokens.clear();
// If reading floating point numbers then must be reading integers // If reading floating point numbers then must be reading integers
assert(config.m_read_floating_point_numbers == false || config.m_read_floating_point_numbers == config.m_read_integer_numbers); assert(m_config.m_read_floating_point_numbers == false || m_config.m_read_floating_point_numbers == m_config.m_read_integer_numbers);
} }
void SimpleLexer::AddMultiCharacterTokenConfigToLookup(Config::MultiCharacterToken tokenConfig) void SimpleLexer::AddMultiCharacterTokenConfigToLookup(Config::MultiCharacterToken tokenConfig)
@ -121,7 +121,7 @@ SimpleParserValue SimpleLexer::GetNextToken()
} }
if (m_config.m_read_strings && c == '\"') if (m_config.m_read_strings && c == '\"')
return SimpleParserValue::String(pos, new std::string(ReadString())); return SimpleParserValue::String(pos, new std::string(m_config.m_string_escape_sequences ? ReadStringWithEscapeSequences() : ReadString()));
if (m_config.m_read_integer_numbers && (isdigit(c) || (c == '+' || c == '-' || (m_config.m_read_floating_point_numbers && c == '.')) && isdigit(PeekChar()))) if (m_config.m_read_integer_numbers && (isdigit(c) || (c == '+' || c == '-' || (m_config.m_read_floating_point_numbers && c == '.')) && isdigit(PeekChar())))
{ {

View File

@ -22,10 +22,11 @@ public:
MultiCharacterToken(int id, std::string value); MultiCharacterToken(int id, std::string value);
}; };
bool m_emit_new_line_tokens; bool m_emit_new_line_tokens = false;
bool m_read_strings; bool m_read_strings = true;
bool m_read_integer_numbers; bool m_string_escape_sequences = false;
bool m_read_floating_point_numbers; bool m_read_integer_numbers = true;
bool m_read_floating_point_numbers = true;
std::vector<MultiCharacterToken> m_multi_character_tokens; std::vector<MultiCharacterToken> m_multi_character_tokens;
}; };

View File

@ -0,0 +1,91 @@
#include "StringUtils.h"
#include <sstream>
namespace utils
{
std::string EscapeStringForQuotationMarks(const std::string_view& str)
{
std::ostringstream ss;
EscapeStringForQuotationMarks(ss, str);
return ss.str();
}
void EscapeStringForQuotationMarks(std::ostream& stream, const std::string_view& str)
{
for (const auto& c : str)
{
switch (c)
{
case '\r':
stream << "\\r";
break;
case '\n':
stream << "\\n";
break;
case '\t':
stream << "\\t";
break;
case '\f':
stream << "\\f";
break;
case '"':
stream << "\\\"";
break;
case '\\':
stream << "\\\\";
break;
default:
stream << c;
break;
}
}
}
std::string UnescapeStringFromQuotationMarks(const std::string_view& str)
{
std::ostringstream ss;
UnescapeStringFromQuotationMarks(ss, str);
return ss.str();
}
void UnescapeStringFromQuotationMarks(std::ostream& stream, const std::string_view& str)
{
auto inEscape = false;
for (const auto& c : str)
{
if (inEscape)
{
switch (c)
{
case 'r':
stream << "\r";
break;
case 'n':
stream << "\n";
break;
case 't':
stream << "\t";
break;
case 'f':
stream << "\f";
break;
case '"':
stream << "\"";
break;
case '\\':
stream << "\\";
break;
default:
stream << c;
break;
}
inEscape = false;
}
else if (c != '\\')
stream << c;
else
inEscape = true;
}
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <string>
namespace utils
{
#define M_LETTERS_AL_NUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
static constexpr const char* LETTERS_AL_NUM = M_LETTERS_AL_NUM;
static constexpr const char* LETTERS_AL_NUM_UNDERSCORE = M_LETTERS_AL_NUM "_";
std::string EscapeStringForQuotationMarks(const std::string_view& str);
void EscapeStringForQuotationMarks(std::ostream& stream, const std::string_view& str);
std::string UnescapeStringFromQuotationMarks(const std::string_view& str);
void UnescapeStringFromQuotationMarks(std::ostream& stream, const std::string_view& str);
}

View File

@ -112,6 +112,7 @@ namespace test::parsing::simple::expression
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_read_strings = true; lexerConfig.m_read_strings = true;
lexerConfig.m_string_escape_sequences = true;
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;