+// HostCache interface
+hostcache_mask_t hostcache_currentmask;
+hostcache_infofield_t hostcache_sortbyfield;
+qboolean hostcache_sortdescending;
+
+int hostcache_viewcount = 0;
+hostcache_t *hostcache_viewset[HOSTCACHE_VIEWCACHESIZE];
+
+int hostcache_cachecount;
+hostcache_t hostcache_cache[HOSTCACHE_TOTALSIZE];
+
+qboolean hostcache_consoleoutput;
+
+// helper function to insert a value into the viewset
+// spare entries will be removed
+static void _HostCache_VS_Insert( int index, hostcache_t *entry )
+{
+ int start;
+ for( start = index, index = hostcache_viewcount; index > start; index-- )
+ hostcache_viewset[index] = hostcache_viewset[index - 1];
+ if( ++hostcache_viewcount > HOSTCACHE_VIEWCACHESIZE )
+ hostcache_viewcount = HOSTCACHE_VIEWCACHESIZE;
+ hostcache_viewset[start] = entry;
+}
+
+// we suppose hostcache_viewcount to be valid, ie > 0
+static void _HostCache_VS_Remove( int index )
+{
+ for( --hostcache_viewcount; index < hostcache_viewcount; index++ )
+ hostcache_viewset[index] = hostcache_viewset[index + 1];
+}
+
+// returns true if A should be inserted before B
+static qboolean _HostCache_SortTest( hostcache_t *A, hostcache_t *B )
+{
+ int result; // > 0 if for numbers A > B and for text if A < B
+
+ if( hostcache_sortbyfield == HCIF_PING )
+ result = A->info.ping - B->info.ping;
+ else if( hostcache_sortbyfield == HCIF_MAXPLAYERS )
+ result = A->info.maxplayers - B->info.maxplayers;
+ else if( hostcache_sortbyfield == HCIF_NUMPLAYERS )
+ result = A->info.numplayers - B->info.numplayers;
+ else if( hostcache_sortbyfield == HCIF_PROTOCOL )
+ result = A->info.protocol - B->info.protocol;
+ else if( hostcache_sortbyfield == HCIF_CNAME )
+ result = strcmp( B->info.cname, A->info.cname );
+ else if( hostcache_sortbyfield == HCIF_GAME )
+ result = strcmp( B->info.game, A->info.game );
+ else if( hostcache_sortbyfield == HCIF_MAP )
+ result = strcmp( B->info.map, A->info.map );
+ else if( hostcache_sortbyfield == HCIF_MOD )
+ result = strcmp( B->info.mod, A->info.mod );
+ else if( hostcache_sortbyfield == HCIF_NAME )
+ result = strcmp( B->info.name, A->info.name );
+
+ if( result > 0 && hostcache_sortdescending)
+ return true;
+ return false;
+}
+
+static qboolean _hc_testint( int A, hostcache_infofield_t op, int B )
+{
+ int diff;
+
+ diff = A - B;
+ switch( hostcache_currentmask.pingtest ) {
+ case HCMO_GREATER:
+ if( !diff )
+ return false;
+ case HCMO_GREATEREQUAL:
+ if( diff < 0 )
+ return false;
+ break;
+ case HCMO_EQUAL:
+ if( diff )
+ return false;
+ break;
+ case HCMO_LESS:
+ if( !diff )
+ return false;
+ case HCMO_LESSEQUAL:
+ if( diff > 0 )
+ return false;
+ break;
+ }
+ return true;
+}
+
+static qboolean _HostCache_TestMask( hostcache_info_t *info )
+{
+ if( !_hc_testint( info->ping, hostcache_currentmask.pingtest, hostcache_currentmask.info.ping ) )
+ return false;
+ if( !_hc_testint( info->maxplayers, hostcache_currentmask.maxplayerstest, hostcache_currentmask.info.maxplayers ) )
+ return false;
+ if( !_hc_testint( info->numplayers, hostcache_currentmask.numplayerstest, hostcache_currentmask.info.numplayers ) )
+ return false;
+ if( !_hc_testint( info->protocol, hostcache_currentmask.protocoltest, hostcache_currentmask.info.protocol ))
+ return false;
+ if( *hostcache_currentmask.info.cname
+ && !strstr( info->cname, hostcache_currentmask.info.cname ) )
+ return false;
+ if( *hostcache_currentmask.info.game
+ && !strstr( info->game, hostcache_currentmask.info.game ) )
+ return false;
+ if( *hostcache_currentmask.info.mod
+ && !strstr( info->mod, hostcache_currentmask.info.mod ) )
+ return false;
+ if( *hostcache_currentmask.info.map
+ && !strstr( info->map, hostcache_currentmask.info.map ) )
+ return false;
+ if( *hostcache_currentmask.info.name
+ && !strstr( info->name, hostcache_currentmask.info.name ) )
+ return false;
+ return true;
+}
+
+static void _HostCache_Insert( hostcache_t *entry )
+{
+ int start, end, size;
+ if( hostcache_viewcount == HOSTCACHE_VIEWCACHESIZE )
+ return;
+ // now check whether it passes through mask
+ if( !_HostCache_TestMask( &entry->info ) )
+ return;
+
+ if( !hostcache_viewcount ) {
+ _HostCache_VS_Insert( 0, entry );
+ return;
+ }
+ // ok, insert it, we just need to find out where exactly:
+
+ // two special cases
+ // check whether to insert it as new first item
+ if( _HostCache_SortTest( entry, hostcache_viewset[0] ) ) {
+ _HostCache_VS_Insert( 0, entry );
+ return;
+ } // check whether to insert it as new last item
+ else if( !_HostCache_SortTest( entry, hostcache_viewset[hostcache_viewcount - 1] ) ) {
+ _HostCache_VS_Insert( hostcache_viewcount, entry );
+ return;
+ }
+ start = 1;
+ end = hostcache_viewcount - 1;
+ while( (size = start - end) > 0 )
+ // test the item that lies in the middle between start and end
+ if( _HostCache_SortTest( entry, hostcache_viewset[(start + end) / 2] ) )
+ // the item has to be in the upper half
+ end = (start + end) / 2 - 1;
+ else
+ // the item has to be in the lower half
+ start = (start + end) / 2 + 1;
+ _HostCache_VS_Insert( start + 1, entry );
+}
+
+void HostCache_RebuildViewSet(void)
+{
+ int i;
+
+ hostcache_viewcount = 0;
+ for( i = 0 ; i < hostcache_cachecount ; i++ )
+ if( hostcache_cache[i].finished )
+ _HostCache_Insert( &hostcache_cache[i] );
+}
+
+void HostCache_ResetMask(void)
+{
+ memset( &hostcache_currentmask, 0, sizeof( hostcache_mask_t ) );
+}
+
+
+void HostCache_QueryList(void)
+{
+ masterquerytime = realtime;
+ masterquerycount = 0;
+ masterreplycount = 0;
+ serverquerycount = 0;
+ serverreplycount = 0;
+ hostcache_cachecount = 0;
+ hostcache_viewcount = 0;
+ hostcache_consoleoutput = false;
+ NetConn_QueryMasters();
+}
+
+// rest
+