unvanquished filesystem

This commit is contained in:
neumond
2017-04-09 14:52:00 +02:00
committed by Thomas Debesse
parent cd99e669a3
commit bbe9ab19c2
11 changed files with 417 additions and 74 deletions
+1 -9
View File
@@ -124,6 +124,7 @@ struct _QERFuncTable_1
const char* ( *getSettingsPath )( );
const char* ( *getMapsPath )( );
const char* ( *getGameFile )( );
const char* ( *getGameName )( );
const char* ( *getGameMode )( );
@@ -134,15 +135,6 @@ struct _QERFuncTable_1
const char* ( *getGameDescriptionKeyValue )(const char* key);
const char* ( *getRequiredGameDescriptionKeyValue )(const char* key);
void ( *attachGameToolsPathObserver )( ModuleObserver& observer );
void ( *detachGameToolsPathObserver )( ModuleObserver& observer );
void ( *attachEnginePathObserver )( ModuleObserver& observer );
void ( *detachEnginePathObserver )( ModuleObserver& observer );
void ( *attachGameNameObserver )( ModuleObserver& observer );
void ( *detachGameNameObserver )( ModuleObserver& observer );
void ( *attachGameModeObserver )( ModuleObserver& observer );
void ( *detachGameModeObserver )( ModuleObserver& observer );
SignalHandlerId ( *XYWindowDestroyed_connect )( const SignalHandler& handler );
void ( *XYWindowDestroyed_disconnect )( SignalHandlerId id );
MouseEventHandlerId ( *XYWindowMouseDown_connect )( const MouseEventHandler& handler );
+44
View File
@@ -26,6 +26,7 @@
/// \brief Text-output-formatting.
#include "itextstream.h"
#include "string/string.h"
#include <cctype>
#include <cstddef>
@@ -33,6 +34,7 @@
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include "generic/arrayrange.h"
@@ -396,6 +398,48 @@ std::size_t write( const char* buffer, std::size_t length ){
}
};
/// \brief A wrapper for a TextInputStream used for reading one text line at a time.
template<typename TextInputStreamType, int SIZE = 1024>
class TextLinesInputStream
{
TextInputStreamType& m_inputStream;
char m_buffer[SIZE + 1];
char* m_cur;
char* m_end;
int fillBuffer(){
m_end = m_buffer + m_inputStream.read( m_buffer, SIZE );
m_cur = m_buffer;
m_buffer[SIZE] = '\0';
*m_end = '\0';
return m_end - m_cur;
}
public:
TextLinesInputStream( TextInputStreamType& inputStream ) : m_inputStream( inputStream ), m_cur( m_buffer ), m_end( m_buffer ){
m_buffer[0] = '\0';
}
CopiedString readLine(){
std::string s;
char* m_fin;
while ( (m_fin = strchr( m_cur, '\n' )) == 0 )
{
s.append( m_cur, m_end - m_cur );
if ( fillBuffer() <= 0 ) break;
}
if ( m_fin != 0 ) {
s.append( m_cur, m_fin - m_cur + 1 );
m_cur = m_fin + 1;
}
return CopiedString( s.c_str() );
}
};
/// \brief A wrapper for a TextOutputStream, optimised for writing a few characters at a time.
template<typename TextOutputStreamType, int SIZE = 1024>
class BufferedTextOutputStream : public TextOutputStream
+8
View File
@@ -194,6 +194,14 @@ inline char* string_new( std::size_t length ){
return string_new( length, allocator );
}
/// \brief Allocates a new buffer large enough to hold two concatenated strings and fills it with strings.
inline char* string_new_concat( const char* a, const char* b ){
char* str = string_new( string_length( a ) + string_length( b ) );
strcpy( str, a );
strcat( str, b );
return str;
}
/// \brief Deallocates the \p buffer large enough to hold \p length characters.
inline void string_release( char* string, std::size_t length ){
DefaultAllocator<char> allocator;
+289 -34
View File
@@ -84,6 +84,7 @@ struct archive_entry_t
};
#include <list>
#include <map>
typedef std::list<archive_entry_t> archives_t;
@@ -132,7 +133,7 @@ const _QERArchiveTable* GetArchiveTable( ArchiveModules& archiveModules, const c
tmp << LowerCase( ext );
return archiveModules.findModule( tmp.c_str() );
}
static void InitPakFile( ArchiveModules& archiveModules, const char *filename ){
static Archive* InitPakFile( ArchiveModules& archiveModules, const char *filename ){
const _QERArchiveTable* table = GetArchiveTable( archiveModules, path_get_extension( filename ) );
if ( table != 0 ) {
@@ -143,7 +144,11 @@ static void InitPakFile( ArchiveModules& archiveModules, const char *filename ){
entry.is_pakfile = true;
g_archives.push_back( entry );
globalOutputStream() << " pak file: " << filename << "\n";
return entry.archive;
}
return 0;
}
inline void pathlist_prepend_unique( GSList*& pathlist, char* path ){
@@ -276,6 +281,219 @@ bool operator()( const CopiedString& self, const CopiedString& other ) const {
typedef std::set<CopiedString, PakLess> Archives;
Archive* AddPk3Dir( const char* fullpath ){
if ( g_numDirs == VFS_MAXDIRS ) return 0;
strncpy( g_strDirs[g_numDirs], fullpath, PATH_MAX );
g_strDirs[g_numDirs][PATH_MAX] = '\0';
g_numDirs++;
{
archive_entry_t entry;
entry.name = fullpath;
entry.archive = OpenArchive( fullpath );
entry.is_pakfile = false;
g_archives.push_back( entry );
return entry.archive;
}
}
// for unvanquished
bool IsUnvanquished(){
return strncmp( GlobalRadiant().getGameFile(), "unvanquished", 12 ) == 0;
}
struct pakfile_path_t
{
CopiedString fullpath; // full pak dir or pk3dir name
bool is_pakfile; // defines is it .pk3dir or .pk3 file
};
typedef std::pair<CopiedString, pakfile_path_t> PakfilePathsKV;
typedef std::map<CopiedString, pakfile_path_t> PakfilePaths; // key must have no extension, only name
static PakfilePaths g_pakfile_paths;
void AddUnvPak( const char* name, const char* fullpath, bool is_pakfile ){
pakfile_path_t pakfile_path;
pakfile_path.fullpath = fullpath;
pakfile_path.is_pakfile = is_pakfile;
g_pakfile_paths.insert( PakfilePathsKV( name, pakfile_path ) );
}
// Comparaison function for version numbers
// Implementation is based on dpkg's version comparison code (verrevcmp() and order())
// http://anonscm.debian.org/gitweb/?p=dpkg/dpkg.git;a=blob;f=lib/dpkg/version.c;hb=74946af470550a3295e00cf57eca1747215b9311
static int char_weight(char c){
if (std::isdigit(c))
return 0;
else if (std::isalpha(c))
return c;
else if (c == '~')
return -1;
else if (c)
return c + 256;
else
return 0;
}
static int VersionCmp(const char* a, const char* b){
while (*a || *b) {
int firstDiff = 0;
while ((*a && !std::isdigit(*a)) || (*b && !std::isdigit(*b))) {
int ac = char_weight(*a);
int bc = char_weight(*b);
if (ac != bc)
return ac - bc;
a++;
b++;
}
while (*a == '0')
a++;
while (*b == '0')
b++;
while (std::isdigit(*a) && std::isdigit(*b)) {
if (firstDiff == 0)
firstDiff = *a - *b;
a++;
b++;
}
if (std::isdigit(*a))
return 1;
if (std::isdigit(*b))
return -1;
if (firstDiff)
return firstDiff;
}
return false;
}
// takes name without ext, returns without ext
static const char* GetLatestVersionOfUnvPak( const char* name ){
const char* maxversion = 0;
const char* result = 0;
const char* pakname;
const char* pakversion;
int namelen = string_length( name );
for ( PakfilePaths::iterator i = g_pakfile_paths.begin(); i != g_pakfile_paths.end(); ++i )
{
pakname = i->first.c_str();
if ( strncmp( pakname, name, namelen ) != 0 || pakname[namelen] != '_' ) continue;
pakversion = pakname + (namelen + 1);
if ( maxversion == 0 || VersionCmp( pakversion, maxversion ) > 0 ){
maxversion = pakversion;
result = pakname;
}
}
return result;
}
// release string after using
static char* GetCurrentMapPakName(){
char* mapdir;
char* mapname;
int mapnamelen;
char* result = 0;
mapname = string_clone( GlobalRadiant().getMapName() );
mapnamelen = string_length( mapname );
mapdir = strrchr( mapname, '/' );
if ( mapdir ) {
mapdir -= 12;
if ( strncmp( mapdir, ".pk3dir/maps/", 13 ) == 0 ) {
*mapdir = '\0';
mapdir = strrchr( mapname, '/' );
if ( mapdir ) mapdir++;
else mapdir = mapname;
result = string_clone( mapdir );
}
}
string_release( mapname, mapnamelen );
return result;
}
// prevent loading duplicates or circular references
static Archives g_loaded_unv_paks;
// actual pak adding on initialise, deferred from InitDirectory
// Unvanquished filesystem doesn't need load all paks it finds
static void LoadPakWithDeps( const char* pakname ){
const char* und = strrchr( pakname, '_' );
if ( !und ) pakname = GetLatestVersionOfUnvPak( pakname );
if ( !pakname || g_loaded_unv_paks.find( pakname ) != g_loaded_unv_paks.end() ) return;
PakfilePaths::iterator i = g_pakfile_paths.find( pakname );
if ( i == g_pakfile_paths.end() ) return;
Archive* arc;
if ( i->second.is_pakfile ){
arc = InitPakFile( FileSystemQ3API_getArchiveModules(), i->second.fullpath.c_str() );
} else {
arc = AddPk3Dir( i->second.fullpath.c_str() );
}
g_loaded_unv_paks.insert( pakname );
ArchiveTextFile* depsFile = arc->openTextFile( "DEPS" );
if ( !depsFile ) return;
{
TextLinesInputStream<TextInputStream> istream = depsFile->getInputStream();
CopiedString line;
const char* c;
const char* p_name;
const char* p_name_end;
const char* p_version;
const char* p_version_end;
while ( line = istream.readLine(), string_length( line.c_str() ) ) {
c = line.c_str();
while ( std::isspace( *c ) && *c != '\0' ) ++c;
p_name = c;
while ( !std::isspace( *c ) && *c != '\0' ) ++c;
p_name_end = c;
while ( std::isspace( *c ) && *c != '\0' ) ++c;
p_version = c;
while ( !std::isspace( *c ) && *c != '\0' ) ++c;
p_version_end = c;
if ( p_name_end - p_name == 0 ) continue;
if ( p_version_end - p_version == 0 ) {
const char* p_pakname;
CopiedString name_final = CopiedString( StringRange( p_name, p_name_end ) );
p_pakname = GetLatestVersionOfUnvPak( name_final.c_str() );
if ( !p_pakname ) continue;
LoadPakWithDeps( p_pakname );
} else {
int len = ( p_name_end - p_name ) + ( p_version_end - p_version ) + 1;
char* p_pakname = string_new( len );
strncpy( p_pakname, p_name, p_name_end - p_name );
p_pakname[ p_name_end - p_name ] = '\0';
strcat( p_pakname, "_" );
strncat( p_pakname, p_version, p_version_end - p_version );
LoadPakWithDeps( p_pakname );
string_release( p_pakname, len );
}
}
}
depsFile->release();
}
// end for unvanquished
// =============================================================================
// Global functions
@@ -337,15 +555,19 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
}
if ( g_bUsePak ) {
GDir* dir = g_dir_open( path, 0, 0 );
if ( dir != 0 ) {
globalOutputStream() << "vfs directory: " << path << "\n";
bool unv;
unv = IsUnvanquished();
const char* ignore_prefix = "";
const char* override_prefix = "";
{
if ( !unv ) {
// See if we are in "sp" or "mp" mapping mode
const char* gamemode = gamemode_get();
@@ -381,23 +603,19 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
}
const char *ext = strrchr( name, '.' );
char tmppath[PATH_MAX];
if ( ext && !string_compare_nocase_upper( ext, ".pk3dir" ) ) {
if ( g_numDirs == VFS_MAXDIRS ) {
continue;
}
snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s%s/", path, name );
g_strDirs[g_numDirs][PATH_MAX] = '\0';
FixDOSName( g_strDirs[g_numDirs] );
AddSlash( g_strDirs[g_numDirs] );
g_numDirs++;
{
archive_entry_t entry;
entry.name = g_strDirs[g_numDirs - 1];
entry.archive = OpenArchive( g_strDirs[g_numDirs - 1] );
entry.is_pakfile = false;
g_archives.push_back( entry );
snprintf( tmppath, PATH_MAX, "%s%s/", path, name );
tmppath[PATH_MAX] = '\0';
FixDOSName( tmppath );
AddSlash( tmppath );
if ( unv ) {
AddUnvPak( CopiedString( StringRange( name, ext ) ).c_str(), tmppath, false );
} else {
AddPk3Dir( tmppath );
}
}
@@ -410,7 +628,11 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
continue;
}
if ( !string_empty( override_prefix ) && strncmp( name, override_prefix, strlen( override_prefix ) ) == 0 ) {
archivesOverride.insert( name );
if ( unv ) {
archives.insert( name );
} else {
archivesOverride.insert( name );
}
continue;
}
@@ -420,19 +642,29 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
g_dir_close( dir );
// add the entries to the vfs
for ( Archives::iterator i = archivesOverride.begin(); i != archivesOverride.end(); ++i )
{
char filename[PATH_MAX];
strcpy( filename, path );
strcat( filename, ( *i ).c_str() );
InitPakFile( archiveModules, filename );
}
for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i )
{
char filename[PATH_MAX];
strcpy( filename, path );
strcat( filename, ( *i ).c_str() );
InitPakFile( archiveModules, filename );
char* fullpath;
if ( unv ) {
for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i ) {
const char* name = i->c_str();
const char* ext = strrchr( name, '.' );
CopiedString name_final = CopiedString( StringRange( name, ext ) );
fullpath = string_new_concat( path, name );
AddUnvPak( name_final.c_str(), fullpath, true );
string_release( fullpath, string_length( fullpath ) );
}
} else {
for ( Archives::iterator i = archivesOverride.begin(); i != archivesOverride.end(); ++i )
{
fullpath = string_new_concat( path, i->c_str() );
InitPakFile( archiveModules, fullpath );
string_release( fullpath, string_length( fullpath ) );
}
for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i )
{
fullpath = string_new_concat( path, i->c_str() );
InitPakFile( archiveModules, fullpath );
string_release( fullpath, string_length( fullpath ) );
}
}
}
else
@@ -454,6 +686,9 @@ void Shutdown(){
g_numDirs = 0;
g_numForbiddenDirs = 0;
g_pakfile_paths.clear();
g_loaded_unv_paks.clear();
}
#define VFS_SEARCH_PAK 0x1
@@ -587,6 +822,25 @@ void initDirectory( const char *path ){
InitDirectory( path, FileSystemQ3API_getArchiveModules() );
}
void initialise(){
if ( IsUnvanquished() ) {
const char* pakname;
g_loaded_unv_paks.clear();
pakname = GetLatestVersionOfUnvPak( "tex-common" );
if ( pakname ) LoadPakWithDeps( pakname );
pakname = GetLatestVersionOfUnvPak( "radiant" );
if ( pakname ) LoadPakWithDeps( pakname );
pakname = GetCurrentMapPakName();
if ( pakname && !string_empty( pakname ) ) {
LoadPakWithDeps( pakname );
}
g_pakfile_paths.clear();
g_loaded_unv_paks.clear();
}
globalOutputStream() << "filesystem initialised\n";
g_observers.realise();
}
@@ -692,14 +946,15 @@ void forEachArchive( const ArchiveNameCallback& callback, bool pakonly, bool rev
}
};
Quake3FileSystem g_Quake3FileSystem;
VirtualFileSystem& GetFileSystem(){
return g_Quake3FileSystem;
}
void FileSystem_Init(){
}
void FileSystem_Shutdown(){
}
VirtualFileSystem& GetFileSystem(){
return g_Quake3FileSystem;
}
+6 -6
View File
@@ -336,14 +336,14 @@ EclassManagerAPI(){
m_eclassmanager.realise = &EntityClass_realise;
m_eclassmanager.unrealise = &EntityClass_unrealise;
GlobalRadiant().attachGameToolsPathObserver( g_EntityClassQuake3 );
GlobalRadiant().attachGameModeObserver( g_EntityClassQuake3 );
GlobalRadiant().attachGameNameObserver( g_EntityClassQuake3 );
Radiant_attachGameToolsPathObserver( g_EntityClassQuake3 );
Radiant_attachGameModeObserver( g_EntityClassQuake3 );
Radiant_attachGameNameObserver( g_EntityClassQuake3 );
}
~EclassManagerAPI(){
GlobalRadiant().detachGameNameObserver( g_EntityClassQuake3 );
GlobalRadiant().detachGameModeObserver( g_EntityClassQuake3 );
GlobalRadiant().detachGameToolsPathObserver( g_EntityClassQuake3 );
Radiant_detachGameNameObserver( g_EntityClassQuake3 );
Radiant_detachGameModeObserver( g_EntityClassQuake3 );
Radiant_detachGameToolsPathObserver( g_EntityClassQuake3 );
EntityClassQuake3_destroy();
}
+5 -4
View File
@@ -28,6 +28,7 @@
#include "ifilesystem.h"
#include "iscriplib.h"
#include "qerplugin.h"
#include "mainframe.h"
#include "string/string.h"
#include "eclasslib.h"
@@ -699,12 +700,12 @@ EntityClassFGDAPI(){
m_eclassmanager.realise = &EntityClassFGD_realise;
m_eclassmanager.unrealise = &EntityClassFGD_unrealise;
GlobalRadiant().attachGameToolsPathObserver( g_EntityClassFGD );
GlobalRadiant().attachGameNameObserver( g_EntityClassFGD );
Radiant_attachGameToolsPathObserver( g_EntityClassFGD );
Radiant_attachGameNameObserver( g_EntityClassFGD );
}
~EntityClassFGDAPI(){
GlobalRadiant().detachGameNameObserver( g_EntityClassFGD );
GlobalRadiant().detachGameToolsPathObserver( g_EntityClassFGD );
Radiant_detachGameNameObserver( g_EntityClassFGD );
Radiant_detachGameToolsPathObserver( g_EntityClassFGD );
EntityClassFGD_destroy();
}
+22 -10
View File
@@ -146,22 +146,33 @@ glwindow_globals_t g_glwindow_globals;
// VFS
bool g_vfsInitialized = false;
void VFS_Init(){
if ( g_vfsInitialized ) return;
QE_InitVFS();
GlobalFileSystem().initialise();
g_vfsInitialized = true;
}
void VFS_Shutdown(){
if ( !g_vfsInitialized ) return;
GlobalFileSystem().shutdown();
g_vfsInitialized = false;
}
void VFS_Restart(){
VFS_Shutdown();
VFS_Init();
}
class VFSModuleObserver : public ModuleObserver
{
std::size_t m_unrealised;
public:
VFSModuleObserver() : m_unrealised( 1 ){
}
void realise(){
if ( --m_unrealised == 0 ) {
QE_InitVFS();
GlobalFileSystem().initialise();
}
VFS_Init();
}
void unrealise(){
if ( ++m_unrealised == 1 ) {
GlobalFileSystem().shutdown();
}
VFS_Shutdown();
}
};
@@ -513,6 +524,7 @@ void gamemode_set( const char* gamemode ){
}
}
#include "os/dir.h"
class CLoadModule
+1 -2
View File
@@ -245,8 +245,7 @@ void gamemode_set( const char* gamemode );
void Radiant_attachGameModeObserver( ModuleObserver& observer );
void Radiant_detachGameModeObserver( ModuleObserver& observer );
void VFS_Restart();
void VFS_Construct();
void VFS_Destroy();
+31
View File
@@ -38,6 +38,7 @@ MapModules& ReferenceAPI_getMapModules();
#include "ifilesystem.h"
#include "namespace.h"
#include "moduleobserver.h"
#include "moduleobservers.h"
#include <set>
@@ -404,6 +405,31 @@ float g_MinWorldCoord = -64 * 1024;
void AddRegionBrushes( void );
void RemoveRegionBrushes( void );
/* Map open/close observers */
ModuleObservers g_mapPathObservers;
class UnvanquishedMapFileObserver : public ModuleObserver
{
void realise() {
if ( strncmp( GlobalRadiant().getGameFile(), "unvanquished", 12 ) == 0 ) {
// Restart VFS to apply new pak filtering based on mapname
VFS_Restart();
}
}
void unrealise() { }
};
UnvanquishedMapFileObserver g_unvanquishedMapFileObserver;
void BindMapFileObservers(){
g_mapPathObservers.attach( g_unvanquishedMapFileObserver );
}
void UnBindMapFileObservers(){
g_mapPathObservers.detach( g_unvanquishedMapFileObserver );
}
/*
================
@@ -422,6 +448,7 @@ void Map_Free(){
g_currentMap = 0;
Brush_unlatchPreferences();
g_mapPathObservers.unrealise();
}
class EntityFindByClassname : public scene::Graph::Walker
@@ -946,6 +973,7 @@ void Map_LoadFile( const char *filename ){
}
Brush_toggleFormat( i );
g_map.m_name = filename;
g_mapPathObservers.realise();
Map_UpdateTitle( g_map );
g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
if ( format ) {
@@ -1177,10 +1205,12 @@ void Map_RenameAbsolute( const char* absolute ){
g_map.m_resource->detach( g_map );
GlobalReferenceCache().release( g_map.m_name.c_str() );
g_mapPathObservers.unrealise();
g_map.m_resource = resource;
g_map.m_name = absolute;
g_mapPathObservers.realise();
Map_UpdateTitle( g_map );
g_map.m_resource->attach( g_map );
@@ -1218,6 +1248,7 @@ void Map_New(){
//globalOutputStream() << "Map_New\n";
g_map.m_name = "unnamed.map";
g_mapPathObservers.realise();
Map_UpdateTitle( g_map );
{
+3
View File
@@ -162,4 +162,7 @@ void Map_mergeClonedNames();
const char* getMapsPath();
void BindMapFileObservers();
void UnBindMapFileObservers();
#endif
+7 -9
View File
@@ -113,6 +113,10 @@ const char* TextureBrowser_getSelectedShader(){
return TextureBrowser_GetSelectedShader( GlobalTextureBrowser() );
}
const char* getGameFile(){
return g_GamesDialog.m_sGameFile.c_str();
}
class RadiantCoreAPI
{
_QERFuncTable_1 m_radiantcore;
@@ -128,6 +132,7 @@ RadiantCoreAPI(){
m_radiantcore.getSettingsPath = &SettingsPath_get;
m_radiantcore.getMapsPath = &getMapsPath;
m_radiantcore.getGameFile = &getGameFile;
m_radiantcore.getGameName = &gamename_get;
m_radiantcore.getGameMode = &gamemode_get;
@@ -138,15 +143,6 @@ RadiantCoreAPI(){
m_radiantcore.getGameDescriptionKeyValue = &GameDescription_getKeyValue;
m_radiantcore.getRequiredGameDescriptionKeyValue = &GameDescription_getRequiredKeyValue;
m_radiantcore.attachGameToolsPathObserver = Radiant_attachGameToolsPathObserver;
m_radiantcore.detachGameToolsPathObserver = Radiant_detachGameToolsPathObserver;
m_radiantcore.attachEnginePathObserver = Radiant_attachEnginePathObserver;
m_radiantcore.detachEnginePathObserver = Radiant_detachEnginePathObserver;
m_radiantcore.attachGameNameObserver = Radiant_attachGameNameObserver;
m_radiantcore.detachGameNameObserver = Radiant_detachGameNameObserver;
m_radiantcore.attachGameModeObserver = Radiant_attachGameModeObserver;
m_radiantcore.detachGameModeObserver = Radiant_detachGameModeObserver;
m_radiantcore.XYWindowDestroyed_connect = XYWindowDestroyed_connect;
m_radiantcore.XYWindowDestroyed_disconnect = XYWindowDestroyed_disconnect;
m_radiantcore.XYWindowMouseDown_connect = XYWindowMouseDown_connect;
@@ -254,10 +250,12 @@ Radiant(){
MapRoot_construct();
EnginePath_verify();
BindMapFileObservers();
EnginePath_Realise();
}
~Radiant(){
EnginePath_Unrealise();
UnBindMapFileObservers();
MapRoot_destroy();
NullModel_destroy();