コード例 #1
0
ファイル: sv_init.cpp プロジェクト: Isaacssv552/ufoai
/**
 * @brief A brand new game has been started
 */
static void SV_InitGame (void)
{
	/* allow next change after map change or restart */
	sv_maxclients->flags |= CVAR_LATCH;

	/* get any latched variable changes (sv_maxclients, etc) */
	Cvar_UpdateLatchedVars();

	if (svs.serverMutex)
		Sys_Error("There is still a server running");

	svs.clients     = Mem_PoolAllocTypeN(client_t, sv_maxclients->integer, sv_genericPool);
	svs.serverMutex = SDL_CreateMutex();

	/* init network stuff */
	if (sv_maxclients->integer > 1) {
		svs.initialized = SV_Start(nullptr, port->string, &SV_ReadPacket);
		svs.netDatagramSocket = NET_DatagramSocketNew(nullptr, port->string, &SV_DiscoveryCallback);
	} else {
		svs.initialized = SV_Start(nullptr, nullptr, &SV_ReadPacket);
	}

	SV_Heartbeat_f();

	/* init game */
	SV_InitGameProgs();

	if (sv_maxclients->integer > 1 && (sv_dedicated->integer || sv_public->integer))
		SV_SetMaster_f();
}
コード例 #2
0
/**
 * @brief Loads brush entities like func_door and func_breakable
 * @sa CMod_LoadSubmodels
 */
static void R_ModLoadSubmodels (const lump_t *l)
{
	const dBspModel_t *in;
	int i, j, count;

	in = (const dBspModel_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadSubmodels: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspHeader_t* out = Mem_PoolAllocTypeN(mBspHeader_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...submodels: %i\n", count);

	r_worldmodel->bsp.submodels = out;
	r_worldmodel->bsp.numsubmodels = count;

	for (i = 0; i < count; i++, in++, out++) {
		/* spread the mins / maxs by a pixel */
		for (j = 0; j < 3; j++) {
			out->mins[j] = LittleFloat(in->mins[j]) - 1.0f + (float)shift[j];
			out->maxs[j] = LittleFloat(in->maxs[j]) + 1.0f + (float)shift[j];
			out->origin[j] = LittleFloat(in->origin[j]) + (float)shift[j];
		}
		out->radius = R_RadiusFromBounds(out->mins, out->maxs);
		out->headnode = LittleLong(in->headnode);
		out->firstface = LittleLong(in->firstface);
		out->numfaces = LittleLong(in->numfaces);
	}
}
コード例 #3
0
ファイル: ui_main.cpp プロジェクト: Ed-von-Schleck/ufoai
void UI_Init (void)
{
	cvar_t* ui_hunkSize = Cvar_Get("ui_hunksize", "3", 0, "UI memory hunk size in megabytes");

#ifdef DEBUG
	ui_debug = Cvar_Get("debug_ui", "0", CVAR_DEVELOPER, "Prints node names for debugging purposes - valid values are 1 and 2");
#endif

	/* reset global UI structures */
	OBJZERO(ui_global);

	ui_sounds = Cvar_Get("ui_sounds", "1", CVAR_ARCHIVE, "Activates UI sounds");

#ifdef DEBUG
	Cmd_AddCommand("debug_uimemory", UI_Memory_f, "Display info about UI memory allocation");
#endif

	Cmd_AddCommand("ui_restart", UI_Restart_f, "Reload the whole ui");

	ui_sysPool = Mem_CreatePool("Client: UI");
	ui_dynStringPool = Mem_CreatePool("Client: Dynamic string for UI");
	ui_dynPool = Mem_CreatePool("Client: Dynamic memory for UI");

	Com_Printf("Allocate %i megabytes for the ui hunk\n", ui_hunkSize->integer);
	ui_global.adataize = ui_hunkSize->integer * 1024 * 1024;
	ui_global.adata    = Mem_PoolAllocTypeN(byte, ui_global.adataize, ui_sysPool);
	ui_global.curadata = ui_global.adata;

	UI_InitData();
	UI_InitNodes();
	UI_InitWindows();
	UI_InitDraw();
	UI_InitActions();
}
コード例 #4
0
ファイル: r_model.cpp プロジェクト: MyWifeRules/ufoai-1
/**
 * @brief Load actor skins from a default skin to a a mesh.
 * @param outMesh Mesh target of skins
 * @param defaultSkin Default skin of the mesh
 */
void R_LoadActorSkinsFromModel (mAliasMesh_t *outMesh, image_t * defaultSkin)
{
	int i;
	assert(outMesh);

	outMesh->num_skins = r_numActorSkinName;
	outMesh->skins = Mem_PoolAllocTypeN(mAliasSkin_t, outMesh->num_skins, vid_modelPool);

	if (defaultSkin == r_noTexture)
		Com_Printf("R_LoadActorSkinsFromModel: No default skin found for model \"%s\"\n", outMesh->name);

	for (i = 0; i < outMesh->num_skins; i++) {
		mAliasSkin_t *modelSkin = &outMesh->skins[i];
		if (i == 0) {
			modelSkin->skin = defaultSkin;
		} else {
			const char *skin = R_GetActorSkin(i);
			modelSkin->skin = R_AliasModelGetSkin(NULL, va("%s_%s", defaultSkin->name, skin));
			/** @todo should we add warning here? */
			if (modelSkin->skin == r_noTexture)
				modelSkin->skin = defaultSkin;
		}
		Q_strncpyz(modelSkin->name, modelSkin->skin->name, sizeof(outMesh->skins[i].name));
	}
}
コード例 #5
0
/**
 * @brief Load the lightmap data
 */
static void R_ModLoadLighting (const lump_t *l)
{
	/* map has no lightmap */
	if (l->filelen == 0)
		return;

	r_worldmodel->bsp.lightdata  = Mem_PoolAllocTypeN(byte, l->filelen, vid_lightPool);
	r_worldmodel->bsp.lightquant = *(const byte *) (mod_base + l->fileofs);
	memcpy(r_worldmodel->bsp.lightdata, mod_base + l->fileofs, l->filelen);
}
コード例 #6
0
/**
 * @sa TR_BuildTracingNode_r
 * @sa R_RecurseSetParent
 */
static void R_ModLoadNodes (const lump_t *l)
{
	int i, j, count;
	const dBspNode_t *in;
	mBspNode_t *parent = NULL;

	in = (const dBspNode_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadNodes: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspNode_t* out = Mem_PoolAllocTypeN(mBspNode_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...nodes: %i\n", count);

	r_worldmodel->bsp.nodes = out;
	r_worldmodel->bsp.numnodes = count;

	for (i = 0; i < count; i++, in++, out++) {
		const int p = LittleLong(in->planenum);

		/* skip special pathfinding nodes - they have a negative index */
		if (p == PLANENUM_LEAF) {
			/* in case of "special" pathfinding nodes (they don't have a plane)
			 * we have to set this to NULL */
			out->plane = NULL;
			out->contents = CONTENTS_PATHFINDING_NODE;
			parent = NULL;
		} else {
			out->plane = r_worldmodel->bsp.planes + p;
			/* differentiate from leafs */
			out->contents = CONTENTS_NODE;
			parent = out;
		}

		for (j = 0; j < 3; j++) {
			out->minmaxs[j] = LittleShort(in->mins[j]) + (float)shift[j];
			out->minmaxs[3 + j] = LittleShort(in->maxs[j]) + (float)shift[j];
		}

		out->firstsurface = LittleShort(in->firstface);
		out->numsurfaces = LittleShort(in->numfaces);

		for (j = 0; j < 2; j++) {
			const int p2 = LittleLong(in->children[j]);
			if (p2 > LEAFNODE) {
				assert(p2 < r_worldmodel->bsp.numnodes);
				out->children[j] = r_worldmodel->bsp.nodes + p2;
			} else {
				assert((LEAFNODE - p2) < r_worldmodel->bsp.numleafs);
				out->children[j] = (mBspNode_t *) (r_worldmodel->bsp.leafs + (LEAFNODE - p2));
			}
			out->children[j]->parent = parent;
		}
	}
}
コード例 #7
0
ファイル: list.cpp プロジェクト: ibrahimmusba/ufoai
/**
 * @brief Adds an entry to a new or to an already existing linked list
 * @sa LIST_AddString
 * @sa LIST_RemoveEntry
 * @return Returns a pointer to the data that has been added, wrapped in a linkedList_t
 * @todo Optimize this to not allocate memory for every entry - but use a hunk
 */
linkedList_t *LIST_Add (linkedList_t **listDest, void const* data, size_t length)
{
	assert(listDest);
	assert(data);

	void*         const dataCopy = memcpy(Mem_PoolAllocTypeN(byte, length, com_genericPool), data, length);
	linkedList_t* const newEntry = LIST_AllocateEntry(dataCopy);

	LIST_AppendEntry(listDest, newEntry);

	return newEntry;
}
コード例 #8
0
static void R_ModLoadEdges (const lump_t *l)
{
	const dBspEdge_t *in;
	int i, count;

	in = (const dBspEdge_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadEdges: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspEdge_t* out = Mem_PoolAllocTypeN(mBspEdge_t, count + 1, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...edges: %i\n", count);

	r_worldmodel->bsp.edges = out;
	r_worldmodel->bsp.numedges = count;

	for (i = 0; i < count; i++, in++, out++) {
		out->v[0] = (unsigned short) LittleShort(in->v[0]);
		out->v[1] = (unsigned short) LittleShort(in->v[1]);
	}
}
コード例 #9
0
static void R_ModLoadVertexes (const lump_t *l)
{
	const dBspVertex_t *in;
	int i, count;

	in = (const dBspVertex_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadVertexes: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspVertex_t* out = Mem_PoolAllocTypeN(mBspVertex_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...verts: %i\n", count);

	r_worldmodel->bsp.vertexes = out;
	r_worldmodel->bsp.numvertexes = count;

	for (i = 0; i < count; i++, in++, out++) {
		out->position[0] = LittleFloat(in->point[0]);
		out->position[1] = LittleFloat(in->point[1]);
		out->position[2] = LittleFloat(in->point[2]);
	}
}
コード例 #10
0
/**
 * @sa CMod_LoadPlanes
 */
static void R_ModLoadPlanes (const lump_t *l)
{
	int i, j;
	const dBspPlane_t *in;
	int count;

	in = (const dBspPlane_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadPlanes: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	cBspPlane_t* out = Mem_PoolAllocTypeN(cBspPlane_t, count * 2, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...planes: %i\n", count);

	r_worldmodel->bsp.planes = out;
	r_worldmodel->bsp.numplanes = count;

	for (i = 0; i < count; i++, in++, out++) {
		for (j = 0; j < 3; j++)
			out->normal[j] = LittleFloat(in->normal[j]);
		out->dist = LittleFloat(in->dist);
		out->type = LittleLong(in->type);
	}
}
コード例 #11
0
static void R_ModLoadLeafs (const lump_t *l)
{
	const dBspLeaf_t *in;
	int i, j, count;

	in = (const dBspLeaf_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadLeafs: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspLeaf_t* out = Mem_PoolAllocTypeN(mBspLeaf_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...leafs: %i\n", count);

	r_worldmodel->bsp.leafs = out;
	r_worldmodel->bsp.numleafs = count;

	for (i = 0; i < count; i++, in++, out++) {
		for (j = 0; j < 3; j++) {
			out->minmaxs[j] = LittleShort(in->mins[j]) + (float)shift[j];
			out->minmaxs[3 + j] = LittleShort(in->maxs[j]) + (float)shift[j];
		}

		out->contents = LittleLong(in->contentFlags);
	}
}
コード例 #12
0
/**
 * @sa CP_StartMissionMap
 */
static void R_ModLoadTexinfo (const lump_t *l)
{
	const dBspTexinfo_t *in;
	int i, j, count;
	char name[MAX_QPATH];

	in = (const dBspTexinfo_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadTexinfo: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspTexInfo_t* out = Mem_PoolAllocTypeN(mBspTexInfo_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...texinfo: %i\n", count);

	r_worldmodel->bsp.texinfo = out;
	r_worldmodel->bsp.numtexinfo = count;

	const char *mapZone = Cvar_GetString("sv_mapzone");
	for (i = 0; i < count; i++, in++, out++) {
		for (j = 0; j < 3; j++) {
			out->uv[j] = LittleFloat(in->vecs[0][j]);
			out->vv[j] = LittleFloat(in->vecs[1][j]);
		}
		out->u_offset = LittleFloat(in->vecs[0][3]);
		out->v_offset = LittleFloat(in->vecs[1][3]);

		out->flags = LittleLong(in->surfaceFlags);

		/* exchange the textures with the ones that are needed for base assembly */
		if (mapZone && strstr(in->texture, "tex_terrain/dummy"))
			Com_sprintf(name, sizeof(name), "textures/tex_terrain/%s", mapZone);
		else
			Com_sprintf(name, sizeof(name), "textures/%s", in->texture);

		out->image = R_FindImage(name, it_world);
	}
}
コード例 #13
0
ファイル: r_program.cpp プロジェクト: ArkyRomania/ufoai
/**
 * @brief Do our own preprocessing to the shader file, before the
 * GLSL implementation calls it's preprocessor.
 *
 * "#if/#endif" pairs, "#unroll", "#endunroll", "#include", "#replace" are handled by our
 * preprocessor, not the GLSL implementation's preprocessor (except "#include" which may
 * also be handled by the implementation's preprocessor).  "#if" operates off
 * of the value of a cvar interpreted as a bool. Note the GLSL implementation
 * preprocessor handles "#ifdef" and "#ifndef", not "#if".
 * @param[in] name The file name of the shader (e.g. "world_fs.glsl").
 * @param[in] inPtr The non-preprocessed shader string.
 * @param[in,out] out The preprocessed shader string, nullptr if we don't want to write to it.
 * @param[in,out] remainingOutChars The number of characters left in the out buffer.
 * @param[in] nested If true, parsing a part of "#if" clause, so "#else" and "#endif" tokens are allowed
 * @param[in] inElse If true, parsing an "#else" clause and shouldn't expect another "#else"
 * @return The number of characters added to the buffer pointed to by out.
 */
static size_t R_PreprocessShaderR (const char* name, const char** inPtr, char* out, long* remainingOutChars, bool nested, bool inElse)
{
	const size_t INITIAL_REMAINING_OUT_CHARS = (size_t)*remainingOutChars;
	/* Keep looping till we reach the end of the shader string, or a parsing error.*/
	while (**inPtr) {
		if ('#' == **inPtr) {
			bool endBlockToken;
			(*inPtr)++;

			endBlockToken = !strncmp(*inPtr, "endif", 5);

			if (!strncmp(*inPtr, "else", 4)) {
				if (inElse) {
					/* Error in shader! Print a message saying our preprocessor failed parsing.*/
					Com_Error(ERR_DROP, "R_PreprocessShaderR: #else without #if: %s", name);
				}
				endBlockToken = true;
			}

			if (endBlockToken) {
				if (!nested) {
					/* Error in shader! Print a message saying our preprocessor failed parsing.*/
					Com_Error(ERR_DROP, "R_PreprocessShaderR: Unmatched #endif/#else: %s", name);
				}
				/* Whoever called us will have to deal with closing the block */
				return (INITIAL_REMAINING_OUT_CHARS - (size_t)*remainingOutChars);
			}

			if (!strncmp((*inPtr), "if ", 3)) {
				/* The line looks like "#if r_postprocess".*/
				(*inPtr) += 3;
				/* Get the corresponding cvar value.*/
				float f = Cvar_GetValue(Com_Parse(inPtr));
				if (f) { /* Condition is true, recursively preprocess #if block, and skip over #else block, if any */
					int size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, false);
					if (out) out += size;

					if (!strncmp((*inPtr), "else", 4)) {/* Preprocess and skip #else block */
						(*inPtr) +=4 ;
						R_PreprocessShaderR(name, inPtr, (char*)0, remainingOutChars, true, true);
					}
				} else {
					/* The cvar was false, don't add to out. Lets look and see if we hit a #else, or #endif.*/
					R_PreprocessShaderR(name, inPtr, (char*)0, remainingOutChars, true, false);
					if (!strncmp((*inPtr), "else", 4)) {
						int size;
						/* All right, we want to add this to out.*/
						(*inPtr) +=4 ;
						size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, true);
						if (out) out += size;
					}
				}
				/* skip #endif, if any (could also get here by unexpected EOF */
				if (!strncmp((*inPtr), "endif", 5))
					(*inPtr) +=5 ;
			} else if (!strncmp((*inPtr), "ifndef", 6) || !strncmp((*inPtr), "ifdef", 5)) { /* leave those for GLSL compiler, but follow #else/#endif nesting */
				int size;
				if (out) {
					if (*remainingOutChars <= 0)
						Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
					*out++ = '#';
					(*remainingOutChars)--;
				}

				size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, false);
				if (out) out += size;

				if (!strncmp((*inPtr), "else", 4)) {
					if (out) {
						if (*remainingOutChars <= 0)
							Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
						*out++ = '#';
						(*remainingOutChars)--;
					}
					size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, true);
					if (out) out += size;
				}

				if (out) {
					if (*remainingOutChars <= 0)
						Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
					*out++ = '#';
					(*remainingOutChars)--;
				}

			} else if (!strncmp((*inPtr), "include", 7)) {
				char path[MAX_QPATH];
				byte* buf = (byte*)0;
				const char* bufAsChar = (const char*)0;
				const char** bufAsCharPtr = (const char**)0;
				(*inPtr) += 8;
				Com_sprintf(path, sizeof(path), "shaders/%s", Com_Parse(inPtr));
				if (FS_LoadFile(path, &buf) == -1) {
					Com_Printf("Failed to resolve #include: %s.\n", path);
					continue;
				}
				bufAsChar = (const char*)buf;
				bufAsCharPtr = &bufAsChar;
				if (out) {
					out += R_PreprocessShaderR(name, bufAsCharPtr, out, remainingOutChars, nested, false);
				} else {
					R_PreprocessShaderR(name, bufAsCharPtr, out, remainingOutChars, nested, false);
				}
				FS_FreeFile(buf);
			} else if (!strncmp((*inPtr), "unroll", 6)) {
				/* loop unrolling */
				size_t subLength = 0;
				byte* const buffer = Mem_PoolAllocTypeN(byte, SHADER_BUF_SIZE, vid_imagePool);
				(*inPtr) += 6;
				int z = Cvar_GetValue(Com_Parse(inPtr));
				while (*(*inPtr)) {
					if (!strncmp((*inPtr), "#endunroll", 10)) {
						(*inPtr) += 10;
						break;
					}
					buffer[subLength++] = *(*inPtr)++;
					if (subLength >= SHADER_BUF_SIZE)
						Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
				}
				if (out) {
					for (int j = 0; j < z; j++) {
						for (int l = 0; l < subLength; l++) {
							if (buffer[l] == '$') {
								byte insertedLen = (j / 10) + 1;
								if (!Com_sprintf(out, (size_t)*remainingOutChars, "%d", j))
									Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
								out += insertedLen;
								(*remainingOutChars) -= insertedLen;
							} else {
								if (*remainingOutChars <= 0)
									Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
								*out++ = buffer[l];
								(*remainingOutChars)--;
							}
						}
					}
				}
				Mem_Free(buffer);
			} else if (!strncmp((*inPtr), "replace", 7)) {
				int r = 0;
				(*inPtr) += 8;
				r = Cvar_GetValue(Com_Parse(inPtr));
				if (out) {
					byte insertedLen = 0;
					if (!Com_sprintf(out, (size_t)*remainingOutChars, "%d", r))
						Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
					insertedLen = (r / 10) + 1;
					out += insertedLen;
					(*remainingOutChars) -= insertedLen;
				}
			} else {
				/* general case is to copy so long as the buffer has room */
				if (out) {
					if (*remainingOutChars <= 0)
						Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
					*out++ = '#';
					(*remainingOutChars)--;
				}
			}
		} else {
			/* general case is to copy so long as the buffer has room */
			if (out) {
				if (*remainingOutChars <= 0)
					Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
				*out++ = *(*inPtr);
				(*remainingOutChars)--;
			}
			(*inPtr)++;
		}
	}
	/* Return the number of characters added to the buffer.*/
	return (INITIAL_REMAINING_OUT_CHARS - *remainingOutChars);
}
コード例 #14
0
ファイル: r_misc.cpp プロジェクト: MyWifeRules/ufoai-1
/**
 * Take a screenshot of the frame buffer
 * @param[in] x
 * @param[in] y
 * @param[in] width
 * @param[in] height
 * @param[in] filename Force to use a filename. Else NULL to autogen a filename
 * @param[in] ext Force to use an image format (tga/png/jpg). Else NULL to use value of r_screenshot_format
 */
void R_ScreenShot (int x, int y, int width, int height, const char *filename, const char *ext)
{
	char	checkName[MAX_OSPATH];
	int		type, shotNum, quality = 100;
	qFILE	f;
	int rowPack;

	glGetIntegerv(GL_PACK_ALIGNMENT, &rowPack);
	glPixelStorei(GL_PACK_ALIGNMENT, 1);

	/* Find out what format to save in */
	if (ext == NULL)
		ext = r_screenshot_format->string;

	if (!Q_strcasecmp(ext, "png"))
		type = SSHOTTYPE_PNG;
	else if (!Q_strcasecmp(ext, "jpg"))
		type = SSHOTTYPE_JPG;
	else
		type = SSHOTTYPE_TGA_COMP;

	/* Set necessary values */
	switch (type) {
	case SSHOTTYPE_TGA_COMP:
		Com_Printf("Taking TGA screenshot...\n");
		ext = "tga";
		break;

	case SSHOTTYPE_PNG:
		Com_Printf("Taking PNG screenshot...\n");
		ext = "png";
		break;

	case SSHOTTYPE_JPG:
		if (Cmd_Argc() == 3)
			quality = atoi(Cmd_Argv(2));
		else
			quality = r_screenshot_jpeg_quality->integer;
		if (quality > 100 || quality <= 0)
			quality = 100;

		Com_Printf("Taking JPG screenshot (at %i%% quality)...\n", quality);
		ext = "jpg";
		break;
	}

	/* Find a file name to save it to */
	if (filename) {
		Com_sprintf(checkName, sizeof(checkName), "scrnshot/%s.%s", filename, ext);
	} else {
		for (shotNum = 0; shotNum < 1000; shotNum++) {
			Com_sprintf(checkName, sizeof(checkName), "scrnshot/ufo%i%i.%s", shotNum / 10, shotNum % 10, ext);
			if (FS_CheckFile("%s", checkName) == -1)
				break;
		}
		if (shotNum == 1000) {
			Com_Printf("R_ScreenShot_f: screenshot limit (of 1000) exceeded!\n");
			return;
		}
	}

	/* Open it */
	FS_OpenFile(checkName, &f, FILE_WRITE);
	if (!f.f) {
		Com_Printf("R_ScreenShot_f: Couldn't create file: %s\n", checkName);
		return;
	}

	/* Allocate room for a copy of the framebuffer */
	byte* const buffer = Mem_PoolAllocTypeN(byte, width * height * 3, vid_imagePool);
	if (!buffer) {
		Com_Printf("R_ScreenShot_f: Could not allocate %i bytes for screenshot!\n", width * height * 3);
		FS_CloseFile(&f);
		return;
	}

	/* Read the framebuffer into our storage */
	glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer);
	R_CheckError();

	/* Write */
	switch (type) {
	case SSHOTTYPE_TGA_COMP:
		R_WriteCompressedTGA(&f, buffer, width, height);
		break;

	case SSHOTTYPE_PNG:
		R_WritePNG(&f, buffer, width, height);
		break;

	case SSHOTTYPE_JPG:
		R_WriteJPG(&f, buffer, width, height, quality);
		break;
	}

	/* Finish */
	FS_CloseFile(&f);
	Mem_Free(buffer);

	Com_Printf("Wrote %s to %s\n", checkName, FS_Gamedir());
	glPixelStorei(GL_PACK_ALIGNMENT, rowPack);
}
コード例 #15
0
ファイル: r_lightmap.cpp プロジェクト: Qazzian/ufoai_suspend
/**
 * @brief Consume raw lightmap and deluxemap RGB/XYZ data from the surface samples,
 * writing processed lightmap and deluxemap RGBA to the specified destinations.
 * @sa R_BuildDefaultLightmap
 */
static void R_BuildLightmap (mBspSurface_t *surf, byte *sout, byte *dout, int stride)
{
	unsigned int i, j;
	byte *lm, *l, *dm;

	const int smax = (surf->stextents[0] / surf->lightmap_scale) + 1;
	const int tmax = (surf->stextents[1] / surf->lightmap_scale) + 1;
	const int size = smax * tmax;
	stride -= (smax * LIGHTMAP_BLOCK_BYTES);

	byte* const lightmap = Mem_PoolAllocTypeN(byte, size * LIGHTMAP_BLOCK_BYTES, vid_lightPool);
	lm = lightmap;

	byte* const deluxemap = Mem_PoolAllocTypeN(byte, size * DELUXEMAP_BLOCK_BYTES, vid_lightPool);
	dm = deluxemap;

	/* convert the raw lightmap samples to floating point and scale them */
	for (i = j = 0; i < size; i++, lm += LIGHTMAP_BLOCK_BYTES, dm += DELUXEMAP_BLOCK_BYTES) {
		lm[0] = surf->samples[j++];
		lm[1] = surf->samples[j++];
		lm[2] = surf->samples[j++];

		/* read in directional samples for deluxe mapping as well */
		dm[0] = surf->samples[j++];
		dm[1] = surf->samples[j++];
		dm[2] = surf->samples[j++];
	}

	/* apply modulate, contrast, resolve average surface color, etc.. */
	R_GetAverageSurfaceColor(lightmap, smax, tmax, surf->color, LIGHTMAP_BLOCK_BYTES);

	if (surf->texinfo->flags & (SURF_BLEND33 | SURF_ALPHATEST))
		surf->color[3] = 0.25;
	else if (surf->texinfo->flags & SURF_BLEND66)
		surf->color[3] = 0.50;
	else
		surf->color[3] = 1.0;

	/* soften it if it's sufficiently large */
	if (r_soften->integer && size > 128)
		for (i = 0; i < 4; i++) {
			R_SoftenTexture(lightmap, smax, tmax, LIGHTMAP_BLOCK_BYTES);
			R_SoftenTexture(deluxemap, smax, tmax, DELUXEMAP_BLOCK_BYTES);
		}

	/* the final lightmap is uploaded to the card via the strided lightmap
	 * block, and also cached on the surface for fast point lighting lookups */

	surf->lightmap = Mem_PoolAllocTypeN(byte, size * LIGHTMAP_BYTES, vid_lightPool);
	l = surf->lightmap;
	lm = lightmap;
	dm = deluxemap;

	for (i = 0; i < tmax; i++, sout += stride, dout += stride) {
		for (j = 0; j < smax; j++) {
			/* copy the lightmap to the strided block */
			sout[0] = lm[0];
			sout[1] = lm[1];
			sout[2] = lm[2];
			sout += LIGHTMAP_BLOCK_BYTES;

			/* and to the surface, discarding alpha */
			l[0] = lm[0];
			l[1] = lm[1];
			l[2] = lm[2];
			l += LIGHTMAP_BYTES;

			lm += LIGHTMAP_BLOCK_BYTES;

			/* lastly copy the deluxemap to the strided block */
			dout[0] = dm[0];
			dout[1] = dm[1];
			dout[2] = dm[2];
			dout += DELUXEMAP_BLOCK_BYTES;

			dm += DELUXEMAP_BLOCK_BYTES;
		}
	}

	Mem_Free(lightmap);
	Mem_Free(deluxemap);
}
コード例 #16
0
static void R_ModLoadSurfaces (bool day, const lump_t *l)
{
	const dBspSurface_t *in;
	int count, surfnum;

	in = (const dBspSurface_t *) (mod_base + l->fileofs);
	if (l->filelen % sizeof(*in))
		Com_Error(ERR_DROP, "R_ModLoadSurfaces: funny lump size in %s", r_worldmodel->name);
	count = l->filelen / sizeof(*in);
	mBspSurface_t* out = Mem_PoolAllocTypeN(mBspSurface_t, count, vid_modelPool);
	Com_DPrintf(DEBUG_RENDERER, "...faces: %i\n", count);

	r_worldmodel->bsp.surfaces = out;
	r_worldmodel->bsp.numsurfaces = count;

	for (surfnum = 0; surfnum < count; surfnum++, in++, out++) {
		uint16_t planenum;
		int16_t side;
		int ti;
		int i;

		out->firstedge = LittleLong(in->firstedge);
		out->numedges = LittleShort(in->numedges);

		/* resolve plane */
		planenum = LittleShort(in->planenum);
		out->plane = r_worldmodel->bsp.planes + planenum;

		/* and sideness */
		side = LittleShort(in->side);
		if (side) {
			out->flags |= MSURF_PLANEBACK;
			VectorNegate(out->plane->normal, out->normal);
		} else {
			VectorCopy(out->plane->normal, out->normal);
		}

		ti = LittleShort(in->texinfo);
		if (ti < 0 || ti >= r_worldmodel->bsp.numtexinfo)
			Com_Error(ERR_DROP, "R_ModLoadSurfaces: bad texinfo number");
		out->texinfo = r_worldmodel->bsp.texinfo + ti;

		out->lightmap_scale = (1 << r_worldmodel->bsp.lightquant);

		/* and size, texcoords, etc */
		R_SetSurfaceExtents(out, r_worldmodel);

		if (!(out->texinfo->flags & SURF_WARP))
			out->flags |= MSURF_LIGHTMAP;

		/* lastly lighting info */
		if (day)
			i = LittleLong(in->lightofs[LIGHTMAP_DAY]);
		else
			i = LittleLong(in->lightofs[LIGHTMAP_NIGHT]);

		if (i == -1)
			out->samples = NULL;
		else
			out->samples = r_worldmodel->bsp.lightdata + i;

		/* create lightmaps */
		R_CreateSurfaceLightmap(out);

		out->tile = r_numMapTiles - 1;
	}
}
コード例 #17
0
/**
 * @brief Puts the map data into buffers
 * @sa R_ModAddMapTile
 * @note Shift the verts after the texcoords for diffuse and lightmap are loaded
 * @sa R_ModShiftTile
 * @todo Don't use the buffers from r_state here - they might overflow
 * @todo Decrease MAX_GL_ARRAY_LENGTH to 32768 again when this is fixed
 */
static void R_LoadBspVertexArrays (model_t *mod)
{
	int i, j;
	int vertOfs, texCoordOfs, tangOfs;
	float *vecShifted;
	float soff, toff, s, t;
	float *point, *sdir, *tdir;
	vec4_t tangent;
	vec3_t binormal;
	mBspSurface_t *surf;
	mBspVertex_t *vert;
	int vertexCount, indexCount;

	vertOfs = texCoordOfs = tangOfs = 0;
	vertexCount = indexCount = 0;

	for (i = 0, surf = mod->bsp.surfaces; i < mod->bsp.numsurfaces; i++, surf++) {
		const int numedges = surf->numedges;
		vertexCount += numedges;
		if (numedges > 2) /* no triangles for degenerate polys */
			indexCount += (numedges - 2) * 3;
	}

	surf = mod->bsp.surfaces;

	/* allocate the vertex arrays */
	mod->bsp.texcoords   = Mem_PoolAllocTypeN(GLfloat, vertexCount * 2, vid_modelPool);
	mod->bsp.lmtexcoords = Mem_PoolAllocTypeN(GLfloat, vertexCount * 2, vid_modelPool);
	mod->bsp.verts       = Mem_PoolAllocTypeN(GLfloat, vertexCount * 3, vid_modelPool);
	mod->bsp.normals     = Mem_PoolAllocTypeN(GLfloat, vertexCount * 3, vid_modelPool);
	mod->bsp.tangents    = Mem_PoolAllocTypeN(GLfloat, vertexCount * 4, vid_modelPool);
	mod->bsp.indexes    = Mem_PoolAllocTypeN(GLint, indexCount, vid_modelPool); /* Will be filled at the end of map loading, after building surface lists */

	for (i = 0; i < mod->bsp.numsurfaces; i++, surf++) {
		surf->index = vertOfs / 3;
		surf->firstTriangle = -1; /* Mark as "no triangles generated yet" */

		for (j = 0; j < surf->numedges; j++) {
			const float *normal;
			const int index = mod->bsp.surfedges[surf->firstedge + j];

			/* vertex */
			if (index > 0) {  /* negative indices to differentiate which end of the edge */
				const mBspEdge_t *edge = &mod->bsp.edges[index];
				vert = &mod->bsp.vertexes[edge->v[0]];
			} else {
				const mBspEdge_t *edge = &mod->bsp.edges[-index];
				vert = &mod->bsp.vertexes[edge->v[1]];
			}

			point = vert->position;

			/* shift it for assembled maps */
			vecShifted = &mod->bsp.verts[vertOfs];
			/* origin (func_door, func_rotating) bmodels must not have shifted vertices,
			 * they are translated by their entity origin value */
			if (surf->isOriginBrushModel)
				VectorCopy(point, vecShifted);
			else
				VectorAdd(point, shift, vecShifted);

			/* texture directional vectors and offsets */
			sdir = surf->texinfo->uv;
			soff = surf->texinfo->u_offset;

			tdir = surf->texinfo->vv;
			toff = surf->texinfo->v_offset;

			/* texture coordinates */
			s = DotProduct(point, sdir) + soff;
			s /= surf->texinfo->image->width;

			t = DotProduct(point, tdir) + toff;
			t /= surf->texinfo->image->height;

			mod->bsp.texcoords[texCoordOfs + 0] = s;
			mod->bsp.texcoords[texCoordOfs + 1] = t;

			if (surf->flags & MSURF_LIGHTMAP) {  /* lightmap coordinates */
				s = DotProduct(point, sdir) + soff;
				s -= surf->stmins[0];
				s += surf->light_s * surf->lightmap_scale;
				s += surf->lightmap_scale / 2.0;
				s /= r_lightmaps.size * surf->lightmap_scale;

				t = DotProduct(point, tdir) + toff;
				t -= surf->stmins[1];
				t += surf->light_t * surf->lightmap_scale;
				t += surf->lightmap_scale / 2.0;
				t /= r_lightmaps.size * surf->lightmap_scale;
			}

			mod->bsp.lmtexcoords[texCoordOfs + 0] = s;
			mod->bsp.lmtexcoords[texCoordOfs + 1] = t;

			/* normal vectors */
			if ((surf->texinfo->flags & SURF_PHONG) && VectorNotEmpty(vert->normal))
				normal = vert->normal; /* phong shaded */
			else
				normal = surf->normal; /* per plane */

			memcpy(&mod->bsp.normals[vertOfs], normal, sizeof(vec3_t));

			/* tangent vector */
			TangentVectors(normal, sdir, tdir, tangent, binormal);
			memcpy(&mod->bsp.tangents[tangOfs], tangent, sizeof(vec4_t));

			vertOfs += 3;
			texCoordOfs += 2;
			tangOfs += 4;
		}
	}

	R_ReallocateStateArrays(vertOfs / 3);

	if (qglBindBuffer) {
		/* and also the vertex buffer objects */
		qglGenBuffers(1, &mod->bsp.vertex_buffer);
		qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.vertex_buffer);
		qglBufferData(GL_ARRAY_BUFFER, vertOfs * sizeof(GLfloat), mod->bsp.verts, GL_STATIC_DRAW);

		qglGenBuffers(1, &mod->bsp.texcoord_buffer);
		qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.texcoord_buffer);
		qglBufferData(GL_ARRAY_BUFFER, texCoordOfs * sizeof(GLfloat), mod->bsp.texcoords, GL_STATIC_DRAW);

		qglGenBuffers(1, &mod->bsp.lmtexcoord_buffer);
		qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.lmtexcoord_buffer);
		qglBufferData(GL_ARRAY_BUFFER, texCoordOfs * sizeof(GLfloat), mod->bsp.lmtexcoords, GL_STATIC_DRAW);

		qglGenBuffers(1, &mod->bsp.normal_buffer);
		qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.normal_buffer);
		qglBufferData(GL_ARRAY_BUFFER, vertOfs * sizeof(GLfloat), mod->bsp.normals, GL_STATIC_DRAW);

		qglGenBuffers(1, &mod->bsp.tangent_buffer);
		qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.tangent_buffer);
		qglBufferData(GL_ARRAY_BUFFER, tangOfs * sizeof(GLfloat), mod->bsp.tangents, GL_STATIC_DRAW);

		qglBindBuffer(GL_ARRAY_BUFFER, 0);
	}
}
コード例 #18
0
ファイル: cl_sequence.cpp プロジェクト: jklemmack/ufoai
/**
 * @brief Reads the sequence values from given text-pointer
 * @sa CL_ParseClientData
 */
void CL_ParseSequence (const char *name, const char **text)
{
	const char *errhead = "CL_ParseSequence: unexpected end of file (sequence ";
	sequence_t *sp;
	const char *token;
	int i;

	/* search for sequences with same name */
	for (i = 0; i < numSequences; i++)
		if (Q_streq(name, sequences[i].name))
			break;

	if (i < numSequences) {
		Com_Printf("CL_ParseSequence: sequence def \"%s\" with same name found, second ignored\n", name);
		return;
	}

	/* initialize the sequence */
	if (numSequences >= MAX_SEQUENCES)
		Com_Error(ERR_FATAL, "Too many sequences");

	sp = &sequences[numSequences++];
	OBJZERO(*sp);
	Q_strncpyz(sp->name, name, sizeof(sp->name));
	sp->start = numSeqCmds;

	/* get it's body */
	token = Com_Parse(text);

	if (!*text || *token != '{') {
		Com_Printf("CL_ParseSequence: sequence def \"%s\" without body ignored\n", name);
		numSequences--;
		return;
	}

	do {
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		/* check for commands */
		int i = CL_FindSequenceCommand(token);
		if (i != -1) {
			int maxLength = MAX_DATA_LENGTH;
			char *data;
			seqCmd_t *sc;

			/* found a command */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				return;

			if (numSeqCmds >= MAX_SEQCMDS)
				Com_Error(ERR_FATAL, "Too many sequence commands for %s", name);

			/* init seqCmd */
			if (seqCmds == NULL)
				seqCmds = Mem_PoolAllocTypeN(seqCmd_t, MAX_SEQCMDS, cl_genericPool);
			sc = &seqCmds[numSeqCmds++];
			OBJZERO(*sc);
			sc->handler = seqCmdFunc[i];
			sp->length++;

			/* copy name */
			Q_strncpyz(sc->name, token, sizeof(sc->name));

			/* read data */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				return;
			if (*token == '{') {
				// TODO depth is useless IMHO (bayo)
				int depth = 1;
				data = &sc->data[0];
				while (depth) {
					if (maxLength <= 0) {
						Com_Printf("Too much data for sequence %s", sc->name);
						break;
					}
					token = Com_EParse(text, errhead, name);
					if (!*text)
						return;

					if (*token == '{')
						depth++;
					else if (*token == '}')
						depth--;
					if (depth) {
						Q_strncpyz(data, token, maxLength);
						data += strlen(token) + 1;
						maxLength -= (strlen(token) + 1);
					}
				}
			} else if (*token == '(') {
				linkedList_t *list;
				Com_UnParseLastToken();
				if (!Com_ParseList(text, &list)) {
					Com_Error(ERR_DROP, "CL_ParseSequence: error while reading list (sequence \"%s\")", name);
				}
				data = &sc->data[0];
				for (linkedList_t *element = list; element != NULL; element = element->next) {
					if (maxLength <= 0) {
						Com_Printf("Too much data for sequence %s", sc->name);
						break;
					}
					const char* v = (char*)element->data;
					Q_strncpyz(data, v, maxLength);
					data += strlen(v) + 1;
					maxLength -= (strlen(v) + 1);
				}
				LIST_Delete(&list);
			} else {
				Com_UnParseLastToken();
			}
		} else {
			Com_Printf("CL_ParseSequence: unknown command \"%s\" ignored (sequence %s)\n", token, name);
			Com_EParse(text, errhead, name);
		}
	} while (*text);
}
コード例 #19
0
ファイル: cp_save.cpp プロジェクト: MyWifeRules/ufoai-1
/**
 * @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;
}
コード例 #20
0
ファイル: cp_save.cpp プロジェクト: MyWifeRules/ufoai-1
/**
 * @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;
}