]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/map.cpp
radiant: replace StringBuffer with std::string
[xonotic/netradiant.git] / radiant / map.cpp
index 5523f431c60a114de44e52e5e372a9fb8fe04d04..36b77ae1b828478767148f91738b156287139b5f 100644 (file)
@@ -26,7 +26,9 @@
 #include "debugging/debugging.h"
 
 #include "imap.h"
+
 MapModules& ReferenceAPI_getMapModules();
+
 #include "iselection.h"
 #include "iundo.h"
 #include "ibrush.h"
@@ -57,6 +59,7 @@ MapModules& ReferenceAPI_getMapModules();
 #include "cmdlib.h"
 #include "stream/textfilestream.h"
 #include "os/path.h"
+#include "os/file.h"
 #include "uniquenames.h"
 #include "modulesystem/singletonmodule.h"
 #include "modulesystem/moduleregistry.h"
@@ -84,6 +87,8 @@ MapModules& ReferenceAPI_getMapModules();
 #include "brushmodule.h"
 #include "brush.h"
 
+bool g_writeMapComments = true;
+
 class NameObserver
 {
 UniqueNames& m_names;
@@ -95,6 +100,7 @@ void construct(){
                m_names.insert( name_read( c_str() ) );
        }
 }
+
 void destroy(){
        if ( !empty() ) {
                //globalOutputStream() << "destroy " << makeQuoted(c_str()) << "\n";
@@ -103,6 +109,7 @@ void destroy(){
 }
 
 NameObserver& operator=( const NameObserver& other );
+
 public:
 NameObserver( UniqueNames& names ) : m_names( names ){
        construct();
@@ -110,20 +117,25 @@ NameObserver( UniqueNames& names ) : m_names( names ){
 NameObserver( const NameObserver& other ) : m_names( other.m_names ), m_name( other.m_name ){
        construct();
 }
+
 ~NameObserver(){
        destroy();
 }
+
 bool empty() const {
        return string_empty( c_str() );
 }
+
 const char* c_str() const {
        return m_name.c_str();
 }
+
 void nameChanged( const char* name ){
        destroy();
        m_name = name;
        construct();
 }
+
 typedef MemberCaller<NameObserver, void(const char*), &NameObserver::nameChanged> NameChangedCaller;
 };
 
@@ -136,12 +148,14 @@ public:
 ~BasicNamespace(){
        ASSERT_MESSAGE( m_names.empty(), "namespace: names still registered at shutdown" );
 }
+
 void attach( const NameCallback& setName, const NameCallbackCallback& attachObserver ){
        std::pair<Names::iterator, bool> result = m_names.insert( Names::value_type( setName, m_uniqueNames ) );
        ASSERT_MESSAGE( result.second, "cannot attach name" );
        attachObserver( NameObserver::NameChangedCaller( ( *result.first ).second ) );
        //globalOutputStream() << "attach: " << reinterpret_cast<const unsigned int&>(setName) << "\n";
 }
+
 void detach( const NameCallback& setName, const NameCallbackCallback& detachObserver ){
        Names::iterator i = m_names.find( setName );
        ASSERT_MESSAGE( i != m_names.end(), "cannot detach name" );
@@ -196,11 +210,13 @@ class NamespaceAPI
 Namespace* m_namespace;
 public:
 typedef Namespace Type;
+
 STRING_CONSTANT( Name, "*" );
 
 NamespaceAPI(){
        m_namespace = &g_defaultNamespace;
 }
+
 Namespace* getTable(){
        return m_namespace;
 }
@@ -258,6 +274,7 @@ public:
 WorldNode()
        : m_node( 0 ){
 }
+
 void set( scene::Node* node ){
        if ( m_node != 0 ) {
                m_node->DecRef();
@@ -267,6 +284,7 @@ void set( scene::Node* node ){
                m_node->IncRef();
        }
 }
+
 scene::Node* get() const {
        return m_node;
 }
@@ -274,7 +292,9 @@ scene::Node* get() const {
 
 class Map;
 void Map_SetValid( Map& map, bool valid );
+
 void Map_UpdateTitle( const Map& map );
+
 void Map_SetWorldspawn( Map& map, scene::Node* node );
 
 
@@ -286,6 +306,7 @@ Resource* m_resource;
 bool m_valid;
 
 bool m_modified;
+
 void ( *m_modified_changed )( const Map& );
 
 Signal0 m_mapValidCallbacks;
@@ -316,6 +337,7 @@ void realise(){
                Map_SetValid( g_map, true );
        }
 }
+
 void unrealise(){
        if ( m_resource != 0 ) {
                Map_SetValid( g_map, false );
@@ -383,7 +405,6 @@ void Map_UpdateTitle( const Map& map ){
 }
 
 
-
 scene::Node* Map_GetWorldspawn( const Map& map ){
        return map.m_world_node.get();
 }
@@ -399,8 +420,8 @@ float g_MaxWorldCoord = 64 * 1024;
 float g_MinWorldCoord = -64 * 1024;
 
 void AddRegionBrushes( void );
-void RemoveRegionBrushes( void );
 
+void RemoveRegionBrushes( void );
 
 
 /*
@@ -430,6 +451,7 @@ public:
 EntityFindByClassname( const char* name, Entity*& entity ) : m_name( name ), m_entity( entity ){
        m_entity = 0;
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( m_entity == 0 ) {
                Entity* entity = Node_getEntity( path.top() );
@@ -539,6 +561,7 @@ UnsortedNodeSet& m_nodes;
 public:
 CollectAllWalker( scene::Node& root, UnsortedNodeSet& nodes ) : m_root( root ), m_nodes( nodes ){
 }
+
 bool pre( scene::Node& node ) const {
        m_nodes.insert( NodeSmartReference( node ) );
        Node_getTraversable( m_root )->erase( node );
@@ -582,12 +605,14 @@ public:
 MapMergeAll( const scene::Path& root )
        : m_path( root ){
 }
+
 bool pre( scene::Node& node ) const {
        Node_getTraversable( m_path.top() )->insert( node );
        m_path.push( makeReference( node ) );
        selectPath( m_path, true );
        return false;
 }
+
 void post( scene::Node& node ) const {
        m_path.pop();
 }
@@ -600,6 +625,7 @@ public:
 MapMergeEntities( const scene::Path& root )
        : m_path( root ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( node_is_worldspawn( node ) ) {
                scene::Node* world_node = Map_FindWorldspawn( g_map );
@@ -629,6 +655,7 @@ bool pre( scene::Node& node ) const {
        }
        return false;
 }
+
 void post( scene::Node& node ) const {
        m_path.pop();
 }
@@ -643,6 +670,7 @@ public:
 TypeCasts(){
        NodeContainedCast<BasicContainer, scene::Traversable>::install( m_casts );
 }
+
 NodeTypeCastTable& get(){
        return m_casts;
 }
@@ -660,9 +688,11 @@ scene::Traversable& get( NullType<scene::Traversable>){
 
 BasicContainer() : m_node( this, this, StaticTypeCasts::instance().get() ){
 }
+
 void release(){
        delete this;
 }
+
 scene::Node& node(){
        return m_node;
 }
@@ -672,6 +702,7 @@ scene::Node& node(){
 void MergeMap( scene::Node& node ){
        Node_getTraversable( node )->traverse( MapMergeEntities( scene::Path( makeReference( GlobalSceneGraph().root() ) ) ) );
 }
+
 void Map_ImportSelected( TextInputStream& in, const MapFormat& format ){
        NodeSmartReference node( ( new BasicContainer )->node() );
        format.readGraph( node, in, GlobalEntityCreator() );
@@ -700,6 +731,7 @@ public:
 CloneAll( scene::Node& root )
        : m_path( makeReference( root ) ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( node.isRoot() ) {
                return false;
@@ -710,6 +742,7 @@ bool pre( scene::Node& node ) const {
 
        return true;
 }
+
 void post( scene::Node& node ) const {
        if ( node.isRoot() ) {
                return;
@@ -741,14 +774,17 @@ public:
 EntityBreakdownWalker( EntityBreakdown& entitymap )
        : m_entitymap( entitymap ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        Entity* entity = Node_getEntity( path.top() );
        if ( entity != 0 ) {
                const EntityClass& eclass = entity->getEntityClass();
                if ( m_entitymap.find( eclass.name() ) == m_entitymap.end() ) {
                        m_entitymap[eclass.name()] = 1;
+               } else
+               {
+                       ++m_entitymap[eclass.name()];
                }
-               else{ ++m_entitymap[eclass.name()]; }
        }
        return true;
 }
@@ -833,9 +869,9 @@ void DoMapInfo(){
                        vbox.pack_start( scr, TRUE, TRUE, 0 );
 
                        {
-                               ui::ListStore store = ui::ListStore(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ));
+                               auto store = ui::ListStore::from(gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ));
 
-                               auto view = ui::TreeView(ui::TreeModel(store ));
+                               auto view = ui::TreeView(ui::TreeModel::from(store._handle));
                                gtk_tree_view_set_headers_clickable(view, TRUE );
 
                                {
@@ -902,13 +938,14 @@ ScopeTimer( const char* message )
        : m_message( message ){
        m_timer.start();
 }
+
 ~ScopeTimer(){
        double elapsed_time = m_timer.elapsed_msec() / 1000.f;
        globalOutputStream() << m_message << " timer: " << FloatFormat( elapsed_time, 5, 2 ) << " second(s) elapsed\n";
 }
 };
 
-CopiedString g_strLastFolder = "";
+CopiedString g_strLastMapFolder = "";
 
 /*
    ================
@@ -917,11 +954,17 @@ CopiedString g_strLastFolder = "";
  */
 
 void Map_LoadFile( const char *filename ){
+       g_map.m_name = filename;
+
+       // refresh VFS to apply new pak filtering based on mapname
+       // needed for daemon DPK VFS
+       VFS_Refresh();
+
        globalOutputStream() << "Loading map from " << filename << "\n";
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
 
        MRU_AddFile( filename );
-       g_strLastFolder = g_path_get_dirname( filename );
+       g_strLastMapFolder = g_path_get_dirname( filename );
 
        {
                ScopeTimer timer( "map load" );
@@ -938,8 +981,8 @@ void Map_LoadFile( const char *filename ){
                                Map_Free();
                        }
                        Brush_toggleFormat( i );
-                       g_map.m_name = filename;
                        Map_UpdateTitle( g_map );
+
                        g_map.m_resource = GlobalReferenceCache().capture( g_map.m_name.c_str() );
                        if ( format ) {
                                format->wrongFormat = false;
@@ -969,10 +1012,6 @@ void Map_LoadFile( const char *filename ){
        Map_StartPosition();
 
        g_currentMap = &g_map;
-
-       // restart VFS to apply new pak filtering based on mapname
-       // needed for daemon DPK VFS
-       VFS_Restart();
 }
 
 class Excluder
@@ -990,6 +1029,7 @@ public:
 ExcludeWalker( const scene::Traversable::Walker& walker, const Excluder& exclude )
        : m_walker( walker ), m_exclude( &exclude ), m_skip( false ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( m_exclude->excluded( node ) || node.isRoot() ) {
                m_skip = true;
@@ -1001,6 +1041,7 @@ bool pre( scene::Node& node ) const {
        }
        return true;
 }
+
 void post( scene::Node& node ) const {
        if ( m_skip ) {
                m_skip = false;
@@ -1019,6 +1060,7 @@ public:
 AnyInstanceSelected( bool& selected ) : m_selected( selected ){
        m_selected = false;
 }
+
 void visit( scene::Instance& instance ) const {
        Selectable* selectable = Instance_getSelectable( instance );
        if ( selectable != 0
@@ -1080,10 +1122,12 @@ mutable bool m_skip;
 bool selectedParent() const {
        return m_selected != 0;
 }
+
 public:
 IncludeSelectedWalker( const scene::Traversable::Walker& walker )
        : m_walker( walker ), m_selected( 0 ), m_skip( false ){
 }
+
 bool pre( scene::Node& node ) const {
        // include node if:
        // node is not a 'root' AND ( node is selected OR any child of node is selected OR any parent of node is selected )
@@ -1100,6 +1144,7 @@ bool pre( scene::Node& node ) const {
                return false;
        }
 }
+
 void post( scene::Node& node ) const {
        if ( m_skip ) {
                m_skip = false;
@@ -1126,7 +1171,7 @@ void Map_Traverse_Selected( scene::Node& root, const scene::Traversable::Walker&
 }
 
 void Map_ExportSelected( TextOutputStream& out, const MapFormat& format ){
-       format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out );
+       format.writeGraph( GlobalSceneGraph().root(), Map_Traverse_Selected, out, g_writeMapComments );
 }
 
 void Map_Traverse( scene::Node& root, const scene::Traversable::Walker& walker ){
@@ -1325,6 +1370,7 @@ public:
 ExcludeAllWalker( bool exclude )
        : m_exclude( exclude ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        exclude_node( path.top(), m_exclude );
 
@@ -1348,6 +1394,7 @@ public:
 ExcludeSelectedWalker( bool exclude )
        : m_exclude( exclude ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        exclude_node( path.top(), ( instance.isSelected() || instance.childSelected() || instance.parentSelected() ) == m_exclude );
        return true;
@@ -1365,6 +1412,7 @@ public:
 ExcludeRegionedWalker( bool exclude )
        : m_exclude( exclude ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        exclude_node(
                path.top(),
@@ -1482,7 +1530,7 @@ void Map_RegionBrush( void ){
 bool Map_ImportFile( const char* filename ){
        ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Map" );
 
-       g_strLastFolder = g_path_get_dirname( filename );
+       g_strLastMapFolder = g_path_get_dirname( filename );
 
        bool success = false;
 
@@ -1527,37 +1575,58 @@ bool Map_ImportFile( const char* filename ){
 
 tryDecompile:
 
-       const char *type = GlobalRadiant().getRequiredGameDescriptionKeyValue( "q3map2_type" );
+       const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
        int n = string_length( path_get_extension( filename ) );
        if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
-               StringBuffer output;
-               output.push_string( AppPath_get() );
-               output.push_string( "q3map2." );
-               output.push_string( RADIANT_EXECUTABLE );
-               output.push_string( " -v -game " );
-               output.push_string( ( type && *type ) ? type : "quake3" );
-               output.push_string( " -fs_basepath \"" );
-               output.push_string( EnginePath_get() );
-               output.push_string( "\" -fs_homepath \"" );
-               output.push_string( g_qeglobals.m_userEnginePath.c_str() );
-               output.push_string( "\" -fs_game " );
-               output.push_string( gamename_get() );
-               output.push_string( " -convert -format " );
-               output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
+               std::string output;
+               output += AppPath_get();
+               output += "q3map2";
+               output += GDEF_OS_EXE_EXT;
+
+               output += " -v -game ";
+               output += ( type && *type ) ? type : "quake3";
+               output += " -fs_basepath \"";
+               output += EnginePath_get();
+               output += "\" -fs_homepath \"";
+               output += g_qeglobals.m_userEnginePath.c_str();
+               output += "\"";
+
+               // extra pakpaths
+               for ( int i = 0; i < g_pakPathCount; i++ ) {
+                       if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) {
+                               output += " -fs_pakpath \"";
+                               output += g_strPakPath[i].c_str();
+                               output += "\"";
+                       }
+               }
+
+               // extra switches
+               if ( g_disableEnginePath ) {
+                       output += " -fs_nobasepath ";
+               }
+
+               if ( g_disableHomePath ) {
+                       output += " -fs_nohomepath ";
+               }
+
+               output += " -fs_game ";
+               output += gamename_get();
+               output += " -convert -format ";
+               output += Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map";
                if ( extension_equal( path_get_extension( filename ), "map" ) ) {
-                       output.push_string( " -readmap " );
+                       output += " -readmap ";
                }
-               output.push_string( " \"" );
-               output.push_string( filename );
-               output.push_string( "\"" );
+               output += " \"";
+               output += filename;
+               output += "\"";
 
                // run
                Q_Exec( NULL, output.c_str(), NULL, false, true );
 
                // rebuild filename as "filenamewithoutext_converted.map"
-               output.clear();
-               output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
-               output.push_string( "_converted.map" );
+               output = "";
+               output.append( filename, string_length( filename ) - ( n + 1 ) );
+               output += "_converted.map";
                filename = output.c_str();
 
                // open
@@ -1614,6 +1683,7 @@ scene::Node& m_parent;
 public:
 ParentSelectedBrushesToEntityWalker( scene::Node& parent ) : m_parent( parent ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.top().get_pointer() != &m_parent
                 && Node_isPrimitive( path.top() ) ) {
@@ -1626,6 +1696,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const {
        }
        return true;
 }
+
 void post( const scene::Path& path, scene::Instance& instance ) const {
        if ( path.top().get_pointer() != &m_parent
                 && Node_isPrimitive( path.top() ) ) {
@@ -1656,6 +1727,7 @@ public:
 CountSelectedBrushes( std::size_t& count ) : m_count( count ), m_depth( 0 ){
        m_count = 0;
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( ++m_depth != 1 && path.top().get().isRoot() ) {
                return false;
@@ -1668,6 +1740,7 @@ bool pre( const scene::Path& path, scene::Instance& instance ) const {
        }
        return true;
 }
+
 void post( const scene::Path& path, scene::Instance& instance ) const {
        --m_depth;
 }
@@ -1754,6 +1827,7 @@ void Scene_parentSelected(){
 public:
                ParentSelectedBrushesToEntityWalker( const scene::Path& parent ) : m_parent( parent ){
                }
+
                void visit( scene::Instance& instance ) const {
                        if ( &m_parent != &instance.path() ) {
                                Path_parent( m_parent, instance.path() );
@@ -1771,7 +1845,6 @@ public:
 }
 
 
-
 void NewMap(){
        if ( ConfirmModified( "New Map" ) ) {
                Map_RegionOff();
@@ -1786,26 +1859,32 @@ const char* getMapsPath(){
        return g_mapsPath.c_str();
 }
 
-const char* getLastFolderPath(){
-       if (g_strLastFolder.empty()) {
-               GlobalPreferenceSystem().registerPreference( "LastFolder", CopiedStringImportStringCaller( g_strLastFolder ), CopiedStringExportStringCaller( g_strLastFolder ) );
-               if (g_strLastFolder.empty()) {
-                       g_strLastFolder = g_qeglobals.m_userGamePath;
+const char* getLastMapFolderPath(){
+       if (g_strLastMapFolder.empty()) {
+               GlobalPreferenceSystem().registerPreference( "LastMapFolder", make_property_string( g_strLastMapFolder ) );
+               if (g_strLastMapFolder.empty()) {
+                       StringOutputStream buffer( 1024 );
+                       buffer << getMapsPath();
+                       if ( !file_readable( buffer.c_str() ) ) {
+                               buffer.clear();
+                               buffer << g_qeglobals.m_userGamePath.c_str() << "/";
+                       }
+                       g_strLastMapFolder = buffer.c_str();
                }
        }
-       return g_strLastFolder.c_str();
+       return g_strLastMapFolder.c_str();
 }
 
 const char* map_open( const char* title ){
-       return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), true, false, false );
+       return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), true, false, false );
 }
 
 const char* map_import( const char* title ){
-       return MainFrame_getWindow().file_dialog( TRUE, title, getLastFolderPath(), MapFormat::Name(), false, true, false );
+       return MainFrame_getWindow().file_dialog( TRUE, title, getLastMapFolderPath(), MapFormat::Name(), false, true, false );
 }
 
 const char* map_save( const char* title ){
-       return MainFrame_getWindow().file_dialog( FALSE, title, getLastFolderPath(), MapFormat::Name(), false, false, true );
+       return MainFrame_getWindow().file_dialog( FALSE, title, getLastMapFolderPath(), MapFormat::Name(), false, false, true );
 }
 
 void OpenMap(){
@@ -1836,7 +1915,7 @@ bool Map_SaveAs(){
        const char* filename = map_save( "Save Map" );
 
        if ( filename != NULL ) {
-               g_strLastFolder = g_path_get_dirname( filename );
+               g_strLastMapFolder = g_path_get_dirname( filename );
                MRU_AddFile( filename );
                Map_Rename( filename );
                return Map_Save();
@@ -1861,7 +1940,7 @@ void ExportMap(){
        const char* filename = map_save( "Export Selection" );
 
        if ( filename != NULL ) {
-               g_strLastFolder = g_path_get_dirname( filename );
+               g_strLastMapFolder = g_path_get_dirname( filename );
                Map_SaveSelected( filename );
        }
 }
@@ -1870,7 +1949,7 @@ void SaveRegion(){
        const char* filename = map_save( "Export Region" );
 
        if ( filename != NULL ) {
-               g_strLastFolder = g_path_get_dirname( filename );
+               g_strLastMapFolder = g_path_get_dirname( filename );
                Map_SaveRegion( filename );
        }
 }
@@ -1913,6 +1992,7 @@ public:
 BrushFindByIndexWalker( std::size_t index, scene::Path& path )
        : m_index( index ), m_path( path ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( Node_isPrimitive( node ) && m_index-- == 0 ) {
                m_path.push( makeReference( node ) );
@@ -1929,6 +2009,7 @@ public:
 EntityFindByIndexWalker( std::size_t index, scene::Path& path )
        : m_index( index ), m_path( path ){
 }
+
 bool pre( scene::Node& node ) const {
        if ( Node_isEntity( node ) && m_index-- == 0 ) {
                m_path.push( makeReference( node ) );
@@ -1977,6 +2058,7 @@ public:
 BrushFindIndexWalker( const scene::Node& node, std::size_t& count )
        : m_node( &node ), m_count( count ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( Node_isPrimitive( path.top() ) ) {
                if ( m_node == path.top().get_pointer() ) {
@@ -1998,6 +2080,7 @@ public:
 EntityFindIndexWalker( const scene::Node& node, std::size_t& count )
        : m_node( &node ), m_count( count ){
 }
+
 bool pre( const scene::Path& path, scene::Instance& instance ) const {
        if ( Node_isEntity( path.top() ) ) {
                if ( m_node == path.top().get_pointer() ) {
@@ -2102,7 +2185,8 @@ void DoFind(){
 }
 
 void Map_constructPreferences( PreferencesPage& page ){
-       page.appendCheckBox( "", "Load last map on open", g_bLoadLastMap );
+       page.appendCheckBox( "", "Load last map at startup", g_bLoadLastMap );
+       page.appendCheckBox( "", "Add entity and brush number comments on map write", g_writeMapComments );
 }
 
 
@@ -2112,6 +2196,7 @@ std::size_t m_unrealised;
 public:
 MapEntityClasses() : m_unrealised( 1 ){
 }
+
 void realise(){
        if ( --m_unrealised == 0 ) {
                if ( g_map.m_resource != 0 ) {
@@ -2120,6 +2205,7 @@ void realise(){
                }
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
                if ( g_map.m_resource != 0 ) {
@@ -2139,6 +2225,7 @@ std::size_t m_unrealised;
 public:
 MapModuleObserver() : m_unrealised( 1 ){
 }
+
 void realise(){
        if ( --m_unrealised == 0 ) {
                ASSERT_MESSAGE( !string_empty( g_qeglobals.m_userGamePath.c_str() ), "maps_directory: user-game-path is empty" );
@@ -2148,6 +2235,7 @@ void realise(){
                g_mapsPath = buffer.c_str();
        }
 }
+
 void unrealise(){
        if ( ++m_unrealised == 1 ) {
                g_mapsPath = "";
@@ -2161,16 +2249,17 @@ CopiedString g_strLastMap;
 bool g_bLoadLastMap = false;
 
 void Map_Construct(){
-       GlobalCommands_insert( "RegionOff", FreeCaller<void(), RegionOff>() );
-       GlobalCommands_insert( "RegionSetXY", FreeCaller<void(), RegionXY>() );
-       GlobalCommands_insert( "RegionSetBrush", FreeCaller<void(), RegionBrush>() );
-       GlobalCommands_insert( "RegionSetSelection", FreeCaller<void(), RegionSelected>(), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
+       GlobalCommands_insert( "RegionOff", makeCallbackF(RegionOff) );
+       GlobalCommands_insert( "RegionSetXY", makeCallbackF(RegionXY) );
+       GlobalCommands_insert( "RegionSetBrush", makeCallbackF(RegionBrush) );
+       GlobalCommands_insert( "RegionSetSelection", makeCallbackF(RegionSelected), Accelerator( 'R', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) );
 
-       GlobalPreferenceSystem().registerPreference( "LastMap", CopiedStringImportStringCaller( g_strLastMap ), CopiedStringExportStringCaller( g_strLastMap ) );
-       GlobalPreferenceSystem().registerPreference( "LoadLastMap", BoolImportStringCaller( g_bLoadLastMap ), BoolExportStringCaller( g_bLoadLastMap ) );
-       GlobalPreferenceSystem().registerPreference( "MapInfoDlg", WindowPositionImportStringCaller( g_posMapInfoWnd ), WindowPositionExportStringCaller( g_posMapInfoWnd ) );
+       GlobalPreferenceSystem().registerPreference( "LastMap", make_property_string( g_strLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "LoadLastMap", make_property_string( g_bLoadLastMap ) );
+       GlobalPreferenceSystem().registerPreference( "MapInfoDlg", make_property<WindowPosition_String>( g_posMapInfoWnd ) );
+       GlobalPreferenceSystem().registerPreference( "WriteMapComments", make_property_string( g_writeMapComments ) );
 
-       PreferencesDialog_addSettingsPreferences( FreeCaller<void(PreferencesPage&), Map_constructPreferences>() );
+       PreferencesDialog_addSettingsPreferences( makeCallbackF(Map_constructPreferences) );
 
        GlobalEntityClassManager().attach( g_MapEntityClasses );
        Radiant_attachHomePathsObserver( g_MapModuleObserver );