diff options
Diffstat (limited to 'src/sv_save.c')
-rw-r--r-- | src/sv_save.c | 313 |
1 files changed, 161 insertions, 152 deletions
diff --git a/src/sv_save.c b/src/sv_save.c index ba91721..e946fae 100644 --- a/src/sv_save.c +++ b/src/sv_save.c @@ -8,7 +8,7 @@ of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -35,57 +35,59 @@ SAVEGAME FILES =============================================================================== */ -static qerror_t write_server_file( qboolean autosave ) { +static qerror_t write_server_file(qboolean autosave) +{ char name[MAX_OSPATH]; cvar_t *var; size_t len; qerror_t ret; // write magic - MSG_WriteLong( SAVE_MAGIC1 ); - MSG_WriteLong( SAVE_VERSION ); + MSG_WriteLong(SAVE_MAGIC1); + MSG_WriteLong(SAVE_VERSION); // write the comment field - MSG_WriteByte( autosave ); - MSG_WriteString( sv.configstrings[CS_NAME] ); + MSG_WriteByte(autosave); + MSG_WriteString(sv.configstrings[CS_NAME]); // write the mapcmd - MSG_WriteString( sv.name ); + MSG_WriteString(sv.name); // write all CVAR_LATCH cvars // these will be things like coop, skill, deathmatch, etc - for( var = cvar_vars; var; var = var->next ) { + for (var = cvar_vars; var; var = var->next) { if (!(var->flags & CVAR_LATCH)) continue; if (var->flags & CVAR_PRIVATE) continue; - MSG_WriteString( var->name ); - MSG_WriteString( var->string ); + MSG_WriteString(var->name); + MSG_WriteString(var->string); } - MSG_WriteString( NULL ); + MSG_WriteString(NULL); // write server state - ret = FS_WriteFile( "save/" SAVE_CURRENT "/server.state", - msg_write.data, msg_write.cursize ); + ret = FS_WriteFile("save/" SAVE_CURRENT "/server.state", + msg_write.data, msg_write.cursize); - SZ_Clear( &msg_write ); + SZ_Clear(&msg_write); - if( ret < 0 ) { + if (ret < 0) { return ret; } // write game state - len = Q_snprintf( name, sizeof( name ), - "%s/save/" SAVE_CURRENT "/game.state", fs_gamedir ); - if( len >= sizeof( name ) ) { + len = Q_snprintf(name, sizeof(name), + "%s/save/" SAVE_CURRENT "/game.state", fs_gamedir); + if (len >= sizeof(name)) { return Q_ERR_NAMETOOLONG; } - ge->WriteGame (name, autosave); + ge->WriteGame(name, autosave); return Q_ERR_SUCCESS; } -static qerror_t write_level_file( void ) { +static qerror_t write_level_file(void) +{ char name[MAX_OSPATH]; int i; char *s; @@ -94,130 +96,134 @@ static qerror_t write_level_file( void ) { qerror_t ret; // write magic - MSG_WriteLong( SAVE_MAGIC2 ); - MSG_WriteLong( SAVE_VERSION ); + MSG_WriteLong(SAVE_MAGIC2); + MSG_WriteLong(SAVE_VERSION); // write configstrings - for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { + for (i = 0; i < MAX_CONFIGSTRINGS; i++) { s = sv.configstrings[i]; - if( !s[0] ) { + if (!s[0]) { continue; } - len = strlen( s ); - if( len > MAX_QPATH ) { + len = strlen(s); + if (len > MAX_QPATH) { len = MAX_QPATH; } - MSG_WriteShort( i ); - MSG_WriteData( s, len ); - MSG_WriteByte( 0 ); + MSG_WriteShort(i); + MSG_WriteData(s, len); + MSG_WriteByte(0); } - MSG_WriteShort( MAX_CONFIGSTRINGS ); + MSG_WriteShort(MAX_CONFIGSTRINGS); - len = CM_WritePortalBits( &sv.cm, portalbits ); - MSG_WriteByte( len ); - MSG_WriteData( portalbits, len ); + len = CM_WritePortalBits(&sv.cm, portalbits); + MSG_WriteByte(len); + MSG_WriteData(portalbits, len); - ret = FS_WriteFile( "save/" SAVE_CURRENT "/server.level", - msg_write.data, msg_write.cursize ); + ret = FS_WriteFile("save/" SAVE_CURRENT "/server.level", + msg_write.data, msg_write.cursize); - SZ_Clear( &msg_write ); + SZ_Clear(&msg_write); - if( ret < 0 ) { + if (ret < 0) { return ret; } // write game level - len = Q_snprintf( name, sizeof( name ), - "%s/save/" SAVE_CURRENT "/game.level", fs_gamedir ); - if( len >= sizeof( name ) ) { + len = Q_snprintf(name, sizeof(name), + "%s/save/" SAVE_CURRENT "/game.level", fs_gamedir); + if (len >= sizeof(name)) { return Q_ERR_NAMETOOLONG; } - ge->WriteLevel( name ); + ge->WriteLevel(name); return Q_ERR_SUCCESS; } -static qerror_t rename_file( const char *dir, const char *base, const char *suf ) { +static qerror_t rename_file(const char *dir, const char *base, const char *suf) +{ char from[MAX_QPATH]; char to[MAX_QPATH]; size_t len; - len = Q_snprintf( from, sizeof( from ), "save/%s/%s%s", SAVE_CURRENT, base, suf ); - if( len >= sizeof( from ) ) + len = Q_snprintf(from, sizeof(from), "save/%s/%s%s", SAVE_CURRENT, base, suf); + if (len >= sizeof(from)) return Q_ERR_NAMETOOLONG; - len = Q_snprintf( to, sizeof( to ), "save/%s/%s%s", dir, base, suf ); - if( len >= sizeof( to ) ) + len = Q_snprintf(to, sizeof(to), "save/%s/%s%s", dir, base, suf); + if (len >= sizeof(to)) return Q_ERR_NAMETOOLONG; - return FS_RenameFile( from, to ); + return FS_RenameFile(from, to); } -static qerror_t move_files( const char *dir ) { +static qerror_t move_files(const char *dir) +{ char name[MAX_OSPATH]; size_t len; qerror_t ret; - len = Q_snprintf( name, sizeof( name ), "%s/save/%s/", fs_gamedir, dir ); - if( len >= sizeof( name ) ) + len = Q_snprintf(name, sizeof(name), "%s/save/%s/", fs_gamedir, dir); + if (len >= sizeof(name)) return Q_ERR_NAMETOOLONG; - ret = FS_CreatePath( name ); - if( ret ) + ret = FS_CreatePath(name); + if (ret) return ret; - ret = rename_file( dir, "game", ".level" ); - if( ret ) + ret = rename_file(dir, "game", ".level"); + if (ret) return ret; - ret = rename_file( dir, "server", ".level" ); - if( ret ) + ret = rename_file(dir, "server", ".level"); + if (ret) return ret; - ret = rename_file( dir, "game", ".state" ); - if( ret ) + ret = rename_file(dir, "game", ".state"); + if (ret) return ret; - ret = rename_file( dir, "server", ".state" ); - if( ret ) + ret = rename_file(dir, "server", ".state"); + if (ret) return ret; return Q_ERR_SUCCESS; } -static qerror_t read_binary_file( const char *name ) { +static qerror_t read_binary_file(const char *name) +{ qhandle_t f; ssize_t len, read; qerror_t ret; - len = FS_FOpenFile( name, &f, - FS_MODE_READ | FS_TYPE_REAL | FS_PATH_GAME ); - if( !f ) { + len = FS_FOpenFile(name, &f, + FS_MODE_READ | FS_TYPE_REAL | FS_PATH_GAME); + if (!f) { return len; } - if( len > MAX_MSGLEN ) { + if (len > MAX_MSGLEN) { ret = Q_ERR_FBIG; goto fail; } - read = FS_Read( msg_read_buffer, len, f ); - if( read != len ) { + read = FS_Read(msg_read_buffer, len, f); + if (read != len) { ret = read < 0 ? read : Q_ERR_UNEXPECTED_EOF; goto fail; } - SZ_Init( &msg_read, msg_read_buffer, len ); + SZ_Init(&msg_read, msg_read_buffer, len); msg_read.cursize = len; ret = Q_ERR_SUCCESS; fail: - FS_FCloseFile( f ); + FS_FCloseFile(f); return ret; } -static qerror_t read_server_file( const char *dir ) { +static qerror_t read_server_file(const char *dir) +{ char name[MAX_OSPATH], string[MAX_STRING_CHARS]; char mapcmd[MAX_QPATH]; char *s, *ch, *spawnpoint; @@ -227,30 +233,30 @@ static qerror_t read_server_file( const char *dir ) { // errors like missing file, bad version, etc are // non-fatal and just return to the command handler - len = Q_snprintf( name, MAX_QPATH, "save/%s/server.state", dir ); - if( len >= MAX_QPATH ) { + len = Q_snprintf(name, MAX_QPATH, "save/%s/server.state", dir); + if (len >= MAX_QPATH) { return Q_ERR_NAMETOOLONG; } - ret = read_binary_file( name ); - if( ret ) { + ret = read_binary_file(name); + if (ret) { return ret; } - if( MSG_ReadLong() != SAVE_MAGIC1 ) { + if (MSG_ReadLong() != SAVE_MAGIC1) { return Q_ERR_UNKNOWN_FORMAT; } - if( MSG_ReadLong() != SAVE_VERSION ) { + if (MSG_ReadLong() != SAVE_VERSION) { return Q_ERR_INVALID_FORMAT; } // read the comment field MSG_ReadByte(); - MSG_ReadString( NULL, 0 ); + MSG_ReadString(NULL, 0); // read the mapcmd - len = MSG_ReadString( mapcmd, sizeof( mapcmd ) ); - if( len >= sizeof( mapcmd ) ) { + len = MSG_ReadString(mapcmd, sizeof(mapcmd)); + if (len >= sizeof(mapcmd)) { return Q_ERR_STRING_TRUNCATED; } @@ -258,19 +264,19 @@ static qerror_t read_server_file( const char *dir ) { // if there is a + in the map, set nextserver to the remainder // we go directly to nextserver as we don't support cinematics - ch = strchr( s, '+' ); - if( ch ) { + ch = strchr(s, '+'); + if (ch) { s = ch + 1; } // skip the end-of-unit flag if necessary - if( *s == '*' ) { + if (*s == '*') { s++; } // if there is a $, use the remainder as a spawnpoint - ch = strchr( s, '$' ); - if( ch ) { + ch = strchr(s, '$'); + if (ch) { *ch = 0; spawnpoint = ch + 1; } else { @@ -278,89 +284,90 @@ static qerror_t read_server_file( const char *dir ) { } // now expand and try to load the map - len = Q_concat( name, MAX_QPATH, "maps/", s, ".bsp", NULL ); - if( len >= MAX_QPATH ) { + len = Q_concat(name, MAX_QPATH, "maps/", s, ".bsp", NULL); + if (len >= MAX_QPATH) { return Q_ERR_NAMETOOLONG; } - ret = CM_LoadMap( &cm, name ); - if( ret ) { + ret = CM_LoadMap(&cm, name); + if (ret) { return ret; } // any error will drop from this point - SV_Shutdown( "Server restarted\n", ERR_RECONNECT ); + SV_Shutdown("Server restarted\n", ERR_RECONNECT); // the rest can't underflow msg_read.allowunderflow = qfalse; // read all CVAR_LATCH cvars // these will be things like coop, skill, deathmatch, etc - while( 1 ) { - len = MSG_ReadString( name, MAX_QPATH ); - if( !len ) + while (1) { + len = MSG_ReadString(name, MAX_QPATH); + if (!len) break; - if( len >= MAX_QPATH ) { + if (len >= MAX_QPATH) { ret = Q_ERR_STRING_TRUNCATED; goto fail; } - len = MSG_ReadString( string, sizeof( string ) ); - if( len >= sizeof( string ) ) { + len = MSG_ReadString(string, sizeof(string)); + if (len >= sizeof(string)) { ret = Q_ERR_STRING_TRUNCATED; goto fail; } - Cvar_UserSet( name, string ); + Cvar_UserSet(name, string); } // start a new game fresh with new cvars - SV_InitGame( MVD_SPAWN_DISABLED ); + SV_InitGame(MVD_SPAWN_DISABLED); // error out immediately if game doesn't support safe savegames - if( !( g_features->integer & GMF_ENHANCED_SAVEGAMES ) ) { - Com_Error( ERR_DROP, "Game does not support enhanced savegames" ); + if (!(g_features->integer & GMF_ENHANCED_SAVEGAMES)) { + Com_Error(ERR_DROP, "Game does not support enhanced savegames"); } // read game state - len = Q_snprintf( name, sizeof( name ), "%s/save/%s/game.state", fs_gamedir, dir ); - if( len >= sizeof( name ) ) { + len = Q_snprintf(name, sizeof(name), "%s/save/%s/game.state", fs_gamedir, dir); + if (len >= sizeof(name)) { ret = Q_ERR_NAMETOOLONG; goto fail; } - ge->ReadGame (name); + ge->ReadGame(name); // go to the map - SV_SpawnServer( &cm, s, spawnpoint ); + SV_SpawnServer(&cm, s, spawnpoint); return Q_ERR_SUCCESS; fail: - Com_Error( ERR_DROP, "Couldn't load %s: %s", dir, Q_ErrorString( ret ) ); + Com_Error(ERR_DROP, "Couldn't load %s: %s", dir, Q_ErrorString(ret)); } -static void read_level_file( const char *dir ) { +static void read_level_file(const char *dir) +{ char name[MAX_OSPATH]; size_t len, maxlen; qerror_t ret; int index; - len = Q_snprintf( name, MAX_QPATH, "save/%s/server.level", dir ); - if( len >= MAX_QPATH ) { + len = Q_snprintf(name, MAX_QPATH, "save/%s/server.level", dir); + if (len >= MAX_QPATH) { ret = Q_ERR_NAMETOOLONG; goto fail; } - ret = read_binary_file( name ); - if( ret ) { + ret = read_binary_file(name); + if (ret) { goto fail; } - if( MSG_ReadLong() != SAVE_MAGIC2 ) { + if (MSG_ReadLong() != SAVE_MAGIC2) { ret = Q_ERR_UNKNOWN_FORMAT; goto fail; } - if( MSG_ReadLong() != SAVE_VERSION ) { + if (MSG_ReadLong() != SAVE_VERSION) { ret = Q_ERR_INVALID_FORMAT; goto fail; } @@ -368,49 +375,49 @@ static void read_level_file( const char *dir ) { // the rest can't underflow msg_read.allowunderflow = qfalse; - while( 1 ) { + while (1) { index = MSG_ReadShort(); - if( index == MAX_CONFIGSTRINGS ) { + if (index == MAX_CONFIGSTRINGS) { break; } - if( index < 0 || index >= MAX_CONFIGSTRINGS ) { + if (index < 0 || index >= MAX_CONFIGSTRINGS) { ret = Q_ERR_BAD_INDEX; goto fail; } - maxlen = CS_SIZE( index ); - len = MSG_ReadString( sv.configstrings[index], maxlen ); - if( len >= maxlen ) { + maxlen = CS_SIZE(index); + len = MSG_ReadString(sv.configstrings[index], maxlen); + if (len >= maxlen) { ret = Q_ERR_STRING_TRUNCATED; goto fail; } } len = MSG_ReadByte(); - if( len > MAX_MAP_PORTAL_BYTES ) { + if (len > MAX_MAP_PORTAL_BYTES) { ret = Q_ERR_INVALID_FORMAT; goto fail; } SV_ClearWorld(); - CM_SetPortalStates( &sv.cm, MSG_ReadData( len ), len ); + CM_SetPortalStates(&sv.cm, MSG_ReadData(len), len); // read game level - len = Q_snprintf( name, sizeof( name ), "%s/save/%s/game.level", fs_gamedir, dir ); - if( len >= sizeof( name ) ) { + len = Q_snprintf(name, sizeof(name), "%s/save/%s/game.level", fs_gamedir, dir); + if (len >= sizeof(name)) { ret = Q_ERR_NAMETOOLONG; goto fail; } - ge->ReadLevel( name ); + ge->ReadLevel(name); ge->RunFrame(); ge->RunFrame(); return; fail: - Com_Error( ERR_DROP, "Couldn't load %s: %s", dir, Q_ErrorString( ret ) ); + Com_Error(ERR_DROP, "Couldn't load %s: %s", dir, Q_ErrorString(ret)); } @@ -420,33 +427,34 @@ SV_Loadgame_f ============== */ -void SV_Loadgame_f (void) { +void SV_Loadgame_f(void) +{ char *dir; qerror_t ret; if (Cmd_Argc() != 2) { - Com_Printf ("Usage: %s <directory>\n", Cmd_Argv(0)); + Com_Printf("Usage: %s <directory>\n", Cmd_Argv(0)); return; } - if( dedicated->integer ) { - Com_Printf ("Savegames are for listen servers only\n"); + if (dedicated->integer) { + Com_Printf("Savegames are for listen servers only\n"); return; } dir = Cmd_Argv(1); - if (!COM_IsPath(dir) ) { - Com_Printf ("Bad savedir.\n"); + if (!COM_IsPath(dir)) { + Com_Printf("Bad savedir.\n"); return; } - ret = read_server_file( dir ); - if( ret ) { - Com_Printf( "Couldn't load %s: %s\n", dir, Q_ErrorString( ret ) ); + ret = read_server_file(dir); + if (ret) { + Com_Printf("Couldn't load %s: %s\n", dir, Q_ErrorString(ret)); return; } - read_level_file( dir ); + read_level_file(dir); } @@ -456,44 +464,45 @@ SV_Savegame_f ============== */ -void SV_Savegame_f( void ) { +void SV_Savegame_f(void) +{ char *dir; qerror_t ret; if (sv.state != ss_game) { - Com_Printf ("You must be in a game to save.\n"); + Com_Printf("You must be in a game to save.\n"); return; } - if( dedicated->integer ) { - Com_Printf ("Savegames are for listen servers only\n"); + if (dedicated->integer) { + Com_Printf("Savegames are for listen servers only\n"); return; } - + // don't bother saving if we can't read them back! - if( !( g_features->integer & GMF_ENHANCED_SAVEGAMES ) ) { - Com_Printf ("Game does not support enhanced savegames\n"); + if (!(g_features->integer & GMF_ENHANCED_SAVEGAMES)) { + Com_Printf("Game does not support enhanced savegames\n"); return; } if (Cvar_VariableInteger("deathmatch")) { - Com_Printf ("Can't savegame in a deathmatch\n"); + Com_Printf("Can't savegame in a deathmatch\n"); return; } if (sv_maxclients->integer == 1 && svs.client_pool[0].edict->client->ps.stats[STAT_HEALTH] <= 0) { - Com_Printf ("Can't savegame while dead!\n"); + Com_Printf("Can't savegame while dead!\n"); return; } if (Cmd_Argc() != 2) { - Com_Printf ("Usage: %s <directory>\n", Cmd_Argv(0)); + Com_Printf("Usage: %s <directory>\n", Cmd_Argv(0)); return; } dir = Cmd_Argv(1); - if (!COM_IsPath(dir) ) { - Com_Printf ("Bad savedir.\n"); + if (!COM_IsPath(dir)) { + Com_Printf("Bad savedir.\n"); return; } @@ -501,23 +510,23 @@ void SV_Savegame_f( void ) { // when the level is reloaded, they will be shells awaiting // a connecting client ret = write_level_file(); - if( ret ) + if (ret) goto fail; // save server state - ret = write_server_file( qfalse ); - if( ret ) + ret = write_server_file(qfalse); + if (ret) goto fail; // rename all stuff - ret = move_files( dir ); - if( ret ) + ret = move_files(dir); + if (ret) goto fail; - Com_Printf ("Game saved.\n"); + Com_Printf("Game saved.\n"); return; fail: - Com_EPrintf( "Couldn't write %s: %s\n", dir, Q_ErrorString( ret ) ); + Com_EPrintf("Couldn't write %s: %s\n", dir, Q_ErrorString(ret)); } |