CRM64Pro GDK v0.80.0
A free cross-platform game development kit built on top of SDL 3.0
Loading...
Searching...
No Matches
Tutorial 03: Configuration

Overview

This tutorial extends Tutorial 02 with the built-in configuration screen. It stores config.xml in the existing Tutorial.cdc, then lets you save and load the active screen and audio settings while the application is running.

Loading a configuration recreates the default Screen, so the tutorial also shows how to restore state that belongs to that Screen.

Final result

CRM64Pro Tutorial 03 configuration screen

The tutorial opens the CRM64Pro configuration screen at startup. After setup, it displays the Tutorial 02 background and ship using the selected video configuration.

Runtime controls

  • C: Open the configuration screen.
  • S: Save the current configuration.
  • L: Load the saved configuration.
  • Grave key: Show or hide the built-in console.
  • Q or ESC: Exit the tutorial.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Run Tutorial 02 first to create Tutorial.cdc with Tutorial_bg and Tutorial_ship.
  • Write access to the platform output directory.

What you will learn

  • Open the built-in ConfigMgr setup screen.
  • Store configuration XML in a shared CDC archive.
  • Save and load the active configuration.
  • Restore the Screen title and render callback after loading.
  • Keep the existing Tutorial 02 resources in Tutorial.cdc.

Step by step

1. Reuse the Tutorial 02 archive

The configuration is another named resource in the shared archive. The tutorial checks that Tutorial 02 has created the archive instead of silently creating one without the required images.

#define RESOURCE_BG_CDC_NAME "Tutorial_bg"
#define RESOURCE_PLAYER_CDC_NAME "Tutorial_ship"
#define RESOURCE_CONFIG_CDC_NAME "config.xml"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"
// Tutorial 02 creates the shared archive and its image resources.
mLog.msg(LL_INFO, " Find Tutorial 02 CDC: %s ... ", OUTPUT_CDC);
if(!mC64.tool().fileExists(OUTPUT_CDC))
{
logTaskFailed(mLog);
Main::terminate();
return -1;
}
logTaskOk(mLog);

2. Open the configuration screen

ConfigMgr::setup() presents the built-in setup UI and writes config.xml to Tutorial.cdc. A negative return value means setup failed or did not produce a usable configuration.

// The built-in setup screen saves config.xml into Tutorial.cdc.
mLog.msg(LL_INFO, " Open configuration screen ... ");
const Sint32 iSetupRet = mC64.configMgr().setup(RESOURCE_CONFIG_CDC_NAME, OUTPUT_CDC);
if(iSetupRet < 0)
{
logTaskFailed(mLog, iSetupRet);
Main::terminate();
return -1;
}
logTaskOk(mLog);

3. Load configuration and restore Screen state

ConfigMgr::load() applies the saved settings and recreates the default Screen. The title and render callback are therefore assigned after every successful load.

// Load config.xml and restore state tied to the recreated default screen.
static bool loadConfiguration(Main& mC64, Log& mLog, TutorialState& rState)
{
mLog.msg(LL_INFO, " Load configuration from CDC ... ");
const Sint32 iRet = mC64.configMgr().load(OUTPUT_CDC, RESOURCE_CONFIG_CDC_NAME);
if(iRet < 0)
{
logTaskFailed(mLog, iRet);
return false;
}
Screen* pScreen = mC64.configMgr().get();
if(!pScreen)
{
logTaskFailed(mLog);
return false;
}
pScreen->setTitle("CRM64Pro - Tutorial 03: Configuration");
if(!pScreen->setRenderCallback([&rState](Sint32 iMode) { return renderWrapper(iMode, &rState); }))
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
return true;
}

4. Load the existing images

The configuration resource does not replace the image blocks already stored by Tutorial 02. ImageMgr loads them by their original resource names.

mLog.msg(LL_INFO, " Load background image from CDC ... ");
state.idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
if(state.idBgImage < 0)
{
logTaskFailed(mLog, state.idBgImage);
Main::terminate();
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load ship image from CDC ... ");
state.idPlayerImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_PLAYER_CDC_NAME);
if(state.idPlayerImage < 0)
{
logTaskFailed(mLog, state.idPlayerImage);
Main::terminate();
return -1;
}
logTaskOk(mLog);

5. Defer configuration commands

Key events only set command flags. Setup, save and load run after the current Main::update() event batch has finished, keeping screen reconfiguration out of event dispatch.

else if(event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat)
{
if(event.key.key == SDLK_Q || event.key.key == SDLK_ESCAPE) bRunning = false;
else if(event.key.key == SDLK_C) bOpenSetup = true;
else if(event.key.key == SDLK_S) bSaveConfig = true;
else if(event.key.key == SDLK_L) bLoadConfig = true;
}

6. Save or reload on demand

Saving updates config.xml without disturbing the other CDC resources. Loading uses the same helper as startup so Screen state is always restored.

if(bSaveConfig)
{
bSaveConfig = false;
mLog.msg(LL_INFO, " Save configuration to CDC ... ");
const Sint32 iRet = mC64.configMgr().save(RESOURCE_CONFIG_CDC_NAME, OUTPUT_CDC);
if(iRet < 0) logTaskFailed(mLog, iRet);
else logTaskOk(mLog);
}
if(bLoadConfig)
{
bLoadConfig = false;
if(!loadConfiguration(mC64, mLog, state)) bRunning = false;
}

7. Release resources

Image and archive resources are closed before CRM64Pro terminates.

mLog.msg(LL_INFO, "\nCLEANUP\n");
mLog.msg(LL_INFO, " Close images and CDC archives ... ");
mC64.imageMgr().close(0);
mC64.archiveMgr().close(0);
logTaskOk(mLog);
Main::terminate();

Complete source

The tutorial source is authoritative. The following listing is included directly from validation/source/Tutorial_03_Config.cpp.

/* ----------------------------------------------------------------------
CRM64Pro GDK - Roberto Prieto
Copyright (C) 2013-2026 MegaStorm Systems
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
------------------------------------------------------------------------
Tutorial 03: Configuration
------------------------------------------------------------------------
Overview:
This tutorial extends Tutorial 02 System with the built-in configuration
screen. It stores config.xml in Tutorial.cdc beside the existing image
resources, then demonstrates saving and loading the active configuration.
Key concepts:
- Open the built-in ConfigMgr setup screen.
- Save the current audio and screen configuration to a CDC file.
- Load a saved configuration from a CDC file.
- Restore the render callback after reloading the screen configuration.
- Keep unrelated resources in the shared Tutorial.cdc archive.
Controls:
- C: Open the configuration screen.
- S: Save the current configuration.
- L: Load the saved configuration.
- Grave: Toggle console.
- Q / ESC: Quit.
------------------------------------------------------------------------ */
#include "CRM64Pro.h"
using namespace CRM64Pro;
#ifdef CRM64PRO_PLATFORM_WINDOWS
#define OUTPUTDIR "Win64/"
#elif defined(CRM64PRO_PLATFORM_LINUX)
#define OUTPUTDIR "Linux/"
#elif defined(CRM64PRO_PLATFORM_MACOS)
#define OUTPUTDIR "macOS/"
#endif
#define RESOURCE_BG_CDC_NAME "Tutorial_bg"
#define RESOURCE_PLAYER_CDC_NAME "Tutorial_ship"
#define RESOURCE_CONFIG_CDC_NAME "config.xml"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"
// Render as often as possible while Main::update() keeps a 20 Hz logic rate.
static const Sint32 iLogicRate = 20;
static const Sint32 iRenderRate = 0;
// Runtime resources used by the render callback.
struct TutorialState
{
Sint32 idBgImage = -1;
Sint32 idPlayerImage = -1;
};
// Record a successful tutorial task.
static void logTaskOk(Log& mLog)
{
mLog.msg(LL_DEBUG, "OK\n");
}
// Record a failed tutorial task with its CRM64Pro error code.
static void logTaskFailed(Log& mLog, Sint32 iCode)
{
mLog.msg(LL_ERROR, "FAILED (Code %d)\n", iCode);
}
// Record a failed tutorial task without an error code.
static void logTaskFailed(Log& mLog)
{
mLog.msg(LL_ERROR, "FAILED\n");
}
// Draw the background and ship using the current screen dimensions.
static void renderFrame(const TutorialState& rState)
{
Main& mC64 = Main::instance();
Screen* pScreen = mC64.configMgr().get();
Image* pBgImage = mC64.imageMgr().get(rState.idBgImage);
Image* pPlayerImage = mC64.imageMgr().get(rState.idPlayerImage);
if(!pScreen) return;
if(pBgImage)
{
SDL_FRect rBg = {
0.0f,
0.0f,
static_cast<float>(pScreen->getWidth()),
static_cast<float>(pScreen->getHeight())
};
pBgImage->render(0, nullptr, &rBg);
}
if(pPlayerImage)
{
SDL_FRect rPlayer = {
static_cast<float>(pScreen->getWidth() - pPlayerImage->getWidth()) * 0.5f,
static_cast<float>(pScreen->getHeight() - pPlayerImage->getHeight()) * 0.5f,
static_cast<float>(pPlayerImage->getWidth()),
static_cast<float>(pPlayerImage->getHeight())
};
pPlayerImage->render(0, nullptr, &rPlayer);
}
}
// Screen render callback called by Main::update().
static Sint32 renderWrapper(Sint32, void* pObj)
{
TutorialState* pState = static_cast<TutorialState*>(pObj);
if(pState) renderFrame(*pState);
return 0;
}
// Load config.xml and restore state tied to the recreated default screen.
static bool loadConfiguration(Main& mC64, Log& mLog, TutorialState& rState)
{
mLog.msg(LL_INFO, " Load configuration from CDC ... ");
const Sint32 iRet = mC64.configMgr().load(OUTPUT_CDC, RESOURCE_CONFIG_CDC_NAME);
if(iRet < 0)
{
logTaskFailed(mLog, iRet);
return false;
}
Screen* pScreen = mC64.configMgr().get();
if(!pScreen)
{
logTaskFailed(mLog);
return false;
}
pScreen->setTitle("CRM64Pro - Tutorial 03: Configuration");
if(!pScreen->setRenderCallback([&rState](Sint32 iMode) { return renderWrapper(iMode, &rState); }))
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
return true;
}
// Run the tutorial application and release its resources.
int main()
{
// Start CRM64Pro memory tracking before creating engine resources.
atexit(CMem::destroy);
Main& mC64 = Main::instance();
Log& mLog = *mC64.logMgr().get();
TutorialState state;
// Write log output to stdout and to a file under validation/bin.
mLog.init("Tutorial_03_Config", LL_DEBUG, LM_FILE | LM_STDOUT, OUTPUTDIR"Tutorial_03_Config.log");
mLog.msg(LL_INFO, "\nSETUP: timing and Tutorial.cdc\n");
mLog.msg(LL_INFO, " Initialize timer ... ");
if(!mC64.timer().init() || !mC64.timer().setRate(iRenderRate, iLogicRate))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
// Tutorial 02 creates the shared archive and its image resources.
mLog.msg(LL_INFO, " Find Tutorial 02 CDC: %s ... ", OUTPUT_CDC);
if(!mC64.tool().fileExists(OUTPUT_CDC))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
// The built-in setup screen saves config.xml into Tutorial.cdc.
mLog.msg(LL_INFO, " Open configuration screen ... ");
const Sint32 iSetupRet = mC64.configMgr().setup(RESOURCE_CONFIG_CDC_NAME, OUTPUT_CDC);
if(iSetupRet < 0)
{
logTaskFailed(mLog, iSetupRet);
return -1;
}
logTaskOk(mLog);
if(!loadConfiguration(mC64, mLog, state))
{
return -1;
}
mLog.msg(LL_INFO, "\nRESOURCES: load Tutorial 02 images\n");
mLog.msg(LL_INFO, " Load background image from CDC ... ");
state.idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
if(state.idBgImage < 0)
{
logTaskFailed(mLog, state.idBgImage);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load ship image from CDC ... ");
state.idPlayerImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_PLAYER_CDC_NAME);
if(state.idPlayerImage < 0)
{
logTaskFailed(mLog, state.idPlayerImage);
return -1;
}
logTaskOk(mLog);
Console* pConsole = mC64.guiMgr().getConsole();
if(pConsole) pConsole->print("Tutorial 03: Configuration ready. Press ` to toggle the console.\n");
mLog.msg(LL_INFO, "\nCONTROLS\n");
mLog.msg(LL_INFO, " [C] Open configuration screen\n");
mLog.msg(LL_INFO, " [S] Save current configuration\n");
mLog.msg(LL_INFO, " [L] Load saved configuration\n");
mLog.msg(LL_INFO, " [Q] / [ESC] Quit\n");
mLog.msg(LL_INFO, " [`] Toggle console\n");
mLog.msg(LL_INFO, "\nRUNNING\n");
SDL_Event event;
bool bRunning = true;
bool bOpenSetup = false;
bool bSaveConfig = false;
bool bLoadConfig = false;
while(bRunning)
{
while(mC64.update(&event))
{
if(event.type == SDL_EVENT_QUIT)
{
bRunning = false;
}
else if(event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat)
{
if(event.key.key == SDLK_Q || event.key.key == SDLK_ESCAPE) bRunning = false;
else if(event.key.key == SDLK_C) bOpenSetup = true;
else if(event.key.key == SDLK_S) bSaveConfig = true;
else if(event.key.key == SDLK_L) bLoadConfig = true;
}
}
if(!bRunning) break;
if(bOpenSetup)
{
bOpenSetup = false;
mLog.msg(LL_INFO, " Open configuration screen ... ");
const Sint32 iRet = mC64.configMgr().setup(RESOURCE_CONFIG_CDC_NAME, OUTPUT_CDC);
if(iRet < 0) logTaskFailed(mLog, iRet);
else
{
logTaskOk(mLog);
if(!loadConfiguration(mC64, mLog, state)) bRunning = false;
}
}
if(bSaveConfig)
{
bSaveConfig = false;
mLog.msg(LL_INFO, " Save configuration to CDC ... ");
const Sint32 iRet = mC64.configMgr().save(RESOURCE_CONFIG_CDC_NAME, OUTPUT_CDC);
if(iRet < 0) logTaskFailed(mLog, iRet);
else logTaskOk(mLog);
}
if(bLoadConfig)
{
bLoadConfig = false;
if(!loadConfiguration(mC64, mLog, state)) bRunning = false;
}
}
mLog.msg(LL_INFO, "\nCLEANUP\n");
mLog.msg(LL_INFO, " Close images and CDC archives ... ");
mC64.imageMgr().close(0);
mC64.archiveMgr().close(0);
logTaskOk(mLog);
return 0;
}
Sint32 close(Sint32 idCDC) override
Close and destroy a CDC object.
Definition CoreArchive.cpp:2476
Screen * get(Sint32 idScreen=0)
Get a pointer to the screen using its handle.
Definition CoreConfig.cpp:1497
void setMTFriendly(Uint8 iDelayMS)
Set the multithread-friendly scheduler yield.
Definition CoreConfig.cpp:1183
Console Object class.
Definition CRM64Pro.h:9678
Image Object class.
Definition CRM64Pro.h:3356
Sint32 getHeight() const
Get the image height.
Definition GraphicsImage.cpp:1839
Sint32 getWidth() const
Get the image width.
Definition GraphicsImage.cpp:1833
Sint32 render(Sint32 idRes=0, SDL_FRect *pSrc=nullptr, SDL_FRect *pDst=nullptr)
Render the image.
Definition GraphicsImage.cpp:1575
Image * get(Sint32 idImg)
Get a pointer to the image using its handle.
Definition GraphicsImage.cpp:2279
Sint32 load(const string &sCDCFile, const string &sName)
Load an image stored in a CDC file.
Definition GraphicsImage.cpp:2476
Sint32 close(Sint32 idImg) override
Close and destroy an image.
Definition GraphicsImage.cpp:2178
Log Object class.
Definition CRM64Pro.h:771
CRM64Pro GDK Main class.
Definition CRM64Pro.h:502
Screen Object class.
Definition CRM64Pro.h:1055
Sint32 getHeight() const
Get the screen height.
Definition CoreConfig.cpp:562
Sint32 getWidth() const
Get the screen width.
Definition CoreConfig.cpp:549
bool setRenderCallback(RenderCallback fnCallback)
Set a callback function for rendering graphics at Render Frame Rate.
Definition CoreConfig.cpp:765
bool setTitle(const string &sName)
Set the title.
Definition CoreConfig.cpp:674
void setStatsLevel(eMemStatsLevel eMSL)
Set the memory stats gathering level.
Definition CMemoryManager.cpp:618
void destroy()
Releases the internal heap and displays final stats.
Definition CMemoryManager.cpp:954
@ MSL_NORMAL
Collect allocations per module (CRM64Pro and user). Default for debug builds.
Definition CRM64Pro.h:386
Sint32 load(const string &sCDCFile, const string &sConfigXML)
Load audio/screen configuration from a CDC file.
Definition CoreConfigSetup.cpp:2279
Sint32 save(const string &sConfigXML, const string &sCDCFile="")
Save current audio/screens configuration.
Definition CoreConfigSetup.cpp:2180
Sint32 setup(const string &sConfigXML, const string &sConfigCDC="", const string &sCustomSetupXML="", vector< string > *pArguments=nullptr)
Open the setup window for configuring audio/screen settings.
Definition CoreConfigSetup.cpp:2450
Sint32 print(const char *format,...)
Print a formatted message to the console.
Definition GUIConsole.cpp:403
Console * getConsole(Sint32 idGUI=CRM64PRO_GUI_CONSOLE)
Get a pointer to a Console using its handle.
Definition GUI.cpp:1366
Log * get(Sint32 idLog=0)
Get a pointer to the log object.
Definition CoreLog.cpp:921
bool init(const string &sAppname, eLogLevel eLL, eLogMode eLM, const string &sFilename="", const string &sAuthor="", const string &sEmail="", const string &sWeb="")
Initialize the log object.
Definition CoreLog.cpp:338
@ LM_FILE
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to a file and overwrite exist...
Definition CRM64Pro.h:748
@ LM_STDOUT
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to standard output (stdout).
Definition CRM64Pro.h:750
@ LL_INFO
Runtime events (startup/shutdown). Messages without timestamp.
Definition CRM64Pro.h:764
@ LL_DEBUG
Detailed information for flow control.
Definition CRM64Pro.h:765
@ LL_ERROR
Runtime errors that do not require immediate termination.
Definition CRM64Pro.h:762
GUIMgr & guiMgr()
Get reference to GUI Manager.
Definition CoreMain.cpp:808
Timer & timer()
Get reference to Timer module.
Definition CoreMain.cpp:730
Sint32 update(SDL_Event *evUser=nullptr)
Manage the GDK update cycle with a fixed virtual logic frame rate.
Definition CoreMain.cpp:451
ImageMgr & imageMgr()
Get reference to Image Manager.
Definition CoreMain.cpp:754
static void terminate()
Main explicit destructor.
Definition CoreMain.cpp:266
static Main & instance()
Access the Main singleton.
Definition CoreMain.cpp:252
LogMgr & logMgr()
Get reference to Log Manager.
Definition CoreMain.cpp:724
Tool & tool()
Get reference to Tool module.
Definition CoreMain.cpp:718
ArchiveMgr & archiveMgr()
Get reference to Archive Manager.
Definition CoreMain.cpp:736
ConfigMgr & configMgr()
Get reference to Config Manager.
Definition CoreMain.cpp:712
bool init(eTimerState eState=TS_INIT)
Initialize the timer system.
Definition CoreTimer.cpp:543
bool setRate(Uint16 iR, Uint16 iL)
Set preferred Render and Logic Frame Rate.
Definition CoreTimer.cpp:556
bool fileExists(const string &sPath)
Check if a file exists.
Definition CoreTool.cpp:750
CRM64Pro GDK.
Definition AudioTrack.cpp:36

Navigation

Previous: Tutorial 02 - System services

Back to the tutorials index

The next tutorial adds and loads a custom cursor resource.

Continue with Tutorial 04: Custom cursor