Ok, the first part is allready done (user interface). Now you need to make it functional. I am going to do some coding here in ways that may not be best for what you need, but will allow for easy additions to the system and greater flexibility. I am writing this on the fly, so you may need to adjust things slightly if they give you any errors, but it should be fine for the most part. We will wait and see. 
===================
To get started, create a new header file in the cgame folder, you can title it whatever you want, but for the purpose of this example we will call it ‘cg_favorites.h’. In this file is where all the data structures and function prototypes will be declared, as well as any #defines that are needed.
Open the new file, cg_favorites.h, and prepare it for usage:
#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H
#endif
Now that we have our header file ready to go, we need a structure to hold the server favorite data. At this point all we need to know is the server ip and the server port. I am placing a prefix of sf_ on all the structures to avoid possible conflicts with other code that may be present in the sdk. You can use whatever prefix you like, just stick to something that makes sense (I am using sf_ in this example which represents server favorites for now).
#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H
//make sure we define some limits to ensure consistency
#define SF_FILENAME "favorites.cfg"
#define SF_MAX_FAVORITES 20 //maximum server favorites to store
#define SF_MAX_IP 15 //maximum length of ip
#define SF_MAX_PORT 4 //maximum length of port
//this structure will hold the data for the server favorites
typedef struct {
char ip[SF_MAX_IP];
char port[SF_MAX_PORT];
} sf_favorite_t;
#endif
Ok, now we have a place to hold the data, but what else are we going to need? Well, for starters, we will need a way to load the data into memory to be read, and we will also need a way to store the data back on a file on disk. It is also easier to break up common pieces of code into helper functions. Let’s create some function protoptypes for handling this now:
#ifndef CG_FAVORITES_H
#define CG_FAVORITES_H
//make sure we define some limits to ensure consistency
#define SF_FILENAME "favorites.cfg"
#define SF_MAX_FAVORITES 20 //maximum server favorites to store
#define SF_MAX_IP 15 //maximum length of ip
#define SF_MAX_PORT 4 //maximum length of port
//this structure will hold the data for the server favorites
typedef struct {
char ip[SF_MAX_IP];
char port[SF_MAX_PORT];
} sf_favorite_t;
//function prototypes
void SF_ReadConfig(char **cnf, char *s, int size); //read from config file
void SF_WriteConfig(char *s, fileHandle_t f); //write to config file
void SF_LoadFavorites (); //load the server favorites from a file
void SF_StoreFavorites (); //save server favorites to file
void SF_Cleanup (); //cleanup resources used by server favorites system
#endif
Ok, let’s make this new header file available to the cgame module. We can do this by including this header file in cg_local.h. Just stick it in at the bottom of the file:
#include "cg_favorites.h"
Ok, great. Now we have a framework to work from. At this point in time we will create a new file that will contain the actual code for these function prototypes and what not. Create a new file, for the purpose of this example we will call it ‘cg_favorites.c’. Once the file has been created, we need to code the functions to store data, as well as declare some instances of the server favorite structures in order to use them.
//includes
#include "cg_local.h"
//globals
sf_favorite_t *sf_favorites[SF_MAX_FAVORITES]; //server favorites
//read from config file
void SF_ReadConfig(char **cnf, char *s, int size) {
char *t;
t = COM_ParseExt(cnf, qfalse);
if(!strcmp(t, "=")) {
t = COM_ParseExt(cnf, qfalse);
} else {
G_Printf("XPSave: readconfig error (missing = before %s on line %d)
", t, COM_GetCurrentParseLine());
}
s[0] = '\0';
while(t[0]) {
if((s[0] == '\0' && strlen(t) <= size) || (strlen(t)+strlen(s) < size)) {
Q_strcat(s, size, t);
Q_strcat(s, size, " ");
}
t = COM_ParseExt(cnf, qfalse);
}
// trim the trailing space
if(strlen(s) > 0 && s[strlen(s)-1] == ' ') {
s[strlen(s)-1] = '\0';
}
}
//write to config file
void SF_WriteConfig(char *s, fileHandle_t f) {
char buf[MAX_STRING_CHARS];
buf[0] = '\0';
if(s[0]) {
Q_strncpyz(buf, s, sizeof(buf));
trap_FS_Write(buf, strlen(buf), f);
}
trap_FS_Write("
", 1, f);
}
//load the server favorites from a file
void SF_LoadFavorites () {
//declare variables
int len, i;
fileHandle_t f;
char *cnf, *cnf2, *t;
qboolean record_open = qfalse;
int rc = 0;
sf_favorite_t *r = sf_favorites[0];
//open server favorites file
len = trap_FS_FOpenFile(SF_FILENAME, &f, FS_READ) ;
if(len < 0) {
CG_Printf("ERROR: could not open file (%s)
", SF_FILENAME);
return;
}
//read file
cnf = malloc(len+1);
cnf2 = cnf;
trap_FS_Read(cnf, len, f);
*(cnf + len) = '\0';
//close file
trap_FS_FCloseFile(f);
//cleanup old data
SF_Cleanup();
//parse file
t = COM_Parse(&cnf);
while(*t) {
if(!Q_stricmp(t, "[favorite]")) {
if(record_open) {
sf_favorites[rc++] = r;
}
record_open = qfalse;
}
if(record_open) {
if(!Q_stricmp(t, "ip")) {
SF_ReadConfig(&cnf, r->ip, sizeof(r->ip));
} else if(!Q_stricmp(t, "port")) {
SF_ReadConfig(&cnf, r->port, sizeof(r->port));
} else {
CG_Printf("ERROR: readconfig error (parse error near %s on line %d)
", t, COM_GetCurrentParseLine());
}
}
if(!Q_stricmp(t, "[favorite]")) {
if(rc >= SF_MAX_FAVORITES) {
CG_Printf("ERROR: SF_MAX_FAVORITES exceeded (%i)", SF_MAX_FAVORITES);
return;
}
r = malloc(sizeof(sf_favorite_t));
r->ip[0] = '\0';
r->port[0] = '\0';
record_open = qtrue;
}
t = COM_Parse(&cnf);
}
//make sure last record is used also
if(record_open) {
sf_favorites[rc++] = r;
}
//release resources
free(cnf2);
//display message
CG_Printf("Client: loaded %d server favorites
", rc);
}
//save server favorites to file
void SF_StoreFavorites () {
//declare variables
int len, i;
fileHandle_t f;
//open server favorites file
len = trap_FS_FOpenFile(SF_FILENAME, &f, FS_WRITE);
if(len < 0) {
CG_Printf("ERROR: could not open file (%s)
", SF_FILENAME);
}
//write server favorite records to file
for(i=0; sf_favorites[i]; i++) {
trap_FS_Write("[favorite]
", 11, f);
trap_FS_Write("ip = ", 8, f);
SF_WriteConfig(sf_favorites[i]->ip, f);
trap_FS_Write("port = ", 8, f);
SF_WriteConfig(sf_favorites[i]->port, f);
trap_FS_Write("
", 1, f);
}
//close file
trap_FS_FCloseFile(f);
//display message
CG_Printf("Client: wrote %d server favorites
", i);
}
//cleanup resources used by server favorites system
void SF_Cleanup () {
//declare variables
int i = 0;
//cleanup resources
for(i=0; sf_favorites[i]; i++) {
free(sf_favorites[i]);
sf_favorites[i] = NULL;
}
}
================
Ok, so at this point we now have code in place to both read and write server favorite records. There is still much work to be done however. At this point in time the code will never be executed. You must determine when you want to load the records and what you want to do with them. Examine this code for now, and get an idea of how files are opened, closed, read, and written. You will notice several trap_* function calls that perform these actions allready for us.
I will leave it to you to finish off the code. Just give it a shot and fill in blanks where you can. I know the formattign here is bad, but the spacing didn’t look right when pasted into these forums. Some tabs were removed to give it a more aligned appearance, and most places I didn’t bother making look pretty. You get the idea.
If you have more troubles finishing this system, post what you have tried and when you would prefer to do things with the code (ie, display data), and such thinsg like that. You need to give it a good effort from here on in to actually learn anything from this system, so for any further help I will probably ask you many questions to get you thinking right before I give much code, unless it is simpler to explain with code itself.
Anyhow, good luck!