Resource Manager
Unit: ResMan
Centralized resource loading from XML manifest.
XML Format
<?xml version="1.0" encoding="US-ASCII"?>
<resources>
<music name="theme" path="THEME.HSC" />
<sound name="explode" path="EXPLODE.VOC" />
<image name="player" path="PLAYER.PCX" palette="player" />
<palette name="default" path="DEFAULT.PAL" />
<font name="small" path="FONT-SM.XML" />
<sprite name="player_run" image="player" duration="0.6">
<frame x="0" y="0" width="32" height="32" />
<frame x="32" y="0" width="32" height="32" />
<frame x="64" y="0" width="32" height="32" />
</sprite>
</resources>
Paths are relative to XML location. If RES.XML is in DATA\, then path="TEST.PCX" loads DATA\TEST.PCX.
Types
type
TResourceType = (ResType_Music, ResType_Sound, ResType_Image,
ResType_Font, ResType_Sprite, ResType_Palette);
TResourceManager = object
procedure Init(UseLazyLoading: Boolean);
function LoadFromXML(const Filename: String): Boolean;
{ Resource access }
function GetImage(const Name: String): PImage;
function GetFont(const Name: String): PFont;
function GetSprite(const Name: String): PSprite;
function GetSound(const Name: String): Integer; { Returns sound ID }
function GetMusic(const Name: String): PHSC_Obj;
function GetPalette(const Name: String): PPalette;
{ Manual resource management }
function LoadResource(const Name: String): Boolean;
procedure UnloadResource(const Name: String); { Free individual resource }
procedure Done;
end;
Example
uses ResMan, VGA, Sprite;
var
ResMgr: TResourceManager;
PlayerSprite: PSprite;
PlayerPalette: PPalette;
Player: TSpriteInstance;
begin
{ Initialize with lazy loading }
ResMgr.Init(True);
if not ResMgr.LoadFromXML('DATA\RES.XML') then
begin
WriteLn('ERROR: ', ResMgr.LastError);
Halt(1);
end;
{ Get resources (auto-loads on first access) }
PlayerSprite := ResMgr.GetSprite('player_run');
if PlayerSprite = nil then
begin
WriteLn('ERROR: ', ResMgr.LastError);
Halt(1);
end;
{ Get extracted palette }
PlayerPalette := ResMgr.GetPalette('player');
if PlayerPalette <> nil then
SetPalette(PlayerPalette^);
{ Setup sprite instance }
Player.Sprite := PlayerSprite;
Player.X := 100;
Player.Y := 50;
Player.CurrentTime := 0.0;
{ Game loop... }
UpdateSprite(Player, DeltaTime);
DrawSprite(Player, BackBuffer);
{ Cleanup }
ResMgr.Done; { Frees all resources }
end.
Palettes
Two types:
Standalone palette:
<palette name="default" path="DEFAULT.PAL" />
Image-extracted palette:
<image name="player" path="PLAYER.PCX" palette="player" />
Usage:
{ Get extracted palette }
Pal := ResMgr.GetPalette('player');
if Pal <> nil then
SetPalette(Pal^);
Music Singleton
Only one music track loads at a time:
{ Load title music }
TitleMusic := ResMgr.GetMusic('title');
TitleMusic^.Start;
{ Load level music (title auto-unloaded) }
GameMusic := ResMgr.GetMusic('level1');
GameMusic^.Start;
{ TitleMusic pointer now invalid! }
Manual Resource Unloading
Free individual resources when no longer needed:
{ Load level assets }
BgImage := ResMgr.GetImage('level1_bg');
EnemySprite := ResMgr.GetSprite('enemy1');
{ Use them... }
{ Switch to next level - free old assets }
ResMgr.UnloadResource('level1_bg');
ResMgr.UnloadResource('enemy1');
{ Load new level assets }
BgImage := ResMgr.GetImage('level2_bg');
EnemySprite := ResMgr.GetSprite('enemy2');
Supported:
Images, fonts, sprites, palettes, music
Not supported:
Sounds (managed by SoundBank as a group)
Behavior:
Frees memory immediately
Marks resource as unloaded (can be re-loaded via lazy loading)
Invalidates all pointers to that resource
⚠️ Warning: After unloading, any existing pointers become invalid!
Img := ResMgr.GetImage('player');
ResMgr.UnloadResource('player');
PutImage(0, 0, Img, FB); { ❌ CRASH - pointer invalid! }
Critical Notes
XML-relative paths - All paths relative to XML file location
Lazy vs eager loading -
Init(True)for lazy,Init(False)for eagerMusic cleanup - Don’t call
Music^.Donemanually, letResMgr.Donehandle itDependency resolution - Sprites auto-load parent images
Error handling - Check
ResMgr.LastErrorwhenGetXXXreturns nil
Error Handling
Img := ResMgr.GetImage('invalid');
if Img = nil then
WriteLn('Error: ', ResMgr.LastError);
Common errors:
"Resource not found: name"- Name not in XML"Failed to load image"- File missing or corrupt"Sprite references unknown image"- Missing dependency
Benefits
Declarative resource definitions (XML, not code)
Name-based lookup (no hardcoded paths)
Automatic dependency resolution
Palette extraction and management
Centralized cleanup
Dependencies
MINIXML (XML parsing)
STRMAP (name lookup)
PCX, VGAFONT, SPRITE, SNDBANK, PLAYHSC