/* ================ FS_LoadFile ================ */ int FS_LoadFile ( char *name, void **buffer ) { FILE *f; int length; if ( !name ) { return -1; } *buffer = NULL; f = fopen ( name, "rb" ); if ( !f ) { return -1; } length = FS_FileLength ( f ); if ( !buffer ) { return length; } if ( !length ) { return 0; } *buffer = malloc ( length ); fread ( *buffer, length, 1, f ); fclose ( f ); return length; }
/* * Returns file size or -1 on error. */ int FS_FOpenFileAppend(fsHandle_t *handle) { char path[MAX_OSPATH]; FS_CreatePath(handle->name); Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, handle->name); handle->file = fopen(path, "ab"); if (handle->file) { if (fs_debug->value) { Com_Printf("FS_FOpenFileAppend: '%s'.\n", path); } return FS_FileLength(handle->file); } if (fs_debug->value) { Com_Printf("FS_FOpenFileAppend: couldn't open '%s'.\n", path); } return -1; }
/* ----------------------------------------------------------------------------- Function: CAL_SetupMapFile -Setup map files for decoding. Parameters: extension -[in] file extension for map data files. Returns: Non-zero on success, zero otherwise. Notes: ----------------------------------------------------------------------------- */ PRIVATE W8 CAL_SetupMapFile( const char *extension ) { FILE *handle; SW32 length; char fname[ 13 ]; // // load maphead.xxx (offsets and tileinfo for map file) // cs_strlcpy( fname, MAPHEADNAME, sizeof( fname ) ); cs_strlcat( fname, extension, sizeof( fname ) ); handle = fopen( cs_strupr( fname ), "rb" ); if( handle == NULL ) { handle = fopen( cs_strlwr( fname ), "rb" ); if( handle == NULL ) { printf( "Could not open file (%s) for read!\n", fname ); return 0; } } length = FS_FileLength( handle ); fread( &RLEWtag, 2, 1, handle ); for( TotalMaps = 0 ; TotalMaps < length ; ++TotalMaps ) { fread( &headeroffsets[ TotalMaps ], 4, 1, handle ); if( ! headeroffsets[ TotalMaps ] ) { break; } } fclose( handle ); cs_strlcpy( fname, MAPNAME, sizeof( fname ) ); cs_strlcat( fname, extension, sizeof( fname ) ); maphandle = fopen( cs_strupr( fname ), "rb"); if( NULL == maphandle ) { maphandle = fopen( cs_strlwr( fname ), "rb"); if( NULL == maphandle ) { return 0; } } return 1; }
uint Sound_GetApproxWavePlayLen( const char *filepath ) { file_t *f; wavehdr_t wav; size_t filesize; float seconds; uint samples; f = FS_Open( filepath, "rb", false ); if( !f ) return 0; if( FS_Read( f, &wav, sizeof( wav )) != sizeof( wav )) { FS_Close( f ); return 0; } filesize = FS_FileLength( f ); filesize -= ( sizeof( wavehdr_t ) + sizeof( chunkhdr_t )); FS_Close( f ); // is real wav file ? if( wav.riff_id != RIFFHEADER || wav.wave_id != WAVEHEADER || wav.fmt_id != FORMHEADER ) return 0; if( wav.wFormatTag != 1 ) return 0; if( wav.nChannels != 1 && wav.nChannels != 2 ) return 0; if( wav.nBitsPerSample != 8 && wav.nBitsPerSample != 16 ) return 0; // calc samplecount seconds = (float)filesize / wav.nAvgBytesPerSec / wav.nChannels; samples = (uint)(( wav.nSamplesPerSec * wav.nChannels ) * seconds ); // g-cont. this function returns samplecount or time in milliseconds ??? return (uint)(seconds * 1000); }
// well exec /home/qqshka/ezquake/config.cfg does't work, security or something, so adding this // so this is some replacement for exec qbool LoadCfg(FILE *f) { char *fileBuffer; int size; if (!f) { return false; } size = FS_FileLength(f); fileBuffer = Q_malloc(size + 1); // +1 for null terminator if (fread(fileBuffer, 1, size, f) != size) { Com_Printf("Error reading config file\n"); Q_free(fileBuffer); return false; } fileBuffer[size] = 0; Cbuf_AddText (fileBuffer); Cbuf_AddText ("\n"); Q_free(fileBuffer); return true; }
// well exec /home/qqshka/ezquake/config.cfg does't work, security or something, so adding this // so this is some replacement for exec void LoadHomeCfg(const char *filename) { char fullname[MAX_PATH] = {0}, *fileBuffer; int size; FILE *f; snprintf(fullname, sizeof(fullname) - 4, "%s/%s", com_homedir, filename); COM_ForceExtensionEx (fullname, ".cfg", sizeof (fullname)); if (!(f = fopen(fullname, "rb"))) { Com_DPrintf("LoadHomeCfg: %s not found\n", filename); // hrm return; } size = FS_FileLength(f); fileBuffer = Q_malloc(size + 1); // +1 for null terminator fread(fileBuffer, 1, size, f); fileBuffer[size] = 0; fclose(f); Cbuf_AddText (fileBuffer); Cbuf_AddText ("\n"); Q_free(fileBuffer); }
/* ----------------------------------------------------------------------------- Function: CAL_SetupAudioFile() -Setup for decoding audio data. Parameters: fextension -[in] Pointer to string with file extension. Returns: Non-zero on success, otherwise zero. Notes: ----------------------------------------------------------------------------- */ PRIVATE W8 CAL_SetupAudioFile( const char *fextension ) { FILE *handle; SW32 length; W32 count; char fname[ 13 ]; if( ! fextension || ! *fextension ) { printf( "NULL extension passed into CAL_SetupAudioFile!\n" ); return 0; } // // load audiohed.XXX (offsets and lengths for audio file) // cs_strlcpy( fname, AHEADFNAME, sizeof( fname ) ); cs_strlcat( fname, fextension, sizeof( fname ) ); handle = fopen( cs_strupr( fname ), "rb" ); if( handle == NULL ) { handle = fopen( cs_strlwr( fname ), "rb" ); if( handle == NULL ) { printf( "Can not open file (%s) for read!\n", fname ); return 0; } } length = FS_FileLength( handle ); if( length < 4 ) { fclose( handle ); printf( "Incorrect audio header size on file: %s\n", fname ); return 0; } audiostarts = (PW32) MM_MALLOC( length ); if( audiostarts == NULL ) { return 0; } count = fread( audiostarts, sizeof( W32 ), length >> 2, handle ); if( count != (W32)(length >> 2) ) { fclose( handle ); printf( "[Error]: Read error on file: (%s)", fname ); return 0; } fclose( handle ); // // open the Audio data file // cs_strlcpy( fname, AUDIOFNAME, sizeof( fname ) ); cs_strlcat( fname, fextension, sizeof( fname ) ); audiohandle = fopen( cs_strupr( fname ), "rb" ); if( audiohandle == NULL ) { audiohandle = fopen( cs_strlwr( fname ), "rb" ); if( audiohandle == NULL ) { printf( "Could not open file (%s) for read!\n", fname ); return 0; } } return 1; }
/* * Returns file size or -1 if not found. Can open separate files as well as * files inside pack files (both PAK and PK3). */ int FS_FOpenFileRead(fsHandle_t *handle) { char path[MAX_OSPATH]; int i; fsSearchPath_t *search; fsPack_t *pack; file_from_pak = 0; #ifdef ZIP file_from_pk3 = 0; #endif /* Search through the path, one element at a time. */ for (search = fs_searchPaths; search; search = search->next) { /* Search inside a pack file. */ if (search->pack) { pack = search->pack; for (i = 0; i < pack->numFiles; i++) { if (Q_stricmp(pack->files[i].name, handle->name) == 0) { /* Found it! */ Com_FilePath(pack->name, fs_fileInPath, sizeof(fs_fileInPath)); fs_fileInPack = true; if (fs_debug->value) { Com_Printf("FS_FOpenFileRead: '%s' (found in '%s').\n", handle->name, pack->name); } if (pack->pak) { /* PAK */ file_from_pak = 1; handle->file = fopen(pack->name, "rb"); if (handle->file) { fseek(handle->file, pack->files[i].offset, SEEK_SET); return pack->files[i].size; } } #ifdef ZIP else if (pack->pk3) { /* PK3 */ file_from_pk3 = 1; Q_strlcpy(file_from_pk3_name, strrchr(pack->name, '/') + 1, sizeof(file_from_pk3_name)); handle->zip = unzOpen(pack->name); if (handle->zip) { if (unzLocateFile(handle->zip, handle->name, 2) == UNZ_OK) { if (unzOpenCurrentFile(handle->zip) == UNZ_OK) { return pack->files[i].size; } } unzClose(handle->zip); } } #endif Com_Error(ERR_FATAL, "Couldn't reopen '%s'", pack->name); } } } else { /* Search in a directory tree. */ Com_sprintf(path, sizeof(path), "%s/%s", search->path, handle->name); handle->file = fopen(path, "rb"); if (!handle->file) { Q_strlwr(path); handle->file = fopen(path, "rb"); } if (!handle->file) { continue; } if (handle->file) { /* Found it! */ Q_strlcpy(fs_fileInPath, search->path, sizeof(fs_fileInPath)); fs_fileInPack = false; if (fs_debug->value) { Com_Printf("FS_FOpenFileRead: '%s' (found in '%s').\n", handle->name, search->path); } return FS_FileLength(handle->file); } } } /* Not found! */ fs_fileInPath[0] = 0; fs_fileInPack = false; if (fs_debug->value) { Com_Printf("FS_FOpenFileRead: couldn't find '%s'.\n", handle->name); } return -1; }
/** * \brief Setup map files for decoding. * \param[in] headFileName Name of file with header offsets. * \param[in] mapFileName Name of file with map data. * \param[in,out] RLEWtag Run length encoded word tag. * \param[in,out] nTotalMaps Number of maps in file. * \return On success true, otherwise false. * \note Must call function MapFile_Shutdown() when done. */ PUBLIC wtBoolean MapFile_Setup( const char *headFileName, const char *mapFileName, W16 *RLEWtag, W32 *nTotalMaps ) { FILE *fileHandle; SW32 fileSize; char *tempFileName; W32 TotalMaps; tempFileName = (char *) MM_MALLOC( strlen( headFileName ) + 1 ); if( tempFileName == NULL ) { return false; } // // Load map head file to get offsets and tile info. // wt_strlcpy( tempFileName, headFileName, strlen( headFileName ) + 1 ); fileHandle = fopen( wt_strupr( tempFileName ), "rb" ); if( fileHandle == NULL ) { fileHandle = fopen( wt_strlwr( tempFileName ), "rb" ); if( fileHandle == NULL ) { fprintf( stderr, "Could not open file (%s) for read!\n", tempFileName ); MM_FREE( tempFileName ); return false; } } fileSize = FS_FileLength( fileHandle ); fread( RLEWtag, 2, 1, fileHandle ); for( TotalMaps = 0 ; TotalMaps < fileSize ; ++TotalMaps ) { fread( &headerOffsets[ TotalMaps ], 4, 1, fileHandle ); if( ! headerOffsets[ TotalMaps ] ) { break; } } fclose( fileHandle ); MM_FREE( tempFileName ); tempFileName = (char *) MM_MALLOC( strlen( mapFileName ) + 1 ); wt_strlcpy( tempFileName, mapFileName, strlen( mapFileName ) + 1 ); // // Open map data file. // map_file_handle = fopen( wt_strupr( tempFileName ), "rb"); if( NULL == map_file_handle ) { map_file_handle = fopen( wt_strlwr( tempFileName ), "rb"); if( NULL == map_file_handle ) { MM_FREE( tempFileName ); return false; } } *nTotalMaps = TotalMaps; MM_FREE( tempFileName ); return true; }
void HPAK_AddLump( qboolean add_to_queue, const char *name, resource_t *DirEnt, byte *data, file_t *f ) { int i, position, length; string pakname1, pakname2; char md5[16]; MD5Context_t MD5_Hash; hpak_container_t hpak1, hpak2; file_t *f1, *f2; hpak_dir_t *dirs; byte *temp; if( !name || !name[0] ) { MsgDev( D_ERROR, "HPAK_AddLump: NULL name\n" ); return; } if( !DirEnt ) { MsgDev( D_ERROR, "HPAK_AddLump: invalid lump\n" ); return; } if( data == NULL && f == NULL ) { MsgDev( D_ERROR, "HPAK_AddLump: missing lump data\n" ); return; } if( DirEnt->nDownloadSize < 1024 || DirEnt->nDownloadSize > 131072 ) { MsgDev( D_ERROR, "HPAK_AddLump: invalid size %s\n", Q_pretifymem( DirEnt->nDownloadSize, 2 )); return; } // hash it Q_memset( &MD5_Hash, 0, sizeof( MD5Context_t )); MD5Init( &MD5_Hash ); if( data == NULL ) { // there are better ways position = FS_Tell( f ); temp = Z_Malloc( DirEnt->nDownloadSize ); FS_Read( f, temp, DirEnt->nDownloadSize ); FS_Seek( f, position, SEEK_SET ); MD5Update( &MD5_Hash, temp, DirEnt->nDownloadSize ); Mem_Free( temp ); } else { MD5Update( &MD5_Hash, data, DirEnt->nDownloadSize ); } MD5Final( md5, &MD5_Hash ); if( Q_memcmp( md5, DirEnt->rgucMD5_hash, 0x10 )) { MsgDev( D_ERROR, "HPAK_AddLump: bad checksum for %s. Ignored\n", DirEnt->szFileName ); return; } if( add_to_queue ) { HPAK_AddToQueue( name, DirEnt, data, f ); return; } Q_strncpy( pakname1, name, sizeof( pakname1 )); FS_StripExtension( pakname1 ); FS_DefaultExtension( pakname1, ".hpk" ); f1 = FS_Open( pakname1, "rb", false ); if( !f1 ) { // create new pack HPAK_CreatePak( name, DirEnt, data, f ); return; } Q_strncpy( pakname2, pakname1, sizeof( pakname2 )); FS_StripExtension( pakname2 ); FS_DefaultExtension( pakname2, ".hp2" ); f2 = FS_Open( pakname2, "w+b", false ); if( !f2 ) { MsgDev( D_ERROR, "HPAK_AddLump: couldn't open %s.\n", pakname2 ); FS_Close( f1 ); return; } // load headers FS_Read( f1, &hash_pack_header, sizeof( hpak_header_t )); if( hash_pack_header.version != IDCUSTOM_VERSION ) { // we don't check the HPAK bit for some reason. MsgDev( D_ERROR, "HPAK_AddLump: %s does not have a valid header.\n", pakname2 ); FS_Close( f1 ); FS_Close( f2 ); } length = FS_FileLength( f1 ); HPAK_FileCopy( f2, f1, length ); FS_Seek( f1, hash_pack_header.seek, SEEK_SET ); FS_Read( f1, &hpak1.count, sizeof( hpak1.count )); if( hpak1.count < 1 || hpak1.count > MAX_FILES_IN_WAD ) { MsgDev( D_ERROR, "HPAK_AddLump: %s contain too many lumps.\n", pakname1 ); FS_Close( f1 ); FS_Close( f2 ); return; } // load the data hpak1.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak1.count ); FS_Read( f1, hpak1.dirs, sizeof( hpak_dir_t ) * hpak1.count ); FS_Close( f1 ); if( HPAK_FindResource( &hpak1, DirEnt->rgucMD5_hash, NULL )) { Mem_Free( hpak1.dirs ); FS_Close( f2 ); } // make a new container hpak2.count = hpak1.count; hpak2.dirs = Z_Malloc( sizeof( hpak_dir_t ) * hpak2.count ); Q_memcpy( hpak2.dirs, hpak1.dirs, hpak1.count ); for( i = 0, dirs = NULL; i < hpak1.count; i++ ) { if( Q_memcmp( hpak1.dirs[i].DirectoryResource.rgucMD5_hash, DirEnt->rgucMD5_hash, 16 ) < 0 ) { dirs = &hpak1.dirs[i]; while( i < hpak1.count ) { hpak2.dirs[i+1] = hpak1.dirs[i]; i++; } break; } } if( dirs == NULL ) dirs = &hpak2.dirs[hpak2.count-1]; Q_memset( dirs, 0, sizeof( hpak_dir_t )); FS_Seek( f2, hash_pack_header.seek, SEEK_SET ); dirs->DirectoryResource = *DirEnt; dirs->seek = FS_Tell( f2 ); dirs->size = DirEnt->nDownloadSize; if( !data ) HPAK_FileCopy( f2, f, dirs->size ); else FS_Write( f2, data, dirs->size ); hash_pack_header.seek = FS_Tell( f2 ); FS_Write( f2, &hpak2.count, sizeof( hpak2.count )); for( i = 0; i < hpak2.count; i++ ) { FS_Write( f2, &hpak2.dirs[i], sizeof( hpak_dir_t )); } // finalize Mem_Free( hpak1.dirs ); Mem_Free( hpak2.dirs ); FS_Seek( f2, 0, SEEK_SET ); FS_Write( f2, &hash_pack_header, sizeof( hpak_header_t )); FS_Close( f2 ); FS_Delete( pakname1 ); FS_Rename( pakname2, pakname1 ); }
/* ================= Stream_OpenMPG ================= */ stream_t *Stream_OpenMPG( const char *filename ) { mpeg_t *mpegFile; stream_t *stream; file_t *file; long filesize, read_len; char tempbuff[FRAME_SIZE]; file = FS_Open( filename, "rb", false ); if( !file ) return NULL; filesize = FS_FileLength( file ); if( filesize < FRAME_SIZE ) { MsgDev( D_ERROR, "Stream_OpenMPG: %s is probably corrupted\n", filename ); FS_Close( file ); return NULL; } // at this point we have valid stream stream = Mem_Alloc( host.soundpool, sizeof( stream_t )); stream->file = file; stream->pos = 0; mpegFile = Mem_Alloc( host.soundpool, sizeof( mpeg_t )); // couldn't create decoder if( !create_decoder( mpegFile )) { MsgDev( D_ERROR, "Stream_OpenMPG: couldn't create decoder\n" ); Mem_Free( mpegFile ); Mem_Free( stream ); FS_Close( file ); return NULL; } read_len = FS_Read( file, tempbuff, sizeof( tempbuff )); if( read_len < sizeof( tempbuff )) { MsgDev( D_ERROR, "Stream_OpenMPG: %s is probably corrupted\n", filename ); close_decoder( mpegFile ); Mem_Free( mpegFile ); Mem_Free( stream ); FS_Close( file ); return NULL; } // trying to read header if( !read_mpeg_header( mpegFile, tempbuff, sizeof( tempbuff ), filesize )) { MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", filename ); close_decoder( mpegFile ); Mem_Free( mpegFile ); Mem_Free( stream ); FS_Close( file ); return NULL; } stream->buffsize = 0; // how many samples left from previous frame stream->channels = mpegFile->channels; stream->pos += mpegFile->outsize; stream->rate = mpegFile->rate; stream->width = 2; // always 16 bit stream->ptr = mpegFile; stream->type = WF_MPGDATA; return stream; }
/** * \brief Setup for audio decoding. * \param[in] aheadfname Audio header file name. * \param[in] audfname Audio file name. * \return On success true, otherwise false. */ PUBLIC wtBoolean AudioFile_Setup( const char *aheadfname, const char *audfname ) { char tempFileName[ 1024 ]; FILE *handle; SW32 length; SW32 count; /* Number of bytes read from file */ if( ! aheadfname || ! *aheadfname || ! audfname || ! *audfname ) { fprintf( stderr, "[AudioFile_Setup]: NULL file name!\n" ); return false; } // // Load audiohed.XXX (offsets and lengths for audio file) // wt_strlcpy( tempFileName, aheadfname, sizeof( tempFileName ) ); handle = fopen( wt_strupr( tempFileName ), "rb" ); if( handle == NULL ) { handle = fopen( wt_strlwr( tempFileName ), "rb" ); if( handle == NULL ) { fprintf( stderr, "Could not open file (%s) for read!\n", tempFileName ); return false; } } length = FS_FileLength( handle ); if( length < 4 ) { fclose( handle ); fprintf( stderr, "[AudioFile_Setup]: Incorrect audio header size on file: %s\n", tempFileName ); return false; } audiostarts = (PW32) MM_MALLOC( length ); if( audiostarts == NULL ) { fclose( handle ); return false; } count = fread( audiostarts, 1, length, handle ); if( count != length ) { fclose( handle ); fprintf( stderr, "[AudioFile_Setup]: Read error on file: (%s)", tempFileName ); return false; } fclose( handle ); // // Open the Audio data file // wt_strlcpy( tempFileName, audfname, sizeof( tempFileName ) ); audiohandle = fopen( wt_strupr( tempFileName ), "rb" ); if( audiohandle == NULL ) { audiohandle = fopen( wt_strlwr( tempFileName ), "rb" ); if( audiohandle == NULL ) { fprintf( stderr, "[AudioFile_Setup]: Could not open file (%s) for read!\n", tempFileName ); return false; } } return true; }
/** * @brief Loads the given savegame from an xml File. * @return true on load success false on failures * @param[in] file The Filename to load from (without extension) * @param[out] error On failure an errormessage may be set. */ bool SAV_GameLoad (const char *file, const char **error) { char filename[MAX_OSPATH]; qFILE f; int i, clen; xmlNode_t *topNode, *node; saveFileHeader_t header; Q_strncpyz(filename, file, sizeof(filename)); /* open file */ FS_OpenFile(va("save/%s.%s", filename, SAVEGAME_EXTENSION), &f, FILE_READ); if (!f.f) { Com_Printf("Couldn't open file '%s'\n", filename); *error = "File not found"; return false; } clen = FS_FileLength(&f); byte* const cbuf = Mem_PoolAllocTypeN(byte, clen + 1 /* for '\0' if not compressed */, cp_campaignPool); if (FS_Read(cbuf, clen, &f) != clen) Com_Printf("Warning: Could not read %i bytes from savefile\n", clen); FS_CloseFile(&f); Com_Printf("Loading savegame xml (size %d)\n", clen); memcpy(&header, cbuf, sizeof(header)); /* swap all int values if needed */ header.compressed = LittleLong(header.compressed); header.version = LittleLong(header.version); header.xmlSize = LittleLong(header.xmlSize); /* doing some header verification */ if (!SAV_VerifyHeader(&header)) { /* our header is not valid, we MUST abort loading the game! */ Com_Printf("The Header of the savegame '%s.%s' is corrupted. Loading aborted\n", filename, SAVEGAME_EXTENSION); Mem_Free(cbuf); *error = "Corrupted header"; return false; } Com_Printf("Loading savegame\n" "...version: %i\n" "...game version: %s\n" "...xml Size: %i, compressed? %c\n", header.version, header.gameVersion, header.xmlSize, header.compressed ? 'y' : 'n'); if (header.compressed) { uLongf len = header.xmlSize + 1 /* for '\0' */; byte* const buf = Mem_PoolAllocTypeN(byte, len /* sic, old savegames contain one (garbage) byte more than the header says. */, cp_campaignPool); /* uncompress data, skipping comment header */ const int res = uncompress(buf, &len, cbuf + sizeof(header), clen - sizeof(header)); buf[header.xmlSize] = '\0'; /* Ensure '\0' termination. */ Mem_Free(cbuf); if (res != Z_OK) { Mem_Free(buf); *error = _("Error decompressing data"); Com_Printf("Error decompressing data in '%s'.\n", filename); return false; } topNode = XML_Parse((const char*)buf); if (!topNode) { Mem_Free(buf); Com_Printf("Error: Failure in loading the xml data!\n"); *error = "Corrupted xml data"; return false; } Mem_Free(buf); } else { topNode = XML_Parse((const char*)(cbuf + sizeof(header))); Mem_Free(cbuf); if (!topNode) { Com_Printf("Error: Failure in loading the xml data!\n"); *error = "Corrupted xml data"; return false; } } /* doing a subsystem run */ GAME_ReloadMode(); node = XML_GetNode(topNode, SAVE_ROOTNODE); if (!node) { Com_Printf("Error: Failure in loading the xml data! (savegame node not found)\n"); mxmlDelete(topNode); *error = "Invalid xml data"; return false; } Com_Printf("Load '%s' %d subsystems\n", filename, saveSubsystemsAmount); for (i = 0; i < saveSubsystemsAmount; i++) { Com_Printf("...Running subsystem '%s'\n", saveSubsystems[i].name); if (!saveSubsystems[i].load(node)) { Com_Printf("...subsystem '%s' returned false - savegame could not be loaded\n", saveSubsystems[i].name); *error = va("Could not load subsystem %s", saveSubsystems[i].name); return false; } else Com_Printf("...subsystem '%s' - loaded.\n", saveSubsystems[i].name); } mxmlDelete(node); mxmlDelete(topNode); if (!SAV_GameActionsAfterLoad()) { Com_Printf("Savegame postprocessing returned false - savegame could not be loaded\n"); *error = "Postprocessing failed"; return false; } Com_Printf("File '%s' successfully loaded from %s xml savegame.\n", filename, header.compressed ? "compressed" : ""); cgi->UI_InitStack("geoscape", NULL, true, true); return true; }