CRM64Pro Tutorial 13

XML configuration data.

Load an XML document, read typed values and node text, and use them to configure rendered content.

  • Intermediate
  • XMLMgr
  • Configuration
  • Typed data

Overview

This tutorial loads Base/tutorial.xml through XMLMgr. It selects nested elements, reads string, integer and floating-point attributes, and retrieves text stored inside a node.

The loaded values control headings, positions and player information rendered over the shared tutorial background.

Final result

CRM64Pro Tutorial 13 XML
Runtime controls

D toggles the debug window, grave toggles the console, and Q or ESC exits.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Run Tutorials 02 and 04 so Tutorial.cdc contains the background and cursor.
  • Included assets available under bin/Base/: tutorial.xml.

What you will learn

  • Load an external document with XMLMgr::loadFromFile().
  • Use the borrowed XML object owned by its manager.
  • Select nested elements with XML::nodePointTo().
  • Read string, integer and floating-point attributes.
  • Read element text.
  • Use XML values in normal rendering code.

Step by step

Step 1: Define the input resources

The XML document remains an external editable file, while the background and cursor come from the shared CDC archive.

#define RESOURCE_XML_FILE BASEDIR"tutorial.xml"
#define RESOURCE_BG_CDC_NAME "Tutorial_bg"
#define RESOURCE_CURSOR_CDC_NAME "Tutorial_cursor"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"

Step 2: Load the XML document

XMLMgr owns the document. Validate both the returned ID and the borrowed pointer before reading data.

// XMLMgr owns the loaded document. The returned XML pointer is borrowed.
mLog.msg(LL_INFO, "  Load XML configuration: %s ... ", RESOURCE_XML_FILE);
rState.idXML = mC64.xmlMgr().loadFromFile(RESOURCE_XML_FILE, "TutorialXMLConfig");
XML* pXML = mC64.xmlMgr().get(rState.idXML);
if(rState.idXML < 0 || pXML == nullptr)
{
    logTaskFailed(mLog, rState.idXML);
    return false;
}
logTaskOk(mLog);

Step 3: Select a node and read attributes

Pass the complete element path to nodePointTo(), then use typed accessors with fallback values.

// nodePointTo() selects a nested node by listing its complete path.
mLog.msg(LL_INFO, "  Read display configuration ... ");
if(!pXML->nodePointTo(2, "tutorial", "display"))
{
    logTaskFailed(mLog);
    return false;
}
rState.sHeadingText = pXML->getAttributeString("heading", "Tutorial 13: XML");
rState.iHeadingX = pXML->getAttributeInt("x", rState.iHeadingX);
rState.iHeadingY = pXML->getAttributeInt("y", rState.iHeadingY);
logTaskOk(mLog);

Step 4: Read node text

Select the nested intro element and retrieve its text before reading its position attributes.

// Node text is useful for configurable rendered text and longer messages.
mLog.msg(LL_INFO, "  Read text and position ... ");
if(!pXML->nodePointTo(3, "tutorial", "display", "intro") || !pXML->getText(rState.sIntroText))
{
    logTaskFailed(mLog);
    return false;
}
rState.iIntroX = pXML->getAttributeInt("x", rState.iIntroX);
rState.iIntroY = pXML->getAttributeInt("y", rState.iIntroY);
logTaskOk(mLog);

Step 5: Read typed player data

The player element demonstrates string, integer and floating-point attribute access in one place.

// Typed accessors convert XML attributes into application data.
mLog.msg(LL_INFO, "  Read player data ... ");
if(!pXML->nodePointTo(2, "tutorial", "player"))
{
    logTaskFailed(mLog);
    return false;
}
rState.sPlayerName = pXML->getAttributeString("name", "Player");
rState.iPlayerScore = pXML->getAttributeInt("score", 0);
rState.fSpawnX = pXML->getAttributeFloat("spawnX", 0.0f);
rState.fSpawnY = pXML->getAttributeFloat("spawnY", 0.0f);
logTaskOk(mLog);

Step 6: Render the configured values

Rendering no longer needs to know how the data was stored. It reads the populated tutorial state like any other application data.

// Draw XML-configured text directly over the background.
static void renderFrame(Image* pBgImage, Font* pBuiltInFont, const TutorialState& rState)
{
    if(pBgImage) pBgImage->render();
    if(!pBuiltInFont) return;

    renderText(pBuiltInFont, rState.iHeadingX, rState.iHeadingY, rState.sHeadingText.c_str());
    renderText(pBuiltInFont, rState.iIntroX, rState.iIntroY, rState.sIntroText.c_str());

    char szLine[128];
    snprintf(szLine, sizeof(szLine), "Player name: %s", rState.sPlayerName.c_str());
    renderText(pBuiltInFont, 48, 180, szLine);
    snprintf(szLine, sizeof(szLine), "Score: %d", rState.iPlayerScore);
    renderText(pBuiltInFont, 48, 220, szLine);
    snprintf(szLine, sizeof(szLine), "Spawn position: %.1f, %.1f", rState.fSpawnX, rState.fSpawnY);
    renderText(pBuiltInFont, 48, 260, szLine);
    renderText(pBuiltInFont, 48, 320, "Source: " RESOURCE_XML_FILE);
}

Step 7: Load the data before the loop

Stop setup if a required node or text value cannot be read.

if(!loadXMLData(mC64, mLog, state))
{
    Main::terminate();
    return -1;
}

Step 8: Finish the loop cleanly

A quit request exits before another state update.

if(!bRunning) break;
state.fMouseX = mC64.cursorMgr().getX();
state.fMouseY = mC64.cursorMgr().getY();
state.iCurrentRenderRate = static_cast<Sint32>(mC64.timer().getCurrentRFR());
state.iCurrentLogicRate = static_cast<Sint32>(mC64.timer().getCurrentLFR());
updateLogic(state);

Step 9: Release the XML document

Close manager-owned XML documents during cleanup, before terminating the engine.

mLog.msg(LL_INFO, "  Close XML documents ... ");
mC64.xmlMgr().close(0);
logTaskOk(mLog);

Complete source

Use the source file as the authoritative version of this tutorial.

Previous tutorial

Build panels and handle widget events.

Go to Tutorial 12: GUI

Tutorial index

Back to tutorials

Next tutorial

Play video with audio and control its viewport.

Go to Tutorial 14: Video