+typedef struct imageformat_s
+{
+ const char *formatstring;
+ unsigned char *(*loadfunc)(const unsigned char *f, int filesize, int *miplevel);
+}
+imageformat_t;
+
+// GAME_TENEBRAE only
+imageformat_t imageformats_tenebrae[] =
+{
+ {"override/%s.tga", LoadTGA_BGRA},
+ {"override/%s.png", PNG_LoadImage_BGRA},
+ {"override/%s.jpg", JPEG_LoadImage_BGRA},
+ {"override/%s.pcx", LoadPCX_BGRA},
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.png", PNG_LoadImage_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.pcx", LoadPCX_BGRA},
+ {NULL, NULL}
+};
+
+imageformat_t imageformats_nopath[] =
+{
+ {"override/%s.tga", LoadTGA_BGRA},
+ {"override/%s.png", PNG_LoadImage_BGRA},
+ {"override/%s.jpg", JPEG_LoadImage_BGRA},
+ {"textures/%s.tga", LoadTGA_BGRA},
+ {"textures/%s.png", PNG_LoadImage_BGRA},
+ {"textures/%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.png", PNG_LoadImage_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.pcx", LoadPCX_BGRA},
+ {NULL, NULL}
+};
+
+// GAME_DELUXEQUAKE only
+// VorteX: the point why i use such messy texture paths is
+// that GtkRadiant can't detect normal/gloss textures
+// and exclude them from texture browser
+// so i just use additional folder to store this textures
+imageformat_t imageformats_dq[] =
+{
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"texturemaps/%s.tga", LoadTGA_BGRA},
+ {"texturemaps/%s.jpg", JPEG_LoadImage_BGRA},
+ {NULL, NULL}
+};
+
+imageformat_t imageformats_textures[] =
+{
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.png", PNG_LoadImage_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.pcx", LoadPCX_BGRA},
+ {"%s.wal", LoadWAL_BGRA},
+ {NULL, NULL}
+};
+
+imageformat_t imageformats_gfx[] =
+{
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.png", PNG_LoadImage_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.pcx", LoadPCX_BGRA},
+ {"%s.lmp", LoadLMP_BGRA},
+ {NULL, NULL}
+};
+
+imageformat_t imageformats_other[] =
+{
+ {"%s.tga", LoadTGA_BGRA},
+ {"%s.png", PNG_LoadImage_BGRA},
+ {"%s.jpg", JPEG_LoadImage_BGRA},
+ {"%s.pcx", LoadPCX_BGRA},
+ {"%s.lmp", LoadLMP_BGRA},
+ {NULL, NULL}
+};
+
+int fixtransparentpixels(unsigned char *data, int w, int h);
+unsigned char *loadimagepixelsbgra (const char *filename, qbool complain, qbool allowFixtrans, qbool convertsRGB, int *miplevel)
+{
+ fs_offset_t filesize;
+ imageformat_t *firstformat, *format;
+ int mymiplevel;
+ unsigned char *f, *data = NULL, *data2 = NULL;
+ char basename[MAX_QPATH], name[MAX_QPATH], name2[MAX_QPATH], path[MAX_QPATH], afterpath[MAX_QPATH], *c;
+ char vabuf[1024];
+ //if (developer_memorydebug.integer)
+ // Mem_CheckSentinelsGlobal();
+ if (developer_texturelogging.integer)
+ Log_Printf("textures.log", "%s\n", filename);
+ Image_StripImageExtension(filename, basename, sizeof(basename)); // strip filename extensions to allow replacement by other types
+ // replace *'s with #, so commandline utils don't get confused when dealing with the external files
+ for (c = basename;*c;c++)
+ if (*c == '*')
+ *c = '#';
+ path[0] = 0;
+ name[0] = 0;
+ strlcpy(afterpath, basename, sizeof(afterpath));
+ if (strchr(basename, '/'))
+ {
+ int i;
+ for (i = 0;i < (int)sizeof(path)-1 && basename[i] != '/' && basename[i];i++)
+ path[i] = basename[i];
+ path[i] = 0;
+ strlcpy(afterpath, basename + i + 1, sizeof(afterpath));
+ }
+ if (gamemode == GAME_TENEBRAE)
+ firstformat = imageformats_tenebrae;
+ else if (gamemode == GAME_DELUXEQUAKE)
+ firstformat = imageformats_dq;
+ else if (!strcasecmp(path, "textures"))
+ firstformat = imageformats_textures;
+ else if (!strcasecmp(path, "gfx") || !strcasecmp(path, "locale")) // locale/ is used in GAME_BLOODOMNICIDE
+ firstformat = imageformats_gfx;
+ else if (!path[0])
+ firstformat = imageformats_nopath;
+ else
+ firstformat = imageformats_other;
+ // now try all the formats in the selected list
+ for (format = firstformat;format->formatstring;format++)
+ {
+ dpsnprintf (name, sizeof(name), format->formatstring, basename);
+
+ FS_SanitizePath(name);
+
+ if(FS_FileExists(name) && (f = FS_LoadFile(name, tempmempool, true, &filesize)) != NULL)
+ {
+ mymiplevel = miplevel ? *miplevel : 0;
+ image_width = 0;
+ image_height = 0;
+ data = format->loadfunc(f, (int)filesize, &mymiplevel);
+ Mem_Free(f);
+ if (data)
+ {
+ if(format->loadfunc == JPEG_LoadImage_BGRA) // jpeg can't do alpha, so let's simulate it by loading another jpeg
+ {
+ dpsnprintf (name2, sizeof(name2), format->formatstring, va(vabuf, sizeof(vabuf), "%s_alpha", basename));
+ f = FS_LoadFile(name2, tempmempool, true, &filesize);
+ if(f)
+ {
+ int mymiplevel2 = miplevel ? *miplevel : 0;
+ int image_width_save = image_width;
+ int image_height_save = image_height;
+ data2 = format->loadfunc(f, (int)filesize, &mymiplevel2);
+ if(data2 && mymiplevel == mymiplevel2 && image_width == image_width_save && image_height == image_height_save)
+ Image_CopyAlphaFromBlueBGRA(data, data2, image_width, image_height);
+ else
+ Con_Printf("loadimagepixelsrgba: corrupt or invalid alpha image %s_alpha\n", basename);
+ image_width = image_width_save;
+ image_height = image_height_save;
+ if(data2)
+ Mem_Free(data2);
+ Mem_Free(f);
+ }
+ }
+ if (developer_loading.integer)
+ Con_DPrintf("loaded image %s (%dx%d)\n", name, image_width, image_height);
+ if(miplevel)
+ *miplevel = mymiplevel;
+ //if (developer_memorydebug.integer)
+ // Mem_CheckSentinelsGlobal();
+ if(allowFixtrans && r_fixtrans_auto.integer)
+ {
+ int n = fixtransparentpixels(data, image_width, image_height);
+ if(n)
+ {
+ Con_Printf("- had to fix %s (%d pixels changed)\n", name, n);
+ if(r_fixtrans_auto.integer >= 2)
+ {
+ char outfilename[MAX_QPATH], buf[MAX_QPATH];
+ Image_StripImageExtension(name, buf, sizeof(buf));
+ dpsnprintf(outfilename, sizeof(outfilename), "fixtrans/%s.tga", buf);
+ Image_WriteTGABGRA(outfilename, image_width, image_height, data);
+ Con_Printf("- %s written.\n", outfilename);
+ }
+ }
+ }
+ if (convertsRGB)
+ Image_MakeLinearColorsFromsRGB(data, data, image_width * image_height);
+ return data;
+ }
+ else
+ Con_DPrintf("Error loading image %s (file loaded but decode failed)\n", name);
+ }
+ }
+ if (!strcasecmp(path, "gfx"))
+ {
+ unsigned char *lmpdata;
+ if ((lmpdata = W_GetLumpName(afterpath, &filesize)))
+ {
+ if (developer_loading.integer)
+ Con_Printf("loading gfx.wad lump \"%s\"\n", afterpath);
+
+ mymiplevel = miplevel ? *miplevel : 0;
+ if (!strcmp(afterpath, "conchars"))
+ {
+ // conchars is a raw image and with color 0 as transparent instead of 255
+ data = LoadConChars_BGRA(lmpdata, filesize, &mymiplevel);
+ }
+ else
+ data = LoadLMP_BGRA(lmpdata, filesize, &mymiplevel);
+ // no cleanup after looking up a wad lump - the whole gfx.wad is loaded at once
+ if (data)
+ return data;
+ Con_DPrintf("Error loading image %s (file loaded but decode failed)\n", name);
+ }
+ }
+
+ // check if the image name exists as an embedded pic
+ if ((data = Image_GetEmbeddedPicBGRA(basename)))
+ return data;
+
+ if (complain)
+ {
+ Con_Printf("Couldn't load %s using ", filename);
+ for (format = firstformat;format->formatstring;format++)
+ {
+ dpsnprintf (name, sizeof(name), format->formatstring, basename);
+ Con_Printf(format == firstformat ? "\"%s\"" : (format[1].formatstring ? ", \"%s\"" : " or \"%s\".\n"), format->formatstring);
+ }
+ }
+
+ // texture loading can take a while, so make sure we're sending keepalives
+ CL_KeepaliveMessage(false);
+
+ //if (developer_memorydebug.integer)
+ // Mem_CheckSentinelsGlobal();
+
+ return NULL;
+}
+
+qbool Image_GetStockPicSize(const char *filename, int *returnwidth, int *returnheight)
+{
+ unsigned char *data;
+ fs_offset_t filesize;
+ char lmppath[MAX_QPATH];
+ if (!strcasecmp(filename, "gfx/conchars"))
+ {
+ *returnwidth = 128;
+ *returnheight = 128;
+ return true;
+ }
+
+ dpsnprintf(lmppath, sizeof(lmppath), "%s.lmp", filename);
+ data = FS_LoadFile(lmppath, tempmempool, true, &filesize);
+ if (data)
+ {
+ if (filesize > 8)
+ {
+ int w = data[0] + data[1] * 0x100 + data[2] * 0x10000 + data[3] * 0x1000000;
+ int h = data[4] + data[5] * 0x100 + data[6] * 0x10000 + data[7] * 0x1000000;
+ if (w >= 1 && w <= 32768 && h >= 1 && h <= 32768)
+ {
+ *returnwidth = w;
+ *returnheight = h;
+ Mem_Free(data);
+ return true;
+ }
+ }
+ Mem_Free(data);
+ }
+ if (!strncasecmp(filename, "gfx/", 4))
+ {
+ data = W_GetLumpName(filename + 4, &filesize);
+ if (data && filesize > 8)
+ {
+ int w = data[0] + data[1] * 0x100 + data[2] * 0x10000 + data[3] * 0x1000000;
+ int h = data[4] + data[5] * 0x100 + data[6] * 0x10000 + data[7] * 0x1000000;
+ if (w >= 1 && w <= 32768 && h >= 1 && h <= 32768)
+ {
+ *returnwidth = w;
+ *returnheight = h;
+ return true;
+ }
+ }
+ }
+ return false;
+}