+ int i, size, dds_format_flags, dds_miplevels, dds_width, dds_height;
+ //int dds_flags;
+ textype_t textype;
+ int bytesperblock, bytesperpixel;
+ int mipcomplete;
+ gltexture_t *glt;
+ gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
+ textypeinfo_t *texinfo;
+ int mip, mipwidth, mipheight, mipsize;
+ unsigned int c;
+ GLint oldbindtexnum;
+ const unsigned char *mippixels, *ddspixels;
+ unsigned char *dds;
+ fs_offset_t ddsfilesize;
+ unsigned int ddssize;
+
+ if (cls.state == ca_dedicated)
+ return NULL;
+
+ dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
+ ddssize = ddsfilesize;
+
+ if (!dds)
+ {
+ Log_Printf("ddstexturefailures.log", "%s\n", filename);
+ return NULL; // not found
+ }
+
+ if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: not a DDS image\n", filename);
+ return NULL;
+ }
+
+ //dds_flags = BuffLittleLong(dds+8);
+ dds_format_flags = BuffLittleLong(dds+80);
+ dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
+ dds_width = BuffLittleLong(dds+16);
+ dds_height = BuffLittleLong(dds+12);
+ ddspixels = dds + 128;
+
+ flags &= ~TEXF_ALPHA;
+ if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
+ {
+ // very sloppy BGRA 32bit identification
+ textype = TEXTYPE_BGRA;
+ bytesperblock = 0;
+ bytesperpixel = 4;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
+ return NULL;
+ }
+ // check alpha
+ for (i = 3;i < size;i += 4)
+ if (ddspixels[i] < 255)
+ break;
+ if (i >= size)
+ flags &= ~TEXF_ALPHA;
+ }
+ else if (!memcmp(dds+84, "DXT1", 4))
+ {
+ // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
+ // LordHavoc: it is my belief that this does not infringe on the
+ // patent because it is not decoding pixels...
+ textype = TEXTYPE_DXT1;
+ bytesperblock = 8;
+ bytesperpixel = 0;
+ //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
+ return NULL;
+ }
+ for (i = 0;i < size;i += bytesperblock)
+ if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
+ break;
+ if (i < size)
+ textype = TEXTYPE_DXT1A;
+ else
+ flags &= ~TEXF_ALPHA;
+ }
+ else if (!memcmp(dds+84, "DXT3", 4))
+ {
+ textype = TEXTYPE_DXT3;
+ bytesperblock = 16;
+ bytesperpixel = 0;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
+ return NULL;
+ }
+ }
+ else if (!memcmp(dds+84, "DXT5", 4))
+ {
+ textype = TEXTYPE_DXT5;
+ bytesperblock = 16;
+ bytesperpixel = 0;
+ size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
+ if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
+ return NULL;
+ }
+ }
+ else
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
+ return NULL;
+ }
+
+ // return whether this texture is transparent
+ if (hasalphaflag)
+ *hasalphaflag = (flags & TEXF_ALPHA) != 0;
+
+ // calculate average color if requested
+ if (avgcolor)
+ {
+ float f;
+ Vector4Clear(avgcolor);
+ if (bytesperblock)
+ {
+ for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
+ {
+ c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
+ avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
+ avgcolor[1] += ((c >> 5) & 0x3F) + ((c >> 21) & 0x3F);
+ avgcolor[2] += ((c ) & 0x1F) + ((c >> 16) & 0x1F);
+ }
+ f = (float)bytesperblock / size;
+ avgcolor[0] *= (0.5f / 31.0f) * f;
+ avgcolor[1] *= (0.5f / 63.0f) * f;
+ avgcolor[2] *= (0.5f / 31.0f) * f;
+ avgcolor[3] = 1; // too hard to calculate
+ }
+ else
+ {
+ for (i = 0;i < size;i += 4)
+ {
+ avgcolor[0] += ddspixels[i+2];
+ avgcolor[1] += ddspixels[i+1];
+ avgcolor[2] += ddspixels[i];
+ avgcolor[3] += ddspixels[i+3];
+ }
+ f = (1.0f / 255.0f) * bytesperpixel / size;
+ avgcolor[0] *= f;
+ avgcolor[1] *= f;
+ avgcolor[2] *= f;
+ avgcolor[3] *= f;
+ }
+ }
+
+ if (dds_miplevels > 1)
+ flags |= TEXF_MIPMAP;
+ else
+ flags &= ~TEXF_MIPMAP;
+
+ // if S3TC is not supported, there's very little we can do about it
+ if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
+ {
+ Mem_Free(dds);
+ Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
+ return NULL;
+ }
+
+ texinfo = R_GetTexTypeInfo(textype, flags);
+
+ glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
+ strlcpy (glt->identifier, filename, sizeof(glt->identifier));
+ glt->pool = pool;
+ glt->chain = pool->gltchain;
+ pool->gltchain = glt;
+ glt->inputwidth = dds_width;
+ glt->inputheight = dds_height;
+ glt->inputdepth = 1;
+ glt->flags = flags;
+ glt->textype = texinfo;
+ glt->texturetype = GLTEXTURETYPE_2D;
+ glt->inputdatasize = ddssize;
+ glt->glinternalformat = texinfo->glinternalformat;
+ glt->glformat = texinfo->glformat;
+ glt->gltype = texinfo->gltype;
+ glt->bytesperpixel = texinfo->internalbytesperpixel;
+ glt->sides = 1;
+ glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
+ glt->tilewidth = dds_width;
+ glt->tileheight = dds_height;
+ glt->tiledepth = 1;
+
+ // texture uploading can take a while, so make sure we're sending keepalives
+ CL_KeepaliveMessage(false);
+
+ // upload the texture
+ // we need to restore the texture binding after finishing the upload
+ CHECKGLERROR
+ GL_ActiveTexture(0);
+ oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
+ qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
+ qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
+ mippixels = ddspixels;
+ mipwidth = dds_width;
+ mipheight = dds_height;
+ mipcomplete = false;
+ for (mip = 0;mip < dds_miplevels+1;mip++)
+ {
+ mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
+ if (mippixels + mipsize > dds + ddssize)
+ break;
+ if (bytesperblock)
+ {
+ qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
+ }
+ else
+ {
+ qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
+ }
+ mippixels += mipsize;
+ if (mipwidth <= 1 && mipheight <= 1)
+ {
+ mipcomplete = true;
+ break;
+ }
+ if (mipwidth > 1)
+ mipwidth >>= 1;
+ if (mipheight > 1)
+ mipheight >>= 1;
+ }
+ if (dds_miplevels > 1 && !mipcomplete)
+ {
+ // need to set GL_TEXTURE_MAX_LEVEL
+ qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
+ }
+ GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
+ qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
+
+ Mem_Free(dds);
+ return (rtexture_t *)glt;