Skip to content

Commit

Permalink
Merge pull request #7 from Laupetin/feature/iw5-menus
Browse files Browse the repository at this point in the history
IW5 Menu writing & dumping improvements
  • Loading branch information
Laupetin authored Sep 24, 2023
2 parents ce8511f + 36cd9e1 commit 6802b1c
Show file tree
Hide file tree
Showing 47 changed files with 2,189 additions and 181 deletions.
4 changes: 2 additions & 2 deletions src/Common/Game/IW4/IW4_Assets.h
Original file line number Diff line number Diff line change
Expand Up @@ -2084,8 +2084,8 @@ namespace IW4
float y;
float w;
float h;
char horzAlign;
char vertAlign;
unsigned char horzAlign;
unsigned char vertAlign;
};

enum WindowDefStaticFlag : unsigned int
Expand Down
35 changes: 31 additions & 4 deletions src/Common/Game/IW5/IW5_Assets.h
Original file line number Diff line number Diff line change
Expand Up @@ -2316,7 +2316,21 @@ namespace IW5
EXP_FUNC_STATIC_DVAR_FLOAT,
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
Expand Down Expand Up @@ -2554,12 +2568,25 @@ namespace IW5
WINDOW_FLAG_AUTO_WRAPPED = 0x800000,
WINDOW_FLAG_POPUP = 0x1000000,
WINDOW_FLAG_LEGACY_SPLIT_SCREEN_SCALE = 0x4000000,
WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG = 0x10000000,
WINDOW_FLAG_HIDDEN_DURING_SCOPE = 0x20000000,
WINDOW_FLAG_HIDDEN_DURING_UI = 0x40000000,
WINDOW_FLAG_HIDDEN_DURING_FLASH_BANG = 0x10000000, // confirmed
WINDOW_FLAG_HIDDEN_DURING_SCOPE = 0x20000000, // confirmed
WINDOW_FLAG_HIDDEN_DURING_UI = 0x40000000, // confirmed
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
{
const char* name;
Expand Down
16 changes: 9 additions & 7 deletions src/ObjLoading/Game/IW4/Menu/MenuConverterIW4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ namespace IW4
static_cast<float>(rect.y),
static_cast<float>(rect.w),
static_cast<float>(rect.h),
static_cast<char>(rect.horizontalAlign),
static_cast<char>(rect.verticalAlign)
static_cast<unsigned char>(rect.horizontalAlign),
static_cast<unsigned char>(rect.verticalAlign)
};
}

Expand All @@ -46,8 +46,8 @@ namespace IW4
static_cast<float>(rectRelativeTo.y + rect.y),
static_cast<float>(rect.w),
static_cast<float>(rect.h),
static_cast<char>(rect.horizontalAlign),
static_cast<char>(rect.verticalAlign)
static_cast<unsigned char>(rect.horizontalAlign),
static_cast<unsigned char>(rect.verticalAlign)
};
}

Expand Down Expand Up @@ -502,7 +502,7 @@ namespace IW4
{
const auto* staticValue = dynamic_cast<const SimpleExpressionValue*>(expression);
isStatic = staticValue != nullptr;
isTruthy = isStatic && staticValue->IsTruthy();
isTruthy = isStatic && (staticValue->m_type == SimpleExpressionValue::Type::INT || staticValue->m_type == SimpleExpressionValue::Type::DOUBLE) && staticValue->IsTruthy();
}
else
{
Expand Down Expand Up @@ -661,7 +661,7 @@ namespace IW4
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())
return nullptr;
Expand Down Expand Up @@ -751,7 +751,8 @@ namespace IW4
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;
}

Expand Down Expand Up @@ -943,6 +944,7 @@ namespace IW4
ApplyFlag(item->window.staticFlags, commonItem.m_auto_wrapped, WINDOW_FLAG_AUTO_WRAPPED);
ApplyFlag(item->window.staticFlags, commonItem.m_horizontal_scroll, WINDOW_FLAG_HORIZONTAL_SCROLL);
item->type = ConvertItemType(commonItem.m_type);
item->dataType = item->type;
item->window.border = commonItem.m_border;
item->window.borderSize = static_cast<float>(commonItem.m_border_size);
item->visibleExp = ConvertVisibleExpression(&item->window, commonItem.m_visible_expression.get(), &parentMenu, &commonItem);
Expand Down
17 changes: 17 additions & 0 deletions src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderMenuDef.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
14 changes: 14 additions & 0 deletions src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderMenuDef.h
Original file line number Diff line number Diff line change
@@ -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;
};
}
207 changes: 207 additions & 0 deletions src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderMenuList.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
18 changes: 18 additions & 0 deletions src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderMenuList.h
Original file line number Diff line number Diff line change
@@ -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;
};
}
Loading

0 comments on commit 6802b1c

Please sign in to comment.