Changeset View
Changeset View
Standalone View
Standalone View
source/ps/TemplateLoader.cpp
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 12 Lines | |||||
#include "lib/utf8.h" | #include "lib/utf8.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include "ps/Filesystem.h" | #include "ps/Filesystem.h" | ||||
#include "ps/XML/Xeromyces.h" | #include "ps/XML/Xeromyces.h" | ||||
static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/"; | static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/"; | ||||
static const wchar_t ACTOR_ROOT[] = L"art/actors/"; | static const wchar_t ACTOR_ROOT[] = L"art/actors/"; | ||||
static CParamNode NULL_NODE(false); | static CParamNode NULL_NODE(nullptr, false); | ||||
bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth) | bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth) | ||||
{ | { | ||||
// If this file was already loaded, we don't need to do anything | // If this file was already loaded, we don't need to do anything | ||||
if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end()) | if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end()) | ||||
return true; | return true; | ||||
// Handle infinite loops more gracefully than running out of stack space and crashing | // Handle infinite loops more gracefully than running out of stack space and crashing | ||||
if (depth > 100) | if (depth > 100) | ||||
{ | { | ||||
LOGERROR("Probable infinite inheritance loop in entity template '%s'", templateName.c_str()); | LOGERROR("Probable infinite inheritance loop in entity template '%s'", templateName.c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
// Handle special case "actor|foo" | // Handle special case "actor|foo" | ||||
if (templateName.find("actor|") == 0) | if (templateName.find("actor|") == 0) | ||||
{ | { | ||||
ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]); | ConstructTemplateActor(templateName.substr(6), m_TemplateFileData.try_emplace(templateName, this).first->second); | ||||
return true; | return true; | ||||
} | } | ||||
// Handle special case "bar|foo" | // Handle special case "bar|foo" | ||||
size_t pos = templateName.find_first_of('|'); | size_t pos = templateName.find_first_of('|'); | ||||
if (pos != std::string::npos) | if (pos != std::string::npos) | ||||
{ | { | ||||
std::string prefix = templateName.substr(0, pos); | std::string prefix = templateName.substr(0, pos); | ||||
Show All 12 Lines | if (!VfsFileExists(path)) | ||||
return false; | return false; | ||||
} | } | ||||
CXeromyces xero; | CXeromyces xero; | ||||
PSRETURN ok = xero.Load(g_VFS, path); | PSRETURN ok = xero.Load(g_VFS, path); | ||||
if (ok != PSRETURN_OK) | if (ok != PSRETURN_OK) | ||||
return false; // (Xeromyces already logged an error with the full filename) | return false; // (Xeromyces already logged an error with the full filename) | ||||
m_TemplateFileData[templateName] = m_TemplateFileData[baseName]; | m_TemplateFileData.insert_or_assign(templateName, m_TemplateFileData.at(baseName)); | ||||
CParamNode::LoadXML(m_TemplateFileData[templateName], xero, path.string().c_str()); | CParamNode::LoadXML(m_TemplateFileData.at(templateName), xero, path.string().c_str()); | ||||
return true; | return true; | ||||
} | } | ||||
// Normal case: templateName is an XML file: | // Normal case: templateName is an XML file: | ||||
VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml"); | VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml"); | ||||
CXeromyces xero; | CXeromyces xero; | ||||
PSRETURN ok = xero.Load(g_VFS, path); | PSRETURN ok = xero.Load(g_VFS, path); | ||||
if (ok != PSRETURN_OK) | if (ok != PSRETURN_OK) | ||||
return false; // (Xeromyces already logged an error with the full filename) | return false; // (Xeromyces already logged an error with the full filename) | ||||
int attr_parent = xero.GetAttributeID("parent"); | |||||
CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent); | |||||
if (!parentName.empty()) | |||||
{ | |||||
// To prevent needless complexity in template design, we don't allow |-separated strings as parents | |||||
if (parentName.find('|') != parentName.npos) | |||||
{ | |||||
LOGERROR("Invalid parent '%s' in entity template '%s'", parentName.c_str(), templateName.c_str()); | |||||
return false; | |||||
} | |||||
// Ensure the parent is loaded | |||||
if (!LoadTemplateFile(parentName, depth+1)) | |||||
{ | |||||
LOGERROR("Failed to load parent '%s' of entity template '%s'", parentName.c_str(), templateName.c_str()); | |||||
return false; | |||||
} | |||||
CParamNode& parentData = m_TemplateFileData[parentName]; | |||||
// Initialise this template with its parent | |||||
m_TemplateFileData[templateName] = parentData; | |||||
} | |||||
// Load the new file into the template data (overriding parent values) | // Load the new file into the template data (overriding parent values) | ||||
CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str()); | CParamNode::LoadXML(m_TemplateFileData.try_emplace(templateName, this).first->second, xero, wstring_from_utf8(templateName).c_str()); | ||||
return true; | return true; | ||||
} | } | ||||
static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) | static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) | ||||
{ | { | ||||
std::vector<std::string>& templates = *(std::vector<std::string>*)cbData; | std::vector<std::string>& templates = *(std::vector<std::string>*)cbData; | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
// Load the template if necessary | // Load the template if necessary | ||||
if (!LoadTemplateFile(templateName, 0)) | if (!LoadTemplateFile(templateName, 0)) | ||||
{ | { | ||||
LOGERROR("Failed to load entity template '%s'", templateName.c_str()); | LOGERROR("Failed to load entity template '%s'", templateName.c_str()); | ||||
return NULL_NODE; | return NULL_NODE; | ||||
} | } | ||||
return m_TemplateFileData[templateName]; | return m_TemplateFileData.at(templateName); | ||||
} | |||||
CParamNode& CTemplateLoader::GetData(const std::string& templateName) | |||||
{ | |||||
// Load the template if necessary | |||||
if (!LoadTemplateFile(templateName, 0)) | |||||
{ | |||||
LOGERROR("Failed to load entity template '%s'", templateName.c_str()); | |||||
return NULL_NODE; | |||||
} | |||||
return m_TemplateFileData.at(templateName); | |||||
} | } | ||||
void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out) | void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out) | ||||
{ | { | ||||
// Copy the actor template | // Copy the actor template | ||||
out = GetTemplateFileData("special/actor"); | out = GetTemplateFileData("special/actor"); | ||||
// Initialize the actor's name and make it an Atlas selectable entity. | // Initialize the actor's name and make it an Atlas selectable entity. | ||||
Show All 14 Lines |
Wildfire Games · Phabricator