PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname) { VfsPath filename_xml = pathname.ChangeExtension(L".xml"); CXeromyces xmb_file; if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK) return PSRETURN_File_ReadFailed; // Define all the relevant elements used in the XML file #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(scenario); EL(scriptsettings); #undef AT #undef EL XMBElement root = xmb_file.GetRoot(); ENSURE(root.GetNodeName() == el_scenario); XERO_ITER_EL(root, child) { int child_name = child.GetNodeName(); if (child_name == el_scriptsettings) { m_ScriptSettings = child.GetText(); } }
/** * @callgraph */ void CGUI::LoadXmlFile(const VfsPath& Filename, boost::unordered_set<VfsPath>& Paths) { Paths.insert(Filename); CXeromyces XeroFile; if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) return; XMBElement node = XeroFile.GetRoot(); CStr root_name(XeroFile.GetElementString(node.GetNodeName())); try { if (root_name == "objects") { Xeromyces_ReadRootObjects(node, &XeroFile, Paths); // Re-cache all values so these gets cached too. //UpdateResolution(); } else if (root_name == "sprites") Xeromyces_ReadRootSprites(node, &XeroFile); else if (root_name == "styles") Xeromyces_ReadRootStyles(node, &XeroFile); else if (root_name == "setup") Xeromyces_ReadRootSetup(node, &XeroFile); else debug_warn(L"CGUI::LoadXmlFile error"); } catch (PSERROR_GUI& e) { LOGERROR("Errors loading GUI file %s (%u)", Filename.string8(), e.getCode()); return; } }
void CGUIManager::LoadPage(SGUIPage& page) { // If we're hotloading then try to grab some data from the previous page CScriptValRooted hotloadData; if (page.gui) m_ScriptInterface.CallFunction(OBJECT_TO_JSVAL(page.gui->GetScriptObject()), "getHotloadData", hotloadData); page.inputs.clear(); page.gui.reset(new CGUI()); page.gui->Initialize(); VfsPath path = VfsPath("gui") / page.name; page.inputs.insert(path); CXeromyces xero; if (xero.Load(g_VFS, path) != PSRETURN_OK) // Fail silently (Xeromyces reported the error) return; int elmt_page = xero.GetElementID("page"); int elmt_include = xero.GetElementID("include"); XMBElement root = xero.GetRoot(); if (root.GetNodeName() != elmt_page) { LOGERROR(L"GUI page '%ls' must have root element <page>", page.name.c_str()); return; } XERO_ITER_EL(root, node) { if (node.GetNodeName() != elmt_include) { LOGERROR(L"GUI page '%ls' must only have <include> elements inside <page>", page.name.c_str()); continue; } CStrW name (node.GetText().FromUTF8()); TIMER(name.c_str()); VfsPath path = VfsPath("gui") / name; page.gui->LoadXmlFile(path, page.inputs); } // Remember this GUI page, in case the scripts call FindObjectByName shared_ptr<CGUI> oldGUI = m_CurrentGUI; m_CurrentGUI = page.gui; page.gui->SendEventToAll("load"); // Call the init() function if (!m_ScriptInterface.CallFunctionVoid(OBJECT_TO_JSVAL(page.gui->GetScriptObject()), "init", page.initData, hotloadData)) { LOGERROR(L"GUI page '%ls': Failed to call init() function", page.name.c_str()); } m_CurrentGUI = oldGUI; }
void XMLWriter_File::ElementXMB(const XMBFile& file, XMBElement el) { XMLWriter_Element writer(*this, file.GetElementString(el.GetNodeName()).c_str()); XERO_ITER_ATTR(el, attr) writer.Attribute(file.GetAttributeString(attr.Name).c_str(), attr.Value); XERO_ITER_EL(el, child) ElementXMB(file, child); }
bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile) { int elmt_item = pFile->GetElementID("item"); if (child.GetNodeName() == elmt_item) { AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8()); return true; } return false; }
/** * @callgraph */ void CGUI::LoadXmlFile(const VfsPath& Filename, boost::unordered_set<VfsPath>& Paths) { Paths.insert(Filename); CXeromyces XeroFile; if (XeroFile.Load(g_VFS, Filename) != PSRETURN_OK) // Fail silently return; XMBElement node = XeroFile.GetRoot(); // Check root element's (node) name so we know what kind of // data we'll be expecting CStr root_name (XeroFile.GetElementString(node.GetNodeName())); try { if (root_name == "objects") { Xeromyces_ReadRootObjects(node, &XeroFile, Paths); // Re-cache all values so these gets cached too. //UpdateResolution(); } else if (root_name == "sprites") { Xeromyces_ReadRootSprites(node, &XeroFile); } else if (root_name == "styles") { Xeromyces_ReadRootStyles(node, &XeroFile); } else if (root_name == "setup") { Xeromyces_ReadRootSetup(node, &XeroFile); } else { debug_warn(L"CGUI::LoadXmlFile error"); // TODO Gee: Output in log } } catch (PSERROR_GUI& e) { LOGERROR(L"Errors loading GUI file %ls (%u)", Filename.string().c_str(), e.getCode()); return; } }
CTerrainPropertiesPtr CTerrainProperties::FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname) { CXeromyces XeroFile; if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK) return CTerrainPropertiesPtr(); XMBElement root = XeroFile.GetRoot(); CStr rootName = XeroFile.GetElementString(root.GetNodeName()); // Check that we've got the right kind of xml document if (rootName != "Terrains") { LOGERROR( L"TerrainProperties: Loading %ls: Root node is not terrains (found \"%hs\")", pathname.string().c_str(), rootName.c_str()); return CTerrainPropertiesPtr(); } #define ELMT(x) int el_##x = XeroFile.GetElementID(#x) ELMT(terrain); #undef ELMT // Ignore all non-terrain nodes, loading the first terrain node and // returning it. // Really, we only expect there to be one child and it to be of the right // type, though. XERO_ITER_EL(root, child) { if (child.GetNodeName() == el_terrain) { CTerrainPropertiesPtr ret (new CTerrainProperties(parent)); ret->LoadXml(child, &XeroFile, pathname); return ret; } else { LOGWARNING( L"TerrainProperties: Loading %ls: Unexpected node %hs\n", pathname.string().c_str(), XeroFile.GetElementString(child.GetNodeName()).c_str()); // Keep reading - typos shouldn't be showstoppers } } return CTerrainPropertiesPtr(); }
void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, boost::unordered_set<VfsPath>& Paths) { int el_script = pFile->GetElementID("script"); std::vector<std::pair<CStr, CStr> > subst; // Iterate main children // they should all be <object> or <script> elements XMBElementList children = Element.GetChildNodes(); for (int i=0; i<children.Count; ++i) { //debug_printf(L"Object %d\n", i); XMBElement child = children.Item(i); if (child.GetNodeName() == el_script) // Execute the inline script Xeromyces_ReadScript(child, pFile, Paths); else // Read in this whole object into the GUI Xeromyces_ReadObject(child, pFile, m_BaseObject, subst, Paths); } }
void CGUI::Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile) { // Iterate main children // they should all be <icon>, <scrollbar> or <tooltip>. XMBElementList children = Element.GetChildNodes(); for (int i=0; i<children.Count; ++i) { XMBElement child = children.Item(i); // Read in this whole object into the GUI CStr name (pFile->GetElementString(child.GetNodeName())); if (name == "scrollbar") { Xeromyces_ReadScrollBarStyle(child, pFile); } else if (name == "icon") { Xeromyces_ReadIcon(child, pFile); } else if (name == "tooltip") { Xeromyces_ReadTooltip(child, pFile); } else if (name == "color") { Xeromyces_ReadColor(child, pFile); } else { debug_warn(L"Invalid data - DTD shouldn't allow this"); } } }
bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML) { CXeromyces XeroFile; if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK) { HandleError(L"error loading file", pathnameXML, ERR::FAIL); return false; } // Define elements used in XML file #define EL(x) int el_##x = XeroFile.GetElementID(#x) #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) EL(soundgroup); EL(gain); EL(looping); EL(omnipresent); EL(distanceless); EL(pitch); EL(priority); EL(randorder); EL(randgain); EL(randpitch); EL(conegain); EL(coneinner); EL(coneouter); EL(sound); EL(gainupper); EL(gainlower); EL(pitchupper); EL(pitchlower); EL(path); EL(threshold); EL(decay); #undef AT #undef EL XMBElement root = XeroFile.GetRoot(); if (root.GetNodeName() != el_soundgroup) { LOGERROR(L"Invalid SoundGroup format (unrecognised root element '%hs')", XeroFile.GetElementString(root.GetNodeName()).c_str()); return false; } XERO_ITER_EL(root, child) { int child_name = child.GetNodeName(); if(child_name == el_gain) { SetGain(child.GetText().ToFloat()); } else if(child_name == el_looping) { if(child.GetText().ToInt() == 1) SetFlag(eLoop); } else if(child_name == el_omnipresent) { if(child.GetText().ToInt() == 1) SetFlag(eOmnipresent); } else if(child_name == el_distanceless) { if(child.GetText().ToInt() == 1) SetFlag(eDistanceless); } else if(child_name == el_pitch) { this->m_Pitch = child.GetText().ToFloat(); } else if(child_name == el_priority) { this->m_Priority = child.GetText().ToFloat(); } else if(child_name == el_randorder) { if(child.GetText().ToInt() == 1) SetFlag(eRandOrder); } else if(child_name == el_randgain) { if(child.GetText().ToInt() == 1) SetFlag(eRandGain); } else if(child_name == el_gainupper) { this->m_GainUpper = child.GetText().ToFloat(); } else if(child_name == el_gainlower) { this->m_GainLower = child.GetText().ToFloat(); } else if(child_name == el_randpitch) { if(child.GetText().ToInt() == 1) SetFlag(eRandPitch); } else if(child_name == el_pitchupper) { this->m_PitchUpper = child.GetText().ToFloat(); } else if(child_name == el_pitchlower) { this->m_PitchLower = child.GetText().ToFloat(); } else if(child_name == el_conegain) { this->m_ConeOuterGain = child.GetText().ToFloat(); } else if(child_name == el_coneinner) { this->m_ConeInnerAngle = child.GetText().ToFloat(); } else if(child_name == el_coneouter) { this->m_ConeOuterAngle = child.GetText().ToFloat(); } else if(child_name == el_sound) { this->filenames.push_back(child.GetText().FromUTF8()); } else if(child_name == el_path) { m_filepath = child.GetText().FromUTF8(); } else if(child_name == el_threshold) { m_IntensityThreshold = child.GetText().ToFloat(); } else if(child_name == el_decay) { m_Decay = child.GetText().ToFloat(); } }
CTextureConverter::SettingsFile* CTextureConverter::LoadSettings(const VfsPath& path) const { CXeromyces XeroFile; if (XeroFile.Load(m_VFS, path) != PSRETURN_OK) return NULL; // Define all the elements used in the XML file #define EL(x) int el_##x = XeroFile.GetElementID(#x) #define AT(x) int at_##x = XeroFile.GetAttributeID(#x) EL(textures); EL(file); AT(pattern); AT(format); AT(mipmap); AT(normal); AT(alpha); AT(filter); AT(kaiserwidth); AT(kaiseralpha); AT(kaiserstretch); #undef AT #undef EL XMBElement root = XeroFile.GetRoot(); if (root.GetNodeName() != el_textures) { LOGERROR("Invalid texture settings file \"%s\" (unrecognised root element)", path.string8()); return NULL; } std::unique_ptr<SettingsFile> settings(new SettingsFile()); XERO_ITER_EL(root, child) { if (child.GetNodeName() == el_file) { Match p; XERO_ITER_ATTR(child, attr) { if (attr.Name == at_pattern) { p.pattern = attr.Value.FromUTF8(); } else if (attr.Name == at_format) { CStr v(attr.Value); if (v == "dxt1") p.settings.format = FMT_DXT1; else if (v == "dxt3") p.settings.format = FMT_DXT3; else if (v == "dxt5") p.settings.format = FMT_DXT5; else if (v == "rgba") p.settings.format = FMT_RGBA; else if (v == "alpha") p.settings.format = FMT_ALPHA; else LOGERROR("Invalid attribute value <file format='%s'>", v.c_str()); } else if (attr.Name == at_mipmap) { CStr v(attr.Value); if (v == "true") p.settings.mipmap = MIP_TRUE; else if (v == "false") p.settings.mipmap = MIP_FALSE; else LOGERROR("Invalid attribute value <file mipmap='%s'>", v.c_str()); } else if (attr.Name == at_normal) { CStr v(attr.Value); if (v == "true") p.settings.normal = NORMAL_TRUE; else if (v == "false") p.settings.normal = NORMAL_FALSE; else LOGERROR("Invalid attribute value <file normal='%s'>", v.c_str()); } else if (attr.Name == at_alpha) { CStr v(attr.Value); if (v == "none") p.settings.alpha = ALPHA_NONE; else if (v == "player") p.settings.alpha = ALPHA_PLAYER; else if (v == "transparency") p.settings.alpha = ALPHA_TRANSPARENCY; else LOGERROR("Invalid attribute value <file alpha='%s'>", v.c_str()); } else if (attr.Name == at_filter) { CStr v(attr.Value); if (v == "box") p.settings.filter = FILTER_BOX; else if (v == "triangle") p.settings.filter = FILTER_TRIANGLE; else if (v == "kaiser") p.settings.filter = FILTER_KAISER; else LOGERROR("Invalid attribute value <file filter='%s'>", v.c_str()); } else if (attr.Name == at_kaiserwidth) { p.settings.kaiserWidth = CStr(attr.Value).ToFloat(); } else if (attr.Name == at_kaiseralpha) { p.settings.kaiserAlpha = CStr(attr.Value).ToFloat(); } else if (attr.Name == at_kaiserstretch) { p.settings.kaiserStretch = CStr(attr.Value).ToFloat(); } else { LOGERROR("Invalid attribute name <file %s='...'>", XeroFile.GetAttributeString(attr.Name).c_str()); } } settings->patterns.push_back(p); } }
void CGUI::Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite &parent) { // Image object we're adding SGUIImage* Image = new SGUIImage; // Set defaults to "0 0 100% 100%" Image->m_TextureSize = CClientArea(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100)); Image->m_Size = CClientArea(CRect(0, 0, 0, 0), CRect(0, 0, 100, 100)); // TODO Gee: Setup defaults here (or maybe they are in the SGUIImage ctor) // // Read Attributes // // Now we can iterate all attributes and store XMBAttributeList attributes = Element.GetAttributes(); for (int i=0; i<attributes.Count; ++i) { XMBAttribute attr = attributes.Item(i); CStr attr_name (pFile->GetAttributeString(attr.Name)); CStrW attr_value (attr.Value.FromUTF8()); if (attr_name == "texture") { Image->m_TextureName = VfsPath("art/textures/ui") / attr_value; } else if (attr_name == "size") { CClientArea ca; if (!GUI<CClientArea>::ParseString(attr_value, ca)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_Size = ca; } else if (attr_name == "texture_size") { CClientArea ca; if (!GUI<CClientArea>::ParseString(attr_value, ca)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_TextureSize = ca; } else if (attr_name == "real_texture_placement") { CRect rect; if (!GUI<CRect>::ParseString(attr_value, rect)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_TexturePlacementInFile = rect; } else if (attr_name == "cell_size") { CSize size; if (!GUI<CSize>::ParseString(attr_value, size)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_CellSize = size; } else if (attr_name == "fixed_h_aspect_ratio") { float val; if (!GUI<float>::ParseString(attr_value, val)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_FixedHAspectRatio = val; } else if (attr_name == "round_coordinates") { bool b; if (!GUI<bool>::ParseString(attr_value, b)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_RoundCoordinates = b; } else if (attr_name == "wrap_mode") { if (attr_value == L"repeat") Image->m_WrapMode = GL_REPEAT; else if (attr_value == L"mirrored_repeat") Image->m_WrapMode = GL_MIRRORED_REPEAT; else if (attr_value == L"clamp_to_edge") Image->m_WrapMode = GL_CLAMP_TO_EDGE; else LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); } else if (attr_name == "z_level") { float z_level; if (!GUI<float>::ParseString(attr_value, z_level)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_DeltaZ = z_level/100.f; } else if (attr_name == "backcolor") { CColor color; if (!GUI<CColor>::ParseString(attr_value, color)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_BackColor = color; } else if (attr_name == "bordercolor") { CColor color; if (!GUI<CColor>::ParseString(attr_value, color)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_BorderColor = color; } else if (attr_name == "border") { bool b; if (!GUI<bool>::ParseString(attr_value, b)) LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str()); else Image->m_Border = b; } else { debug_warn(L"Invalid data - DTD shouldn't allow this"); } } // Look for effects XMBElementList children = Element.GetChildNodes(); for (int i=0; i<children.Count; ++i) { XMBElement child = children.Item(i); CStr ElementName (pFile->GetElementString(child.GetNodeName())); if (ElementName == "effect") { if (Image->m_Effects) { LOGERROR(L"GUI <image> must not have more than one <effect>"); } else { Image->m_Effects = new SGUIImageEffects; Xeromyces_ReadEffects(child, pFile, *Image->m_Effects); } } else { debug_warn(L"Invalid data - DTD shouldn't allow this"); } } // // Input // parent.AddImage(Image); }
void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile) { // Sprite object we're adding CGUISprite* Sprite = new CGUISprite; // and what will be its reference name CStr name; // // Read Attributes // // Get name, we know it exists because of DTD requirements name = Element.GetAttributes().GetNamedItem( pFile->GetAttributeID("name") ); if (m_Sprites.find(name) != m_Sprites.end()) LOGWARNING(L"GUI sprite name '%hs' used more than once; first definition will be discarded", name.c_str()); // // Read Children (the images) // SGUIImageEffects* effects = NULL; // Iterate children XMBElementList children = Element.GetChildNodes(); for (int i=0; i<children.Count; ++i) { // Get node XMBElement child = children.Item(i); CStr ElementName (pFile->GetElementString(child.GetNodeName())); if (ElementName == "image") { Xeromyces_ReadImage(child, pFile, *Sprite); } else if (ElementName == "effect") { if (effects) { LOGERROR(L"GUI <sprite> must not have more than one <effect>"); } else { effects = new SGUIImageEffects; Xeromyces_ReadEffects(child, pFile, *effects); } } else { debug_warn(L"Invalid data - DTD shouldn't allow this"); } } // Apply the effects to every image (unless the image overrides it with // different effects) if (effects) for (std::vector<SGUIImage*>::iterator it = Sprite->m_Images.begin(); it != Sprite->m_Images.end(); ++it) if (! (*it)->m_Effects) (*it)->m_Effects = new SGUIImageEffects(*effects); // do a copy just so it can be deleted correctly later delete effects; // // Add Sprite // m_Sprites[name] = Sprite; }
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent, const std::vector<std::pair<CStr, CStr> >& NameSubst, boost::unordered_set<VfsPath>& Paths) { ENSURE(pParent); int i; // Our object we are going to create IGUIObject *object = NULL; XMBAttributeList attributes = Element.GetAttributes(); // Well first of all we need to determine the type CStr type (attributes.GetNamedItem(pFile->GetAttributeID("type"))); if (type.empty()) type = "empty"; // Construct object from specified type // henceforth, we need to do a rollback before aborting. // i.e. releasing this object object = ConstructObject(type); if (!object) { // Report error that object was unsuccessfully loaded LOGERROR(L"GUI: Unrecognized object type \"%hs\"", type.c_str()); return; } // Cache some IDs for element attribute names, to avoid string comparisons #define ELMT(x) int elmt_##x = pFile->GetElementID(#x) #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) ELMT(object); ELMT(action); ELMT(repeat); ATTR(style); ATTR(type); ATTR(name); ATTR(hotkey); ATTR(z); ATTR(on); ATTR(file); // // Read Style and set defaults // // If the setting "style" is set, try loading that setting. // // Always load default (if it's available) first! // CStr argStyle (attributes.GetNamedItem(attr_style)); if (m_Styles.count("default") == 1) object->LoadStyle(*this, "default"); if (! argStyle.empty()) { // additional check if (m_Styles.count(argStyle) == 0) { LOGERROR(L"GUI: Trying to use style '%hs' that doesn't exist.", argStyle.c_str()); } else object->LoadStyle(*this, argStyle); } // // Read Attributes // bool NameSet = false; bool ManuallySetZ = false; // if z has been manually set, this turn true CStr hotkeyTag; // Now we can iterate all attributes and store for (i=0; i<attributes.Count; ++i) { XMBAttribute attr = attributes.Item(i); // If value is "null", then it is equivalent as never being entered if (CStr(attr.Value) == "null") continue; // Ignore "type" and "style", we've already checked it if (attr.Name == attr_type || attr.Name == attr_style) continue; // Also the name needs some special attention if (attr.Name == attr_name) { CStr name (attr.Value); // Apply the requested substitutions for (size_t j = 0; j < NameSubst.size(); ++j) name.Replace(NameSubst[j].first, NameSubst[j].second); object->SetName(name); NameSet = true; continue; } // Wire up the hotkey tag, if it has one if (attr.Name == attr_hotkey) hotkeyTag = attr.Value; if (attr.Name == attr_z) ManuallySetZ = true; // Try setting the value if (object->SetSetting(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), true) != PSRETURN_OK) { LOGERROR(L"GUI: (object: %hs) Can't set \"%hs\" to \"%ls\"", object->GetPresentableName().c_str(), pFile->GetAttributeString(attr.Name).c_str(), attr.Value.FromUTF8().c_str()); // This is not a fatal error } } // Check if name isn't set, generate an internal name in that case. if (!NameSet) { object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); ++m_InternalNameNumber; } // Attempt to register the hotkey tag, if one was provided if (! hotkeyTag.empty()) m_HotkeyObjects[hotkeyTag].push_back(object); CStrW caption (Element.GetText().FromUTF8()); if (! caption.empty()) { // Set the setting caption to this object->SetSetting("caption", caption, true); // There is no harm if the object didn't have a "caption" } // // Read Children // // Iterate children XMBElementList children = Element.GetChildNodes(); for (i=0; i<children.Count; ++i) { // Get node XMBElement child = children.Item(i); // Check what name the elements got int element_name = child.GetNodeName(); if (element_name == elmt_object) { // Call this function on the child Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths); } else if (element_name == elmt_action) { // Scripted <action> element // Check for a 'file' parameter CStrW filename (child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStr code; // If there is a file, open it and use it as the code if (! filename.empty()) { Paths.insert(filename); CVFSFile scriptfile; if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) { LOGERROR(L"Error opening GUI script action file '%ls'", filename.c_str()); throw PSERROR_GUI_JSOpenFailed(); } code = scriptfile.DecodeUTF8(); // assume it's UTF-8 } // Read the inline code (concatenating to the file code, if both are specified) code += CStr(child.GetText()); CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on)); // We need to set the GUI this object belongs to because RegisterScriptHandler requires an associated GUI. object->SetGUI(this); object->RegisterScriptHandler(action.LowerCase(), code, this); } else if (element_name == elmt_repeat) { Xeromyces_ReadRepeat(child, pFile, object, Paths); } else { // Try making the object read the tag. if (!object->HandleAdditionalChildren(child, pFile)) { LOGERROR(L"GUI: (object: %hs) Reading unknown children for its type", object->GetPresentableName().c_str()); } } } // // Check if Z wasn't manually set // if (!ManuallySetZ) { // Set it automatically to 10 plus its parents bool absolute; GUI<bool>::GetSetting(object, "absolute", absolute); // If the object is absolute, we'll have to get the parent's Z buffered, // and add to that! if (absolute) { GUI<float>::SetSetting(object, "z", pParent->GetBufferedZ() + 10.f, true); } else // If the object is relative, then we'll just store Z as "10" { GUI<float>::SetSetting(object, "z", 10.f, true); } } // // Input Child // try { if (pParent == m_BaseObject) AddObject(object); else pParent->AddChild(object); } catch (PSERROR_GUI& e) { LOGERROR(L"GUI error: %hs", e.what()); } }
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, boost::unordered_set<VfsPath>& Paths, u32 nesting_depth) { ENSURE(pParent); XMBAttributeList attributes = Element.GetAttributes(); CStr type(attributes.GetNamedItem(pFile->GetAttributeID("type"))); if (type.empty()) type = "empty"; // Construct object from specified type // henceforth, we need to do a rollback before aborting. // i.e. releasing this object IGUIObject* object = ConstructObject(type); if (!object) { LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); return; } // Cache some IDs for element attribute names, to avoid string comparisons #define ELMT(x) int elmt_##x = pFile->GetElementID(#x) #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) ELMT(object); ELMT(action); ELMT(repeat); ELMT(translatableAttribute); ELMT(translate); ELMT(attribute); ELMT(keep); ELMT(include); ATTR(style); ATTR(type); ATTR(name); ATTR(hotkey); ATTR(z); ATTR(on); ATTR(file); ATTR(directory); ATTR(id); ATTR(context); // // Read Style and set defaults // // If the setting "style" is set, try loading that setting. // // Always load default (if it's available) first! // CStr argStyle(attributes.GetNamedItem(attr_style)); if (m_Styles.count("default") == 1) object->LoadStyle(*this, "default"); if (!argStyle.empty()) { if (m_Styles.count(argStyle) == 0) LOGERROR("GUI: Trying to use style '%s' that doesn't exist.", argStyle.c_str()); else object->LoadStyle(*this, argStyle); } bool NameSet = false; bool ManuallySetZ = false; CStrW inclusionPath; CStr hotkeyTag; for (XMBAttribute attr : attributes) { // If value is "null", then it is equivalent as never being entered if (CStr(attr.Value) == "null") continue; // Ignore "type" and "style", we've already checked it if (attr.Name == attr_type || attr.Name == attr_style) continue; if (attr.Name == attr_name) { CStr name(attr.Value); for (const std::pair<CStr, CStr>& sub : NameSubst) name.Replace(sub.first, sub.second); object->SetName(name); NameSet = true; continue; } if (attr.Name == attr_hotkey) hotkeyTag = attr.Value; if (attr.Name == attr_z) ManuallySetZ = true; if (object->SetSetting(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), true) != PSRETURN_OK) LOGERROR("GUI: (object: %s) Can't set \"%s\" to \"%s\"", object->GetPresentableName(), pFile->GetAttributeString(attr.Name), attr.Value); } // Check if name isn't set, generate an internal name in that case. if (!NameSet) { object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); ++m_InternalNameNumber; } if (!hotkeyTag.empty()) m_HotkeyObjects[hotkeyTag].push_back(object); CStrW caption(Element.GetText().FromUTF8()); if (!caption.empty()) object->SetSetting("caption", caption, true); for (XMBElement child : Element.GetChildNodes()) { // Check what name the elements got int element_name = child.GetNodeName(); if (element_name == elmt_object) { // Call this function on the child Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths, nesting_depth); } else if (element_name == elmt_action) { // Scripted <action> element // Check for a 'file' parameter CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStr code; // If there is a file, open it and use it as the code if (!filename.empty()) { Paths.insert(filename); CVFSFile scriptfile; if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) { LOGERROR("Error opening GUI script action file '%s'", utf8_from_wstring(filename)); throw PSERROR_GUI_JSOpenFailed(); } code = scriptfile.DecodeUTF8(); // assume it's UTF-8 } XMBElementList grandchildren = child.GetChildNodes(); if (!grandchildren.empty()) // The <action> element contains <keep> and <translate> tags. for (XMBElement grandchild : grandchildren) { if (grandchild.GetNodeName() == elmt_translate) code += g_L10n.Translate(grandchild.GetText()); else if (grandchild.GetNodeName() == elmt_keep) code += grandchild.GetText(); } else // It’s pure JavaScript code. // Read the inline code (concatenating to the file code, if both are specified) code += CStr(child.GetText()); CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on)); // We need to set the GUI this object belongs to because RegisterScriptHandler requires an associated GUI. object->SetGUI(this); object->RegisterScriptHandler(action.LowerCase(), code, this); } else if (element_name == elmt_repeat) { Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); } else if (element_name == elmt_translatableAttribute) { // This is an element in the form “<translatableAttribute id="attributeName">attributeValue</translatableAttribute>”. CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. if (attributeName.empty()) { LOGERROR("GUI: ‘translatableAttribute’ XML element with empty ‘id’ XML attribute found. (object: %s)", object->GetPresentableName().c_str()); continue; } CStr value(child.GetText()); if (value.empty()) continue; CStr context(child.GetAttributes().GetNamedItem(attr_context)); // Read the context if any. if (!context.empty()) { CStr translatedValue(g_L10n.TranslateWithContext(context, value)); object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } else { CStr translatedValue(g_L10n.Translate(value)); object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } } else if (element_name == elmt_attribute) { // This is an element in the form “<attribute id="attributeName"><keep>Don’t translate this part // </keep><translate>but translate this one.</translate></attribute>”. CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. if (attributeName.empty()) { LOGERROR("GUI: ‘attribute’ XML element with empty ‘id’ XML attribute found. (object: %s)", object->GetPresentableName().c_str()); continue; } CStr translatedValue; for (XMBElement grandchild : child.GetChildNodes()) { if (grandchild.GetNodeName() == elmt_translate) translatedValue += g_L10n.Translate(grandchild.GetText()); else if (grandchild.GetNodeName() == elmt_keep) translatedValue += grandchild.GetText(); } object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } else if (element_name == elmt_include) { CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStrW directory(child.GetAttributes().GetNamedItem(attr_directory).FromUTF8()); if (!filename.empty()) { if (!directory.empty()) LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); Paths.insert(filename); CXeromyces XeroIncluded; if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) { LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); continue; } XMBElement node = XeroIncluded.GetRoot(); if (node.GetNodeName() != XeroIncluded.GetElementID("object")) { LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); continue; } if (nesting_depth+1 >= MAX_OBJECT_DEPTH) { LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); continue; } Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); } else if (!directory.empty()) { if (nesting_depth+1 >= MAX_OBJECT_DEPTH) { LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); continue; } VfsPaths pathnames; vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); for (const VfsPath& path : pathnames) { // as opposed to loading scripts, don't care if it's loaded before // one might use the same parts of the GUI in different situations Paths.insert(path); CXeromyces XeroIncluded; if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) { LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); continue; } XMBElement node = XeroIncluded.GetRoot(); if (node.GetNodeName() != XeroIncluded.GetElementID("object")) { LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); continue; } Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); } } else LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); } else { // Try making the object read the tag. if (!object->HandleAdditionalChildren(child, pFile)) LOGERROR("GUI: (object: %s) Reading unknown children for its type", object->GetPresentableName().c_str()); } } if (!ManuallySetZ) { // Set it automatically to 10 plus its parents bool absolute; GUI<bool>::GetSetting(object, "absolute", absolute); if (absolute) // If the object is absolute, we'll have to get the parent's Z buffered, // and add to that! GUI<float>::SetSetting(object, "z", pParent->GetBufferedZ() + 10.f, true); else // If the object is relative, then we'll just store Z as "10" GUI<float>::SetSetting(object, "z", 10.f, true); } try { if (pParent == m_BaseObject) AddObject(object); else pParent->AddChild(object); } catch (PSERROR_GUI& e) { LOGERROR("GUI error: %s", e.what()); } }
void CParamNode::ApplyLayer(const XMBFile& xmb, const XMBElement& element, const wchar_t* sourceIdentifier /*= NULL*/) { ResetScriptVal(); std::string name = xmb.GetElementString(element.GetNodeName()); // TODO: is GetElementString inefficient? CStrW value = element.GetText().FromUTF8(); bool hasSetValue = false; // Look for special attributes int at_disable = xmb.GetAttributeID("disable"); int at_replace = xmb.GetAttributeID("replace"); int at_op = xmb.GetAttributeID("op"); int at_datatype = xmb.GetAttributeID("datatype"); enum op { INVALID, ADD, MUL } op = INVALID; bool replacing = false; { XERO_ITER_ATTR(element, attr) { if (attr.Name == at_disable) { m_Childs.erase(name); return; } else if (attr.Name == at_replace) { m_Childs.erase(name); replacing = true; } else if (attr.Name == at_op) { if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"add") op = ADD; else if (std::wstring(attr.Value.begin(), attr.Value.end()) == L"mul") op = MUL; else LOGWARNING("Invalid op '%ls'", attr.Value); } } } { XERO_ITER_ATTR(element, attr) { if (attr.Name == at_datatype && std::wstring(attr.Value.begin(), attr.Value.end()) == L"tokens") { CParamNode& node = m_Childs[name]; // Split into tokens std::vector<std::wstring> oldTokens; std::vector<std::wstring> newTokens; if (!replacing && !node.m_Value.empty()) // ignore the old tokens if replace="" was given boost::algorithm::split(oldTokens, node.m_Value, boost::algorithm::is_space(), boost::algorithm::token_compress_on); if (!value.empty()) boost::algorithm::split(newTokens, value, boost::algorithm::is_space(), boost::algorithm::token_compress_on); // Merge the two lists std::vector<std::wstring> tokens = oldTokens; for (size_t i = 0; i < newTokens.size(); ++i) { if (newTokens[i][0] == L'-') { std::vector<std::wstring>::iterator tokenIt = std::find(tokens.begin(), tokens.end(), newTokens[i].substr(1)); if (tokenIt != tokens.end()) tokens.erase(tokenIt); else LOGWARNING("[ParamNode] Could not remove token '%s' from node '%s'%s; not present in list nor inherited (possible typo?)", utf8_from_wstring(newTokens[i].substr(1)), name, sourceIdentifier ? (" in '" + utf8_from_wstring(sourceIdentifier) + "'").c_str() : ""); } else { if (std::find(oldTokens.begin(), oldTokens.end(), newTokens[i]) == oldTokens.end()) tokens.push_back(newTokens[i]); } } node.m_Value = boost::algorithm::join(tokens, L" "); hasSetValue = true; break; } } } // Add this element as a child node CParamNode& node = m_Childs[name]; if (op != INVALID) { // TODO: Support parsing of data types other than fixed; log warnings in other cases fixed oldval = node.ToFixed(); fixed mod = fixed::FromString(CStrW(value)); switch (op) { case ADD: node.m_Value = (oldval + mod).ToString().FromUTF8(); break; case MUL: node.m_Value = (oldval.Multiply(mod)).ToString().FromUTF8(); break; } hasSetValue = true; } if (!hasSetValue) node.m_Value = value; // We also need to reset node's script val, even if it has no children // or if the attributes change. node.ResetScriptVal(); // Recurse through the element's children XERO_ITER_EL(element, child) { node.ApplyLayer(xmb, child, sourceIdentifier); }
void CGUIManager::LoadPage(SGUIPage& page) { // If we're hotloading then try to grab some data from the previous page shared_ptr<ScriptInterface::StructuredClone> hotloadData; if (page.gui) { shared_ptr<ScriptInterface> scriptInterface = page.gui->GetScriptInterface(); JSContext* cx = scriptInterface->GetContext(); JSAutoRequest rq(cx); JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); JS::RootedValue hotloadDataVal(cx); scriptInterface->CallFunction(global, "getHotloadData", &hotloadDataVal); hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal); } page.inputs.clear(); page.gui.reset(new CGUI(m_ScriptRuntime)); page.gui->Initialize(); VfsPath path = VfsPath("gui") / page.name; page.inputs.insert(path); CXeromyces xero; if (xero.Load(g_VFS, path, "gui_page") != PSRETURN_OK) // Fail silently (Xeromyces reported the error) return; int elmt_page = xero.GetElementID("page"); int elmt_include = xero.GetElementID("include"); XMBElement root = xero.GetRoot(); if (root.GetNodeName() != elmt_page) { LOGERROR("GUI page '%s' must have root element <page>", utf8_from_wstring(page.name)); return; } XERO_ITER_EL(root, node) { if (node.GetNodeName() != elmt_include) { LOGERROR("GUI page '%s' must only have <include> elements inside <page>", utf8_from_wstring(page.name)); continue; } std::string name = node.GetText(); CStrW nameW (node.GetText().FromUTF8()); PROFILE2("load gui xml"); PROFILE2_ATTR("name: %s", name.c_str()); TIMER(nameW.c_str()); if (name.back() == '/') { VfsPath directory = VfsPath("gui") / nameW; VfsPaths pathnames; vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); for (const VfsPath& path : pathnames) page.gui->LoadXmlFile(path, page.inputs); } else { VfsPath path = VfsPath("gui") / nameW; page.gui->LoadXmlFile(path, page.inputs); } } // Remember this GUI page, in case the scripts call FindObjectByName shared_ptr<CGUI> oldGUI = m_CurrentGUI; m_CurrentGUI = page.gui; page.gui->SendEventToAll("load"); shared_ptr<ScriptInterface> scriptInterface = page.gui->GetScriptInterface(); JSContext* cx = scriptInterface->GetContext(); JSAutoRequest rq(cx); JS::RootedValue initDataVal(cx); JS::RootedValue hotloadDataVal(cx); JS::RootedValue global(cx, scriptInterface->GetGlobalObject()); if (page.initData) scriptInterface->ReadStructuredClone(page.initData, &initDataVal); if (hotloadData) scriptInterface->ReadStructuredClone(hotloadData, &hotloadDataVal); // Call the init() function if (!scriptInterface->CallFunctionVoid( global, "init", initDataVal, hotloadDataVal) ) { LOGERROR("GUI page '%s': Failed to call init() function", utf8_from_wstring(page.name)); } m_CurrentGUI = oldGUI; }
CMaterial& CMaterialManager::LoadMaterial(const VfsPath& pathname) { if(pathname.empty()) return NullMaterial; std::map<VfsPath, CMaterial*>::iterator iter = m_Materials.find(pathname); if(iter != m_Materials.end()) { if((*iter).second) return *(*iter).second; } CXeromyces xeroFile; if(xeroFile.Load(g_VFS, pathname) != PSRETURN_OK) return NullMaterial; #define EL(x) int el_##x = xeroFile.GetElementID(#x) #define AT(x) int at_##x = xeroFile.GetAttributeID(#x) EL(texture); EL(alpha); AT(usage); #undef AT #undef EL CMaterial *material = NULL; try { XMBElement root = xeroFile.GetRoot(); XMBElementList childNodes = root.GetChildNodes(); material = new CMaterial(); for(int i = 0; i < childNodes.Count; i++) { XMBElement node = childNodes.Item(i); int token = node.GetNodeName(); XMBAttributeList attrs = node.GetAttributes(); CStr temp; if(token == el_texture) { CStr value(node.GetText()); material->SetTexture(value); } else if(token == el_alpha) { temp = CStr(attrs.GetNamedItem(at_usage)); // Determine whether the alpha is used for basic transparency or player color if (temp == "playercolor") material->SetUsePlayerColor(true); else if (temp == "objectcolor") material->SetUseTextureColor(true); else material->SetUsesAlpha(ParseUsage(temp)); } } m_Materials[pathname] = material; } catch(...) { SAFE_DELETE(material); throw; } return *material; }