Merge pull request #569 from diamante0018/zonebuilder-moment

[ZoneBuilder]: Add .str parsing
This commit is contained in:
Edo 2022-11-17 14:52:52 +00:00 committed by GitHub
commit 45bd4e027c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 156 additions and 71 deletions

View File

@ -52,4 +52,33 @@ namespace Assets
buffer->popBlock(); buffer->popBlock();
} }
void ILocalizeEntry::ParseLocalizedStringsFile(Components::ZoneBuilder::Zone* builder, const std::string& name, const std::string& filename)
{
std::vector<Game::LocalizeEntry*> assets;
const auto _0 = gsl::finally([]
{
Components::Localization::ParseOutput(nullptr);
Components::Localization::PrefixOverride = {};
});
Components::Localization::PrefixOverride = Utils::String::ToUpper(name) + "_";
Components::Localization::ParseOutput([&assets](Game::LocalizeEntry* asset)
{
assets.push_back(asset);
});
const auto* psLoadedFile = Game::SE_Load(filename.data(), false);
if (psLoadedFile)
{
Game::Com_PrintError(Game::CON_CHANNEL_SYSTEM, "^1Localization ERROR: %s\n", psLoadedFile);
return;
}
auto type = Game::DB_GetXAssetNameType("localize");
for (const auto& entry : assets)
{
builder->addRawAsset(type, entry);
}
}
} }

View File

@ -5,9 +5,11 @@ namespace Assets
class ILocalizeEntry : public Components::AssetHandler::IAsset class ILocalizeEntry : public Components::AssetHandler::IAsset
{ {
public: public:
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY; }; Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY; }
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
static void ParseLocalizedStringsFile(Components::ZoneBuilder::Zone* builder, const std::string& name, const std::string& filename);
}; };
} }

View File

@ -6,23 +6,36 @@ namespace Components
Dvar::Var Localization::UseLocalization; Dvar::Var Localization::UseLocalization;
std::unordered_map<std::string, Game::LocalizeEntry*> Localization::LocalizeMap; std::unordered_map<std::string, Game::LocalizeEntry*> Localization::LocalizeMap;
void Localization::Set(const std::string& key, const std::string& value) std::optional<std::string> Localization::PrefixOverride;
std::function<void(Game::LocalizeEntry*)> Localization::ParseCallback = nullptr;
void Localization::Set(const std::string& psLocalReference, const std::string& psNewString)
{ {
std::lock_guard _(Localization::LocalizeMutex); std::lock_guard _(LocalizeMutex);
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
if (Localization::LocalizeMap.contains(key)) auto key = psLocalReference;
if (PrefixOverride.has_value())
{ {
Game::LocalizeEntry* entry = Localization::LocalizeMap[key]; key.insert(0, PrefixOverride.value());
}
char* newStaticValue = allocator->duplicateString(value); if (LocalizeMap.contains(key))
{
auto* entry = LocalizeMap[key];
const auto* newStaticValue = allocator->duplicateString(psNewString);
if (!newStaticValue) return; if (!newStaticValue) return;
if (entry->value) allocator->free(entry->value); if (entry->value) allocator->free(entry->value);
entry->value = newStaticValue; entry->value = newStaticValue;
SaveParseOutput(entry);
return; return;
} }
Game::LocalizeEntry* entry = allocator->allocate<Game::LocalizeEntry>(); auto* entry = allocator->allocate<Game::LocalizeEntry>();
if (!entry) return; if (!entry) return;
entry->name = allocator->duplicateString(key); entry->name = allocator->duplicateString(key);
@ -32,7 +45,7 @@ namespace Components
return; return;
} }
entry->value = allocator->duplicateString(value); entry->value = allocator->duplicateString(psNewString);
if (!entry->value) if (!entry->value)
{ {
allocator->free(entry->name); allocator->free(entry->name);
@ -40,21 +53,23 @@ namespace Components
return; return;
} }
Localization::LocalizeMap[key] = entry; SaveParseOutput(entry);
LocalizeMap[key] = entry;
} }
const char* Localization::Get(const char* key) const char* Localization::Get(const char* key)
{ {
if (!Localization::UseLocalization.get<bool>()) return key; if (!UseLocalization.get<bool>()) return key;
Game::LocalizeEntry* entry = nullptr; Game::LocalizeEntry* entry = nullptr;
{ {
std::lock_guard _(Localization::LocalizeMutex); std::lock_guard _(LocalizeMutex);
if (Localization::LocalizeMap.contains(key)) if (LocalizeMap.contains(key))
{ {
entry = Localization::LocalizeMap[key]; entry = LocalizeMap[key];
} }
} }
@ -71,9 +86,22 @@ namespace Components
return key; return key;
} }
void __stdcall Localization::SetStringStub(const char* key, const char* value, bool /*isEnglish*/) void __stdcall Localization::SetStringStub(const char* psLocalReference, const char* psNewString, [[maybe_unused]] int bSentenceIsEnglish)
{ {
Localization::Set(key, value); Set(psLocalReference, psNewString);
}
void Localization::ParseOutput(const std::function<void(Game::LocalizeEntry*)>& callback)
{
ParseCallback = callback;
}
void Localization::SaveParseOutput(Game::LocalizeEntry* asset)
{
if (ParseCallback)
{
ParseCallback(asset);
}
} }
void Localization::SetCredits() void Localization::SetCredits()
@ -161,7 +189,7 @@ namespace Components
// I have no idea why, but the last 2 lines are invisible! // I have no idea why, but the last 2 lines are invisible!
credits.append("-\n-"); credits.append("-\n-");
Localization::Set("IW4X_CREDITS", credits); Set("IW4X_CREDITS", credits);
} }
const char* Localization::SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType) const char* Localization::SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType)
@ -304,31 +332,31 @@ namespace Components
Localization::Localization() Localization::Localization()
{ {
Localization::SetCredits(); SetCredits();
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [](Game::XAssetType, const std::string& filename) AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [](Game::XAssetType, const std::string& name)
{ {
Game::XAssetHeader header = { nullptr }; Game::XAssetHeader header = { nullptr };
std::lock_guard _(Localization::LocalizeMutex); std::lock_guard _(LocalizeMutex);
if (Localization::LocalizeMap.contains(filename)) if (const auto itr = LocalizeMap.find(name); itr != LocalizeMap.end())
{ {
header.localize = Localization::LocalizeMap[filename]; header.localize = itr->second;
} }
return header; return header;
}); });
// Resolving hook // Resolving hook
Utils::Hook(0x629B90, Localization::Get, HOOK_JUMP).install()->quick(); Utils::Hook(0x629B90, Get, HOOK_JUMP).install()->quick();
// Overwrite SetString // Overwrite SetString
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4CE5EE, SetStringStub, HOOK_CALL).install()->quick();
Utils::Hook(0x49D4A0, Localization::SEH_LocalizeTextMessageStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x49D4A0, SEH_LocalizeTextMessageStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x49D4A5, 1); Utils::Hook::Nop(0x49D4A5, 1);
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::DVAR_NONE, "Use localization strings"); UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::DVAR_NONE, "Use localization strings");
// Generate localized entries for custom classes above 10 // Generate localized entries for custom classes above 10
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/) AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/)
@ -344,7 +372,7 @@ namespace Components
std::string value = asset.localize->value; std::string value = asset.localize->value;
Utils::String::Replace(value, "1", std::to_string(i)); // Pretty ugly, but it should work Utils::String::Replace(value, "1", std::to_string(i)); // Pretty ugly, but it should work
Localization::Set(key, value); Set(key, value);
} }
} }
}); });
@ -352,6 +380,6 @@ namespace Components
Localization::~Localization() Localization::~Localization()
{ {
Localization::LocalizeMap.clear(); LocalizeMap.clear();
} }
} }

View File

@ -8,15 +8,23 @@ namespace Components
Localization(); Localization();
~Localization(); ~Localization();
static void Set(const std::string& key, const std::string& value); static void Set(const std::string& psLocalReference, const std::string& psNewString);
static const char* Get(const char* key); static const char* Get(const char* key);
static std::optional<std::string> PrefixOverride;
static void ParseOutput(const std::function<void(Game::LocalizeEntry*)>& callback);
private: private:
static std::recursive_mutex LocalizeMutex; static std::recursive_mutex LocalizeMutex;
static std::unordered_map<std::string, Game::LocalizeEntry*> LocalizeMap; static std::unordered_map<std::string, Game::LocalizeEntry*> LocalizeMap;
static Dvar::Var UseLocalization; static Dvar::Var UseLocalization;
static void __stdcall SetStringStub(const char* key, const char* value, bool isEnglish); static std::function<void(Game::LocalizeEntry*)> ParseCallback;
static void __stdcall SetStringStub(const char* psLocalReference, const char* psNewString, int bSentenceIsEnglish);
static void SaveParseOutput(Game::LocalizeEntry* asset);
static void SetCredits(); static void SetCredits();
static const char* SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType); static const char* SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType);

View File

@ -16,7 +16,7 @@ namespace Components
Game::SockadrToNetadr(addr, &this->address); Game::SockadrToNetadr(addr, &this->address);
} }
bool Network::Address::operator==(const Network::Address& obj) const bool Network::Address::operator==(const Address& obj) const
{ {
return Game::NET_CompareAdr(this->address, obj.address); return Game::NET_CompareAdr(this->address, obj.address);
} }
@ -150,13 +150,11 @@ namespace Components
void Network::Send(Game::netsrc_t type, Address target, const std::string& data) void Network::Send(Game::netsrc_t type, Address target, const std::string& data)
{ {
// NET_OutOfBandPrint only supports non-binary data! // Do not use NET_OutOfBandPrint. It only supports non-binary data!
//Game::NET_OutOfBandPrint(type, *target.Get(), data.data());
std::string rawData; std::string rawData;
rawData.append("\xFF\xFF\xFF\xFF", 4); rawData.append("\xFF\xFF\xFF\xFF", 4);
rawData.append(data); rawData.append(data);
//rawData.append("\0", 1);
SendRaw(type, target, rawData); SendRaw(type, target, rawData);
} }
@ -170,8 +168,7 @@ namespace Components
{ {
if (!target.isValid()) return; if (!target.isValid()) return;
// NET_OutOfBandData doesn't seem to work properly // NET_OutOfBandData doesn't seem to work properly. Do not use it
//Game::NET_OutOfBandData(type, *target.Get(), data.data(), data.size());
Game::Sys_SendPacket(type, data.size(), data.data(), *target.get()); Game::Sys_SendPacket(type, data.size(), data.data(), *target.get());
} }
@ -294,7 +291,7 @@ namespace Components
const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset); const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
Address address_ = address; auto address_ = Address(address);
handler->second(address_, data); handler->second(address_, data);
return true; return true;
} }

View File

@ -8,7 +8,7 @@ namespace Components
class Address class Address
{ {
public: public:
Address() { setType(Game::netadrtype_t::NA_BAD); } Address() { setType(Game::NA_BAD); }
Address(const std::string& addrString); Address(const std::string& addrString);
Address(sockaddr* addr); Address(sockaddr* addr);
Address(sockaddr addr) : Address(&addr) {} Address(sockaddr addr) : Address(&addr) {}

View File

@ -30,7 +30,7 @@ namespace Components
return; return;
} }
const auto addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44); auto* addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
if (Password.empty()) if (Password.empty())
{ {
Logger::Print("You need to be logged in and connected to a server!\n"); Logger::Print("You need to be logged in and connected to a server!\n");
@ -57,7 +57,7 @@ namespace Components
RconContainer.command = params->get(1); RconContainer.command = params->get(1);
const auto addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44); auto* addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
Network::Address target(addr); Network::Address target(addr);
if (!target.isValid() || target.getIP().full == 0) if (!target.isValid() || target.getIP().full == 0)
{ {

View File

@ -1,4 +1,5 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "AssetInterfaces/ILocalizeEntry.hpp"
namespace Components namespace Components
{ {
@ -146,30 +147,44 @@ namespace Components
{ {
for (std::size_t i = 0; i < this->dataMap.getRows(); ++i) for (std::size_t i = 0; i < this->dataMap.getRows(); ++i)
{ {
if (this->dataMap.getElementAt(i, 0) != "require") if (this->dataMap.getElementAt(i, 0) == "require"s)
{ {
if (this->dataMap.getColumns(i) > 2) continue;
{ }
std::string oldName = this->dataMap.getElementAt(i, 1);
std::string newName = this->dataMap.getElementAt(i, 2);
std::string typeName = this->dataMap.getElementAt(i, 0);
Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data());
if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) if (this->dataMap.getElementAt(i, 0) == "localize"s)
{ {
this->renameAsset(type, oldName, newName); const auto filename = this->dataMap.getElementAt(i, 1);
} FileSystem::File file(std::format("localizedstrings/{}.str", filename));
else if (file.exists())
{
Logger::Error(Game::ERR_FATAL, "Unable to rename '{}' to '{}' as the asset type '{}' is invalid!", oldName, newName, typeName);
}
}
if (!this->loadAssetByName(this->dataMap.getElementAt(i, 0), this->dataMap.getElementAt(i, 1), false))
{ {
return false; Assets::ILocalizeEntry::ParseLocalizedStringsFile(this, filename, file.getName());
continue;
} }
} }
if (this->dataMap.getColumns(i) > 2)
{
auto oldName = this->dataMap.getElementAt(i, 1);
auto newName = this->dataMap.getElementAt(i, 2);
auto typeName = this->dataMap.getElementAt(i, 0);
auto type = Game::DB_GetXAssetNameType(typeName.data());
if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0)
{
this->renameAsset(type, oldName, newName);
}
else
{
Logger::Error(Game::ERR_FATAL, "Unable to rename '{}' to '{}' as the asset type '{}' is invalid!", oldName, newName, typeName);
}
}
if (!this->loadAssetByName(this->dataMap.getElementAt(i, 0), this->dataMap.getElementAt(i, 1), false))
{
return false;
}
} }
return true; return true;
@ -194,10 +209,9 @@ namespace Components
{ {
Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data()); Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data());
if (name.find(" ", 0) != std::string::npos) if (name.find(' ', 0) != std::string::npos)
{ {
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Asset with name '{}' contains spaces. Check your zone source file to ensure this is correct!\n", name);
"Asset with name '{}' contains spaces. Check your zone source file to ensure this is correct!\n", name);
} }
// Sanitize name for empty assets // Sanitize name for empty assets
@ -408,8 +422,7 @@ namespace Components
Utils::IO::WriteFile(outFile, outBuffer); Utils::IO::WriteFile(outFile, outBuffer);
Logger::Print("done.\n"); Logger::Print("done.\n");
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", Logger::Print("Zone '{}' written with {} assets and {} script strings\n", outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
} }
void ZoneBuilder::Zone::saveData() void ZoneBuilder::Zone::saveData()
@ -440,7 +453,7 @@ namespace Components
// That's the reason why the count is incremented by 1, if scriptStrings are available. // That's the reason why the count is incremented by 1, if scriptStrings are available.
// Write ScriptString pointer table // Write ScriptString pointer table
for (size_t i = 0; i < this->scriptStrings.size(); ++i) for (std::size_t i = 0; i < this->scriptStrings.size(); ++i)
{ {
this->buffer.saveMax(4); this->buffer.saveMax(4);
} }
@ -606,6 +619,11 @@ namespace Components
return -1; return -1;
} }
void ZoneBuilder::Zone::addRawAsset(Game::XAssetType type, void* ptr)
{
this->loadedAssets.push_back({type, {ptr}});
}
// Remap a scriptString to it's corresponding value in the local scriptString table. // Remap a scriptString to it's corresponding value in the local scriptString table.
void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex) void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex)
{ {
@ -907,11 +925,12 @@ namespace Components
void ZoneBuilder::HandleError(Game::errorParm_t code, const char* fmt, ...) void ZoneBuilder::HandleError(Game::errorParm_t code, const char* fmt, ...)
{ {
char buffer[4096] = {0}; char buffer[0x1000]{};
va_list args; va_list ap;
va_start(args, fmt);
vsnprintf_s(buffer, _TRUNCATE, fmt, args); va_start(ap, fmt);
va_end(args); vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
va_end(ap);
if (!Flags::HasFlag("stdout")) if (!Flags::HasFlag("stdout"))
{ {

View File

@ -43,7 +43,7 @@ namespace Components
void storePointer(const void* pointer); void storePointer(const void* pointer);
template<typename T> template<typename T>
inline T* getPointer(const T* pointer) { return reinterpret_cast<T*>(this->safeGetPointer(pointer)); } T* getPointer(const T* pointer) { return reinterpret_cast<T*>(this->safeGetPointer(pointer)); }
int findAsset(Game::XAssetType type, std::string name); int findAsset(Game::XAssetType type, std::string name);
Game::XAssetHeader findSubAsset(Game::XAssetType type, std::string name); Game::XAssetHeader findSubAsset(Game::XAssetType type, std::string name);
@ -58,6 +58,7 @@ namespace Components
int addScriptString(unsigned short gameIndex); int addScriptString(unsigned short gameIndex);
int addScriptString(const std::string& str); int addScriptString(const std::string& str);
int findScriptString(const std::string& str); int findScriptString(const std::string& str);
void addRawAsset(Game::XAssetType type, void* ptr);
void mapScriptString(unsigned short* gameIndex); void mapScriptString(unsigned short* gameIndex);
@ -101,6 +102,7 @@ namespace Components
std::vector<Game::XAsset> markedAssets; std::vector<Game::XAsset> markedAssets;
std::vector<Game::XAsset> loadedSubAssets; std::vector<Game::XAsset> loadedSubAssets;
std::vector<std::string> scriptStrings; std::vector<std::string> scriptStrings;
std::map<unsigned short, unsigned int> scriptStringMap; std::map<unsigned short, unsigned int> scriptStringMap;
std::map<std::string, std::string> renameMap[Game::XAssetType::ASSET_TYPE_COUNT]; std::map<std::string, std::string> renameMap[Game::XAssetType::ASSET_TYPE_COUNT];

View File

@ -377,7 +377,7 @@ namespace Game
typedef int(*Script_CleanString_t)(char* buffer); typedef int(*Script_CleanString_t)(char* buffer);
extern Script_CleanString_t Script_CleanString; extern Script_CleanString_t Script_CleanString;
typedef char*(*SE_Load_t)(const char* file, int Unk); typedef char*(*SE_Load_t)(const char* psFileName, bool forceEnglish);
extern SE_Load_t SE_Load; extern SE_Load_t SE_Load;
typedef char*(*SEH_StringEd_GetString_t)(const char* string); typedef char*(*SEH_StringEd_GetString_t)(const char* string);