/** * @brief Save callback for savegames in xml * @param[out] p XML Node structure, where we write the information to * @sa INS_LoadXML * @sa SAV_GameSaveXML */ qboolean INS_SaveXML (xmlNode_t *p) { xmlNode_t *n; installation_t *inst; n = XML_AddNode(p, SAVE_INSTALLATION_INSTALLATIONS); Com_RegisterConstList(saveInstallationConstants); INS_Foreach(inst) { xmlNode_t *s, *ss; s = XML_AddNode(n, SAVE_INSTALLATION_INSTALLATION); XML_AddString(s, SAVE_INSTALLATION_TEMPLATEID, inst->installationTemplate->id); XML_AddInt(s, SAVE_INSTALLATION_IDX, inst->idx); XML_AddString(s, SAVE_INSTALLATION_NAME, inst->name); XML_AddPos3(s, SAVE_INSTALLATION_POS, inst->pos); XML_AddString(s, SAVE_INSTALLATION_STATUS, Com_GetConstVariable(SAVE_INSTALLATIONSTATUS_NAMESPACE, inst->installationStatus)); XML_AddInt(s, SAVE_INSTALLATION_DAMAGE, inst->installationDamage); XML_AddFloat(s, SAVE_INSTALLATION_ALIENINTEREST, inst->alienInterest); XML_AddInt(s, SAVE_INSTALLATION_BUILDSTART, inst->buildStart); ss = XML_AddNode(s, SAVE_INSTALLATION_BATTERIES); XML_AddIntValue(ss, SAVE_INSTALLATION_NUM, inst->numBatteries); B_SaveBaseSlotsXML(inst->batteries, inst->numBatteries, ss); } Com_UnregisterConstList(saveInstallationConstants); return qtrue; }
/** * @brief Save callback for savegames in XML Format * @param[out] parent XML Node structure, where we write the information to */ bool INT_SaveXML (xmlNode_t *parent) { xmlNode_t *interestsNode = XML_AddNode(parent, SAVE_INTERESTS); int i; XML_AddShortValue(interestsNode, SAVE_INTERESTS_LASTINCREASEDELAY, ccs.lastInterestIncreaseDelay); XML_AddShortValue(interestsNode, SAVE_INTERESTS_LASTMISSIONSPAWNEDDELAY, ccs.lastMissionSpawnedDelay); XML_AddShortValue(interestsNode, SAVE_INTERESTS_OVERALL, ccs.overallInterest); Com_RegisterConstList(saveInterestConstants); for (i = 0; i < INTERESTCATEGORY_MAX; i++) { xmlNode_t * interestNode = XML_AddNode(interestsNode, SAVE_INTERESTS_INTEREST); XML_AddString(interestNode, SAVE_INTERESTS_ID, Com_GetConstVariable(SAVE_INTERESTCAT_NAMESPACE, i)); XML_AddShort(interestNode, SAVE_INTERESTS_VAL, ccs.interest[i]); } Com_UnregisterConstList(saveInterestConstants); return true; }
/** * @brief Save callback for savegames in XML Format * @param[out] parent XML Node structure, where we write the information to */ qboolean AIRFIGHT_SaveXML (xmlNode_t *parent) { int i; for (i = 0; i < ccs.numProjectiles; i++) { int j; aircraftProjectile_t *projectile = &ccs.projectiles[i]; xmlNode_t *node = XML_AddNode(parent, SAVE_AIRFIGHT_PROJECTILE); XML_AddString(node, SAVE_AIRFIGHT_ITEMID, projectile->aircraftItem->id); for (j = 0; j < projectile->numProjectiles; j++) XML_AddPos2(node, SAVE_AIRFIGHT_POS, projectile->pos[j]); XML_AddPos3(node, SAVE_AIRFIGHT_IDLETARGET, projectile->idleTarget); XML_AddInt(node, SAVE_AIRFIGHT_TIME, projectile->time); XML_AddFloat(node, SAVE_AIRFIGHT_ANGLE, projectile->angle); XML_AddBoolValue(node, SAVE_AIRFIGHT_BULLET, projectile->bullets); XML_AddBoolValue(node, SAVE_AIRFIGHT_BEAM, projectile->beam); if (projectile->attackingAircraft) { xmlNode_t *attacking = XML_AddNode(node, SAVE_AIRFIGHT_ATTACKINGAIRCRAFT); XML_AddBoolValue(attacking, SAVE_AIRFIGHT_ISUFO, projectile->attackingAircraft->type == AIRCRAFT_UFO); if (projectile->attackingAircraft->type == AIRCRAFT_UFO) XML_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, UFO_GetGeoscapeIDX(projectile->attackingAircraft)); else XML_AddInt(attacking, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->attackingAircraft->idx); } if (projectile->aimedAircraft) { xmlNode_t *aimed = XML_AddNode(node, SAVE_AIRFIGHT_AIMEDAIRCRAFT); XML_AddBoolValue(aimed, SAVE_AIRFIGHT_ISUFO, projectile->aimedAircraft->type == AIRCRAFT_UFO); if (projectile->aimedAircraft->type == AIRCRAFT_UFO) XML_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, UFO_GetGeoscapeIDX(projectile->aimedAircraft)); else XML_AddInt(aimed, SAVE_AIRFIGHT_AIRCRAFTIDX, projectile->aimedAircraft->idx); } } return qtrue; }
/** * @brief This is a savegame function which stores the game in xml-Format. * @param[in] filename The Filename to save to (without extension) * @param[in] comment Description of the savegame * @param[out] error On failure an errormessage may be set. */ static bool SAV_GameSave (const char *filename, const char *comment, char **error) { xmlNode_t *topNode, *node; char savegame[MAX_OSPATH]; int res; int requiredBufferLength; uLongf bufLen; saveFileHeader_t header; char dummy[2]; int i; dateLong_t date; char message[30]; char timeStampBuffer[32]; if (!CP_IsRunning()) { *error = _("No campaign active."); Com_Printf("Error: No campaign active.\n"); return false; } if (!B_AtLeastOneExists()) { *error = _("Nothing to save yet."); Com_Printf("Error: Nothing to save yet.\n"); return false; } Com_MakeTimestamp(timeStampBuffer, sizeof(timeStampBuffer)); Com_sprintf(savegame, sizeof(savegame), "save/%s.%s", filename, SAVEGAME_EXTENSION); topNode = mxmlNewXML("1.0"); node = XML_AddNode(topNode, SAVE_ROOTNODE); /* writing Header */ XML_AddInt(node, SAVE_SAVEVERSION, SAVE_FILE_VERSION); XML_AddString(node, SAVE_COMMENT, comment); XML_AddString(node, SAVE_UFOVERSION, UFO_VERSION); XML_AddString(node, SAVE_REALDATE, timeStampBuffer); CP_DateConvertLong(&ccs.date, &date); Com_sprintf(message, sizeof(message), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day); XML_AddString(node, SAVE_GAMEDATE, message); /* working through all subsystems. perhaps we should redesign it, order is not important anymore */ Com_Printf("Calling subsystems\n"); for (i = 0; i < saveSubsystemsAmount; i++) { if (!saveSubsystems[i].save(node)) Com_Printf("...subsystem '%s' failed to save the data\n", saveSubsystems[i].name); else Com_Printf("...subsystem '%s' - saved\n", saveSubsystems[i].name); } /* calculate the needed buffer size */ OBJZERO(header); header.compressed = LittleLong(save_compressed->integer); header.version = LittleLong(SAVE_FILE_VERSION); header.subsystems = LittleLong(saveSubsystemsAmount); Q_strncpyz(header.name, comment, sizeof(header.name)); Q_strncpyz(header.gameVersion, UFO_VERSION, sizeof(header.gameVersion)); CP_DateConvertLong(&ccs.date, &date); Com_sprintf(header.gameDate, sizeof(header.gameDate), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day); Q_strncpyz(header.realDate, timeStampBuffer, sizeof(header.realDate)); requiredBufferLength = mxmlSaveString(topNode, dummy, 2, MXML_NO_CALLBACK); header.xmlSize = LittleLong(requiredBufferLength); byte* const buf = Mem_PoolAllocTypeN(byte, requiredBufferLength + 1, cp_campaignPool); if (!buf) { mxmlDelete(topNode); *error = _("Could not allocate enough memory to save this game"); Com_Printf("Error: Could not allocate enough memory to save this game\n"); return false; } res = mxmlSaveString(topNode, (char*)buf, requiredBufferLength + 1, MXML_NO_CALLBACK); mxmlDelete(topNode); Com_Printf("XML Written to buffer (%d Bytes)\n", res); if (header.compressed) bufLen = compressBound(requiredBufferLength); else bufLen = requiredBufferLength; byte* const fbuf = Mem_PoolAllocTypeN(byte, bufLen + sizeof(header), cp_campaignPool); memcpy(fbuf, &header, sizeof(header)); if (header.compressed) { res = compress(fbuf + sizeof(header), &bufLen, buf, requiredBufferLength); Mem_Free(buf); if (res != Z_OK) { Mem_Free(fbuf); *error = _("Memory error compressing save-game data - set save_compressed cvar to 0"); Com_Printf("Memory error compressing save-game data (%s) (Error: %i)!\n", comment, res); return false; } } else { memcpy(fbuf + sizeof(header), buf, requiredBufferLength); Mem_Free(buf); } /* last step - write data */ res = FS_WriteFile(fbuf, bufLen + sizeof(header), savegame); Mem_Free(fbuf); return true; }
/** * @brief add a non-empty String attribute to the XML Node * @param[out] parent XML Node structure to add to * @param[in] name Name of the attribute * @param[in] value Value of the attribute * @note if the value is empty nothing will be added */ void XML_AddStringValue (xmlNode_t* parent, const char* name, const char* value) { if (Q_strnull(value)) return; XML_AddString(parent, name, value); }