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();
}
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
{
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 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;
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();
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 (entry->value) allocator->free(entry->value);
entry->value = newStaticValue;
SaveParseOutput(entry);
return;
}
Game::LocalizeEntry* entry = allocator->allocate<Game::LocalizeEntry>();
auto* entry = allocator->allocate<Game::LocalizeEntry>();
if (!entry) return;
entry->name = allocator->duplicateString(key);
@ -32,7 +45,7 @@ namespace Components
return;
}
entry->value = allocator->duplicateString(value);
entry->value = allocator->duplicateString(psNewString);
if (!entry->value)
{
allocator->free(entry->name);
@ -40,21 +53,23 @@ namespace Components
return;
}
Localization::LocalizeMap[key] = entry;
SaveParseOutput(entry);
LocalizeMap[key] = entry;
}
const char* Localization::Get(const char* key)
{
if (!Localization::UseLocalization.get<bool>()) return key;
if (!UseLocalization.get<bool>()) return key;
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;
}
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()
@ -161,7 +189,7 @@ namespace Components
// I have no idea why, but the last 2 lines are invisible!
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)
@ -304,31 +332,31 @@ namespace Components
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 };
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;
});
// Resolving hook
Utils::Hook(0x629B90, Localization::Get, HOOK_JUMP).install()->quick();
Utils::Hook(0x629B90, Get, HOOK_JUMP).install()->quick();
// 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);
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
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;
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::LocalizeMap.clear();
LocalizeMap.clear();
}
}

View File

@ -8,15 +8,23 @@ namespace Components
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 std::optional<std::string> PrefixOverride;
static void ParseOutput(const std::function<void(Game::LocalizeEntry*)>& callback);
private:
static std::recursive_mutex LocalizeMutex;
static std::unordered_map<std::string, Game::LocalizeEntry*> LocalizeMap;
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 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);
}
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);
}
@ -150,13 +150,11 @@ namespace Components
void Network::Send(Game::netsrc_t type, Address target, const std::string& data)
{
// NET_OutOfBandPrint only supports non-binary data!
//Game::NET_OutOfBandPrint(type, *target.Get(), data.data());
// Do not use NET_OutOfBandPrint. It only supports non-binary data!
std::string rawData;
rawData.append("\xFF\xFF\xFF\xFF", 4);
rawData.append(data);
//rawData.append("\0", 1);
SendRaw(type, target, rawData);
}
@ -170,8 +168,7 @@ namespace Components
{
if (!target.isValid()) return;
// NET_OutOfBandData doesn't seem to work properly
//Game::NET_OutOfBandData(type, *target.Get(), data.data(), data.size());
// NET_OutOfBandData doesn't seem to work properly. Do not use it
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);
Address address_ = address;
auto address_ = Address(address);
handler->second(address_, data);
return true;
}

View File

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

View File

@ -30,7 +30,7 @@ namespace Components
return;
}
const auto addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
auto* addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
if (Password.empty())
{
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);
const auto addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
auto* addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
Network::Address target(addr);
if (!target.isValid() || target.getIP().full == 0)
{

View File

@ -1,4 +1,5 @@
#include <STDInclude.hpp>
#include "AssetInterfaces/ILocalizeEntry.hpp"
namespace Components
{
@ -146,30 +147,44 @@ namespace Components
{
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)
{
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());
continue;
}
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))
if (this->dataMap.getElementAt(i, 0) == "localize"s)
{
const auto filename = this->dataMap.getElementAt(i, 1);
FileSystem::File file(std::format("localizedstrings/{}.str", filename));
if (file.exists())
{
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;
@ -194,10 +209,9 @@ namespace Components
{
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,
"Asset with name '{}' contains spaces. Check your zone source file to ensure this is correct!\n", name);
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Asset with name '{}' contains spaces. Check your zone source file to ensure this is correct!\n", name);
}
// Sanitize name for empty assets
@ -408,8 +422,7 @@ namespace Components
Utils::IO::WriteFile(outFile, outBuffer);
Logger::Print("done.\n");
Logger::Print("Zone '{}' written with {} assets and {} script strings\n",
outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
}
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.
// 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);
}
@ -606,6 +619,11 @@ namespace Components
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.
void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex)
{
@ -907,11 +925,12 @@ namespace Components
void ZoneBuilder::HandleError(Game::errorParm_t code, const char* fmt, ...)
{
char buffer[4096] = {0};
va_list args;
va_start(args, fmt);
vsnprintf_s(buffer, _TRUNCATE, fmt, args);
va_end(args);
char buffer[0x1000]{};
va_list ap;
va_start(ap, fmt);
vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
va_end(ap);
if (!Flags::HasFlag("stdout"))
{

View File

@ -43,7 +43,7 @@ namespace Components
void storePointer(const void* pointer);
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);
Game::XAssetHeader findSubAsset(Game::XAssetType type, std::string name);
@ -58,6 +58,7 @@ namespace Components
int addScriptString(unsigned short gameIndex);
int addScriptString(const std::string& str);
int findScriptString(const std::string& str);
void addRawAsset(Game::XAssetType type, void* ptr);
void mapScriptString(unsigned short* gameIndex);
@ -101,6 +102,7 @@ namespace Components
std::vector<Game::XAsset> markedAssets;
std::vector<Game::XAsset> loadedSubAssets;
std::vector<std::string> scriptStrings;
std::map<unsigned short, unsigned int> scriptStringMap;
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);
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;
typedef char*(*SEH_StringEd_GetString_t)(const char* string);