From 062cdf6b217ed51079c1b004584b563ae5ec4be9 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 7 Mar 2020 09:00:40 +0000 Subject: [PATCH] Q3TA: Added snd_ignorecueloops cvar to ignore auto-looping sounds. Q3TA: Fixed script parsing, so the menus are not so broken. Q3TA: added the UI_CIN_* and CG_CIN_* builtins for 2d cinematic playbacks. Q3TA: backspace should work for gamecode text entry now. Q3TA: map_restart now directly restarts without needing loading screens. Fixed multiple envmap generation bugs. Fixed envmaps getting flushed with r_keepimages 0. Console image previews can now display cubemaps (although their orientation is probably a little off). Made a 'remapshader' csqc builtin. Depending on the r_remapshader console command was stupid. Fixed packet command to create a udp socket, if needed. sv_public can now be set to a non-numeric name for custom names (instead of needing to poke sv_port too). Support extended data on the end of miptex entries in bsps. Requires a qbsp (like vanilla) that doesn't rewrite the miptex entries. Updated imgtool to generate/read our new extended miptex data. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5644 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_cg.c | 15 + engine/client/cl_main.c | 7 +- engine/client/cl_screen.c | 35 +- engine/client/cl_ui.c | 397 ++++++++-- engine/client/clq3_parse.c | 8 +- engine/client/clq3defs.h | 11 +- engine/client/console.c | 75 +- engine/client/image.c | 316 +++++--- engine/client/m_mp3.c | 7 + engine/client/merged.h | 2 + engine/client/net_master.c | 13 +- engine/client/pr_clcmd.c | 9 + engine/client/pr_csqc.c | 1 + engine/client/r_surf.c | 6 +- engine/client/render.h | 5 +- engine/client/renderer.c | 51 +- engine/client/roq_read.c | 2 +- engine/client/screen.h | 9 +- engine/client/snd_dma.c | 2 + engine/client/snd_mem.c | 3 +- engine/common/cmd.c | 3 +- engine/common/com_mesh.c | 21 +- engine/common/fs.c | 2 +- engine/common/gl_q2bsp.c | 30 +- engine/common/net_wins.c | 9 +- engine/common/netinc.h | 5 +- engine/common/pr_common.h | 1 + engine/common/q3common.c | 4 +- engine/d3d/d3d11_image.c | 8 +- engine/d3d/vid_d3d11.c | 8 +- engine/gl/gl_backend.c | 6 +- engine/gl/gl_draw.c | 18 +- engine/gl/gl_heightmap.c | 5 +- engine/gl/gl_model.c | 262 +++++-- engine/gl/gl_model.h | 8 +- engine/gl/gl_shader.c | 40 +- engine/gl/gl_warp.c | 113 ++- engine/gl/r_bishaders.h | 416 +++++----- engine/gl/shader.h | 2 +- engine/server/pr_cmds.c | 7 +- engine/server/server.h | 12 +- engine/server/sv_ccmds.c | 9 + engine/server/sv_init.c | 12 +- engine/server/sv_main.c | 15 +- engine/server/sv_user.c | 12 + engine/server/svq3_game.c | 230 ++++-- engine/shaders/glsl/defaultskybox.glsl | 1 - engine/shaders/glsl/defaultwall.glsl | 2 +- engine/shaders/hlsl11/defaultskybox.hlsl | 1 - engine/shaders/hlsl9/defaultskybox.hlsl | 1 - engine/shaders/vulkan/defaultskybox.glsl | 1 - engine/vk/vk_init.c | 20 +- imgtool.c | 945 +++++++++++++++++------ 53 files changed, 2231 insertions(+), 972 deletions(-) diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index 8435303a..ab1b7383 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -383,6 +383,10 @@ qboolean CG_GetServerCommand(int cmdnum) if (!strcmp(Cmd_Argv(0), "cs")) CG_InsertIntoGameState(atoi(Cmd_Argv(1)), Cmd_Argv(2)); + else if (!strcmp(Cmd_Argv(0), "map_restart")) + Con_ClearNotify(); + else if (!strcmp(Cmd_Argv(0), "disconnect")) + Host_EndGame("Server disconnected - %s", (Cmd_Argc()>1)?Cmd_Argv(1):"No reason given"); return true; } @@ -1232,6 +1236,17 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con UI_RegisterFont(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2])); break; + case CG_CIN_PLAYCINEMATIC: + return UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5])); + case CG_CIN_STOPCINEMATIC: + return UI_Cin_Stop(VM_LONG(arg[0])); + case CG_CIN_RUNCINEMATIC: + return UI_Cin_Run(VM_LONG(arg[0])); + case CG_CIN_DRAWCINEMATIC: + return UI_Cin_Draw(VM_LONG(arg[0])); + case CG_CIN_SETEXTENTS: + return UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4])); + case CG_FTE_FINDPARTICLEEFFECT: return pe->FindParticleType(VM_POINTER(arg[0])); case CG_FTE_SPAWNPARTICLEEFFECT: diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index b80fb9b7..38d34ad8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3867,7 +3867,10 @@ void CL_ReadPacket(void) if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on { char adr[MAX_ADR_SIZE]; - Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from)); + if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK) + Con_TPrintf ("%s: Ack (Pong)\n", NET_AdrToString(adr, sizeof(adr), &net_from)); + else + Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from)); return; } @@ -3965,7 +3968,7 @@ void CL_ReadPackets (void) NET_ReadPackets(cls.sockets); #ifdef HAVE_DTLS - NET_DTLS_Timeouts(cls.sockets); + NET_DTLS_Timeouts(cls.sockets); #endif // diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 0326bf6b..8f1d4645 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -2850,7 +2850,7 @@ void SCR_ScreenShot_Cubemap_f(void) qboolean horizontalflip; } sides[] = { - //standard cubemap + //standard cubemap (flipping is done on save) {{0, 0, 90}, "_px", true}, {{0, 180, -90}, "_nx", true}, {{0, 90, 0}, "_py", true}, //upside down @@ -2899,7 +2899,7 @@ void SCR_ScreenShot_Cubemap_f(void) { qboolean fail = false; mips.type = PTI_CUBE; - mips.encoding = 0; + mips.encoding = PTI_INVALID; mips.extrafree = NULL; mips.mipcount = 1; @@ -2909,13 +2909,14 @@ void SCR_ScreenShot_Cubemap_f(void) VectorCopy(sides[i].angle, cl.playerview->simangles); VectorCopy(cl.playerview->simangles, cl.playerview->viewangles); - facedata = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, true); + //don't use hdr when saving dds files. it generally means dx10 dds files and most tools suck too much and then I get blamed for writing 'corrupt' dds files. + facedata = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, !!strcmp(ext, ".dds")); if (!facedata) break; - if (!i) + if (!bb) { Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); - if (bw != 1 || bh != 1) + if (!bb || bw != 1 || bh != 1 || fbwidth != fbheight) { //erk, no block compression here... BZ_Free(facedata); break; //zomgwtfbbq @@ -2936,23 +2937,27 @@ void SCR_ScreenShot_Cubemap_f(void) break; //zomgwtfbbq } - Image_FlipImage(facedata, (qbyte*)mips.mip[0].data + i*mips.mip[0].datasize/6, &fbwidth, &fbheight, bb, sides[i].horizontalflip, sides[i].verticalflip, false); + Image_FlipImage(facedata, (qbyte*)mips.mip[0].data + i*(mips.mip[0].datasize/6), &fbwidth, &fbheight, bb, sides[i].horizontalflip, sides[i].verticalflip^(stride<0), false); BZ_Free(facedata); } if (i == 6) { - qboolean pixelformats[PTI_MAX] = {0}; - pixelformats[PTI_E5BGR9] = true; - Image_ChangeFormat(&mips, pixelformats, mips.encoding, fname); + if (mips.encoding == PTI_RGB32F || mips.encoding == PTI_RGBA16F || mips.encoding == PTI_RGBA32F) + { //convert to a more memory-efficient hdr format. + qboolean pixelformats[PTI_MAX] = {0}; + pixelformats[PTI_E5BGR9] = true; + Image_ChangeFormat(&mips, pixelformats, mips.encoding, fname); + } + else if ((mips.encoding == PTI_RGBX8 || mips.encoding == PTI_BGRX8) && !strcmp(ext, ".dds")) + { //gimp-dds plugin is buggy. convert to a less problematic format, so that I don't get invalid complaints. + qboolean pixelformats[PTI_MAX] = {0}; + pixelformats[PTI_BGR8] = true; + Image_ChangeFormat(&mips, pixelformats, mips.encoding, fname); + } Q_snprintfz(filename, sizeof(filename), "textures/%s", fname); COM_DefaultExtension (filename, ext, sizeof(filename)); -#ifdef IMAGEFMT_KTX - COM_DefaultExtension (filename, ".ktx", sizeof(filename)); -#endif -#ifdef IMAGEFMT_DDS - COM_DefaultExtension (filename, ".dds", sizeof(filename)); -#endif + ext = COM_GetFileExtension(filename, NULL); if (fail) Con_Printf("Unable to generate cubemap data\n"); diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 1e83c805..3526b8ad 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -181,29 +181,39 @@ int Script_Read(int handle, struct pc_token_s *token) StripCSyntax(token->string); - token->intvalue = atoi(token->string); - token->floatvalue = atof(token->string); - if (token->floatvalue || *token->string == '0' || *token->string == '.') + if (token->string[0] == '0' && (token->string[1] == 'x'||token->string[1] == 'X')) { + token->intvalue = strtoul(token->string, NULL, 16); + token->floatvalue = token->intvalue; token->type = TT_NUMBER; - token->subtype = 0; - } - else if (com_tokentype == TTP_STRING) - { - token->type = TT_STRING; - token->subtype = strlen(token->string); + token->subtype = 0x100;//TT_HEX; } else { - if (token->string[1] == '\0') + token->intvalue = atoi(token->string); + token->floatvalue = atof(token->string); + if (token->floatvalue || *token->string == '0' || *token->string == '.') { - token->type = TT_PUNCTUATION; - token->subtype = token->string[0]; + token->type = TT_NUMBER; + token->subtype = 0; + } + else if (com_tokentype == TTP_STRING) + { + token->type = TT_STRING; + token->subtype = strlen(token->string); } else { - token->type = TT_NAME; - token->subtype = strlen(token->string); + if (token->string[1] == '\0') + { + token->type = TT_PUNCTUATION; + token->subtype = token->string[0]; + } + else + { + token->type = TT_NAME; + token->subtype = strlen(token->string); + } } } @@ -303,10 +313,11 @@ struct #define UITAGNUM 2452 extern model_t *mod_known; -#define VM_FROMMHANDLE(a) (a?mod_known+a-1:NULL) +extern int mod_numknown; +#define VM_FROMMHANDLE(a) (a>0&&a<=mod_numknown?mod_known+a-1:NULL) #define VM_TOMHANDLE(a) (a?a-mod_known+1:0) -#define VM_FROMSHANDLE(a) (a?r_shaders[a-1]:NULL) +#define VM_FROMSHANDLE(a) (a>0&&a<=r_numshaders?r_shaders[a-1]:NULL) #define VM_TOSHANDLE(a) (a?a->id+1:0) @@ -359,8 +370,8 @@ struct q3polyvert_s #define Q3RF_NOSHADOW 64 #define Q3RF_LIGHTING_ORIGIN 128 -#define MAX_VMQ3_CACHED_STRINGS 1024 -char *stringcache[1024]; +#define MAX_VMQ3_CACHED_STRINGS 2048 +char *stringcache[MAX_VMQ3_CACHED_STRINGS]; void VMQ3_FlushStringHandles(void) { @@ -675,6 +686,7 @@ void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font) int i; char name[MAX_QPATH]; size_t sz; + shader_t *shader; #define readInt() LittleLong(*in.i++) #define readFloat() LittleFloat(*in.f++) @@ -701,17 +713,138 @@ void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font) in.c += 32; } font->glyphScale = readFloat(); - memcpy(font->name, in.i, MAX_QPATH); + memcpy(font->name, in.i, sizeof(font->name)); // Com_Memcpy(font, faceData, sizeof(fontInfo_t)); Q_strncpyz(font->name, name, sizeof(font->name)); for (i = GLYPH_START; i < GLYPH_END; i++) { - font->glyphs[i].glyph = VM_TOSHANDLE(R_RegisterPic(font->glyphs[i].shaderName, NULL)); + shader = R_RegisterPic(font->glyphs[i].shaderName, NULL); + font->glyphs[i].glyph = VM_TOSHANDLE(shader); } } } +static struct +{ + int shaderhandle; + int x, y, w, h; + qboolean loop; +} uicinematics[16]; +int UI_Cin_Play(const char *name, int x, int y, int w, int h, unsigned int flags) +{ + int idx; + shader_t *mediashader; + cin_t *cin; + for (idx = 0; ; idx++) + { + if (idx == countof(uicinematics)) + return -1; //out of handles + if (uicinematics[idx].shaderhandle) + continue; //slot in use + break; //this slot is usable + } + + mediashader = R_RegisterCustom(name, SUF_NONE, Shader_DefaultCinematic, va("video/%s", name)); + if (!mediashader) + return -1; //wtf? + cin = R_ShaderGetCinematic(mediashader); + if (cin) + Media_SetState(cin, CINSTATE_PLAY); + else + return -1; //FAIL! + + uicinematics[idx].x = x; + uicinematics[idx].y = y; + uicinematics[idx].w = w; + uicinematics[idx].h = h; + uicinematics[idx].loop = !!(flags&1); + uicinematics[idx].shaderhandle = VM_TOSHANDLE(mediashader); + + return idx; +} +int UI_Cin_Stop(int idx) +{ + if (idx >= 0 && idx < countof(uicinematics)) + { + shader_t *shader = VM_FROMSHANDLE(uicinematics[idx].shaderhandle); + R_UnloadShader(shader); + uicinematics[idx].shaderhandle = 0; + } + return 0; +} +int UI_Cin_Run(int idx) +{ + enum { + FMV_IDLE, + FMV_PLAY, // play + FMV_EOF, // all other conditions, i.e. stop/EOF/abort + FMV_ID_BLT, + FMV_ID_IDLE, + FMV_LOOPED, + FMV_ID_WAIT + }; + int ret = FMV_IDLE; + + + if (idx >= 0 && idx < countof(uicinematics)) + { + shader_t *shader = VM_FROMSHANDLE(uicinematics[idx].shaderhandle); + cin_t *cin = R_ShaderGetCinematic(shader); + if (cin) + { + switch(Media_GetState(cin)) + { + case CINSTATE_INVALID: ret = FMV_IDLE; break; + case CINSTATE_PLAY: ret = FMV_PLAY; break; + case CINSTATE_LOOP: ret = FMV_PLAY; break; + case CINSTATE_PAUSE: ret = FMV_PLAY; break; + case CINSTATE_ENDED: + Media_SetState(cin, CINSTATE_FLUSHED); + ret = FMV_EOF; + break; + case CINSTATE_FLUSHED: + //FIXME: roq decoder has no reset method! + Media_Send_Reset(cin); + ret = FMV_LOOPED; + break; + } + } + } + return ret; +} +int UI_Cin_Draw(int idx) +{ + if (idx >= 0 && idx < countof(uicinematics)) + { + shader_t *shader = VM_FROMSHANDLE(uicinematics[idx].shaderhandle); + float x = uicinematics[idx].x; + float y = uicinematics[idx].y; + float w = uicinematics[idx].w; + float h = uicinematics[idx].h; + + //gah! q3 compat sucks! + x *= vid.pixelwidth/640.0; + w *= vid.pixelwidth/640.0; + y *= vid.pixelheight/480.0; + h *= vid.pixelheight/480.0; + + R2D_Image(x, y, w, h, 0, 0, 1, 1, shader); + } + return 0; +} +int UI_Cin_SetExtents(int idx, int x, int y, int w, int h) +{ + if (idx >= 0 && idx < countof(uicinematics)) + { + uicinematics[idx].x = x; + uicinematics[idx].y = y; + uicinematics[idx].w = w; + uicinematics[idx].h = h; + } + return 0; +} + static cvar_t *Cvar_Q3FindVar (const char *var_name) { struct { @@ -760,10 +893,24 @@ static cvar_t *Cvar_Q3FindVar (const char *var_name) return NULL; } +static void UI_SimulateTextEntry(void *cb, char *utf8) +{ + const char *line = utf8; + unsigned int unicode; + int err; + while(*line) + { + unicode = utf8_decode(&err, line, &line); + if (uivm) + VM_Call(uivm, UI_KEY_EVENT, unicode|1024, true); + } +} + #define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds. static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg) { + static int overstrikemode; int ret=0; char adrbuf[MAX_ADR_SIZE]; @@ -779,6 +926,15 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con switch((uiImport_t)fn) { + case UI_CVAR_CREATE: + case UI_CVAR_INFOSTRINGBUFFER: + case UI_R_ADDPOLYTOSCENE: + case UI_R_REMAP_SHADER: + case UI_UPDATESCREEN: + case UI_CM_LOADMODEL: + case UI_FS_SEEK: + Con_Printf("Q3UI: Not implemented system trap: %i\n", (int)fn); + break; case UI_ERROR: Con_Printf("%s", (char*)VM_POINTER(arg[0])); break; @@ -810,7 +966,8 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con char *vval = VM_POINTER(arg[1]); if (!strcmp(vname, "fs_game")) { - Cbuf_AddText(va("gamedir %s\nui_restart\n", (char*)vval), RESTRICT_SERVER); + char quoted[256]; + Cbuf_AddText(va("gamedir %s\nui_restart\n", COM_QuotedString(vval, quoted, sizeof(quoted), false)), RESTRICT_SERVER); } else { @@ -972,7 +1129,10 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con if (!*(char*)VM_POINTER(arg[0])) VM_LONG(ret) = 0; else - VM_LONG(ret) = VM_TOSHANDLE(R_RegisterPic(VM_POINTER(arg[0]), NULL)); + { + shader_t *shader = R_RegisterPic(VM_POINTER(arg[0]), NULL); + VM_LONG(ret) = VM_TOSHANDLE(shader); + } break; case UI_R_CLEARSCENE: //clear scene @@ -1023,8 +1183,31 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con S_LocalSound(VM_FROMSTRCACHE(arg[0])); //now we can fix up the sound name break; + case UI_S_STARTBACKGROUNDTRACK: + Media_NamedTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1])); + break; + case UI_S_STOPBACKGROUNDTRACK: + Media_NamedTrack(NULL, NULL); + break; + + //q3 shares insert mode between its ui and console. whereas fte doesn't support it (FIXME: add to Key_EntryInsert). + case UI_KEY_SETOVERSTRIKEMODE: + overstrikemode = arg[0]; + return 0; case UI_KEY_GETOVERSTRIKEMODE: - return true; + return overstrikemode; + + case UI_GETCLIPBOARDDATA: + if (VM_OOB(arg[0], VM_LONG(arg[1]))) + break; //out of bounds. + //our clipboard doesn't allow us to simply query without blocking (would result in stalls on x11/wayland) + //so just return nothing - we can send the text as if it were regular text entry for a similar result. + Q_strncpyz(VM_POINTER(arg[0]), "", VM_LONG(arg[1])); + + //but do we really want to let mods read the system clipboard? I suppose it SHOULD be okay if the UI was manually installed by the user. + //side note: q3's text entry logic is kinda flawed. + Sys_Clipboard_PasteText(CBT_CLIPBOARD, UI_SimulateTextEntry, NULL); + break; case UI_KEY_KEYNUMTOSTRINGBUF: if (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) > 255 || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1) @@ -1223,6 +1406,110 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con strcpy(buf, ""); } break; + + case UI_LAN_UPDATEVISIBLEPINGS: + return CL_QueryServers(); //return true while we're still going. + case UI_LAN_RESETPINGS: + return 0; + case UI_LAN_ADDSERVER: + return 0; + case UI_LAN_REMOVESERVER: + return 0; + case UI_LAN_SERVERSTATUS: + //*address, *status, statuslen + return 0; + case UI_LAN_COMPARESERVERS: + { + hostcachekey_t q3tofte[] = {SLKEY_NAME, SLKEY_MAP, SLKEY_NUMHUMANS, SLKEY_GAMEDIR, SLKEY_PING, 0}; + qboolean keyisstring[] = {true, true, false, true, false, false}; + //int source = VM_LONG(arg[0]); + int key = bound(0, VM_LONG(arg[1]), countof(q3tofte)); + int sortdir = VM_LONG(arg[2]); + serverinfo_t *s1 = Master_InfoForNum(VM_LONG(arg[3])); + serverinfo_t *s2 = Master_InfoForNum(VM_LONG(arg[4])); + if (keyisstring[key]) + ret = strcasecmp(Master_ReadKeyString(s2, q3tofte[key]), Master_ReadKeyString(s1, q3tofte[key])); + else + { + ret = Master_ReadKeyFloat(s2, q3tofte[key]) - Master_ReadKeyFloat(s1, q3tofte[key]); + if (ret < 0) + ret = -1; + else if (ret > 0) + ret = 1; + } + if (sortdir) + ret = -ret; + } + return ret; + + case UI_LAN_GETSERVERCOUNT: //LAN Get server count + //int (int source) + Master_CheckPollSockets(); + VM_LONG(ret) = Master_TotalCount(); + break; + case UI_LAN_GETSERVERADDRESSSTRING: //LAN get server address + //void (int source, int svnum, char *buffer, int buflen) + if ((int)arg[2] + VM_LONG(arg[3]) >= mask || VM_POINTER(arg[2]) < offset) + break; //out of bounds. + { + char *buf = VM_POINTER(arg[2]); + char *adr; + serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1])); + if (info) + { + adr = Master_ServerToString(adrbuf, sizeof(adrbuf), info); + if (strlen(adr) < VM_LONG(arg[3])) + { + strcpy(buf, adr); + VM_LONG(ret) = true; + } + } + else + strcpy(buf, ""); + } + break; + case UI_LAN_LOADCACHEDSERVERS: + break; + case UI_LAN_SAVECACHEDSERVERS: + break; + case UI_LAN_GETSERVERPING: + return 50; + case UI_LAN_GETSERVERINFO: + if (VM_OOB(arg[2], arg[3]) || !arg[3]) + break; //out of bounds. + { + //int source = VM_LONG(arg[0]); + int servernum = VM_LONG(arg[1]); + char *out = VM_POINTER(arg[2]); + int maxsize = VM_LONG(arg[3]); + char adr[MAX_ADR_SIZE]; + serverinfo_t *info = Master_InfoForNum(servernum); + *out = 0; + if (info) + { + Info_SetValueForStarKey(out, "hostname", info->name, maxsize); + Info_SetValueForStarKey(out, "mapname", info->map, maxsize); + Info_SetValueForStarKey(out, "clients", va("%i", info->players), maxsize); + Info_SetValueForStarKey(out, "sv_maxclients", va("%i", info->maxplayers), maxsize); + Info_SetValueForStarKey(out, "ping", va("%i", info->ping), maxsize); +// Info_SetValueForStarKey(out, "minping", info->map, maxsize); +// Info_SetValueForStarKey(out, "maxping", info->map, maxsize); +// Info_SetValueForStarKey(out, "game", info->map, maxsize); +// Info_SetValueForStarKey(out, "gametype", info->map, maxsize); +// Info_SetValueForStarKey(out, "nettype", info->map, maxsize); + Info_SetValueForStarKey(out, "addr", NET_AdrToString(adr, sizeof(adr), &info->adr), maxsize); +// Info_SetValueForStarKey(out, "punkbuster", info->map, maxsize); +// Info_SetValueForStarKey(out, "g_needpass", info->map, maxsize); +// Info_SetValueForStarKey(out, "g_humanplayers", info->map, maxsize); + + } + } + break; + case UI_LAN_MARKSERVERVISIBLE: + /*not implemented*/ + return 0; + case UI_LAN_SERVERISVISIBLE: + return 1; #endif case UI_CVAR_REGISTER: @@ -1262,45 +1549,6 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con VALIDATEPOINTER(arg[0], sizeof(q3time_t)); return Q3VM_GetRealtime(VM_POINTER(arg[0])); -#ifdef CL_MASTER - case UI_LAN_GETSERVERCOUNT: //LAN Get server count - //int (int source) - VM_LONG(ret) = Master_TotalCount(); - break; - case UI_LAN_GETSERVERADDRESSSTRING: //LAN get server address - //void (int source, int svnum, char *buffer, int buflen) - if ((int)arg[2] + VM_LONG(arg[3]) >= mask || VM_POINTER(arg[2]) < offset) - break; //out of bounds. - { - char *buf = VM_POINTER(arg[2]); - char *adr; - serverinfo_t *info = Master_InfoForNum(VM_LONG(arg[1])); - if (info) - { - adr = Master_ServerToString(adrbuf, sizeof(adrbuf), info); - if (strlen(adr) < VM_LONG(arg[3])) - { - strcpy(buf, adr); - VM_LONG(ret) = true; - } - } - else - strcpy(buf, ""); - } - break; - case UI_LAN_LOADCACHEDSERVERS: - break; - case UI_LAN_SAVECACHEDSERVERS: - break; - case UI_LAN_GETSERVERPING: - return 50; - case UI_LAN_GETSERVERINFO: - break; - case UI_LAN_SERVERISVISIBLE: - return 1; - break; -#endif - case UI_VERIFY_CDKEY: VM_LONG(ret) = true; break; @@ -1429,19 +1677,18 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con return Script_Read(arg[0], VM_POINTER(arg[1])); case UI_CIN_PLAYCINEMATIC: - //handle(name, x, y, w, h, looping) + return UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5])); case UI_CIN_STOPCINEMATIC: - //(handle) + return UI_Cin_Stop(VM_LONG(arg[0])); case UI_CIN_RUNCINEMATIC: - //(handle) + return UI_Cin_Run(VM_LONG(arg[0])); case UI_CIN_DRAWCINEMATIC: - //(handle) + return UI_Cin_Draw(VM_LONG(arg[0])); case UI_CIN_SETEXTENTS: - //(handle, x, y, w, h) - break; + return UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4])); default: - Con_Printf("Q3UI: Not implemented system trap: %i\n", (int)fn); + Con_Printf("Q3UI: Unknown system trap: %i\n", (int)fn); return 0; } @@ -1513,8 +1760,6 @@ static void UI_DrawMenu(menu_t *m) } qboolean UI_KeyPress(struct menu_s *m, qboolean isdown, unsigned int devid, int key, int unicode) { - extern qboolean keydown[K_MAX]; - extern int keyshift[K_MAX]; // key to map to if shift held down in console // qboolean result; if (!uivm) return false; @@ -1530,12 +1775,10 @@ qboolean UI_KeyPress(struct menu_s *m, qboolean isdown, unsigned int devid, int return false; } - if (keydown[K_SHIFT]) - key = keyshift[key]; - if (key < K_BACKSPACE && key >= ' ') - key |= 1024; - - /*result = */VM_Call(uivm, UI_KEY_EVENT, key, isdown); + if (key && key < 1024) + /*result = */VM_Call(uivm, UI_KEY_EVENT, key, isdown); + if (unicode && unicode < 1024) + /*result = */VM_Call(uivm, UI_KEY_EVENT, unicode|1024, isdown); return true; } diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index ae3b095e..48f7fdee 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -717,6 +717,7 @@ void CLQ3_ParseServerMessage (void) qboolean CLQ3_Netchan_Process(void) { +#ifndef Q3_NOENCRYPT int sequence; int lastClientCommandNum; qbyte bitmask; @@ -725,12 +726,14 @@ qboolean CLQ3_Netchan_Process(void) char *string; int bit; int readcount; +#endif if(!Netchan_ProcessQ3(&cls.netchan)) { return false; } +#ifndef Q3_NOENCRYPT // archive buffer state bit = net_message.currentbit; readcount = msg_readcount; @@ -748,7 +751,6 @@ qboolean CLQ3_Netchan_Process(void) bitmask = (sequence ^ cls.challenge) & 0xff; string = ccs.clientCommands[lastClientCommandNum & Q3TEXTCMD_MASK]; -#ifndef Q3_NOENCRYPT // decrypt the packet for(i=msg_readcount+4,j=0 ; idefaulttextures->base = Image_TextureIsValid(strtoull(imgname, NULL, 0)); - if (pic && pic->defaulttextures->base) + image_t *img = Image_TextureIsValid(strtoull(imgname, NULL, 0)); + if (img && (img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_CUBE) { - if (!pic->defaulttextures->base->width || !pic->defaulttextures->base->height || !TEXLOADED(pic->defaulttextures->base)) + pic = R_RegisterShader("tiprawimgcube", 0, "{\nprogram postproc_equirectangular\n{\nmap \"$cube:$reflectcube\"\n}\n}"); + pic->defaulttextures->reflectcube = img; + } + else + { + pic = R2D_SafeCachePic("tiprawimg"); + pic->defaulttextures->base = img; + } + if (img) + { + if (!img->width || !img->height || !TEXLOADED(img)) picw = pich = 64; - else if (pic->defaulttextures->base->width > pic->defaulttextures->base->height) + else if (img->width > img->height) { picw = 64; - pich = (64.0*pic->defaulttextures->base->height)/pic->defaulttextures->base->width; + pich = (64.0*img->height)/img->width; } else { - picw = (64.0*pic->defaulttextures->base->width)/pic->defaulttextures->base->height; + picw = (64.0*img->width)/img->height; pich = 64; } break; @@ -2936,41 +2945,53 @@ void Con_DrawConsole (int lines, qboolean noback) } else { + image_t *img = NULL; key = Info_ValueForKey(info, "tiprawimg"); if (*key) { - shader = R2D_SafeCachePic("tiprawimg"); - shader->defaulttextures->base = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA); - if (!shader->defaulttextures->base) - shader->defaulttextures->base = Image_FindTexture(key, NULL, IF_NOREPLACE); - if (!shader->defaulttextures->base) + img = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA|IF_TEXTYPE_ANY); + if (!img) + img = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_TEXTYPE_ANY); + if (!img) { size_t fsize; char *buf; - shader->defaulttextures->base = Image_CreateTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA); + img = Image_CreateTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA|IF_TEXTYPE_ANY); if ((buf = FS_LoadMallocFile (key, &fsize))) - Image_LoadTextureFromMemory(shader->defaulttextures->base, shader->defaulttextures->base->flags|IF_NOWORKER, key, key, buf, fsize); + Image_LoadTextureFromMemory(img, img->flags|IF_NOWORKER, key, key, buf, fsize); } } + key = Info_ValueForKey(info, "tipimgptr"); if (*key) + img = Image_TextureIsValid(strtoull(key, NULL, 0)); + if (img && img->status == TEX_LOADED) { - shader = R2D_SafeCachePic("tiprawimg"); - shader->defaulttextures->base = Image_TextureIsValid(strtoull(key, NULL, 0)); - } - if (shader && shader->defaulttextures->base && shader->defaulttextures->base->status == TEX_LOADED && ((shader->defaulttextures->base->flags&IF_TEXTYPEMASK) == (PTI_2D<width = shader->defaulttextures->base->width; - shader->height = shader->defaulttextures->base->height; - if (shader->width > 320) + if ((img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_CUBE) { - shader->height *= 320.0/shader->width; - shader->width = 320; + shader = R_RegisterShader("tiprawimgcube", 0, "{\nprogram postproc_equirectangular\n{\nmap \"$cube:$reflectcube\"\n}\n}"); + shader->defaulttextures->reflectcube = img; } - if (shader->height > 240) + else if ((img->flags&IF_TEXTYPEMASK) == IF_TEXTYPE_2D) { - shader->width *= 240.0/shader->height; - shader->height = 240; + shader = R2D_SafeCachePic("tiprawimg"); + shader->defaulttextures->base = img; + } + + if (shader) + { + shader->width = img->width; + shader->height = img->height; + if (shader->width > 320) + { + shader->height *= 320.0/shader->width; + shader->width = 320; + } + if (shader->height > 240) + { + shader->width *= 240.0/shader->height; + shader->height = 240; + } } } else diff --git a/engine/client/image.c b/engine/client/image.c index f7861df1..eb486560 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -31,7 +31,7 @@ static void QDECL R_Image_BuggyCvar (struct cvar_s *var, char *oldvalue) { //force these cvars to value 1 if they're empty. //cvars using this should be changed to 0 by default, once our engine bugs are debugged/fixed. if (!*var->string) - var->ival = true; + var->ival = var->value = true; } cvar_t r_keepimages = CVARCD("r_keepimages", "", R_Image_BuggyCvar, "Retain unused images in memory for slightly faster map loading. FIXME: a setting of 0 may be crashy! (empty is treated as 1 for now)"); cvar_t r_ignoremapprefixes = CVARCD("r_ignoremapprefixes", "", R_Image_BuggyCvar, "Ignores when textures were loaded from map-specific paths. FIXME: empty is currently interpretted as 1 because the alternative is too memory hungary with r_keepimages 1."); @@ -2552,18 +2552,19 @@ qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression WritePCXfile ============== */ -void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height, +qboolean WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height, int rowbytes, qbyte *palette, qboolean upload) //data is 8bit. { int i, j, length; pcx_t *pcx; qbyte *pack; + qboolean ret; pcx = BZ_Malloc(width*height*2+1000); if (pcx == NULL) { Con_Printf("WritePCXfile: not enough memory\n"); - return; + return false; } pcx->manufacturer = 0x0a; // PCX id @@ -2610,11 +2611,16 @@ void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, i #ifdef HAVE_CLIENT if (upload) + { CL_StartUpload((void *)pcx, length); + ret = true; + } else #endif - COM_WriteFile (filename, fsroot, pcx, length); + ret = COM_WriteFile (filename, fsroot, pcx, length); BZ_Free(pcx); + + return ret; } /* @@ -4600,10 +4606,11 @@ static void Image_LoadTexture_Failed(void *ctx, void *data, size_t a, size_t b) texid_t tex = ctx; tex->status = TEX_FAILED; } -static void Image_FixupImageSize(texid_t tex, unsigned int w, unsigned int h) +static void Image_FixupImageSize(texid_t tex, unsigned int w, unsigned int h, unsigned int d) { tex->width = w; tex->height = h; + tex->depth = d; //ezhud breaks without this. I assume other things will too. this is why you shouldn't depend upon querying an image's size. if (!strncmp(tex->ident, "gfx/", 4)) @@ -4698,6 +4705,9 @@ static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) break; } + if ((tex->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_ANY) + tex->flags = (tex->flags&~IF_TEXTYPEMASK)|(mips->type<IMG_LoadTextureMips(tex, mips)) { tex->format = mips->encoding; @@ -4811,10 +4821,10 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc case PTI_BC2_RGBA_SRGB: header.glinternalformat = 0x8C4E/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT*/; break; case PTI_BC3_RGBA: header.glinternalformat = 0x83F3/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/; break; case PTI_BC3_RGBA_SRGB: header.glinternalformat = 0x8C4F/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT*/; break; - case PTI_BC4_R8_SNORM: header.glinternalformat = 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/; break; - case PTI_BC4_R8: header.glinternalformat = 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/; break; - case PTI_BC5_RG8_SNORM: header.glinternalformat = 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/; break; - case PTI_BC5_RG8: header.glinternalformat = 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/; break; + case PTI_BC4_R_SNORM: header.glinternalformat = 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/; break; + case PTI_BC4_R: header.glinternalformat = 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/; break; + case PTI_BC5_RG_SNORM: header.glinternalformat = 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/; break; + case PTI_BC5_RG: header.glinternalformat = 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/; break; case PTI_BC6_RGB_UFLOAT: header.glinternalformat = 0x8E8F/*GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB*/; break; case PTI_BC6_RGB_SFLOAT: header.glinternalformat = 0x8E8E/*GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB*/; break; case PTI_BC7_RGBA: header.glinternalformat = 0x8E8C/*GL_COMPRESSED_RGBA_BPTC_UNORM_ARB*/; break; @@ -4937,6 +4947,9 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc } switch(mips->type) { + case PTI_ANY: + VFS_CLOSE(file); + return false; case PTI_2D: case PTI_2D_ARRAY: case PTI_CUBE: @@ -4975,7 +4988,6 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch ktxheader_t header; int nummips; int mipnum; - int face; int datasize; unsigned int *swap, w, h, d, f, l, browbytes,padbytes,y,x,rows; struct pendingtextureinfo *mips; @@ -5041,10 +5053,10 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch case 0x8C4E/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT*/: encoding = PTI_BC2_RGBA_SRGB; break; case 0x83F3/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/: encoding = PTI_BC3_RGBA; break; case 0x8C4F/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT*/: encoding = PTI_BC3_RGBA_SRGB; break; - case 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/: encoding = PTI_BC4_R8_SNORM; break; - case 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/: encoding = PTI_BC4_R8; break; - case 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/: encoding = PTI_BC5_RG8_SNORM; break; - case 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/: encoding = PTI_BC5_RG8; break; + case 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/: encoding = PTI_BC4_R_SNORM; break; + case 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/: encoding = PTI_BC4_R; break; + case 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/: encoding = PTI_BC5_RG_SNORM; break; + case 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/: encoding = PTI_BC5_RG; break; case 0x8E8F/*GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB*/: encoding = PTI_BC6_RGB_UFLOAT; break; case 0x8E8E/*GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB*/: encoding = PTI_BC6_RGB_SFLOAT; break; case 0x8E8C/*GL_COMPRESSED_RGBA_BPTC_UNORM_ARB*/: encoding = PTI_BC7_RGBA; break; @@ -5266,7 +5278,8 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch w = max(1, w>>1); h = max(1, h>>1); - d = max(1, d>>1); + if (mips->type == PTI_3D) + d = max(1, d>>1); } if (!mips->mipcount) @@ -5278,6 +5291,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch #ifdef ASTC_WITH_HDRTEST if (encoding >= PTI_ASTC_4X4_LDR && encoding <= PTI_ASTC_12X12_LDR) { + int face; for (face = 0; face < header.numberoffaces; face++) { if (ASTC_BlocksAreHDR(mips->mip[face].data, mips->mip[face].datasize, blockwidth, blockheight, 1)) @@ -5564,14 +5578,14 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch } else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('1'<<24)) || *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('U'<<24))) - encoding = PTI_BC4_R8; + encoding = PTI_BC4_R; else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('2'<<24)) || *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('U'<<24))) - encoding = PTI_BC5_RG8; + encoding = PTI_BC5_RG; else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('S'<<24))) - encoding = PTI_BC4_R8_SNORM; + encoding = PTI_BC4_R_SNORM; else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('S'<<24))) - encoding = PTI_BC5_RG8_SNORM; + encoding = PTI_BC5_RG_SNORM; else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('E'<<0)|('T'<<8)|('C'<<16)|('2'<<24))) encoding = PTI_ETC2_RGB8; else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('1'<<16)|('0'<<24))) @@ -5661,11 +5675,11 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch case 0x4d/*DXGI_FORMAT_BC3_UNORM*/: encoding = PTI_BC3_RGBA; break; case 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/: encoding = PTI_BC3_RGBA_SRGB; break; // case 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/: encoding = PTI_INVALID; break; - case 0x50/*DXGI_FORMAT_BC4_UNORM*/: encoding = PTI_BC4_R8; break; - case 0x51/*DXGI_FORMAT_BC4_SNORM*/: encoding = PTI_BC4_R8_SNORM; break; + case 0x50/*DXGI_FORMAT_BC4_UNORM*/: encoding = PTI_BC4_R; break; + case 0x51/*DXGI_FORMAT_BC4_SNORM*/: encoding = PTI_BC4_R_SNORM; break; // case 0x52/*DXGI_FORMAT_BC5_TYPELESS*/: encoding = PTI_INVALID; break; - case 0x53/*DXGI_FORMAT_BC5_UNORM*/: encoding = PTI_BC5_RG8; break; - case 0x54/*DXGI_FORMAT_BC5_SNORM*/: encoding = PTI_BC5_RG8_SNORM; break; + case 0x53/*DXGI_FORMAT_BC5_UNORM*/: encoding = PTI_BC5_RG; break; + case 0x54/*DXGI_FORMAT_BC5_SNORM*/: encoding = PTI_BC5_RG_SNORM; break; case 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/: encoding = PTI_RGB565; break; case 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/: encoding = PTI_ARGB1555; break; case 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/: encoding = PTI_BGRA8; break; @@ -5759,12 +5773,12 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch if (fmt10header.arraysize == 6) { ttype = PTI_CUBE; - fmtheader.dwDepth = 6; + layers = 6; } else { ttype = PTI_CUBE_ARRAY; - fmtheader.dwDepth = fmt10header.arraysize; + layers = fmt10header.arraysize; } } else if (fmtheader.ddsCaps[1] & 0x200000) @@ -5779,7 +5793,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch ttype = PTI_2D; else ttype = PTI_2D_ARRAY; - fmtheader.dwDepth = fmt10header.arraysize; + layers = fmt10header.arraysize; } mips = Z_Malloc(sizeof(*mips)); @@ -5794,27 +5808,55 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch h = fmtheader.dwHeight; d = fmtheader.dwDepth; - for (mipnum = 0; mipnum < nummips; mipnum++) - { - if (mips->mipcount >= countof(mips->mip)) - break; - -// if (datasize < 8) -// datasize = pad; - datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (d) * blockbytes; - - for (layer = 0; layer < layers; layer++) + if (layers == 1) + { //can just use the data without copying. + for (mipnum = 0; mipnum < nummips; mipnum++) { - mips->mip[mips->mipcount].data = filedata; - mips->mip[mips->mipcount].datasize = datasize; - mips->mip[mips->mipcount].width = w; - mips->mip[mips->mipcount].height = h; - mips->mip[mips->mipcount].depth = d; + datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (d) * blockbytes; + + mips->mip[mipnum].data = filedata; + mips->mip[mipnum].datasize = datasize; + mips->mip[mipnum].width = w; + mips->mip[mipnum].height = h; + mips->mip[mipnum].depth = d; mips->mipcount++; filedata += datasize; + + w = max(1, w>>1); + h = max(1, h>>1); + d = max(1, d>>1); } - w = (w+1)>>1; - h = (h+1)>>1; + } + else + { //we need to copy stuff in order to pack it properly. :( + //allocate space and calc mip sizes + for (mipnum = 0; mipnum < nummips; mipnum++) + { + datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (layers*d) * blockbytes; + mips->mip[mipnum].data = BZ_Malloc(datasize); + mips->mip[mipnum].datasize = datasize; + mips->mip[mipnum].width = w; + mips->mip[mipnum].height = h; + mips->mip[mipnum].depth = layers*d; + + w = max(1, w>>1); + h = max(1, h>>1); + d = max(1, d>>1); + } + mips->mipcount = mipnum; + //and now copy over the data + for (layer = 0; layer < layers; layer++) + { + for (mipnum = 0; mipnum < nummips; mipnum++) + { + datasize = mips->mip[mipnum].datasize/layers; + memcpy((qbyte*)mips->mip[mipnum].data+datasize*layer, filedata, datasize); + filedata += datasize; + } + } + //and now we're done with the source file. we might as well free it early. + BZ_Free(mips->extrafree); + mips->extrafree = NULL; } return mips; @@ -5869,6 +5911,8 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc arraysize = mips->mip[0].depth; switch(mips->type) { + case PTI_ANY: + return false; case PTI_3D: arraysize = 1; h9.ddsCaps[1] |= 0x200000; //VOLUME @@ -6007,11 +6051,11 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc case PTI_BC3_RGBA: h10.dxgiformat = 0x4d/*DXGI_FORMAT_BC3_UNORM*/; DX9FOURCC('D','X','T','5'); break; case PTI_BC3_RGBA_SRGB: h10.dxgiformat = 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/; break; // case PTI_INVALID: h10.dxgiformat = 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/; break; - case PTI_BC4_R8: h10.dxgiformat = 0x50/*DXGI_FORMAT_BC4_UNORM*/; /*DX9FOURCC('B','C','4','U');*/ DX9FOURCC('A','T','I','1'); break; - case PTI_BC4_R8_SNORM: h10.dxgiformat = 0x51/*DXGI_FORMAT_BC4_SNORM*/; DX9FOURCC('B','C','4','S'); break; + case PTI_BC4_R: h10.dxgiformat = 0x50/*DXGI_FORMAT_BC4_UNORM*/; /*DX9FOURCC('B','C','4','U');*/ DX9FOURCC('A','T','I','1'); break; + case PTI_BC4_R_SNORM: h10.dxgiformat = 0x51/*DXGI_FORMAT_BC4_SNORM*/; DX9FOURCC('B','C','4','S'); break; // case PTI_INVALID: h10.dxgiformat = 0x52/*DXGI_FORMAT_BC5_TYPELESS*/; break; - case PTI_BC5_RG8: h10.dxgiformat = 0x53/*DXGI_FORMAT_BC5_UNORM*/; /*DX9FOURCC('B','C','5','U');*/ DX9FOURCC('A','T','I','2'); break; - case PTI_BC5_RG8_SNORM: h10.dxgiformat = 0x54/*DXGI_FORMAT_BC5_SNORM*/; DX9FOURCC('B','C','5','S'); break; + case PTI_BC5_RG: h10.dxgiformat = 0x53/*DXGI_FORMAT_BC5_UNORM*/; /*DX9FOURCC('B','C','5','U');*/ DX9FOURCC('A','T','I','2'); break; + case PTI_BC5_RG_SNORM: h10.dxgiformat = 0x54/*DXGI_FORMAT_BC5_SNORM*/; DX9FOURCC('B','C','5','S'); break; case PTI_RGB565: h10.dxgiformat = 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/; DX9FMT(16, 0xf800, 0x07e0, 0x001f, 0x0000,DX9RGB); break; case PTI_ARGB1555: h10.dxgiformat = 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/; DX9FMT(16, 0x7c00, 0x03e0, 0x001f, 0x8000,DX9RGBA); break; case PTI_BGRA8: h10.dxgiformat = 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/; DX9FMT(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000,DX9RGBA); break; @@ -7126,8 +7170,11 @@ static float HalfToFloat(unsigned short val) float f; unsigned int u; } u; - u.u = (((val&0x7c00)>>10)-15+127)<<23; //read exponent, rebias it, and reshift. - u.u |= ((val & 0x3ff)<<13) | ((val & 0x3ff)<<3) | ((val & 0x3ff)>>7); //shift up the mantissa, and fold a little + if (val&0x7c00) + u.u = (((val&0x7c00)>>10)-15+127)<<23; //read exponent, rebias it, and reshift. + else + u.u = 0; //denormal (or 0). + u.u |= ((val & 0x3ff)<<13);//shift up the mantissa, but don't fold u.u |= (val&0x8000)<<16; //retain the sign bit. return u.f; } @@ -8448,8 +8495,8 @@ static void Image_Tr_FloatToE5BGR9(struct pendingtextureinfo *mips, int dummy) float *dofree = mips->mip[mip].needfree?in:NULL; unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth; mips->mip[mip].needfree = true; - mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); mips->mip[mip].datasize = p*sizeof(*out); + mips->mip[mip].data = out = BZ_Malloc(mips->mip[mip].datasize); for (; p-->0; out++, in+=4) { int e = 0; @@ -8594,13 +8641,13 @@ static void Image_Tr_HalfToFloat(struct pendingtextureinfo *mips, int channels) unsigned int mip; for (mip = 0; mip < mips->mipcount; mip++) { - float *in = mips->mip[mip].data; + unsigned short *in = mips->mip[mip].data; float *out = mips->mip[mip].data; - float *dofree = mips->mip[mip].needfree?in:NULL; - unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth; + unsigned short *dofree = mips->mip[mip].needfree?in:NULL; + unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*channels; mips->mip[mip].needfree = true; - mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4); - mips->mip[mip].datasize = p*sizeof(*out)*4; + mips->mip[mip].datasize = p*sizeof(*out); + mips->mip[mip].data = out = BZ_Malloc(mips->mip[mip].datasize); while(p-->0) *out++ = HalfToFloat(*in++); BZ_Free(dofree); @@ -9251,7 +9298,7 @@ static void Image_Decode_BC4_Block(qbyte *fte_restrict in, pixel32_t *fte_restri Vector4Set(r.v, 0, 0, 0, 0xff); for (i = 0; i < 4; i++) out[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r; - Image_Decode_RGTC_Block_Internal(in, out->v+0, w*4, fmt==PTI_BC4_R8_SNORM); + Image_Decode_RGTC_Block_Internal(in, out->v+0, w*4, fmt==PTI_BC4_R_SNORM); } static void Image_Decode_BC5_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt) { //BC5: two of BC3's alpha channels but used as red+green only. @@ -9260,8 +9307,8 @@ static void Image_Decode_BC5_Block(qbyte *fte_restrict in, pixel32_t *fte_restri Vector4Set(r.v, 0, 0, 0, 0xff); for (i = 0; i < 4; i++) out[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r; - Image_Decode_RGTC_Block_Internal(in+0, out->v+0, w*4, fmt==PTI_BC5_RG8_SNORM); - Image_Decode_RGTC_Block_Internal(in+8, out->v+1, w*4, fmt==PTI_BC5_RG8_SNORM); + Image_Decode_RGTC_Block_Internal(in+0, out->v+0, w*4, fmt==PTI_BC5_RG_SNORM); + Image_Decode_RGTC_Block_Internal(in+8, out->v+1, w*4, fmt==PTI_BC5_RG_SNORM); } #endif @@ -10389,8 +10436,8 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, case PTI_BC1_RGB_SRGB: case PTI_BC1_RGBA: case PTI_BC1_RGBA_SRGB: - case PTI_BC4_R8: - case PTI_BC4_R8_SNORM: + case PTI_BC4_R: + case PTI_BC4_R_SNORM: case PTI_ETC1_RGB8: case PTI_ETC2_RGB8: case PTI_ETC2_RGB8_SRGB: @@ -10405,8 +10452,8 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, case PTI_BC2_RGBA_SRGB: case PTI_BC3_RGBA: case PTI_BC3_RGBA_SRGB: - case PTI_BC5_RG8: - case PTI_BC5_RG8_SNORM: + case PTI_BC5_RG: + case PTI_BC5_RG_SNORM: case PTI_BC6_RGB_UFLOAT: case PTI_BC6_RGB_SFLOAT: case PTI_BC7_RGBA: @@ -10508,10 +10555,10 @@ qboolean Image_FormatHasAlpha(uploadfmt_t encoding) case PTI_L8_SRGB: case PTI_BC1_RGB: case PTI_BC1_RGB_SRGB: - case PTI_BC4_R8: - case PTI_BC4_R8_SNORM: - case PTI_BC5_RG8: - case PTI_BC5_RG8_SNORM: + case PTI_BC4_R: + case PTI_BC4_R_SNORM: + case PTI_BC5_RG: + case PTI_BC5_RG_SNORM: case PTI_BC6_RGB_UFLOAT: case PTI_BC6_RGB_SFLOAT: case PTI_ETC1_RGB8: @@ -10621,13 +10668,13 @@ const char *Image_FormatName(uploadfmt_t fmt) case PTI_BGRA8_SRGB: return "BGRA8_SRGB"; case PTI_BGRX8_SRGB: return "BGRX8_SRGB"; case PTI_A2BGR10: return "A2BGR10"; - case PTI_E5BGR9: return "E5BGR9"; - case PTI_B10G11R11F: return "B10G11R11F"; - case PTI_R16F: return "R16F"; - case PTI_R32F: return "R32F"; - case PTI_RGBA16F: return "RGBA16F"; - case PTI_RGBA32F: return "RGBA32F"; - case PTI_RGB32F: return "RGB32F"; + case PTI_E5BGR9: return "E5BGR9_UF"; + case PTI_B10G11R11F: return "B10G11R11_UF"; + case PTI_R16F: return "R16_SF"; + case PTI_R32F: return "R32_SF"; + case PTI_RGBA16F: return "RGBA16_SF"; + case PTI_RGBA32F: return "RGBA32_SF"; + case PTI_RGB32F: return "RGB32_SF"; case PTI_R16: return "R16"; case PTI_RGBA16: return "RGBA16"; case PTI_P8: return "P8"; @@ -10655,12 +10702,12 @@ const char *Image_FormatName(uploadfmt_t fmt) case PTI_BC2_RGBA_SRGB: return "BC2_RGBA_SRGB"; case PTI_BC3_RGBA: return "BC3_RGBA"; case PTI_BC3_RGBA_SRGB: return "BC3_RGBA_SRGB"; - case PTI_BC4_R8: return "BC4_R8"; - case PTI_BC4_R8_SNORM: return "BC4_R8_SNORM"; - case PTI_BC5_RG8: return "BC5_RG8"; - case PTI_BC5_RG8_SNORM: return "BC5_RG8_SNORM"; - case PTI_BC6_RGB_UFLOAT: return "BC6_RGBF"; - case PTI_BC6_RGB_SFLOAT: return "BC6_RGBF_SNORM"; + case PTI_BC4_R: return "BC4_R"; + case PTI_BC4_R_SNORM: return "BC4_R_SNORM"; + case PTI_BC5_RG: return "BC5_RG"; + case PTI_BC5_RG_SNORM: return "BC5_RG_SNORM"; + case PTI_BC6_RGB_UFLOAT: return "BC6_RGB_UF"; + case PTI_BC6_RGB_SFLOAT: return "BC6_RGB_SF"; case PTI_BC7_RGBA: return "BC7_RGBA"; case PTI_BC7_RGBA_SRGB: return "BC7_RGBA_SRGB"; case PTI_ETC1_RGB8: return "ETC1_RGB8"; @@ -10999,13 +11046,13 @@ static qboolean Image_DecompressFormat(struct pendingtextureinfo *mips, const ch #endif break; #ifdef DECOMPRESS_RGTC - case PTI_BC4_R8_SNORM: - case PTI_BC4_R8: + case PTI_BC4_R_SNORM: + case PTI_BC4_R: decodefunc = Image_Decode_BC4_Block; rcoding = PTI_RGBX8; break; - case PTI_BC5_RG8_SNORM: - case PTI_BC5_RG8: + case PTI_BC5_RG_SNORM: + case PTI_BC5_RG: decodefunc = Image_Decode_BC5_Block; rcoding = PTI_RGBX8; break; @@ -11459,6 +11506,39 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag switch(fmt) { default: + if (fmt&PTI_FULLMIPCHAIN) + { + fmt = fmt&~PTI_FULLMIPCHAIN; + Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags); + if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight) //make sure its okay + { + size_t sz = 0; + Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); + for (i = 0; i < countof(mips->mip) && (imgwidth || imgheight); i++, imgwidth>>=1, imgheight>>=1) + { + mips->mip[i].width = max(1,imgwidth); + mips->mip[i].height = max(1,imgheight); + mips->mip[i].depth = 1; + mips->mip[i].datasize = bb * ((mips->mip[i].width+bw-1)/bw) * ((mips->mip[i].height+bh-1)/bh); + mips->mip[i].needfree = false; + sz += mips->mip[i].datasize; + } + mips->mipcount = i; + mips->encoding = fmt; + if (!freedata) + { + rgbadata = BZ_Malloc(sz); + memcpy(rgbadata, rawdata, sz); + } + mips->extrafree = rawdata = rgbadata; + for (i = 0; i < mips->mipcount; i++) + { + mips->mip[i].data = rawdata; + rawdata = (qbyte*)rawdata+mips->mip[i].datasize; + } + return true; + } + } mips->encoding = fmt; break; @@ -11985,10 +12065,10 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_BGRX8_SRGB: case PTI_BC1_RGB: case PTI_BC1_RGB_SRGB: - case PTI_BC4_R8: - case PTI_BC4_R8_SNORM: - case PTI_BC5_RG8: - case PTI_BC5_RG8_SNORM: + case PTI_BC4_R: + case PTI_BC4_R_SNORM: + case PTI_BC5_RG: + case PTI_BC5_RG_SNORM: case PTI_BC6_RGB_UFLOAT: case PTI_BC6_RGB_SFLOAT: case PTI_ETC1_RGB8: @@ -12498,7 +12578,7 @@ void *Image_FlipImage(const void *inbuffer, void *outbuffer, int *inoutwidth, in { inb = inr; //reset the input after each row, so we have truely independant row+column strides inr += rowstride; - for (x = 0; x < inheight; x++) + for (x = 0; x < inwidth; x++) { for (b = 0; b < pixelbytes; b++) *outb++ = inb[b]; @@ -12545,12 +12625,12 @@ static struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicen } cmscheme[][6] = { { - {"rt", true, false, true}, - {"lf", false, true, true}, - {"ft", true, true, false}, - {"bk", false, false, false}, - {"up", true, false, true}, - {"dn", true, false, true} + {"rt", false, false, true}, + {"lf", true, true, true}, + {"bk", false, true, false}, + {"ft", true, false, false}, + {"up", false, false, true}, + {"dn", false, false, true} }, { @@ -12614,8 +12694,8 @@ static struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicen extern cvar_t vid_hardwaregamma; int bb,bw,bh; Image_BlockSizeForEncoding(format, &bb, &bw, &bh); - if (needsflipping && (bb!=4 || bw!=1 || bh!=1)) - ; + if (needsflipping && (bw!=1 || bh!=1)) + /*can't do it*/; else if (width == height && (!mips || width == mips->mip[0].width)) //cubemaps must be square and all the same size (npot is fine though) { //(skies have a fallback for invalid sizes, but it'll run a bit slower) @@ -12624,13 +12704,13 @@ static struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicen mips = Z_Malloc(sizeof(*mips)); mips->type = PTI_CUBE; mips->mipcount = 1; - mips->encoding = PTI_RGBA8; + mips->encoding = format; mips->extrafree = NULL; - mips->mip[0].datasize = width*height*4*6; + mips->mip[0].datasize = width*height*bb*6; mips->mip[0].data = BZ_Malloc(mips->mip[0].datasize); mips->mip[0].width = width; mips->mip[0].height = height; - mips->mip[0].depth = 6;; + mips->mip[0].depth = 6; mips->mip[0].needfree = true; } @@ -12691,10 +12771,11 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd COM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0); return false; } + fmt &= ~PTI_FULLMIPCHAIN; Image_GenerateMips(mips, flags); Image_ChangeFormatFlags(mips, flags, fmt, tex->ident); - Image_FixupImageSize(tex, imgwidth, imgheight); + Image_FixupImageSize(tex, imgwidth, imgheight, mips->mip[0].depth); if (flags & IF_NOWORKER) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -12712,7 +12793,7 @@ qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, BZ_Free(tex->fallbackdata); tex->fallbackdata = NULL; - Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height); + Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height, mips->mip[0].depth); if ((flags & IF_NOWORKER) || Sys_IsMainThread()) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -13065,7 +13146,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t if (mips) { - Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height); + Image_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height, mips->mip[0].depth); if (tex->flags & IF_NOWORKER) Image_LoadTextureMips(tex, mips, 0, 0); else @@ -13358,7 +13439,14 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns (fallbackwidth>>3)*(fallbackheight>>3); break; default: - Sys_Error("Image_GetTexture: bad format"); + { + unsigned int bb, bw, bh; + unsigned int lev; + Image_BlockSizeForEncoding(fallbackfmt&~PTI_FULLMIPCHAIN, &bb, &bw, &bh); + for (b=0, lev = 0; fallbackwidth>>lev||fallbackheight>>lev; lev++) + b += bb * (max(1,fallbackwidth>>lev)+bw-1)/bw * (max(1,fallbackheight>>lev)+bh-1)/bh; + } + break; } tex->fallbackdata = BZ_Malloc(b + pb); memcpy(tex->fallbackdata, fallbackdata, b); @@ -13601,11 +13689,11 @@ void Image_List_f(void) failed++; continue; } - if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D) + if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D || ((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_CUBE) Con_Printf("^[\\imgptr\\%#"PRIxSIZE"^]", (size_t)tex); if (tex->subpath) Con_Printf("^h(%s)^h", tex->subpath); - Con_DLPrintf(1, " %x", tex->flags); +// Con_DLPrintf(1, " %x", tex->flags); if (Image_LocateHighResTexture(tex, &loc, fname, sizeof(fname), &loadflags)) { @@ -13614,7 +13702,7 @@ void Image_List_f(void) while((bullshit=strchr(defuck, '\\'))) *bullshit = '/'; - if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D || tex->format == PTI_P8) + if (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D||((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_CUBE || tex->format == PTI_P8) Con_Printf("^[%s\\tip\\%s/%s\\tipimgptr\\%#"PRIxSIZE"^]: ", tex->ident, defuck, fname, (size_t)tex); else Con_Printf("^[%s\\tip\\%s/%s^]: ", tex->ident, defuck, fname); @@ -13635,15 +13723,25 @@ void Image_List_f(void) if (tex->status == TEX_LOADED) { + char *type; unsigned int blockbytes, blockwidth, blockheight; Image_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight); - imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight; + imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * tex->depth; + switch((tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) + { + case PTI_2D: type = ""; break; + case PTI_3D: type = "3D "; break; + case PTI_CUBE: type = "Cube "; break; + case PTI_2D_ARRAY: type = "Array "; break; + case PTI_CUBE_ARRAY:type = "CubeArray "; break; + default: type = "UNKNOWN "; break; + } if (!(tex->flags & IF_NOMIPMAP)) imgmem += imgmem/3; //mips take about a third extra mem. if (tex->depth != 1) - Con_Printf("^2loaded (%i*%i*%i ^4%s^2, %3fKB->%3fKB)\n", tex->width, tex->height, tex->depth, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0)); + Con_Printf("^2loaded (%s%i*%i*%i ^4%s^2, %3fKB->%3fKB)\n", type, tex->width, tex->height, tex->depth, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0)); else - Con_Printf("^2loaded (%i*%i ^4%s^2, %3fKB->%3fKB)\n", tex->width, tex->height, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0)); + Con_Printf("^2loaded (%s%i*%i ^4%s^2, %3fKB->%3fKB)\n", type, tex->width, tex->height, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0)); if (tex->aliasof) { aliasedmem += imgmem; diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index c9a76927..d217a2ef 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -360,6 +360,13 @@ qboolean Media_NamedTrack(const char *track, const char *looptrack) int ie, ip; char *trackend; + if (!track && !looptrack) + { + *media_playtrack = *media_loopingtrack = 0; + Media_Changed(MEDIA_GAMEMUSIC); + return true; + } + if (!track || !*track) //ignore calls if the primary track is invalid. whatever is already playing will continue to play. return false; if (!looptrack || !*looptrack) //null or empty looptrack loops using the primary track, for compat with q3. diff --git a/engine/client/merged.h b/engine/client/merged.h index fe9af7a4..56b3cd96 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -298,6 +298,8 @@ struct pendingtextureinfo PTI_CUBE, //w*h*6 - depth MUST be 6 (faces must be tightly packed) PTI_2D_ARRAY, //w*h*layers - depth is =layers PTI_CUBE_ARRAY, //w*h*(layers*6) - depth is =(layers*6). + + PTI_ANY //says we don't care. } type; uploadfmt_t encoding; //PTI_* formats diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 8a6c2325..5fde24d7 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -103,7 +103,7 @@ typedef struct { } net_masterlist_t; static net_masterlist_t net_masterlist[] = { #if 0 //for debugging - {MP_DPMASTER, CVARFC("net_masterextra1", "localhost:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara + {MP_DPMASTER, CVARFC("net_masterextra1", "localhost:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: the reader... #else #ifndef QUAKETC @@ -119,7 +119,7 @@ static net_masterlist_t net_masterlist[] = { #endif //dpmaster is the generic non-quake-specific master protocol that we use for custom stand-alone mods. - {MP_DPMASTER, CVARAFC("net_master1", "localhost", "sv_master1", 0, Net_Masterlist_Callback)}, + {MP_DPMASTER, CVARAFC("net_master1", "", "sv_master1", 0, Net_Masterlist_Callback)}, {MP_DPMASTER, CVARAFC("net_master2", "", "sv_master2", 0, Net_Masterlist_Callback)}, {MP_DPMASTER, CVARAFC("net_master3", "", "sv_master3", 0, Net_Masterlist_Callback)}, {MP_DPMASTER, CVARAFC("net_master4", "", "sv_master4", 0, Net_Masterlist_Callback)}, @@ -145,11 +145,11 @@ static net_masterlist_t net_masterlist[] = { #ifdef HAVE_PACKET #ifndef QUAKETC //engine-specified/maintained master lists (so users can be lazy and update the engine without having to rewrite all their configs). - {MP_QUAKEWORLD, CVARFC("net_qwmasterextra1", "qwmaster.ocrana.de:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Ocrana(2nd)"}, //german. admin unknown - {MP_QUAKEWORLD, CVARFC("net_qwmasterextra2", ""/*"masterserver.exhale.de:27000" seems dead*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //german. admin unknown + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra1", ""/*"qwmaster.ocrana.de:27000" not responding*/, CVAR_NOSAVE, Net_Masterlist_Callback), "Ocrana(2nd)"}, //german. admin unknown + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra2", ""/*"masterserver.exhale.de:27000" dns dead*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //german. admin unknown // {MP_QUAKEWORLD, CVARFC("net_qwmasterextra3", "asgaard.morphos-team.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Germany, admin: bigfoot"}, {MP_QUAKEWORLD, CVARFC("net_qwmasterextra4", "master.quakeservers.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Germany, admin: raz0?"}, -// {MP_QUAKEWORLD, CVARFC("net_qwmasterextra5", "qwmaster.fodquake.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "admin: bigfoot"}, + {MP_QUAKEWORLD, CVARFC("net_qwmasterextra5", "qwmaster.fodquake.net:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "admin: bigfoot"}, // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master"}, // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27002", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For CTF Servers"}, // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "satan.idsoftware.com:27003", CVAR_NOSAVE, Net_Masterlist_Callback), "Official id Master For TeamFortress Servers"}, @@ -167,7 +167,7 @@ static net_masterlist_t net_masterlist[] = { // {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"}, //Total conversions will need to define their own in defaults.cfg or whatever. - {MP_DPMASTER, CVARFC("net_masterextra1", "master.frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara + {MP_DPMASTER, CVARFC("net_masterextra1", "master.frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara // {MP_DPMASTER, CVARFC("net_masterextra1", ""/*"ghdigital.com:27950 207.55.114.154:27950"*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc {MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis {MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr @@ -197,7 +197,6 @@ static net_masterlist_t net_masterlist[] = { {MP_QUAKE3, CVARFC("net_q3masterextra3", "master.ioquake3.org:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "DE: ioquake3"}, {MP_QUAKE3, CVARFC("net_q3masterextra4", "master.huxxer.de:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "DE: BMA Team"}, {MP_QUAKE3, CVARFC("net_q3masterextra5", "master.maverickservers.com:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "US: Maverickservers.com"}, - {MP_QUAKE3, CVARFC("net_q3masterextra6", "dpmaster.deathmask.net:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "US: DeathMask.net"}, {MP_QUAKE3, CVARFC("net_q3masterextra8", "master3.idsoftware.com:27950", CVAR_NOSAVE, Net_Masterlist_Callback), "US: id Software Quake III Master"}, #endif #endif diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 289d276b..c798fc2e 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -917,6 +917,15 @@ void QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr G_FLOAT(OFS_RETURN) = 0; } +void QCBUILTIN PF_remapshader (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *shadername = PR_GetStringOfs(prinst, OFS_PARM0); + const char *replacement = PR_GetStringOfs(prinst, OFS_PARM1); + float timeoffset = G_FLOAT(OFS_PARM2); + + R_RemapShader(shadername, replacement, timeoffset); +} + void QCBUILTIN PF_cl_GetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int bm[2]; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index cb2c5eac..54df7fef 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -6548,6 +6548,7 @@ static struct { {"rotatevectorsbyvectors", PF_rotatevectorsbymatrix, 236}, // #236 {"skinforname", PF_skinforname, 237}, // #237 {"shaderforname", PF_shaderforname, 238}, // #238 + {"remapshader", PF_remapshader, 0}, {"te_bloodqw", PF_cl_te_bloodqw, 239}, // #239 void te_bloodqw(vector org[, float count]) (FTE_TE_STANDARDEFFECTBUILTINS) {"checkpvs", PF_checkpvs, 240}, diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 235038f4..7eba0e07 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -4127,7 +4127,7 @@ Groups surfaces into their respective batches (based on the lightmap number). */ void Surf_BuildLightmaps (void) { - int i; + unsigned int i, j; model_t *m; extern model_t *mod_known; @@ -4170,6 +4170,10 @@ void Surf_BuildLightmaps (void) if (m->loadstate != MLS_LOADED) continue; Surf_BuildModelLightmaps(m); + + for (j = 0; j < m->numenvmaps; j++) + if (m->envmaps[j].image) + m->envmaps[j].image->regsequence = r_regsequence; } BE_UploadAllLightmaps(); } diff --git a/engine/client/render.h b/engine/client/render.h index 07bec4fc..aaf4a123 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -337,7 +337,7 @@ void R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t * void R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int vertcount, vec3_t *normals, float scale, qboolean colormod); qboolean R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and calls back into it*/ -void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int width, unsigned int height); /*generate q1 sky texnums*/ +void R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *src, unsigned int width, unsigned int height); /*generate q1 sky texnums*/ void R_Clutter_Emit(struct batch_s **batches); void R_Clutter_Purge(void); @@ -429,6 +429,7 @@ enum imageflags #define IF_TEXTYPE_CUBE (PTI_CUBE<string); // Shader_NeedReload(false); } void R_ForceSky_f(void) @@ -211,7 +212,7 @@ cvar_t r_menutint = CVARF ("r_menutint", "0.68 0.4 0.13", CVAR_RENDERERCALLBACK); cvar_t r_netgraph = CVARD ("r_netgraph", "0", "Displays a graph of packet latency. A value of 2 will give additional info about what sort of data is being received from the server."); extern cvar_t r_lerpmuzzlehack; -extern cvar_t mod_h2holey_bugged; +extern cvar_t mod_h2holey_bugged, mod_halftexel; cvar_t r_nolerp = CVARF ("r_nolerp", "0", CVAR_ARCHIVE); cvar_t r_noframegrouplerp = CVARF ("r_noframegrouplerp", "0", CVAR_ARCHIVE); cvar_t r_nolightdir = CVARF ("r_nolightdir", "0", CVAR_ARCHIVE); @@ -561,6 +562,7 @@ void GLRenderer_Init(void) #endif #ifdef MD1MODELS Cvar_Register (&mod_h2holey_bugged, GLRENDEREROPTIONS); + Cvar_Register (&mod_halftexel, GLRENDEREROPTIONS); #endif Cvar_Register (&r_lerpmuzzlehack, GLRENDEREROPTIONS); Cvar_Register (&r_noframegrouplerp, GLRENDEREROPTIONS); @@ -642,7 +644,9 @@ void R_InitTextures (void) // create a simple checkerboard texture for the default r_notexture_mip = (texture_t*)r_notexture_mip_mem; - r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->vwidth = r_notexture_mip->vheight = 16; + r_notexture_mip->srcwidth = r_notexture_mip->srcheight = 16; + r_notexture_mip->srcfmt = TF_SOLID8; for (m=0 ; m<1 ; m++) { @@ -777,17 +781,6 @@ void Renderer_Init(void) Cmd_AddCommand("r_remapshader", Shader_RemapShader_f); Cmd_AddCommand("r_showshader", Shader_ShowShader_f); -#if defined(D3DQUAKE) - GLD3DRenderer_Init(); -#endif -#if defined(GLQUAKE) - GLRenderer_Init(); -#endif - -#if defined(GLQUAKE) || defined(VKQUAKE) - R_BloomRegister(); -#endif - #ifdef SWQUAKE { extern cvar_t sw_interlace; @@ -869,6 +862,8 @@ void Renderer_Init(void) Cvar_Register(&r_keepimages, GRAPHICALNICETIES); Cvar_Register(&r_ignoremapprefixes, GRAPHICALNICETIES); + Cvar_ForceCallback(&r_keepimages); + Cvar_ForceCallback(&r_ignoremapprefixes); #ifdef IMAGEFMT_TGA Cvar_Register(&r_dodgytgafiles, "Hacky bug workarounds"); #endif @@ -1067,6 +1062,18 @@ void Renderer_Init(void) Cmd_AddCommand ("listskyboxes", R_ListSkyBoxes_f); Cmd_AddCommand ("listconfigs", R_ListConfigs_f); + +#if defined(D3DQUAKE) + GLD3DRenderer_Init(); +#endif +#if defined(GLQUAKE) + GLRenderer_Init(); +#endif + +#if defined(GLQUAKE) || defined(VKQUAKE) + R_BloomRegister(); +#endif + P_InitParticleSystem(); R_InitTextures(); } @@ -3198,7 +3205,7 @@ void R_InitParticleTexture (void) } } - TEXASSIGN(particletexture, R_LoadTexture32("dotparticle", 8, 8, data, IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP)); + TEXASSIGN(particletexture, R_LoadTexture32("dotparticle", 8, 8, data, IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE)); // @@ -3222,7 +3229,7 @@ void R_InitParticleTexture (void) data[y*32+x][3] = 255; } } - particlecqtexture = Image_GetTexture("classicparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP, data, NULL, 32, 32, TF_RGBA32); + particlecqtexture = Image_GetTexture("classicparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE, data, NULL, 32, 32, TF_RGBA32); //draw a square in the top left. still a triangle. for (x=0 ; x<16 ; x++) @@ -3232,7 +3239,7 @@ void R_InitParticleTexture (void) data[y*32+x][3] = 255; } } - Image_GetTexture("classicparticle_square", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP, data, NULL, 32, 32, TF_RGBA32); + Image_GetTexture("classicparticle_square", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE, data, NULL, 32, 32, TF_RGBA32); for (x=0 ; x<16 ; x++) @@ -3245,7 +3252,7 @@ void R_InitParticleTexture (void) data[y*16+x][3] = exptexture[x][y]*255/9.0; } } - explosiontexture = Image_GetTexture("fte_fuzzyparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); + explosiontexture = Image_GetTexture("fte_fuzzyparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE, data, NULL, 16, 16, TF_RGBA32); for (x=0 ; x<16 ; x++) { @@ -3257,7 +3264,7 @@ void R_InitParticleTexture (void) data[y*16+x][3] = exptexture[x][y]*255/9.0; } } - Image_GetTexture("fte_bloodparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); + Image_GetTexture("fte_bloodparticle", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE, data, NULL, 16, 16, TF_RGBA32); for (x=0 ; x<16 ; x++) { @@ -3269,7 +3276,7 @@ void R_InitParticleTexture (void) data[y*16+x][3] = 255; } } - Image_GetTexture("fte_blooddecal", "particles", IF_NOMIPMAP|IF_NOPICMIP, data, NULL, 16, 16, TF_RGBA32); + Image_GetTexture("fte_blooddecal", "particles", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE, data, NULL, 16, 16, TF_RGBA32); memset(data, 255, sizeof(data)); for (y = 0;y < PARTICLETEXTURESIZE;y++) @@ -3283,7 +3290,7 @@ void R_InitParticleTexture (void) data[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d; } } - balltexture = R_LoadTexture32("balltexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); + balltexture = R_LoadTexture32("balltexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE); memset(data, 255, sizeof(data)); for (y = 0;y < PARTICLETEXTURESIZE;y++) @@ -3296,7 +3303,7 @@ void R_InitParticleTexture (void) data[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d; } } - beamtexture = R_LoadTexture32("beamparticle", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); + beamtexture = R_LoadTexture32("beamparticle", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE); for (y = 0;y < PARTICLETEXTURESIZE;y++) { @@ -3314,6 +3321,6 @@ void R_InitParticleTexture (void) data[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d/2; } } - ptritexture = R_LoadTexture32("ptritexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); + ptritexture = R_LoadTexture32("ptritexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE); } diff --git a/engine/client/roq_read.c b/engine/client/roq_read.c index c5acdc25..8aea28ff 100644 --- a/engine/client/roq_read.c +++ b/engine/client/roq_read.c @@ -309,7 +309,7 @@ int i; if((fp = FS_OpenVFS(fname, "rb", FS_GAME)) == NULL) { - if((fp = FS_OpenVFS(va("video/%s.roq", fname), "rb", FS_GAME)) == NULL) //for q3 compat + if((fp = FS_OpenVFS(va("video/%s", fname), "rb", FS_GAME)) == NULL) //for q3 compat return NULL; } diff --git a/engine/client/screen.h b/engine/client/screen.h index 5277ce82..f3de64bd 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -139,10 +139,10 @@ typedef enum uploadfmt PTI_BC2_RGBA_SRGB, /*8bpp*/ PTI_BC3_RGBA, /*8bpp*/ //maybe add a bc3 normalmapswizzle type for d3d9? PTI_BC3_RGBA_SRGB, /*8bpp*/ - PTI_BC4_R8, /*4bpp*/ //greyscale, kinda - PTI_BC4_R8_SNORM, /*4bpp*/ - PTI_BC5_RG8, /*8bpp*/ //useful for normalmaps - PTI_BC5_RG8_SNORM, /*8bpp*/ //useful for normalmaps + PTI_BC4_R, /*4bpp*/ //greyscale, kinda + PTI_BC4_R_SNORM, /*4bpp*/ + PTI_BC5_RG, /*8bpp*/ //useful for normalmaps + PTI_BC5_RG_SNORM, /*8bpp*/ //useful for normalmaps PTI_BC6_RGB_UFLOAT, /*8bpp*/ //unsigned (half) floats! PTI_BC6_RGB_SFLOAT, /*8bpp*/ //signed (half) floats! PTI_BC7_RGBA, /*8bpp*/ //multimode compression, using as many bits as bc2/bc3 @@ -251,6 +251,7 @@ typedef enum uploadfmt //these are emulated formats. this 'case' value allows drivers to easily ignore them #define PTI_EMULATED TF_INVALID:case TF_BGR24_FLIP:case TF_MIP4_P8:case TF_MIP4_SOLID8:case TF_MIP4_8PAL24:case TF_MIP4_8PAL24_T255:case TF_SOLID8:case TF_TRANS8:case TF_TRANS8_FULLBRIGHT:case TF_HEIGHT8:case TF_HEIGHT8PAL:case TF_H2_T7G1:case TF_H2_TRANS8_0:case TF_H2_T4A4:case TF_8PAL24:case TF_8PAL32:case PTI_LLLX8:case PTI_LLLA8 } uploadfmt_t; +#define PTI_FULLMIPCHAIN 0x80000000 //valid for Image_GetTexture (and thus GenMip0) to signify that there's a full round-down mipchain there, not a single one (or 4) qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, qintptr_t bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 64a19a41..21d3aeb8 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -133,6 +133,7 @@ cvar_t snd_doppler_max = CVARAFD( "s_doppler_max", "2", cvar_t snd_playbackrate = CVARFD( "snd_playbackrate", "1", CVAR_CHEAT, "Debugging cvar that changes the playback rate of all new sounds."); cvar_t snd_ignoregamespeed = CVARFD( "snd_ignoregamespeed", "0", 0, "When set, allows sounds to desynchronise with game time or demo speeds."); +cvar_t snd_ignorecueloops = CVARD( "snd_ignorecueloops", "0", "Ignores cue commands in wav files, for q3 compat."); cvar_t snd_linearresample = CVARAF( "s_linearresample", "1", "snd_linearresample", 0); cvar_t snd_linearresample_stream = CVARAF( "s_linearresample_stream", "0", @@ -2321,6 +2322,7 @@ void S_Init (void) Cvar_Register(&snd_device, "Sound controls"); Cvar_Register(&snd_device_opts, "Sound controls"); + Cvar_Register(&snd_ignorecueloops, "Sound controls"); Cvar_Register(&snd_linearresample, "Sound controls"); Cvar_Register(&snd_linearresample_stream, "Sound controls"); diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index 09d58abb..67e5e73a 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -1109,6 +1109,7 @@ GetWavinfo */ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength) { + extern cvar_t snd_ignorecueloops; wavinfo_t info; int i; int samples; @@ -1156,7 +1157,7 @@ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength) // get cue chunk chunklen = FindChunk(&ctx, "cue "); - if (chunklen >= 36-8) + if (chunklen >= 36-8 && !snd_ignorecueloops.ival) { ctx.data_p += 32; info.loopstart = GetLittleLong(&ctx); diff --git a/engine/common/cmd.c b/engine/common/cmd.c index b5a54851..6a67612a 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1913,7 +1913,8 @@ qboolean Cmd_AddCommandAD (const char *cmd_name, xcommand_t function, xcommandar cmd_function_t *cmd; // fail if the command is a variable name - if (Cvar_VariableString(cmd_name)[0]) + cvar_t *var = Cvar_FindVar (cmd_name); + if (var && function) { Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name); return false; diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index 8ade4969..f7e34a7a 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -22,6 +22,7 @@ extern cvar_t r_noframegrouplerp; cvar_t r_lerpmuzzlehack = CVARF ("r_lerpmuzzlehack", "1", CVAR_ARCHIVE); #ifdef MD1MODELS cvar_t mod_h2holey_bugged = CVARD ("mod_h2holey_bugged", "0", "Hexen2's holey-model flag uses index 0 as transparent (and additionally 255 in gl, due to a bug). GLQuake engines tend to have bugs that use ONLY index 255, resulting in a significant compatibility issue that can be resolved only with this shitty cvar hack."); +cvar_t mod_halftexel = CVARD ("mod_halftexel", "1", "Offset texture coords by a half-texel, for compatibility with glquake and the majority of engine forks."); #endif static void QDECL r_meshpitch_callback(cvar_t *var, char *oldvalue) { @@ -3059,7 +3060,6 @@ static int Mod_CountSkinFiles(model_t *mod) void Mod_LoadAliasShaders(model_t *mod) { - qbyte *mipdata[4]; galiasinfo_t *ai = mod->meshinfo; galiasskin_t *s; skinframe_t *f; @@ -3198,11 +3198,7 @@ void Mod_LoadAliasShaders(model_t *mod) loadflags |= SHADER_HASFULLBRIGHT; if (r_loadbumpmapping) loadflags |= SHADER_HASNORMALMAP; - mipdata[0] = f->texels; - mipdata[1] = NULL; - mipdata[2] = NULL; - mipdata[3] = NULL; - R_BuildLegacyTexnums(f->shader, basename, alttexpath, loadflags, imageflags, skintranstype, s->skinwidth, s->skinheight, mipdata, host_basepal); + R_BuildLegacyTexnums(f->shader, basename, alttexpath, loadflags, imageflags, skintranstype, s->skinwidth, s->skinheight, f->texels, host_basepal); } else R_BuildDefaultTexnums(&f->texnums, f->shader, 0); @@ -3812,6 +3808,7 @@ static qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) #ifndef SERVERONLY vec2_t *st_array; int j; + float halftexel = mod_halftexel.ival?0.5:0; #endif int version; int i, onseams; @@ -3993,13 +3990,13 @@ static qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) { if (stremap[k] > pq1inmodel->num_st) { /*onseam verts? shrink the index, and add half a texture width to the s coord*/ - st_array[k][0] = 0.5+(LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].s)+0.5)/(float)pq1inmodel->skinwidth; - st_array[k][1] = (LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].t)+0.5)/(float)pq1inmodel->skinheight; + st_array[k][0] = 0.5+(LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].s)+halftexel)/(float)pq1inmodel->skinwidth; + st_array[k][1] = (LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].t)+halftexel)/(float)pq1inmodel->skinheight; } else { - st_array[k][0] = (LittleLong(pinstverts[stremap[k]].s)+0.5)/(float)pq1inmodel->skinwidth; - st_array[k][1] = (LittleLong(pinstverts[stremap[k]].t)+0.5)/(float)pq1inmodel->skinheight; + st_array[k][0] = (LittleLong(pinstverts[stremap[k]].s)+halftexel)/(float)pq1inmodel->skinwidth; + st_array[k][1] = (LittleLong(pinstverts[stremap[k]].t)+halftexel)/(float)pq1inmodel->skinheight; } } #endif @@ -4038,8 +4035,8 @@ static qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) galias->ofs_st_array = st_array; for (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++) { - st_array[i][0] = (LittleLong(pinstverts[i].s)+0.5)/(float)pq1inmodel->skinwidth; - st_array[i][1] = (LittleLong(pinstverts[i].t)+0.5)/(float)pq1inmodel->skinheight; + st_array[i][0] = (LittleLong(pinstverts[i].s)+halftexel)/(float)pq1inmodel->skinwidth; + st_array[i][1] = (LittleLong(pinstverts[i].t)+halftexel)/(float)pq1inmodel->skinheight; if (pinstverts[i].onseam) { diff --git a/engine/common/fs.c b/engine/common/fs.c index cb9b38ff..c0bf866b 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -3377,7 +3377,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths) /*yay q2!*/ #define Q2CFG "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n" /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ -#define Q3CFG "set v_gammainverted 0\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n" +#define Q3CFG "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n" //#define RMQCFG "sv_bigcoords 1\n" #ifdef HAVE_SSL diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index c4a91fe6..5f2553ff 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -1338,10 +1338,10 @@ static texture_t *Mod_LoadWall(model_t *loadmodel, char *mapname, char *texname, tex = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t)); - tex->width = wal->width; - tex->height = wal->height; + tex->vwidth = wal->width; + tex->vheight = wal->height; - if (!tex->width || !tex->height || wal == &replacementwal) + if (!tex->vwidth || !tex->vheight || wal == &replacementwal) { imageflags |= IF_LOADNOW; //make sure the size is known BEFORE it returns. if (wal->offsets[0]) @@ -1358,8 +1358,8 @@ static texture_t *Mod_LoadWall(model_t *loadmodel, char *mapname, char *texname, { if (base->status == TEX_LOADED||base->status==TEX_LOADING) { - tex->width = base->width; - tex->height = base->height; + tex->vwidth = base->width; + tex->vheight = base->height; } else Con_Printf("Unable to load textures/%s.wal\n", wal->name); @@ -1368,21 +1368,23 @@ static texture_t *Mod_LoadWall(model_t *loadmodel, char *mapname, char *texname, } else { + qbyte *out; unsigned int size = (wal->width>>0)*(wal->height>>0) + (wal->width>>1)*(wal->height>>1) + (wal->width>>2)*(wal->height>>2) + (wal->width>>3)*(wal->height>>3); - tex->mips[0] = BZ_Malloc(size); + tex->srcdata = out = BZ_Malloc(size); tex->palette = host_basepal; - tex->mips[1] = tex->mips[0] + (wal->width>>0)*(wal->height>>0); - tex->mips[2] = tex->mips[1] + (wal->width>>1)*(wal->height>>1); - tex->mips[3] = tex->mips[2] + (wal->width>>2)*(wal->height>>2); - memcpy(tex->mips[0], (qbyte *)wal + wal->offsets[0], (wal->width>>0)*(wal->height>>0)); - memcpy(tex->mips[1], (qbyte *)wal + wal->offsets[1], (wal->width>>1)*(wal->height>>1)); - memcpy(tex->mips[2], (qbyte *)wal + wal->offsets[2], (wal->width>>2)*(wal->height>>2)); - memcpy(tex->mips[3], (qbyte *)wal + wal->offsets[3], (wal->width>>3)*(wal->height>>3)); + memcpy(out, (qbyte *)wal + wal->offsets[0], (wal->width>>0)*(wal->height>>0)); + out += (wal->width>>0)*(wal->height>>0); + memcpy(out, (qbyte *)wal + wal->offsets[1], (wal->width>>1)*(wal->height>>1)); + out += (wal->width>>1)*(wal->height>>1); + memcpy(out, (qbyte *)wal + wal->offsets[2], (wal->width>>2)*(wal->height>>2)); + out += (wal->width>>2)*(wal->height>>2); + memcpy(out, (qbyte *)wal + wal->offsets[3], (wal->width>>3)*(wal->height>>3)); + out += (wal->width>>3)*(wal->height>>3); BZ_Free(wal); } @@ -1476,7 +1478,7 @@ static qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, ch *lwr = *lwr - 'A' + 'a'; } out->texture = Mod_LoadWall (mod, mapname, in->texture, sname, (out->flags&TEX_SPECIAL)?0:IF_NOALPHA); - if (!out->texture || !out->texture->width || !out->texture->height) + if (!out->texture || !out->texture->srcwidth || !out->texture->srcheight) { out->texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t) + 16*16+8*8+4*4+2*2); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 3c822c3d..134173ca 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -7006,8 +7006,9 @@ int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned return 0; } -static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(qboolean isserver, const char *address, netadr_t adr) +static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr) { + qboolean isserver = col->islisten; ftenet_websocket_connection_t *newcon; int brokersocket = INVALID_SOCKET; @@ -7311,8 +7312,9 @@ static neterr_t FTENET_NaClWebSocket_SendPacket(ftenet_generic_connection_t *gco } /*nacl websockets implementation...*/ -static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(qboolean isserver, const char *address, netadr_t adr) +static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr) { + qboolean isserver = col->islisten; ftenet_websocket_connection_t *newcon; PP_Resource newsocket; @@ -7594,6 +7596,9 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char case NP_DGRAM: if (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE) return true; + if (!FTENET_AddToCollection(collection, routename, "0", adr->type, adr->prot)) + return false; + break; case NP_WS: case NP_WSS: case NP_TLS: diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 3b04669e..8eafe74a 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -146,8 +146,11 @@ //requires linux 2.6.27 up (and equivelent libc) //note that BSD does tend to support the api, but emulated. //this works around the select FD limit, and supposedly has better performance. - #define HAVE_EPOLL #include + #ifdef EPOLL_CLOEXEC + #define HAVE_EPOLL + //#else too old, probably android... + #endif #endif #if defined(__MORPHOS__) && !defined(ixemul) diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 2ffff08e..50369d86 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -479,6 +479,7 @@ typedef enum{ SLIST_SORTDESCENDING } hostcacheglobal_t; void QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_remapshader (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_sprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/common/q3common.c b/engine/common/q3common.c index 5cd4cf68..09d78743 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -280,6 +280,8 @@ static int QDECL VMEnumMods(const char *match, qofs_t size, time_t modtime, void } memcpy(((vmsearch_t *)args)->buffer, match, newlen); + if (newlen > 1 && match[newlen-2] == '/') + ((vmsearch_t *)args)->buffer[--newlen-1] = 0; ((vmsearch_t *)args)->buffer+=newlen; ((vmsearch_t *)args)->bufferleft-=newlen; @@ -325,7 +327,7 @@ int VM_GetFileList(const char *path, const char *ext, char *output, int buffersi #include "clq3defs.h" //okay, urr, this is bad for dedicated servers. urhum. Maybe they're not looking? It's only typedefs and one extern. -#define MAX_VMQ3_CVARS 256 //can be blindly increased +#define MAX_VMQ3_CVARS 512 //can be blindly increased cvar_t *q3cvlist[MAX_VMQ3_CVARS]; int VMQ3_Cvar_Register(q3vmcvar_t *v, char *name, char *defval, int flags) { diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index 8ba18825..1df25118 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -186,16 +186,16 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi case PTI_BC3_RGBA_SRGB: tdesc.Format = DXGI_FORMAT_BC3_UNORM_SRGB; break; - case PTI_BC4_R8: + case PTI_BC4_R: tdesc.Format = DXGI_FORMAT_BC4_UNORM; break; - case PTI_BC4_R8_SNORM: + case PTI_BC4_R_SNORM: tdesc.Format = DXGI_FORMAT_BC4_SNORM; break; - case PTI_BC5_RG8: + case PTI_BC5_RG: tdesc.Format = DXGI_FORMAT_BC5_UNORM; break; - case PTI_BC5_RG8_SNORM: + case PTI_BC5_RG_SNORM: tdesc.Format = DXGI_FORMAT_BC5_SNORM; break; case PTI_BC6_RGB_UFLOAT: diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 6a06d96e..692df29a 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -925,13 +925,13 @@ static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREA if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC3_UNORM_SRGB, &support))) sh_config.texfmt[PTI_BC3_RGBA_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC4_UNORM, &support))) - sh_config.texfmt[PTI_BC4_R8] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); + sh_config.texfmt[PTI_BC4_R] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC4_SNORM, &support))) - sh_config.texfmt[PTI_BC4_R8_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); + sh_config.texfmt[PTI_BC4_R_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC5_UNORM, &support))) - sh_config.texfmt[PTI_BC5_RG8] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); + sh_config.texfmt[PTI_BC5_RG] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC5_SNORM, &support))) - sh_config.texfmt[PTI_BC5_RG8_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); + sh_config.texfmt[PTI_BC5_RG_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC6H_UF16, &support))) sh_config.texfmt[PTI_BC6_RGB_UFLOAT] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D); if (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC6H_SF16, &support))) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 9c9d0cfa..7880883f 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -1626,7 +1626,11 @@ void GLBE_Init(void) shaderstate.identitylighting = 1; shaderstate.identitylightmap = 1; for (i = 0; i < MAXRLIGHTMAPS; i++) + { shaderstate.dummybatch.lightmap[i] = -1; + shaderstate.dummybatch.lmlightstyle[i] = INVALID_LIGHTSTYLE; + shaderstate.dummybatch.vtlightstyle[i] = ~0; + } #ifdef RTLIGHTS Sh_CheckSettings(); @@ -1826,7 +1830,7 @@ static float *tcgen3(const shaderpass_t *pass, int cnt, float *dst, const mesh_t for (i = 0; i < cnt; i++, dst += 3) { dst[0] = src[i][0] - r_refdef.vieworg[0]; - dst[1] = r_refdef.vieworg[1] - src[i][1]; + dst[1] = src[i][1] - r_refdef.vieworg[1]; dst[2] = src[i][2] - r_refdef.vieworg[2]; } return dst-cnt*3; diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 92dd43b6..3b796cbf 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -347,10 +347,10 @@ void GL_SetupFormats(void) } if (bc45) { - glfmtb(PTI_BC4_R8, GL_COMPRESSED_RED_RGTC1); - glfmtb(PTI_BC4_R8_SNORM, GL_COMPRESSED_SIGNED_RED_RGTC1); - glfmtb(PTI_BC5_RG8, GL_COMPRESSED_RG_RGTC2); - glfmtb(PTI_BC5_RG8_SNORM, GL_COMPRESSED_SIGNED_RG_RGTC2); + glfmtb(PTI_BC4_R, GL_COMPRESSED_RED_RGTC1); + glfmtb(PTI_BC4_R_SNORM, GL_COMPRESSED_SIGNED_RED_RGTC1); + glfmtb(PTI_BC5_RG, GL_COMPRESSED_RG_RGTC2); + glfmtb(PTI_BC5_RG_SNORM, GL_COMPRESSED_SIGNED_RG_RGTC2); } if (bc67) { @@ -830,7 +830,7 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips) { if (mips->mip[i].width != max(1,(mips->mip[i-1].width>>1)) || mips->mip[i].height != max(1,(mips->mip[i-1].height>>1))) - { //okay, this mip looks like it was sized wrongly. this can easily happen with npot dds files made for direct3d. + { //okay, this mip looks like it was sized wrongly. I've seen this happen with some dds files. nummips = i; break; } @@ -1053,10 +1053,10 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips) case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: out.encoding = PTI_BC1_RGBA_SRGB; break; case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: out.encoding = PTI_BC2_RGBA_SRGB; break; case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: out.encoding = PTI_BC3_RGBA_SRGB; break; - case GL_COMPRESSED_RED_RGTC1: out.encoding = PTI_BC4_R8; break; - case GL_COMPRESSED_SIGNED_RED_RGTC1: out.encoding = PTI_BC4_R8_SNORM; break; - case GL_COMPRESSED_RG_RGTC2: out.encoding = PTI_BC5_RG8; break; - case GL_COMPRESSED_SIGNED_RG_RGTC2: out.encoding = PTI_BC5_RG8_SNORM; break; + case GL_COMPRESSED_RED_RGTC1: out.encoding = PTI_BC4_R; break; + case GL_COMPRESSED_SIGNED_RED_RGTC1: out.encoding = PTI_BC4_R_SNORM; break; + case GL_COMPRESSED_RG_RGTC2: out.encoding = PTI_BC5_RG; break; + case GL_COMPRESSED_SIGNED_RG_RGTC2: out.encoding = PTI_BC5_RG_SNORM; break; case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB: out.encoding = PTI_BC6_RGB_UFLOAT; break; case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB: out.encoding = PTI_BC6_RGB_SFLOAT; break; case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: out.encoding = PTI_BC7_RGBA; break; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index af455b11..99b321da 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -5849,12 +5849,11 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) } if (!Q_strncasecmp(bt->shadername, "sky", 3) && tx) - R_InitSky (bt->shader, bt->shadername, (qbyte*)tx + tx->offsets[0], tx->width, tx->height); + R_InitSky (bt->shader, bt->shadername, TF_SOLID8, (qbyte*)tx + tx->offsets[0], tx->width, tx->height); else if (tx) { - qbyte *mips[4] = {(qbyte*)tx + tx->offsets[0], (qbyte*)tx + tx->offsets[1], (qbyte*)tx + tx->offsets[2], (qbyte*)tx + tx->offsets[3]}; unsigned int mapflags = SHADER_HASPALETTED | SHADER_HASDIFFUSE | SHADER_HASFULLBRIGHT | SHADER_HASNORMALMAP | SHADER_HASGLOSS; - R_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, 0, TF_MIP4_SOLID8, tx->width, tx->height, mips, NULL); + R_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, 0, TF_SOLID8, tx->width, tx->height, (qbyte*)tx + tx->offsets[0], NULL); } else R_BuildDefaultTexnums(NULL, bt->shader, IF_WORLDTEX); diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 6fc82102..6aa22e47 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -1428,7 +1428,7 @@ static void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safe if (!safetoloadfromwads) { //remap to avoid bugging out on textures with the same name and different images (vanilla content sucks) - shadername = Mod_RemapBuggyTexture(shadername, tx->mips[0], tx->width*tx->height); + shadername = Mod_RemapBuggyTexture(shadername, tx->srcdata, tx->srcwidth*tx->srcheight); if (shadername) origname = tx->name; else @@ -1450,20 +1450,19 @@ static void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safe tx->shader = R_RegisterCustom (shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); - if (!tx->mips[0] && !safetoloadfromwads) + if (!tx->srcdata && !safetoloadfromwads) return; } else { //already loaded. don't waste time / crash (this will be a dead pointer). - if (tx->mips[0]) + if (tx->srcdata) return; } if (!strncmp(tx->name, "sky", 3)) - R_InitSky (tx->shader, shadername, tx->mips[0], tx->width, tx->height); + R_InitSky (tx->shader, shadername, tx->srcfmt, tx->srcdata, tx->srcwidth, tx->srcheight); else { - uploadfmt_t fmt; unsigned int maps = 0; maps |= SHADER_HASPALETTED; maps |= SHADER_HASDIFFUSE; @@ -1474,24 +1473,10 @@ static void Mod_FinishTexture(texture_t *tx, const char *loadname, qboolean safe if (gl_specular.ival) maps |= SHADER_HASGLOSS; - if (tx->palette) - { //halflife, probably... - if (*tx->name == '{') - fmt = TF_MIP4_8PAL24_T255; - else - fmt = TF_MIP4_8PAL24; - } - else - { - if (*tx->name == '{') - fmt = TF_TRANS8; - else - fmt = TF_MIP4_SOLID8; - } - - R_BuildLegacyTexnums(tx->shader, origname, loadname, maps, 0, fmt, tx->width, tx->height, tx->mips, tx->palette); + R_BuildLegacyTexnums(tx->shader, origname, loadname, maps, 0, tx->srcfmt, tx->srcwidth, tx->srcheight, tx->srcdata, tx->palette); } - BZ_Free(tx->mips[0]); + BZ_Free(tx->srcdata); + tx->srcdata = NULL; } #endif @@ -1513,7 +1498,7 @@ void Mod_NowLoadExternal(model_t *loadmodel) if (!tx) //e1m2, this happens continue; - if (tx->mips[0]) + if (tx->srcdata) continue; Mod_FinishTexture(tx, loadname, true); @@ -2421,10 +2406,10 @@ void ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *co { mesh->st_array[i][0] = s; mesh->st_array[i][1] = t; - if (surf->texinfo->texture->width) - mesh->st_array[i][0] /= surf->texinfo->texture->width; - if (surf->texinfo->texture->height) - mesh->st_array[i][1] /= surf->texinfo->texture->height; + if (surf->texinfo->texture->vwidth) + mesh->st_array[i][0] /= surf->texinfo->texture->vwidth; + if (surf->texinfo->texture->vheight) + mesh->st_array[i][1] /= surf->texinfo->texture->vheight; } #ifndef SERVERONLY @@ -3359,34 +3344,196 @@ static void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, } #ifndef SERVERONLY -static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt) +static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, size_t miptexsize) { - unsigned int size = + unsigned int legacysize = (mt->width>>0)*(mt->height>>0) + (mt->width>>1)*(mt->height>>1) + (mt->width>>2)*(mt->height>>2) + (mt->width>>3)*(mt->height>>3); - if (loadmodel->fromgame == fg_halflife && *(short*)((qbyte *)mt + mt->offsets[3] + (mt->width>>3)*(mt->height>>3)) == 256) + uploadfmt_t newfmt = PTI_INVALID; + size_t neww=0, newh=0; + qbyte *newdata=NULL; + qbyte *pal = NULL; + + //bug: vanilla quake ignored offsets and just made assumptions. + //this means we can't just play with offsets to hide stuff, we have to postfix it (which requires guessing lump sizes) + //issue: halflife textures have (leshort)256,(byte)pal[256*3] stuck on the end + //we signal the presence of our extended data using 0x00,0xfb,0x2b,0xaf (this should be uncommon as the next mip's name shouldn't normally be empty, nor a weird char (which should hopefully also not come from random stack junk in the wad tool) + //each extended block of data then has a size value, followed by a block name. + //compressed formats then contain a width+height value, and then a FULL (round-down) mip chain. + //if the gpu doesn't support npot, or its too big, or can't use the pixelformat then the engine will simply have to fall back on the paletted data. lets hope it was present. + size_t extofs; + if (!mt->offsets[0]) + extofs = sizeof(miptex_t); + else if (mt->offsets[0] == sizeof(miptex_t) && + mt->offsets[1] == mt->offsets[0]+(mt->width>>0)*(mt->height>>0) && + mt->offsets[2] == mt->offsets[1]+(mt->width>>1)*(mt->height>>1) && + mt->offsets[3] == mt->offsets[2]+(mt->width>>2)*(mt->height>>2)) + { + extofs = mt->offsets[3]+(mt->width>>3)*(mt->height>>3); + if (loadmodel->fromgame == fg_halflife && *(short*)((qbyte *)mt + mt->offsets[3] + (mt->width>>3)*(mt->height>>3)) == 256) + { + pal = (qbyte*)mt + extofs+2; + extofs += 2+256*3; + } + } + else + extofs = miptexsize; //the numbers don't match what we expect... something weird is going on here... don't misinterpret it. + if (extofs+4 <= miptexsize && ((qbyte*)mt)[extofs+0] == 0 && ((qbyte*)mt)[extofs+1]==0xfb && ((qbyte*)mt)[extofs+2]==0x2b && ((qbyte*)mt)[extofs+3]==0xaf) + { + unsigned int extsize; + extofs += 4; + for (; extofs < miptexsize; extofs += extsize) + { + size_t sz, w, h; + unsigned int bb,bw,bh; + int mip; + qbyte *extdata = (void*)((qbyte*)mt+extofs); + char *extfmt = (char*)(extdata+4); + extsize = (extdata[0]<<0)|(extdata[1]<<8)|(extdata[2]<<16)|(extdata[3]<<24); + if (extsize<8 || extofs+extsize>miptexsize) break; //not a valid entry... something weird is happening here + else if (!strncmp(extfmt, "NAME", 4)) + { //replacement name, for longer shader/external names + size_t sz = extsize-8; + if (sz >= sizeof(tx->name)) + continue; + memcpy(tx->name, (qbyte*)extdata+8, sz); + tx->name[sz] = 0; + } + else if (!strncmp(extfmt, "LPAL", 4) && extsize == 8+256*3) + { //replacement palette for the 8bit data, for feature parity with halflife, but with extra markup so we know its actually meant to be a replacement palette. + pal = extdata+8; + continue; + } + else if (extsize <= 16) continue; //too small for an altformat lump + else if (newdata != PTI_INVALID) continue; //only accept the first accepted format (allowing for eg astc+bc1 fallbacks) + else if (!strncmp(extfmt, "RGBA", 4)) newfmt = PTI_RGBA8; //32bpp, we don't normally need this alpha precision (padding can be handy though, for the lazy). + else if (!strncmp(extfmt, "RGB", 4)) newfmt = PTI_RGB8; //24bpp + else if (!strncmp(extfmt, "565", 4)) newfmt = PTI_RGB565; //16bpp + else if (!strncmp(extfmt, "5551", 4)) newfmt = PTI_RGBA5551; //16bpp + else if (!strncmp(extfmt, "EXP5", 4)) newfmt = PTI_E5BGR9; //32bpp, we don't normally need this alpha precision... + else if (!strncmp(extfmt, "BC1", 4)) newfmt = PTI_BC1_RGBA; //4bpp + else if (!strncmp(extfmt, "BC2", 4)) newfmt = PTI_BC2_RGBA; //8bpp, we don't normally need this alpha precision... + else if (!strncmp(extfmt, "BC3", 4)) newfmt = PTI_BC3_RGBA; //8bpp, we don't normally need this alpha precision... + else if (!strncmp(extfmt, "BC4", 4)) newfmt = PTI_BC4_R; //4bpp, wtf + else if (!strncmp(extfmt, "BC5", 4)) newfmt = PTI_BC5_RG; //8bpp, wtf + else if (!strncmp(extfmt, "BC6", 4)) newfmt = PTI_BC6_RGB_UFLOAT; //8bpp, weird + else if (!strncmp(extfmt, "BC7", 4)) newfmt = PTI_BC7_RGBA; //8bpp + else if (!strncmp(extfmt, "AST4", 4)) newfmt = PTI_ASTC_4X4_LDR; //8 bpp + else if (!strncmp(extfmt, "AS54", 4)) newfmt = PTI_ASTC_5X4_LDR; //6.40bpp + else if (!strncmp(extfmt, "AST5", 4)) newfmt = PTI_ASTC_5X5_LDR; //5.12bpp + else if (!strncmp(extfmt, "AS65", 4)) newfmt = PTI_ASTC_6X5_LDR; //4.17bpp + else if (!strncmp(extfmt, "AST6", 4)) newfmt = PTI_ASTC_6X6_LDR; //3.56bpp + else if (!strncmp(extfmt, "AS85", 4)) newfmt = PTI_ASTC_8X5_LDR; //3.20bpp + else if (!strncmp(extfmt, "AS86", 4)) newfmt = PTI_ASTC_8X6_LDR; //2.67bpp + else if (!strncmp(extfmt, "AS05", 4)) newfmt = PTI_ASTC_10X5_LDR; //2.56bpp + else if (!strncmp(extfmt, "AS06", 4)) newfmt = PTI_ASTC_10X6_LDR; //2.13bpp + else if (!strncmp(extfmt, "AST8", 4)) newfmt = PTI_ASTC_8X8_LDR; //2 bpp + else if (!strncmp(extfmt, "AS08", 4)) newfmt = PTI_ASTC_10X8_LDR; //1.60bpp + else if (!strncmp(extfmt, "AS00", 4)) newfmt = PTI_ASTC_10X10_LDR; //1.28bpp + else if (!strncmp(extfmt, "AS20", 4)) newfmt = PTI_ASTC_12X10_LDR; //1.07bpp + else if (!strncmp(extfmt, "AST2", 4)) newfmt = PTI_ASTC_12X12_LDR; //0.89bpp + else if (!strncmp(extfmt, "ETC1", 4)) newfmt = PTI_ETC1_RGB8; //4bpp + else if (!strncmp(extfmt, "ETC2", 4)) newfmt = PTI_ETC2_RGB8; //4bpp + else if (!strncmp(extfmt, "ETCP", 4)) newfmt = PTI_ETC2_RGB8A1; //4bpp + else if (!strncmp(extfmt, "ETCA", 4)) newfmt = PTI_ETC2_RGB8A8; //8bpp, we don't normally need this alpha precision... + else continue; //dunno what that is, ignore it + + //alternative textures are usually compressed + //this means we insist on a FULL mip chain + //npot mips are explicitly round-down (but don't drop to 0 with non-square). + Image_BlockSizeForEncoding(newfmt, &bb, &bw, &bh); + neww = (extdata[8]<<0)|(extdata[9]<<8)|(extdata[10]<<16)|(extdata[11]<<24); + newh = (extdata[12]<<0)|(extdata[13]<<8)|(extdata[14]<<16)|(extdata[15]<<24); + for (mip = 0, w=neww, h=newh, sz=0; w || h; mip++, w>>=1,h>>=1) + { + w = max(1, w); + h = max(1, h); + sz += bb * + ((w+bw-1)/bw) * + ((h+bh-1)/bh); + //Support truncation to top-mip only? tempting... + } + if (extsize != 16+sz) + { + Con_Printf("miptex %s has incomplete mipchain\n", Image_FormatName(newfmt)); + continue; + } + + //make sure we're not going to need to rescale compressed formats. + //gles<3 or gl<2 requires npot inputs for this to work. I guess that means dx9.3+ gpus, so all astc+bc7 but not necessarily all bc1+etc2. oh well. + if (!sh_config.texture_non_power_of_two) + { + if (neww & (neww - 1)) + continue; + if (newh & (newh - 1)) + continue; + } + //make sure its within our limits + if (!neww || !newh || neww > sh_config.texture2d_maxsize || newh > sh_config.texture2d_maxsize) + continue; + //that our hardware supports it... (Note: FTE can soft-decompress all of the above so this doesn't make too much sense if there's only one) + //if (!sh_config.texfmt[newfmt]) + // continue; + //that we can actually use non-paletted data... + if (r_softwarebanding && mt->offsets[0]) + continue; + + newdata = BZ_Malloc(sz); + memcpy(newdata, extdata+16, sz); + } + } + + if (newdata) + { + tx->srcfmt = newfmt|PTI_FULLMIPCHAIN; + tx->srcwidth = neww; + tx->srcheight = newh; + tx->srcdata = newdata; + tx->palette = NULL; + return; + } + + if (pal) { //mostly identical, just a specific palette hidden at the end. handle fences elsewhere. - tx->mips[0] = BZ_Malloc(size + 768); - tx->palette = tx->mips[0] + size; - memcpy(tx->palette, (qbyte *)mt + mt->offsets[3] + (mt->width>>3)*(mt->height>>3) + 2, 768); + tx->srcdata = BZ_Malloc(legacysize + 768); + tx->palette = tx->srcdata + legacysize; + memcpy(tx->palette, pal, 768); } else { - tx->mips[0] = BZ_Malloc(size); + tx->srcdata = BZ_Malloc(legacysize); tx->palette = NULL; } - tx->mips[1] = tx->mips[0] + (mt->width>>0)*(mt->height>>0); - tx->mips[2] = tx->mips[1] + (mt->width>>1)*(mt->height>>1); - tx->mips[3] = tx->mips[2] + (mt->width>>2)*(mt->height>>2); - memcpy(tx->mips[0], (qbyte *)mt + mt->offsets[0], (mt->width>>0)*(mt->height>>0)); - memcpy(tx->mips[1], (qbyte *)mt + mt->offsets[1], (mt->width>>1)*(mt->height>>1)); - memcpy(tx->mips[2], (qbyte *)mt + mt->offsets[2], (mt->width>>2)*(mt->height>>2)); - memcpy(tx->mips[3], (qbyte *)mt + mt->offsets[3], (mt->width>>3)*(mt->height>>3)); + if (tx->palette) + { //halflife, probably... + if (*tx->name == '{') + tx->srcfmt = TF_MIP4_8PAL24_T255; + else + tx->srcfmt = TF_MIP4_8PAL24; + } + else + { + if (*tx->name == '{') + tx->srcfmt = TF_TRANS8; + else + tx->srcfmt = TF_MIP4_SOLID8; + } + tx->srcwidth = mt->width; + tx->srcheight = mt->height; + legacysize = 0; + memcpy(tx->srcdata+legacysize, (qbyte *)mt + mt->offsets[0], (mt->width>>0)*(mt->height>>0)); + legacysize += (mt->width>>0)*(mt->height>>0); + memcpy(tx->srcdata+legacysize, (qbyte *)mt + mt->offsets[1], (mt->width>>1)*(mt->height>>1)); + legacysize += (mt->width>>1)*(mt->height>>1); + memcpy(tx->srcdata+legacysize, (qbyte *)mt + mt->offsets[2], (mt->width>>2)*(mt->height>>2)); + legacysize += (mt->width>>2)*(mt->height>>2); + memcpy(tx->srcdata+legacysize, (qbyte *)mt + mt->offsets[3], (mt->width>>3)*(mt->height>>3)); +// legacysize += (mt->width>>3)*(mt->height>>3); } #endif @@ -3403,6 +3550,8 @@ static qboolean Mod_LoadTextures (model_t *loadmodel, qbyte *mod_base, lump_t *l texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; + unsigned int *sizes; + unsigned int e, o; TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); @@ -3429,11 +3578,12 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); loadmodel->numtextures = m->nummiptex; loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, m->nummiptex * sizeof(*loadmodel->textures)); + sizes = alloca(sizeof(*sizes)*m->nummiptex); - for (i=0 ; inummiptex ; i++) + for (i=m->nummiptex, e = l->filelen; i-->0; ) { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) //e1m2, this happens + o = LittleLong(m->dataofs[i]); + if (o >= l->filelen) //e1m2, this happens { tx = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t)); memcpy(tx, r_notexture_mip, sizeof(texture_t)); @@ -3441,7 +3591,9 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); loadmodel->textures[i] = tx; continue; } - mt = (miptex_t *)((qbyte *)m + m->dataofs[i]); + if (o >= e) + e = l->filelen; //something doesn't make sense. try to avoid making too many assumptions. + mt = (miptex_t *)((qbyte *)m + o); TRACE(("dbg: Mod_LoadTextures: texture %s\n", mt->name)); @@ -3464,17 +3616,16 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); loadmodel->textures[i] = tx; Q_strncpyz(tx->name, mt->name, min(sizeof(mt->name)+1, sizeof(tx->name))); - tx->width = mt->width; - tx->height = mt->height; - - if (!mt->offsets[0]) //this is a hl external style texture, load it a little later (from a wad) - { - continue; - } + tx->vwidth = mt->width; + tx->vheight = mt->height; #ifndef SERVERONLY - Mod_LoadMiptex(loadmodel, tx, mt); + Mod_LoadMiptex(loadmodel, tx, mt, e-o); +#else + (void)e; #endif + + e = o; } // // sequence the animations @@ -4989,8 +5140,9 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b) // maps |= SHADER_HASNORMALMAP; if (gl_specular.ival) maps |= SHADER_HASGLOSS; - R_BuildLegacyTexnums(mod->textures[a]->shader, mod->textures[a]->name, loadname, maps, IF_WORLDTEX, TF_MIP4_8PAL24_T255, mod->textures[a]->width, mod->textures[a]->height, mod->textures[a]->mips, mod->textures[a]->palette); - BZ_Free(mod->textures[a]->mips[0]); + R_BuildLegacyTexnums(mod->textures[a]->shader, mod->textures[a]->name, loadname, maps, IF_WORLDTEX, TF_MIP4_8PAL24_T255, mod->textures[a]->srcwidth, mod->textures[a]->srcheight, mod->textures[a]->srcdata, mod->textures[a]->palette); + BZ_Free(mod->textures[a]->srcdata); + mod->textures[a]->srcdata = NULL; } } else diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 09b965d5..2c552a63 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -325,7 +325,7 @@ void GL_DeselectVAO(void); typedef struct texture_s { char name[64]; - unsigned width, height; + unsigned vwidth, vheight; //used for lightmap coord generation struct shader_s *shader; char *partname; //parsed from the worldspawn entity @@ -335,8 +335,10 @@ typedef struct texture_s struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these - qbyte *mips[4]; //the different mipmap levels. - qbyte *palette; //host_basepal or halflife per-texture palette + uploadfmt_t srcfmt; + unsigned int srcwidth, srcheight; //actual size (updated miptex format) + qbyte *srcdata; //the different mipmap levels. + qbyte *palette; //host_basepal or halflife per-texture palette (or null) } texture_t; /* typedef struct diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 6f30b559..75ecec35 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -1776,6 +1776,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip if (strncmp("DELUX", script, end - script)) if (strncmp("OFFSETMAPPING", script, end - script)) if (strncmp("RELIEFMAPPING", script, end - script)) + if (strncmp("FAKESHADOWS", script, end - script)) Con_DPrintf("Unknown pemutation in glsl program %s\n", name); } script = end; @@ -5923,7 +5924,7 @@ static qbyte *ReadRGBA8ImageFile(const char *fname, const char *subpath, int *wi #endif //call this with some fallback textures to directly load some textures -void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *mipdata[4], qbyte *palette) +void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *srcdata, qbyte *palette) { char *h; char imagename[MAX_QPATH]; @@ -5931,9 +5932,6 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons //extern cvar_t gl_miptexLevel; texnums_t *tex = shader->defaulttextures; int a, aframes; - qbyte *dontcrashme[4] = {NULL}; - if (!mipdata) - mipdata = dontcrashme; /*else if (gl_miptexLevel.ival) { unsigned int miplevel = 0, i; @@ -5985,7 +5983,7 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons imageflags |= IF_NOALPHA; //fallthrough case TF_MIP4_8PAL24_T255: - if (!mipdata || !mipdata[0] || !mipdata[1] || !mipdata[2] || !mipdata[3]) + if (!srcdata) basefmt = TF_SOLID8; break; default: @@ -6097,10 +6095,10 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons } } if (!TEXLOADED(tex->base)) - tex->base = Image_GetTexture(imagename, subpath, imageflags, mipdata[0], palette, width, height, basefmt); + tex->base = Image_GetTexture(imagename, subpath, imageflags, srcdata, palette, width, height, basefmt); } else if (!TEXVALID(tex->base)) - tex->base = Image_GetTexture(imagename, subpath, imageflags, mipdata[0], palette, width, height, basefmt); + tex->base = Image_GetTexture(imagename, subpath, imageflags, srcdata, palette, width, height, basefmt); } if (loadflags & SHADER_HASPALETTED) @@ -6108,7 +6106,7 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (!TEXVALID(tex->paletted) && *mapname) tex->paletted = R_LoadHiResTexture(va("%s_pal", mapname), NULL, imageflags|IF_NEAREST); if (!TEXVALID(tex->paletted)) - tex->paletted = Image_GetTexture(va("%s_pal", imagename), subpath, imageflags|IF_NEAREST|IF_NOSRGB, mipdata[0], palette, width, height, (basefmt==TF_MIP4_SOLID8)?TF_MIP4_P8:PTI_P8); + tex->paletted = Image_GetTexture(va("%s_pal", imagename), subpath, imageflags|IF_NEAREST|IF_NOSRGB, srcdata, palette, width, height, (basefmt==TF_MIP4_SOLID8)?TF_MIP4_P8:PTI_P8); } imageflags |= IF_LOWPRIORITY; @@ -6122,7 +6120,14 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (!TEXVALID(tex->bump) && *mapname) tex->bump = R_LoadHiResTexture(va("%s_norm", mapname), NULL, imageflags|IF_TRYBUMP|IF_NOSRGB); if (!TEXVALID(tex->bump) && (r_shadow_bumpscale_basetexture.ival||*imagename=='#'||gl_load24bit.ival)) - tex->bump = Image_GetTexture(va("%s_norm", imagename), subpath, imageflags|IF_TRYBUMP|IF_NOSRGB|(*imagename=='#'?IF_LINEAR:0), (r_shadow_bumpscale_basetexture.ival||*imagename=='#')?mipdata[0]:NULL, palette, width, height, TF_HEIGHT8PAL); + { + qbyte *fallbackheight; + if ((r_shadow_bumpscale_basetexture.ival||*imagename=='#') && !(basefmt&PTI_FULLMIPCHAIN)) + fallbackheight = srcdata; //generate normalmap from assumed heights. + else + fallbackheight = NULL; //disabled + tex->bump = Image_GetTexture(va("%s_norm", imagename), subpath, imageflags|IF_TRYBUMP|IF_NOSRGB|(*imagename=='#'?IF_LINEAR:0), fallbackheight, palette, width, height, TF_HEIGHT8PAL); + } } if (loadflags & SHADER_HASTOPBOTTOM) @@ -6163,13 +6168,13 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (!TEXVALID(tex->fullbright)) { int s=-1; - if (mipdata[0] && (!palette || palette == host_basepal)) + if (srcdata && !(basefmt&PTI_FULLMIPCHAIN) && (!palette || palette == host_basepal)) for(s = width*height-1; s>=0; s--) { - if (mipdata[0][s] >= 256-vid.fullbright) + if (srcdata[s] >= 256-vid.fullbright) break; } - tex->fullbright = Image_GetTexture(va("%s_luma:%s_glow", imagename,imagename), subpath, imageflags, (s>=0)?mipdata[0]:NULL, palette, width, height, TF_TRANS8_FULLBRIGHT); + tex->fullbright = Image_GetTexture(va("%s_luma:%s_glow", imagename,imagename), subpath, imageflags, (s>=0)?srcdata:NULL, palette, width, height, TF_TRANS8_FULLBRIGHT); } } } @@ -7796,10 +7801,11 @@ void Shader_ShowShader_f(void) o = R_LoadShader(sourcename, SUF_2D, NULL, NULL); if (o) { - char *body = Shader_GetShaderBody(o, NULL, 0); + char fname[256]; + char *body = Shader_GetShaderBody(o, fname, sizeof(fname)); if (body) { - Con_Printf("%s\n{%s\n", o->name, body); + Con_Printf("^h(%s)^h\n%s\n{%s\n", fname, o->name, body); Z_Free(body); } else @@ -8014,19 +8020,21 @@ void R_RemapShader(const char *sourcename, const char *destname, float timeoffse shader_t *o; shader_t *n; int i; + size_t l; char cleansrcname[MAX_QPATH]; Q_strncpyz(cleansrcname, sourcename, sizeof(cleansrcname)); COM_CleanUpPath(cleansrcname); + l = strlen(cleansrcname); for (i = 0; i < r_numshaders; i++) { o = r_shaders[i]; if (o && o->uses) { - if (!strcmp(o->name, cleansrcname)) + if (!strncmp(o->name, cleansrcname, l) && (!o->name[l] || o->name[l]=='#')) { - n = R_LoadShader (destname, o->usageflags, NULL, NULL); + n = R_LoadShader (va("%s%s", destname, o->name+l), o->usageflags, NULL, NULL); if (!n) { //if it isn't actually available on disk then don't care about usageflags, just find ANY that's already loaded. // check the hash first diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 328e7a2c..4d77b749 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -77,7 +77,9 @@ void R_SetSky(const char *sky) texnums_t tex; memset(&tex, 0, sizeof(tex)); tex.reflectcube = R_LoadHiResTexture(sky, "env:gfx/env", IF_LOADNOW|IF_TEXTYPE_CUBE|IF_CLAMP); - if (tex.reflectcube->width) + if (tex.reflectcube && tex.reflectcube->status == TEX_LOADING) + COM_WorkerPartialSync(tex.reflectcube, &tex.reflectcube->status, TEX_LOADING); + if (tex.reflectcube->width && TEXLOADED(tex.reflectcube)) { /* FIXME: Q2/HL require the skybox to not draw over geometry, shouldn't we force it? --eukara */ if (cls.allow_skyboxes) { @@ -1011,7 +1013,7 @@ A sky image is 256*128 and comprises two logical textures. the left is the transparent/blended part. the right is the opaque/background part. ============== */ -void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int width, unsigned int height) +void R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *src, unsigned int width, unsigned int height) { int i, j, p; unsigned *temp; @@ -1083,53 +1085,88 @@ void R_InitSky (shader_t *shader, const char *skyname, qbyte *src, unsigned int } } - temp = BZ_Malloc(width*height*sizeof(*temp)); + if (fmt & PTI_FULLMIPCHAIN) + { //input is expected to make sense... + qbyte *front, *back; + unsigned int bb, bw, bh; + unsigned int w, h, y; + fmt = fmt&~PTI_FULLMIPCHAIN; + Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); - // make an average value for the back to avoid - // a fringe on the top level + w = (width+bw-1)/bw; + h = (height+bh-1)/bh; - r = g = b = 0; - for (i=0 ; idefaulttextures->base) - { - Q_snprintfz(name, sizeof(name), "%s_solid", skyname); - Q_strlwr(name); - shader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, width, height, TF_RGBX32); + if (!shader->defaulttextures->base) + { + Q_snprintfz(name, sizeof(name), "%s_solid", skyname); + Q_strlwr(name); + shader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, back, width, height, fmt); + } + if (!shader->defaulttextures->fullbright) + { //FIXME: support _trans + Q_snprintfz(name, sizeof(name), "%s_alpha:%s_trans", skyname, skyname); + Q_strlwr(name); + shader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, front, width, height, fmt); + } + BZ_Free(back); } - - if (!shader->defaulttextures->fullbright) + else { - //fixme: use premultiplied alpha here. - ((qbyte *)&transpix)[0] = r/(width*height); - ((qbyte *)&transpix)[1] = g/(width*height); - ((qbyte *)&transpix)[2] = b/(width*height); - ((qbyte *)&transpix)[3] = 0; - alphamask = LittleLong(0x7fffffff); + temp = BZ_Malloc(width*height*sizeof(*temp)); + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; for (i=0 ; idefaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, width, height, TF_RGBA32); + if (!shader->defaulttextures->base) + { + Q_snprintfz(name, sizeof(name), "%s_solid", skyname); + Q_strlwr(name); + shader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, width, height, TF_RGBX32); + } + + if (!shader->defaulttextures->fullbright) + { + //fixme: use premultiplied alpha here. + ((qbyte *)&transpix)[0] = r/(width*height); + ((qbyte *)&transpix)[1] = g/(width*height); + ((qbyte *)&transpix)[2] = b/(width*height); + ((qbyte *)&transpix)[3] = 0; + alphamask = LittleLong(0x7fffffff); + for (i=0 ; idefaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, width, height, TF_RGBA32); + } + BZ_Free(temp); } - BZ_Free(temp); } #endif diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 36e34e96..7784fb53 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -4632,7 +4632,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "void main ()\n" "{\n" "pos = v_position.xyz - e_eyepos;\n" -"pos.y = -pos.y;\n" "if (r_glsl_skybox_orientation.xyz != vec3(0.0))\n" "pos = pos*rotateAroundAxis(r_glsl_skybox_orientation);\n" @@ -4652,11 +4651,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #ifdef VKQUAKE {QR_VULKAN, -1, "defaultskybox", "\xFF\x53\x50\x56\x01\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x2C\x00\x00\x00\x13\x00\x00\x00\x40\x00\x00\x00" -"\xB8\x0F\x00\x00\xF8\x0F\x00\x00\x70\x11\x00\x00\x01\x00\x62\x31\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00\x00\x00" -"\x03\x02\x23\x07\x00\x00\x01\x00\x07\x00\x08\x00\x58\x00\x00\x00\x00\x00\x00\x00\x11\x00\x02\x00\x01\x00\x00\x00\x0B\x00\x06\x00" +"\x54\x0F\x00\x00\x94\x0F\x00\x00\x70\x11\x00\x00\x01\x00\x62\x31\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00\x00\x00" +"\x03\x02\x23\x07\x00\x00\x01\x00\x07\x00\x08\x00\x53\x00\x00\x00\x00\x00\x00\x00\x11\x00\x02\x00\x01\x00\x00\x00\x0B\x00\x06\x00" "\x01\x00\x00\x00\x47\x4C\x53\x4C\x2E\x73\x74\x64\x2E\x34\x35\x30\x00\x00\x00\x00\x0E\x00\x03\x00\x00\x00\x00\x00\x01\x00\x00\x00" -"\x0F\x00\x0E\x00\x00\x00\x00\x00\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00\x1C\x00\x00\x00\x39\x00\x00\x00\x47\x00\x00\x00" -"\x51\x00\x00\x00\x53\x00\x00\x00\x54\x00\x00\x00\x55\x00\x00\x00\x56\x00\x00\x00\x57\x00\x00\x00\x03\x00\x03\x00\x02\x00\x00\x00" +"\x0F\x00\x0E\x00\x00\x00\x00\x00\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00\x1C\x00\x00\x00\x39\x00\x00\x00\x42\x00\x00\x00" +"\x4C\x00\x00\x00\x4E\x00\x00\x00\x4F\x00\x00\x00\x50\x00\x00\x00\x51\x00\x00\x00\x52\x00\x00\x00\x03\x00\x03\x00\x02\x00\x00\x00" "\xC2\x01\x00\x00\x05\x00\x04\x00\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00\x05\x00\x06\x00\x09\x00\x00\x00\x66\x74\x65\x74" "\x72\x61\x6E\x73\x66\x6F\x72\x6D\x28\x00\x00\x00\x05\x00\x04\x00\x0C\x00\x00\x00\x70\x72\x6F\x6A\x00\x00\x00\x00\x05\x00\x05\x00" "\x13\x00\x00\x00\x65\x6E\x74\x69\x74\x79\x62\x6C\x6F\x63\x6B\x00\x06\x00\x07\x00\x13\x00\x00\x00\x00\x00\x00\x00\x6D\x5F\x6D\x6F" @@ -4678,22 +4677,22 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "\x00\x00\x00\x00\x06\x00\x07\x00\x13\x00\x00\x00\x15\x00\x00\x00\x77\x5F\x66\x6F\x67\x64\x65\x70\x74\x68\x62\x69\x61\x73\x00\x00" "\x06\x00\x05\x00\x13\x00\x00\x00\x16\x00\x00\x00\x65\x70\x61\x64\x37\x00\x00\x00\x05\x00\x03\x00\x15\x00\x00\x00\x00\x00\x00\x00" "\x05\x00\x05\x00\x1C\x00\x00\x00\x76\x5F\x70\x6F\x73\x69\x74\x69\x6F\x6E\x00\x00\x05\x00\x03\x00\x39\x00\x00\x00\x70\x6F\x73\x00" -"\x05\x00\x06\x00\x45\x00\x00\x00\x67\x6C\x5F\x50\x65\x72\x56\x65\x72\x74\x65\x78\x00\x00\x00\x00\x06\x00\x06\x00\x45\x00\x00\x00" -"\x00\x00\x00\x00\x67\x6C\x5F\x50\x6F\x73\x69\x74\x69\x6F\x6E\x00\x05\x00\x03\x00\x47\x00\x00\x00\x00\x00\x00\x00\x05\x00\x07\x00" -"\x4B\x00\x00\x00\x5F\x63\x76\x61\x72\x5F\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00\x05\x00\x04\x00\x4C\x00\x00\x00" -"\x5F\x46\x4F\x47\x00\x00\x00\x00\x05\x00\x05\x00\x4D\x00\x00\x00\x6C\x69\x67\x68\x74\x62\x6C\x6F\x63\x6B\x00\x00\x06\x00\x07\x00" -"\x4D\x00\x00\x00\x00\x00\x00\x00\x6C\x5F\x63\x75\x62\x65\x6D\x61\x74\x72\x69\x78\x00\x00\x00\x00\x06\x00\x07\x00\x4D\x00\x00\x00" -"\x01\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x70\x6F\x73\x69\x74\x69\x6F\x6E\x00\x06\x00\x05\x00\x4D\x00\x00\x00\x02\x00\x00\x00" -"\x6C\x70\x61\x64\x31\x00\x00\x00\x06\x00\x07\x00\x4D\x00\x00\x00\x03\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75" -"\x72\x00\x00\x00\x06\x00\x05\x00\x4D\x00\x00\x00\x04\x00\x00\x00\x6C\x70\x61\x64\x32\x00\x00\x00\x06\x00\x08\x00\x4D\x00\x00\x00" -"\x05\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x73\x63\x61\x6C\x65\x00\x00\x06\x00\x07\x00\x4D\x00\x00\x00" -"\x06\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x72\x61\x64\x69\x75\x73\x00\x00\x00\x06\x00\x07\x00\x4D\x00\x00\x00\x07\x00\x00\x00" -"\x6C\x5F\x73\x68\x61\x64\x6F\x77\x6D\x61\x70\x70\x72\x6F\x6A\x00\x06\x00\x08\x00\x4D\x00\x00\x00\x08\x00\x00\x00\x6C\x5F\x73\x68" -"\x61\x64\x6F\x77\x6D\x61\x70\x73\x63\x61\x6C\x65\x00\x00\x00\x00\x06\x00\x05\x00\x4D\x00\x00\x00\x09\x00\x00\x00\x6C\x70\x61\x64" -"\x33\x00\x00\x00\x05\x00\x03\x00\x4F\x00\x00\x00\x00\x00\x00\x00\x05\x00\x05\x00\x51\x00\x00\x00\x76\x5F\x74\x65\x78\x63\x6F\x6F" -"\x72\x64\x00\x00\x05\x00\x05\x00\x53\x00\x00\x00\x76\x5F\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x00\x05\x00\x05\x00\x54\x00\x00\x00" -"\x76\x5F\x6C\x6D\x63\x6F\x6F\x72\x64\x00\x00\x00\x05\x00\x05\x00\x55\x00\x00\x00\x76\x5F\x6E\x6F\x72\x6D\x61\x6C\x00\x00\x00\x00" -"\x05\x00\x05\x00\x56\x00\x00\x00\x76\x5F\x73\x76\x65\x63\x74\x6F\x72\x00\x00\x00\x05\x00\x05\x00\x57\x00\x00\x00\x76\x5F\x74\x76" +"\x05\x00\x06\x00\x40\x00\x00\x00\x67\x6C\x5F\x50\x65\x72\x56\x65\x72\x74\x65\x78\x00\x00\x00\x00\x06\x00\x06\x00\x40\x00\x00\x00" +"\x00\x00\x00\x00\x67\x6C\x5F\x50\x6F\x73\x69\x74\x69\x6F\x6E\x00\x05\x00\x03\x00\x42\x00\x00\x00\x00\x00\x00\x00\x05\x00\x07\x00" +"\x46\x00\x00\x00\x5F\x63\x76\x61\x72\x5F\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00\x05\x00\x04\x00\x47\x00\x00\x00" +"\x5F\x46\x4F\x47\x00\x00\x00\x00\x05\x00\x05\x00\x48\x00\x00\x00\x6C\x69\x67\x68\x74\x62\x6C\x6F\x63\x6B\x00\x00\x06\x00\x07\x00" +"\x48\x00\x00\x00\x00\x00\x00\x00\x6C\x5F\x63\x75\x62\x65\x6D\x61\x74\x72\x69\x78\x00\x00\x00\x00\x06\x00\x07\x00\x48\x00\x00\x00" +"\x01\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x70\x6F\x73\x69\x74\x69\x6F\x6E\x00\x06\x00\x05\x00\x48\x00\x00\x00\x02\x00\x00\x00" +"\x6C\x70\x61\x64\x31\x00\x00\x00\x06\x00\x07\x00\x48\x00\x00\x00\x03\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75" +"\x72\x00\x00\x00\x06\x00\x05\x00\x48\x00\x00\x00\x04\x00\x00\x00\x6C\x70\x61\x64\x32\x00\x00\x00\x06\x00\x08\x00\x48\x00\x00\x00" +"\x05\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x73\x63\x61\x6C\x65\x00\x00\x06\x00\x07\x00\x48\x00\x00\x00" +"\x06\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x72\x61\x64\x69\x75\x73\x00\x00\x00\x06\x00\x07\x00\x48\x00\x00\x00\x07\x00\x00\x00" +"\x6C\x5F\x73\x68\x61\x64\x6F\x77\x6D\x61\x70\x70\x72\x6F\x6A\x00\x06\x00\x08\x00\x48\x00\x00\x00\x08\x00\x00\x00\x6C\x5F\x73\x68" +"\x61\x64\x6F\x77\x6D\x61\x70\x73\x63\x61\x6C\x65\x00\x00\x00\x00\x06\x00\x05\x00\x48\x00\x00\x00\x09\x00\x00\x00\x6C\x70\x61\x64" +"\x33\x00\x00\x00\x05\x00\x03\x00\x4A\x00\x00\x00\x00\x00\x00\x00\x05\x00\x05\x00\x4C\x00\x00\x00\x76\x5F\x74\x65\x78\x63\x6F\x6F" +"\x72\x64\x00\x00\x05\x00\x05\x00\x4E\x00\x00\x00\x76\x5F\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x00\x05\x00\x05\x00\x4F\x00\x00\x00" +"\x76\x5F\x6C\x6D\x63\x6F\x6F\x72\x64\x00\x00\x00\x05\x00\x05\x00\x50\x00\x00\x00\x76\x5F\x6E\x6F\x72\x6D\x61\x6C\x00\x00\x00\x00" +"\x05\x00\x05\x00\x51\x00\x00\x00\x76\x5F\x73\x76\x65\x63\x74\x6F\x72\x00\x00\x00\x05\x00\x05\x00\x52\x00\x00\x00\x76\x5F\x74\x76" "\x65\x63\x74\x6F\x72\x00\x00\x00\x47\x00\x04\x00\x11\x00\x00\x00\x06\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00\x13\x00\x00\x00" "\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x13\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00" "\x13\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00\x13\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00" @@ -4715,20 +4714,20 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "\x13\x00\x00\x00\x16\x00\x00\x00\x23\x00\x00\x00\x98\x01\x00\x00\x47\x00\x03\x00\x13\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00" "\x15\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x15\x00\x00\x00\x21\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00" "\x1C\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x39\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00" -"\x45\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x47\x00\x03\x00\x45\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00" -"\x4B\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x47\x00\x04\x00\x4C\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x48\x00\x04\x00" -"\x4D\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00" -"\x48\x00\x05\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x01\x00\x00\x00" -"\x23\x00\x00\x00\x40\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x48\x00\x05\x00" -"\x4D\x00\x00\x00\x03\x00\x00\x00\x23\x00\x00\x00\x50\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00" -"\x5C\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x05\x00\x00\x00\x23\x00\x00\x00\x60\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00" -"\x06\x00\x00\x00\x23\x00\x00\x00\x6C\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\x70\x00\x00\x00" -"\x48\x00\x05\x00\x4D\x00\x00\x00\x08\x00\x00\x00\x23\x00\x00\x00\x80\x00\x00\x00\x48\x00\x05\x00\x4D\x00\x00\x00\x09\x00\x00\x00" -"\x23\x00\x00\x00\x88\x00\x00\x00\x47\x00\x03\x00\x4D\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x4F\x00\x00\x00\x22\x00\x00\x00" -"\x00\x00\x00\x00\x47\x00\x04\x00\x4F\x00\x00\x00\x21\x00\x00\x00\x01\x00\x00\x00\x47\x00\x04\x00\x51\x00\x00\x00\x1E\x00\x00\x00" -"\x01\x00\x00\x00\x47\x00\x04\x00\x53\x00\x00\x00\x1E\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x54\x00\x00\x00\x1E\x00\x00\x00" -"\x03\x00\x00\x00\x47\x00\x04\x00\x55\x00\x00\x00\x1E\x00\x00\x00\x04\x00\x00\x00\x47\x00\x04\x00\x56\x00\x00\x00\x1E\x00\x00\x00" -"\x05\x00\x00\x00\x47\x00\x04\x00\x57\x00\x00\x00\x1E\x00\x00\x00\x06\x00\x00\x00\x13\x00\x02\x00\x02\x00\x00\x00\x21\x00\x03\x00" +"\x40\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x47\x00\x03\x00\x40\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00" +"\x46\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x47\x00\x04\x00\x47\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x48\x00\x04\x00" +"\x48\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00" +"\x48\x00\x05\x00\x48\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x01\x00\x00\x00" +"\x23\x00\x00\x00\x40\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x48\x00\x05\x00" +"\x48\x00\x00\x00\x03\x00\x00\x00\x23\x00\x00\x00\x50\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00" +"\x5C\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x05\x00\x00\x00\x23\x00\x00\x00\x60\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00" +"\x06\x00\x00\x00\x23\x00\x00\x00\x6C\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\x70\x00\x00\x00" +"\x48\x00\x05\x00\x48\x00\x00\x00\x08\x00\x00\x00\x23\x00\x00\x00\x80\x00\x00\x00\x48\x00\x05\x00\x48\x00\x00\x00\x09\x00\x00\x00" +"\x23\x00\x00\x00\x88\x00\x00\x00\x47\x00\x03\x00\x48\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x4A\x00\x00\x00\x22\x00\x00\x00" +"\x00\x00\x00\x00\x47\x00\x04\x00\x4A\x00\x00\x00\x21\x00\x00\x00\x01\x00\x00\x00\x47\x00\x04\x00\x4C\x00\x00\x00\x1E\x00\x00\x00" +"\x01\x00\x00\x00\x47\x00\x04\x00\x4E\x00\x00\x00\x1E\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x4F\x00\x00\x00\x1E\x00\x00\x00" +"\x03\x00\x00\x00\x47\x00\x04\x00\x50\x00\x00\x00\x1E\x00\x00\x00\x04\x00\x00\x00\x47\x00\x04\x00\x51\x00\x00\x00\x1E\x00\x00\x00" +"\x05\x00\x00\x00\x47\x00\x04\x00\x52\x00\x00\x00\x1E\x00\x00\x00\x06\x00\x00\x00\x13\x00\x02\x00\x02\x00\x00\x00\x21\x00\x03\x00" "\x03\x00\x00\x00\x02\x00\x00\x00\x16\x00\x03\x00\x06\x00\x00\x00\x20\x00\x00\x00\x17\x00\x04\x00\x07\x00\x00\x00\x06\x00\x00\x00" "\x04\x00\x00\x00\x21\x00\x03\x00\x08\x00\x00\x00\x07\x00\x00\x00\x20\x00\x04\x00\x0B\x00\x00\x00\x07\x00\x00\x00\x07\x00\x00\x00" "\x18\x00\x04\x00\x0D\x00\x00\x00\x07\x00\x00\x00\x04\x00\x00\x00\x17\x00\x04\x00\x0E\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00" @@ -4746,179 +4745,176 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "\x02\x00\x00\x00\x2B\x00\x04\x00\x0F\x00\x00\x00\x2E\x00\x00\x00\x03\x00\x00\x00\x2B\x00\x04\x00\x06\x00\x00\x00\x32\x00\x00\x00" "\x00\x00\x00\x40\x20\x00\x04\x00\x38\x00\x00\x00\x03\x00\x00\x00\x0E\x00\x00\x00\x3B\x00\x04\x00\x38\x00\x00\x00\x39\x00\x00\x00" "\x03\x00\x00\x00\x2B\x00\x04\x00\x16\x00\x00\x00\x3B\x00\x00\x00\x03\x00\x00\x00\x20\x00\x04\x00\x3C\x00\x00\x00\x02\x00\x00\x00" -"\x0E\x00\x00\x00\x20\x00\x04\x00\x40\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x1E\x00\x03\x00\x45\x00\x00\x00\x07\x00\x00\x00" -"\x20\x00\x04\x00\x46\x00\x00\x00\x03\x00\x00\x00\x45\x00\x00\x00\x3B\x00\x04\x00\x46\x00\x00\x00\x47\x00\x00\x00\x03\x00\x00\x00" -"\x20\x00\x04\x00\x49\x00\x00\x00\x03\x00\x00\x00\x07\x00\x00\x00\x32\x00\x04\x00\x16\x00\x00\x00\x4B\x00\x00\x00\x00\x01\x00\x00" -"\x32\x00\x04\x00\x16\x00\x00\x00\x4C\x00\x00\x00\x15\x00\x00\x00\x1E\x00\x0C\x00\x4D\x00\x00\x00\x0D\x00\x00\x00\x0E\x00\x00\x00" -"\x06\x00\x00\x00\x0E\x00\x00\x00\x06\x00\x00\x00\x0E\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x12\x00\x00\x00" -"\x20\x00\x04\x00\x4E\x00\x00\x00\x02\x00\x00\x00\x4D\x00\x00\x00\x3B\x00\x04\x00\x4E\x00\x00\x00\x4F\x00\x00\x00\x02\x00\x00\x00" -"\x20\x00\x04\x00\x50\x00\x00\x00\x01\x00\x00\x00\x12\x00\x00\x00\x3B\x00\x04\x00\x50\x00\x00\x00\x51\x00\x00\x00\x01\x00\x00\x00" -"\x20\x00\x04\x00\x52\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x3B\x00\x04\x00\x52\x00\x00\x00\x53\x00\x00\x00\x01\x00\x00\x00" -"\x3B\x00\x04\x00\x50\x00\x00\x00\x54\x00\x00\x00\x01\x00\x00\x00\x3B\x00\x04\x00\x1B\x00\x00\x00\x55\x00\x00\x00\x01\x00\x00\x00" -"\x3B\x00\x04\x00\x1B\x00\x00\x00\x56\x00\x00\x00\x01\x00\x00\x00\x3B\x00\x04\x00\x1B\x00\x00\x00\x57\x00\x00\x00\x01\x00\x00\x00" -"\x36\x00\x05\x00\x02\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\xF8\x00\x02\x00\x05\x00\x00\x00\x3D\x00\x04\x00" -"\x0E\x00\x00\x00\x3A\x00\x00\x00\x1C\x00\x00\x00\x41\x00\x05\x00\x3C\x00\x00\x00\x3D\x00\x00\x00\x15\x00\x00\x00\x3B\x00\x00\x00" -"\x3D\x00\x04\x00\x0E\x00\x00\x00\x3E\x00\x00\x00\x3D\x00\x00\x00\x83\x00\x05\x00\x0E\x00\x00\x00\x3F\x00\x00\x00\x3A\x00\x00\x00" -"\x3E\x00\x00\x00\x3E\x00\x03\x00\x39\x00\x00\x00\x3F\x00\x00\x00\x41\x00\x05\x00\x40\x00\x00\x00\x41\x00\x00\x00\x39\x00\x00\x00" -"\x25\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x42\x00\x00\x00\x41\x00\x00\x00\x7F\x00\x04\x00\x06\x00\x00\x00\x43\x00\x00\x00" -"\x42\x00\x00\x00\x41\x00\x05\x00\x40\x00\x00\x00\x44\x00\x00\x00\x39\x00\x00\x00\x25\x00\x00\x00\x3E\x00\x03\x00\x44\x00\x00\x00" -"\x43\x00\x00\x00\x39\x00\x04\x00\x07\x00\x00\x00\x48\x00\x00\x00\x09\x00\x00\x00\x41\x00\x05\x00\x49\x00\x00\x00\x4A\x00\x00\x00" -"\x47\x00\x00\x00\x17\x00\x00\x00\x3E\x00\x03\x00\x4A\x00\x00\x00\x48\x00\x00\x00\xFD\x00\x01\x00\x38\x00\x01\x00\x36\x00\x05\x00" -"\x07\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\xF8\x00\x02\x00\x0A\x00\x00\x00\x3B\x00\x04\x00\x0B\x00\x00\x00" -"\x0C\x00\x00\x00\x07\x00\x00\x00\x41\x00\x05\x00\x18\x00\x00\x00\x19\x00\x00\x00\x15\x00\x00\x00\x17\x00\x00\x00\x3D\x00\x04\x00" -"\x0D\x00\x00\x00\x1A\x00\x00\x00\x19\x00\x00\x00\x3D\x00\x04\x00\x0E\x00\x00\x00\x1D\x00\x00\x00\x1C\x00\x00\x00\x51\x00\x05\x00" -"\x06\x00\x00\x00\x1F\x00\x00\x00\x1D\x00\x00\x00\x00\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x20\x00\x00\x00\x1D\x00\x00\x00" -"\x01\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x21\x00\x00\x00\x1D\x00\x00\x00\x02\x00\x00\x00\x50\x00\x07\x00\x07\x00\x00\x00" -"\x22\x00\x00\x00\x1F\x00\x00\x00\x20\x00\x00\x00\x21\x00\x00\x00\x1E\x00\x00\x00\x91\x00\x05\x00\x07\x00\x00\x00\x23\x00\x00\x00" -"\x1A\x00\x00\x00\x22\x00\x00\x00\x3E\x00\x03\x00\x0C\x00\x00\x00\x23\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x27\x00\x00\x00" -"\x0C\x00\x00\x00\x25\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x28\x00\x00\x00\x27\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00" -"\x29\x00\x00\x00\x28\x00\x00\x00\x24\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2A\x00\x00\x00\x0C\x00\x00\x00\x25\x00\x00\x00" -"\x3E\x00\x03\x00\x2A\x00\x00\x00\x29\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2C\x00\x00\x00\x0C\x00\x00\x00\x2B\x00\x00\x00" -"\x3D\x00\x04\x00\x06\x00\x00\x00\x2D\x00\x00\x00\x2C\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2F\x00\x00\x00\x0C\x00\x00\x00" -"\x2E\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x30\x00\x00\x00\x2F\x00\x00\x00\x81\x00\x05\x00\x06\x00\x00\x00\x31\x00\x00\x00" -"\x2D\x00\x00\x00\x30\x00\x00\x00\x88\x00\x05\x00\x06\x00\x00\x00\x33\x00\x00\x00\x31\x00\x00\x00\x32\x00\x00\x00\x41\x00\x05\x00" -"\x26\x00\x00\x00\x34\x00\x00\x00\x0C\x00\x00\x00\x2B\x00\x00\x00\x3E\x00\x03\x00\x34\x00\x00\x00\x33\x00\x00\x00\x3D\x00\x04\x00" -"\x07\x00\x00\x00\x35\x00\x00\x00\x0C\x00\x00\x00\xFE\x00\x02\x00\x35\x00\x00\x00\x38\x00\x01\x00\x03\x02\x23\x07\x00\x00\x01\x00" -"\x07\x00\x08\x00\x72\x00\x00\x00\x00\x00\x00\x00\x11\x00\x02\x00\x01\x00\x00\x00\x0B\x00\x06\x00\x01\x00\x00\x00\x47\x4C\x53\x4C" -"\x2E\x73\x74\x64\x2E\x34\x35\x30\x00\x00\x00\x00\x0E\x00\x03\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0F\x00\x08\x00\x04\x00\x00\x00" -"\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00\x27\x00\x00\x00\x62\x00\x00\x00\x66\x00\x00\x00\x10\x00\x03\x00\x04\x00\x00\x00" -"\x07\x00\x00\x00\x03\x00\x03\x00\x02\x00\x00\x00\xC2\x01\x00\x00\x05\x00\x04\x00\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00" -"\x05\x00\x05\x00\x0B\x00\x00\x00\x66\x6F\x67\x33\x28\x76\x66\x33\x3B\x00\x00\x00\x05\x00\x06\x00\x0A\x00\x00\x00\x72\x65\x67\x75" -"\x6C\x61\x72\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x05\x00\x04\x00\x0E\x00\x00\x00\x5F\x46\x4F\x47\x00\x00\x00\x00\x05\x00\x03\x00" -"\x18\x00\x00\x00\x7A\x00\x00\x00\x05\x00\x05\x00\x1F\x00\x00\x00\x65\x6E\x74\x69\x74\x79\x62\x6C\x6F\x63\x6B\x00\x06\x00\x07\x00" -"\x1F\x00\x00\x00\x00\x00\x00\x00\x6D\x5F\x6D\x6F\x64\x65\x6C\x76\x69\x65\x77\x70\x72\x6F\x6A\x00\x06\x00\x05\x00\x1F\x00\x00\x00" -"\x01\x00\x00\x00\x6D\x5F\x6D\x6F\x64\x65\x6C\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x6D\x5F\x6D\x6F\x64\x65\x6C\x69" -"\x6E\x76\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x03\x00\x00\x00\x65\x5F\x65\x79\x65\x70\x6F\x73\x00\x00\x00\x00\x06\x00\x05\x00" -"\x1F\x00\x00\x00\x04\x00\x00\x00\x65\x5F\x74\x69\x6D\x65\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x05\x00\x00\x00\x65\x5F\x6C\x69" -"\x67\x68\x74\x5F\x61\x6D\x62\x69\x65\x6E\x74\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x06\x00\x00\x00\x65\x70\x61\x64\x31\x00\x00\x00" -"\x06\x00\x06\x00\x1F\x00\x00\x00\x07\x00\x00\x00\x65\x5F\x6C\x69\x67\x68\x74\x5F\x64\x69\x72\x00\x06\x00\x05\x00\x1F\x00\x00\x00" -"\x08\x00\x00\x00\x65\x70\x61\x64\x32\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x09\x00\x00\x00\x65\x5F\x6C\x69\x67\x68\x74\x5F" -"\x6D\x75\x6C\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x0A\x00\x00\x00\x65\x70\x61\x64\x33\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00" -"\x0B\x00\x00\x00\x65\x5F\x6C\x6D\x73\x63\x61\x6C\x65\x73\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x0C\x00\x00\x00\x65\x5F\x75\x70" -"\x70\x65\x72\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x0D\x00\x00\x00\x65\x70\x61\x64\x34\x00\x00\x00" -"\x06\x00\x07\x00\x1F\x00\x00\x00\x0E\x00\x00\x00\x65\x5F\x6C\x6F\x77\x65\x72\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x06\x00\x05\x00" -"\x1F\x00\x00\x00\x0F\x00\x00\x00\x65\x70\x61\x64\x35\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x10\x00\x00\x00\x65\x5F\x67\x6C" -"\x6F\x77\x6D\x6F\x64\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x11\x00\x00\x00\x65\x70\x61\x64\x36\x00\x00\x00\x06\x00\x07\x00" -"\x1F\x00\x00\x00\x12\x00\x00\x00\x65\x5F\x63\x6F\x6C\x6F\x75\x72\x69\x64\x65\x6E\x74\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00" -"\x13\x00\x00\x00\x77\x5F\x66\x6F\x67\x63\x6F\x6C\x6F\x75\x72\x73\x00\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x14\x00\x00\x00" -"\x77\x5F\x66\x6F\x67\x64\x65\x6E\x73\x69\x74\x79\x00\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x15\x00\x00\x00\x77\x5F\x66\x6F" -"\x67\x64\x65\x70\x74\x68\x62\x69\x61\x73\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x16\x00\x00\x00\x65\x70\x61\x64\x37\x00\x00\x00" -"\x05\x00\x03\x00\x21\x00\x00\x00\x00\x00\x00\x00\x05\x00\x06\x00\x27\x00\x00\x00\x67\x6C\x5F\x46\x72\x61\x67\x43\x6F\x6F\x72\x64" -"\x00\x00\x00\x00\x05\x00\x07\x00\x38\x00\x00\x00\x5F\x63\x76\x61\x72\x5F\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00" -"\x05\x00\x03\x00\x3F\x00\x00\x00\x66\x61\x63\x00\x05\x00\x04\x00\x5B\x00\x00\x00\x73\x6B\x79\x62\x6F\x78\x00\x00\x05\x00\x06\x00" -"\x5F\x00\x00\x00\x73\x5F\x72\x65\x66\x6C\x65\x63\x74\x63\x75\x62\x65\x00\x00\x00\x05\x00\x03\x00\x62\x00\x00\x00\x70\x6F\x73\x00" -"\x05\x00\x05\x00\x66\x00\x00\x00\x6F\x75\x74\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x05\x00\x04\x00\x67\x00\x00\x00\x70\x61\x72\x61" -"\x6D\x00\x00\x00\x05\x00\x05\x00\x6F\x00\x00\x00\x6C\x69\x67\x68\x74\x62\x6C\x6F\x63\x6B\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00" -"\x00\x00\x00\x00\x6C\x5F\x63\x75\x62\x65\x6D\x61\x74\x72\x69\x78\x00\x00\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x01\x00\x00\x00" -"\x6C\x5F\x6C\x69\x67\x68\x74\x70\x6F\x73\x69\x74\x69\x6F\x6E\x00\x06\x00\x05\x00\x6F\x00\x00\x00\x02\x00\x00\x00\x6C\x70\x61\x64" -"\x31\x00\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x03\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00" -"\x06\x00\x05\x00\x6F\x00\x00\x00\x04\x00\x00\x00\x6C\x70\x61\x64\x32\x00\x00\x00\x06\x00\x08\x00\x6F\x00\x00\x00\x05\x00\x00\x00" -"\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x73\x63\x61\x6C\x65\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x06\x00\x00\x00" -"\x6C\x5F\x6C\x69\x67\x68\x74\x72\x61\x64\x69\x75\x73\x00\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x07\x00\x00\x00\x6C\x5F\x73\x68" -"\x61\x64\x6F\x77\x6D\x61\x70\x70\x72\x6F\x6A\x00\x06\x00\x08\x00\x6F\x00\x00\x00\x08\x00\x00\x00\x6C\x5F\x73\x68\x61\x64\x6F\x77" -"\x6D\x61\x70\x73\x63\x61\x6C\x65\x00\x00\x00\x00\x06\x00\x05\x00\x6F\x00\x00\x00\x09\x00\x00\x00\x6C\x70\x61\x64\x33\x00\x00\x00" -"\x05\x00\x03\x00\x71\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x0E\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x47\x00\x04\x00" -"\x1D\x00\x00\x00\x06\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00\x1F\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00" -"\x1F\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00" -"\x10\x00\x00\x00\x48\x00\x04\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x01\x00\x00\x00" -"\x23\x00\x00\x00\x40\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00" -"\x1F\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x80\x00\x00\x00" -"\x48\x00\x05\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x03\x00\x00\x00" -"\x23\x00\x00\x00\xC0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00\xCC\x00\x00\x00\x48\x00\x05\x00" -"\x1F\x00\x00\x00\x05\x00\x00\x00\x23\x00\x00\x00\xD0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x06\x00\x00\x00\x23\x00\x00\x00" -"\xDC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\xE0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" -"\x08\x00\x00\x00\x23\x00\x00\x00\xEC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x09\x00\x00\x00\x23\x00\x00\x00\xF0\x00\x00\x00" -"\x48\x00\x05\x00\x1F\x00\x00\x00\x0A\x00\x00\x00\x23\x00\x00\x00\xFC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0B\x00\x00\x00" -"\x23\x00\x00\x00\x00\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0C\x00\x00\x00\x23\x00\x00\x00\x40\x01\x00\x00\x48\x00\x05\x00" -"\x1F\x00\x00\x00\x0D\x00\x00\x00\x23\x00\x00\x00\x4C\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0E\x00\x00\x00\x23\x00\x00\x00" -"\x50\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0F\x00\x00\x00\x23\x00\x00\x00\x5C\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" -"\x10\x00\x00\x00\x23\x00\x00\x00\x60\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x11\x00\x00\x00\x23\x00\x00\x00\x6C\x01\x00\x00" -"\x48\x00\x05\x00\x1F\x00\x00\x00\x12\x00\x00\x00\x23\x00\x00\x00\x70\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x13\x00\x00\x00" -"\x23\x00\x00\x00\x80\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x14\x00\x00\x00\x23\x00\x00\x00\x90\x01\x00\x00\x48\x00\x05\x00" -"\x1F\x00\x00\x00\x15\x00\x00\x00\x23\x00\x00\x00\x94\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x16\x00\x00\x00\x23\x00\x00\x00" -"\x98\x01\x00\x00\x47\x00\x03\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x21\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00" -"\x47\x00\x04\x00\x21\x00\x00\x00\x21\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x27\x00\x00\x00\x0B\x00\x00\x00\x0F\x00\x00\x00" -"\x47\x00\x04\x00\x38\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x47\x00\x04\x00\x5F\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00" -"\x47\x00\x04\x00\x5F\x00\x00\x00\x21\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x62\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00" -"\x47\x00\x04\x00\x66\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00\x48\x00\x04\x00\x6F\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00" -"\x48\x00\x05\x00\x6F\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x00\x00\x00\x00" -"\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x01\x00\x00\x00\x23\x00\x00\x00\x40\x00\x00\x00\x48\x00\x05\x00" -"\x6F\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x03\x00\x00\x00\x23\x00\x00\x00" -"\x50\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00\x5C\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00" -"\x05\x00\x00\x00\x23\x00\x00\x00\x60\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x06\x00\x00\x00\x23\x00\x00\x00\x6C\x00\x00\x00" -"\x48\x00\x05\x00\x6F\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\x70\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x08\x00\x00\x00" -"\x23\x00\x00\x00\x80\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x09\x00\x00\x00\x23\x00\x00\x00\x88\x00\x00\x00\x47\x00\x03\x00" -"\x6F\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x71\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x71\x00\x00\x00" -"\x21\x00\x00\x00\x01\x00\x00\x00\x13\x00\x02\x00\x02\x00\x00\x00\x21\x00\x03\x00\x03\x00\x00\x00\x02\x00\x00\x00\x16\x00\x03\x00" -"\x06\x00\x00\x00\x20\x00\x00\x00\x17\x00\x04\x00\x07\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x20\x00\x04\x00\x08\x00\x00\x00" -"\x07\x00\x00\x00\x07\x00\x00\x00\x21\x00\x04\x00\x09\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\x15\x00\x04\x00\x0D\x00\x00\x00" -"\x20\x00\x00\x00\x01\x00\x00\x00\x32\x00\x04\x00\x0D\x00\x00\x00\x0E\x00\x00\x00\x15\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00" -"\x0F\x00\x00\x00\x00\x00\x00\x00\x14\x00\x02\x00\x10\x00\x00\x00\x34\x00\x06\x00\x10\x00\x00\x00\x11\x00\x00\x00\xAB\x00\x00\x00" -"\x0E\x00\x00\x00\x0F\x00\x00\x00\x34\x00\x05\x00\x10\x00\x00\x00\x12\x00\x00\x00\xA8\x00\x00\x00\x11\x00\x00\x00\x20\x00\x04\x00" -"\x17\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x17\x00\x04\x00\x19\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x18\x00\x04\x00" -"\x1A\x00\x00\x00\x19\x00\x00\x00\x04\x00\x00\x00\x15\x00\x04\x00\x1B\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x2B\x00\x04\x00" -"\x1B\x00\x00\x00\x1C\x00\x00\x00\x04\x00\x00\x00\x1C\x00\x04\x00\x1D\x00\x00\x00\x19\x00\x00\x00\x1C\x00\x00\x00\x17\x00\x04\x00" -"\x1E\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x1E\x00\x19\x00\x1F\x00\x00\x00\x1A\x00\x00\x00\x1A\x00\x00\x00\x1A\x00\x00\x00" -"\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00" -"\x1D\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x19\x00\x00\x00" -"\x19\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x1E\x00\x00\x00\x20\x00\x04\x00\x20\x00\x00\x00\x02\x00\x00\x00\x1F\x00\x00\x00" -"\x3B\x00\x04\x00\x20\x00\x00\x00\x21\x00\x00\x00\x02\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00\x22\x00\x00\x00\x14\x00\x00\x00" -"\x20\x00\x04\x00\x23\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x20\x00\x04\x00\x26\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00" -"\x3B\x00\x04\x00\x26\x00\x00\x00\x27\x00\x00\x00\x01\x00\x00\x00\x2B\x00\x04\x00\x1B\x00\x00\x00\x28\x00\x00\x00\x02\x00\x00\x00" -"\x20\x00\x04\x00\x29\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x2B\x00\x04\x00\x1B\x00\x00\x00\x2D\x00\x00\x00\x03\x00\x00\x00" -"\x2B\x00\x04\x00\x06\x00\x00\x00\x31\x00\x00\x00\x00\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00\x33\x00\x00\x00\x15\x00\x00\x00" -"\x32\x00\x04\x00\x0D\x00\x00\x00\x38\x00\x00\x00\x00\x01\x00\x00\x34\x00\x06\x00\x10\x00\x00\x00\x39\x00\x00\x00\xAB\x00\x00\x00" -"\x38\x00\x00\x00\x0F\x00\x00\x00\x2B\x00\x04\x00\x06\x00\x00\x00\x41\x00\x00\x00\x3B\xAA\xB8\x3F\x2B\x00\x04\x00\x06\x00\x00\x00" -"\x45\x00\x00\x00\x00\x00\x80\x3F\x2B\x00\x04\x00\x0D\x00\x00\x00\x46\x00\x00\x00\x13\x00\x00\x00\x20\x00\x04\x00\x50\x00\x00\x00" -"\x02\x00\x00\x00\x19\x00\x00\x00\x20\x00\x04\x00\x5A\x00\x00\x00\x07\x00\x00\x00\x19\x00\x00\x00\x19\x00\x09\x00\x5C\x00\x00\x00" -"\x06\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x1B\x00\x03\x00" -"\x5D\x00\x00\x00\x5C\x00\x00\x00\x20\x00\x04\x00\x5E\x00\x00\x00\x00\x00\x00\x00\x5D\x00\x00\x00\x3B\x00\x04\x00\x5E\x00\x00\x00" -"\x5F\x00\x00\x00\x00\x00\x00\x00\x20\x00\x04\x00\x61\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x3B\x00\x04\x00\x61\x00\x00\x00" -"\x62\x00\x00\x00\x01\x00\x00\x00\x20\x00\x04\x00\x65\x00\x00\x00\x03\x00\x00\x00\x19\x00\x00\x00\x3B\x00\x04\x00\x65\x00\x00\x00" -"\x66\x00\x00\x00\x03\x00\x00\x00\x1E\x00\x0C\x00\x6F\x00\x00\x00\x1A\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00" -"\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x19\x00\x00\x00\x1E\x00\x00\x00\x1E\x00\x00\x00\x20\x00\x04\x00\x70\x00\x00\x00" -"\x02\x00\x00\x00\x6F\x00\x00\x00\x3B\x00\x04\x00\x70\x00\x00\x00\x71\x00\x00\x00\x02\x00\x00\x00\x36\x00\x05\x00\x02\x00\x00\x00" -"\x04\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\xF8\x00\x02\x00\x05\x00\x00\x00\x3B\x00\x04\x00\x5A\x00\x00\x00\x5B\x00\x00\x00" -"\x07\x00\x00\x00\x3B\x00\x04\x00\x08\x00\x00\x00\x67\x00\x00\x00\x07\x00\x00\x00\x3D\x00\x04\x00\x5D\x00\x00\x00\x60\x00\x00\x00" -"\x5F\x00\x00\x00\x3D\x00\x04\x00\x07\x00\x00\x00\x63\x00\x00\x00\x62\x00\x00\x00\x57\x00\x05\x00\x19\x00\x00\x00\x64\x00\x00\x00" -"\x60\x00\x00\x00\x63\x00\x00\x00\x3E\x00\x03\x00\x5B\x00\x00\x00\x64\x00\x00\x00\x3D\x00\x04\x00\x19\x00\x00\x00\x68\x00\x00\x00" -"\x5B\x00\x00\x00\x4F\x00\x08\x00\x07\x00\x00\x00\x69\x00\x00\x00\x68\x00\x00\x00\x68\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" -"\x02\x00\x00\x00\x3E\x00\x03\x00\x67\x00\x00\x00\x69\x00\x00\x00\x39\x00\x05\x00\x07\x00\x00\x00\x6A\x00\x00\x00\x0B\x00\x00\x00" -"\x67\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x6B\x00\x00\x00\x6A\x00\x00\x00\x00\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00" -"\x6C\x00\x00\x00\x6A\x00\x00\x00\x01\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x6D\x00\x00\x00\x6A\x00\x00\x00\x02\x00\x00\x00" -"\x50\x00\x07\x00\x19\x00\x00\x00\x6E\x00\x00\x00\x6B\x00\x00\x00\x6C\x00\x00\x00\x6D\x00\x00\x00\x45\x00\x00\x00\x3E\x00\x03\x00" -"\x66\x00\x00\x00\x6E\x00\x00\x00\xFD\x00\x01\x00\x38\x00\x01\x00\x36\x00\x05\x00\x07\x00\x00\x00\x0B\x00\x00\x00\x00\x00\x00\x00" -"\x09\x00\x00\x00\x37\x00\x03\x00\x08\x00\x00\x00\x0A\x00\x00\x00\xF8\x00\x02\x00\x0C\x00\x00\x00\x3B\x00\x04\x00\x17\x00\x00\x00" -"\x18\x00\x00\x00\x07\x00\x00\x00\x3B\x00\x04\x00\x17\x00\x00\x00\x3F\x00\x00\x00\x07\x00\x00\x00\xF7\x00\x03\x00\x14\x00\x00\x00" -"\x00\x00\x00\x00\xFA\x00\x04\x00\x12\x00\x00\x00\x13\x00\x00\x00\x14\x00\x00\x00\xF8\x00\x02\x00\x13\x00\x00\x00\x3D\x00\x04\x00" -"\x07\x00\x00\x00\x15\x00\x00\x00\x0A\x00\x00\x00\xFE\x00\x02\x00\x15\x00\x00\x00\xF8\x00\x02\x00\x14\x00\x00\x00\x41\x00\x05\x00" -"\x23\x00\x00\x00\x24\x00\x00\x00\x21\x00\x00\x00\x22\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x25\x00\x00\x00\x24\x00\x00\x00" -"\x41\x00\x05\x00\x29\x00\x00\x00\x2A\x00\x00\x00\x27\x00\x00\x00\x28\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x2B\x00\x00\x00" -"\x2A\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x2C\x00\x00\x00\x25\x00\x00\x00\x2B\x00\x00\x00\x41\x00\x05\x00\x29\x00\x00\x00" -"\x2E\x00\x00\x00\x27\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x2F\x00\x00\x00\x2E\x00\x00\x00\x88\x00\x05\x00" -"\x06\x00\x00\x00\x30\x00\x00\x00\x2C\x00\x00\x00\x2F\x00\x00\x00\x3E\x00\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x3D\x00\x04\x00" -"\x06\x00\x00\x00\x32\x00\x00\x00\x18\x00\x00\x00\x41\x00\x05\x00\x23\x00\x00\x00\x34\x00\x00\x00\x21\x00\x00\x00\x33\x00\x00\x00" -"\x3D\x00\x04\x00\x06\x00\x00\x00\x35\x00\x00\x00\x34\x00\x00\x00\x83\x00\x05\x00\x06\x00\x00\x00\x36\x00\x00\x00\x32\x00\x00\x00" -"\x35\x00\x00\x00\x0C\x00\x07\x00\x06\x00\x00\x00\x37\x00\x00\x00\x01\x00\x00\x00\x28\x00\x00\x00\x31\x00\x00\x00\x36\x00\x00\x00" -"\x3E\x00\x03\x00\x18\x00\x00\x00\x37\x00\x00\x00\xF7\x00\x03\x00\x3B\x00\x00\x00\x00\x00\x00\x00\xFA\x00\x04\x00\x39\x00\x00\x00" -"\x3A\x00\x00\x00\x3B\x00\x00\x00\xF8\x00\x02\x00\x3A\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x3C\x00\x00\x00\x18\x00\x00\x00" -"\x3D\x00\x04\x00\x06\x00\x00\x00\x3D\x00\x00\x00\x18\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x3E\x00\x00\x00\x3D\x00\x00\x00" -"\x3C\x00\x00\x00\x3E\x00\x03\x00\x18\x00\x00\x00\x3E\x00\x00\x00\xF9\x00\x02\x00\x3B\x00\x00\x00\xF8\x00\x02\x00\x3B\x00\x00\x00" -"\x3D\x00\x04\x00\x06\x00\x00\x00\x40\x00\x00\x00\x18\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x42\x00\x00\x00\x40\x00\x00\x00" -"\x41\x00\x00\x00\x7F\x00\x04\x00\x06\x00\x00\x00\x43\x00\x00\x00\x42\x00\x00\x00\x0C\x00\x06\x00\x06\x00\x00\x00\x44\x00\x00\x00" -"\x01\x00\x00\x00\x1D\x00\x00\x00\x43\x00\x00\x00\x3E\x00\x03\x00\x3F\x00\x00\x00\x44\x00\x00\x00\x41\x00\x06\x00\x23\x00\x00\x00" -"\x47\x00\x00\x00\x21\x00\x00\x00\x46\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x48\x00\x00\x00\x47\x00\x00\x00" -"\x83\x00\x05\x00\x06\x00\x00\x00\x49\x00\x00\x00\x45\x00\x00\x00\x48\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x4A\x00\x00\x00" -"\x3F\x00\x00\x00\x0C\x00\x08\x00\x06\x00\x00\x00\x4B\x00\x00\x00\x01\x00\x00\x00\x2B\x00\x00\x00\x4A\x00\x00\x00\x31\x00\x00\x00" -"\x45\x00\x00\x00\x41\x00\x06\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x21\x00\x00\x00\x46\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00" -"\x06\x00\x00\x00\x4D\x00\x00\x00\x4C\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x4E\x00\x00\x00\x4B\x00\x00\x00\x4D\x00\x00\x00" -"\x81\x00\x05\x00\x06\x00\x00\x00\x4F\x00\x00\x00\x49\x00\x00\x00\x4E\x00\x00\x00\x3E\x00\x03\x00\x3F\x00\x00\x00\x4F\x00\x00\x00" -"\x41\x00\x05\x00\x50\x00\x00\x00\x51\x00\x00\x00\x21\x00\x00\x00\x46\x00\x00\x00\x3D\x00\x04\x00\x19\x00\x00\x00\x52\x00\x00\x00" -"\x51\x00\x00\x00\x4F\x00\x08\x00\x07\x00\x00\x00\x53\x00\x00\x00\x52\x00\x00\x00\x52\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" -"\x02\x00\x00\x00\x3D\x00\x04\x00\x07\x00\x00\x00\x54\x00\x00\x00\x0A\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x55\x00\x00\x00" -"\x3F\x00\x00\x00\x50\x00\x06\x00\x07\x00\x00\x00\x56\x00\x00\x00\x55\x00\x00\x00\x55\x00\x00\x00\x55\x00\x00\x00\x0C\x00\x08\x00" -"\x07\x00\x00\x00\x57\x00\x00\x00\x01\x00\x00\x00\x2E\x00\x00\x00\x53\x00\x00\x00\x54\x00\x00\x00\x56\x00\x00\x00\xFE\x00\x02\x00" -"\x57\x00\x00\x00\x38\x00\x01\x00"}, +"\x0E\x00\x00\x00\x1E\x00\x03\x00\x40\x00\x00\x00\x07\x00\x00\x00\x20\x00\x04\x00\x41\x00\x00\x00\x03\x00\x00\x00\x40\x00\x00\x00" +"\x3B\x00\x04\x00\x41\x00\x00\x00\x42\x00\x00\x00\x03\x00\x00\x00\x20\x00\x04\x00\x44\x00\x00\x00\x03\x00\x00\x00\x07\x00\x00\x00" +"\x32\x00\x04\x00\x16\x00\x00\x00\x46\x00\x00\x00\x00\x01\x00\x00\x32\x00\x04\x00\x16\x00\x00\x00\x47\x00\x00\x00\x15\x00\x00\x00" +"\x1E\x00\x0C\x00\x48\x00\x00\x00\x0D\x00\x00\x00\x0E\x00\x00\x00\x06\x00\x00\x00\x0E\x00\x00\x00\x06\x00\x00\x00\x0E\x00\x00\x00" +"\x06\x00\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x12\x00\x00\x00\x20\x00\x04\x00\x49\x00\x00\x00\x02\x00\x00\x00\x48\x00\x00\x00" +"\x3B\x00\x04\x00\x49\x00\x00\x00\x4A\x00\x00\x00\x02\x00\x00\x00\x20\x00\x04\x00\x4B\x00\x00\x00\x01\x00\x00\x00\x12\x00\x00\x00" +"\x3B\x00\x04\x00\x4B\x00\x00\x00\x4C\x00\x00\x00\x01\x00\x00\x00\x20\x00\x04\x00\x4D\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00" +"\x3B\x00\x04\x00\x4D\x00\x00\x00\x4E\x00\x00\x00\x01\x00\x00\x00\x3B\x00\x04\x00\x4B\x00\x00\x00\x4F\x00\x00\x00\x01\x00\x00\x00" +"\x3B\x00\x04\x00\x1B\x00\x00\x00\x50\x00\x00\x00\x01\x00\x00\x00\x3B\x00\x04\x00\x1B\x00\x00\x00\x51\x00\x00\x00\x01\x00\x00\x00" +"\x3B\x00\x04\x00\x1B\x00\x00\x00\x52\x00\x00\x00\x01\x00\x00\x00\x36\x00\x05\x00\x02\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" +"\x03\x00\x00\x00\xF8\x00\x02\x00\x05\x00\x00\x00\x3D\x00\x04\x00\x0E\x00\x00\x00\x3A\x00\x00\x00\x1C\x00\x00\x00\x41\x00\x05\x00" +"\x3C\x00\x00\x00\x3D\x00\x00\x00\x15\x00\x00\x00\x3B\x00\x00\x00\x3D\x00\x04\x00\x0E\x00\x00\x00\x3E\x00\x00\x00\x3D\x00\x00\x00" +"\x83\x00\x05\x00\x0E\x00\x00\x00\x3F\x00\x00\x00\x3A\x00\x00\x00\x3E\x00\x00\x00\x3E\x00\x03\x00\x39\x00\x00\x00\x3F\x00\x00\x00" +"\x39\x00\x04\x00\x07\x00\x00\x00\x43\x00\x00\x00\x09\x00\x00\x00\x41\x00\x05\x00\x44\x00\x00\x00\x45\x00\x00\x00\x42\x00\x00\x00" +"\x17\x00\x00\x00\x3E\x00\x03\x00\x45\x00\x00\x00\x43\x00\x00\x00\xFD\x00\x01\x00\x38\x00\x01\x00\x36\x00\x05\x00\x07\x00\x00\x00" +"\x09\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\xF8\x00\x02\x00\x0A\x00\x00\x00\x3B\x00\x04\x00\x0B\x00\x00\x00\x0C\x00\x00\x00" +"\x07\x00\x00\x00\x41\x00\x05\x00\x18\x00\x00\x00\x19\x00\x00\x00\x15\x00\x00\x00\x17\x00\x00\x00\x3D\x00\x04\x00\x0D\x00\x00\x00" +"\x1A\x00\x00\x00\x19\x00\x00\x00\x3D\x00\x04\x00\x0E\x00\x00\x00\x1D\x00\x00\x00\x1C\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00" +"\x1F\x00\x00\x00\x1D\x00\x00\x00\x00\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x20\x00\x00\x00\x1D\x00\x00\x00\x01\x00\x00\x00" +"\x51\x00\x05\x00\x06\x00\x00\x00\x21\x00\x00\x00\x1D\x00\x00\x00\x02\x00\x00\x00\x50\x00\x07\x00\x07\x00\x00\x00\x22\x00\x00\x00" +"\x1F\x00\x00\x00\x20\x00\x00\x00\x21\x00\x00\x00\x1E\x00\x00\x00\x91\x00\x05\x00\x07\x00\x00\x00\x23\x00\x00\x00\x1A\x00\x00\x00" +"\x22\x00\x00\x00\x3E\x00\x03\x00\x0C\x00\x00\x00\x23\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x27\x00\x00\x00\x0C\x00\x00\x00" +"\x25\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x28\x00\x00\x00\x27\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x29\x00\x00\x00" +"\x28\x00\x00\x00\x24\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2A\x00\x00\x00\x0C\x00\x00\x00\x25\x00\x00\x00\x3E\x00\x03\x00" +"\x2A\x00\x00\x00\x29\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2C\x00\x00\x00\x0C\x00\x00\x00\x2B\x00\x00\x00\x3D\x00\x04\x00" +"\x06\x00\x00\x00\x2D\x00\x00\x00\x2C\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00\x2F\x00\x00\x00\x0C\x00\x00\x00\x2E\x00\x00\x00" +"\x3D\x00\x04\x00\x06\x00\x00\x00\x30\x00\x00\x00\x2F\x00\x00\x00\x81\x00\x05\x00\x06\x00\x00\x00\x31\x00\x00\x00\x2D\x00\x00\x00" +"\x30\x00\x00\x00\x88\x00\x05\x00\x06\x00\x00\x00\x33\x00\x00\x00\x31\x00\x00\x00\x32\x00\x00\x00\x41\x00\x05\x00\x26\x00\x00\x00" +"\x34\x00\x00\x00\x0C\x00\x00\x00\x2B\x00\x00\x00\x3E\x00\x03\x00\x34\x00\x00\x00\x33\x00\x00\x00\x3D\x00\x04\x00\x07\x00\x00\x00" +"\x35\x00\x00\x00\x0C\x00\x00\x00\xFE\x00\x02\x00\x35\x00\x00\x00\x38\x00\x01\x00\x03\x02\x23\x07\x00\x00\x01\x00\x07\x00\x08\x00" +"\x72\x00\x00\x00\x00\x00\x00\x00\x11\x00\x02\x00\x01\x00\x00\x00\x0B\x00\x06\x00\x01\x00\x00\x00\x47\x4C\x53\x4C\x2E\x73\x74\x64" +"\x2E\x34\x35\x30\x00\x00\x00\x00\x0E\x00\x03\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0F\x00\x08\x00\x04\x00\x00\x00\x04\x00\x00\x00" +"\x6D\x61\x69\x6E\x00\x00\x00\x00\x27\x00\x00\x00\x62\x00\x00\x00\x66\x00\x00\x00\x10\x00\x03\x00\x04\x00\x00\x00\x07\x00\x00\x00" +"\x03\x00\x03\x00\x02\x00\x00\x00\xC2\x01\x00\x00\x05\x00\x04\x00\x04\x00\x00\x00\x6D\x61\x69\x6E\x00\x00\x00\x00\x05\x00\x05\x00" +"\x0B\x00\x00\x00\x66\x6F\x67\x33\x28\x76\x66\x33\x3B\x00\x00\x00\x05\x00\x06\x00\x0A\x00\x00\x00\x72\x65\x67\x75\x6C\x61\x72\x63" +"\x6F\x6C\x6F\x75\x72\x00\x00\x00\x05\x00\x04\x00\x0E\x00\x00\x00\x5F\x46\x4F\x47\x00\x00\x00\x00\x05\x00\x03\x00\x18\x00\x00\x00" +"\x7A\x00\x00\x00\x05\x00\x05\x00\x1F\x00\x00\x00\x65\x6E\x74\x69\x74\x79\x62\x6C\x6F\x63\x6B\x00\x06\x00\x07\x00\x1F\x00\x00\x00" +"\x00\x00\x00\x00\x6D\x5F\x6D\x6F\x64\x65\x6C\x76\x69\x65\x77\x70\x72\x6F\x6A\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x01\x00\x00\x00" +"\x6D\x5F\x6D\x6F\x64\x65\x6C\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x6D\x5F\x6D\x6F\x64\x65\x6C\x69\x6E\x76\x00\x00" +"\x06\x00\x06\x00\x1F\x00\x00\x00\x03\x00\x00\x00\x65\x5F\x65\x79\x65\x70\x6F\x73\x00\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00" +"\x04\x00\x00\x00\x65\x5F\x74\x69\x6D\x65\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x05\x00\x00\x00\x65\x5F\x6C\x69\x67\x68\x74\x5F" +"\x61\x6D\x62\x69\x65\x6E\x74\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x06\x00\x00\x00\x65\x70\x61\x64\x31\x00\x00\x00\x06\x00\x06\x00" +"\x1F\x00\x00\x00\x07\x00\x00\x00\x65\x5F\x6C\x69\x67\x68\x74\x5F\x64\x69\x72\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x08\x00\x00\x00" +"\x65\x70\x61\x64\x32\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x09\x00\x00\x00\x65\x5F\x6C\x69\x67\x68\x74\x5F\x6D\x75\x6C\x00" +"\x06\x00\x05\x00\x1F\x00\x00\x00\x0A\x00\x00\x00\x65\x70\x61\x64\x33\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x0B\x00\x00\x00" +"\x65\x5F\x6C\x6D\x73\x63\x61\x6C\x65\x73\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x0C\x00\x00\x00\x65\x5F\x75\x70\x70\x65\x72\x63" +"\x6F\x6C\x6F\x75\x72\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x0D\x00\x00\x00\x65\x70\x61\x64\x34\x00\x00\x00\x06\x00\x07\x00" +"\x1F\x00\x00\x00\x0E\x00\x00\x00\x65\x5F\x6C\x6F\x77\x65\x72\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00" +"\x0F\x00\x00\x00\x65\x70\x61\x64\x35\x00\x00\x00\x06\x00\x06\x00\x1F\x00\x00\x00\x10\x00\x00\x00\x65\x5F\x67\x6C\x6F\x77\x6D\x6F" +"\x64\x00\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x11\x00\x00\x00\x65\x70\x61\x64\x36\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00" +"\x12\x00\x00\x00\x65\x5F\x63\x6F\x6C\x6F\x75\x72\x69\x64\x65\x6E\x74\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x13\x00\x00\x00" +"\x77\x5F\x66\x6F\x67\x63\x6F\x6C\x6F\x75\x72\x73\x00\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x14\x00\x00\x00\x77\x5F\x66\x6F" +"\x67\x64\x65\x6E\x73\x69\x74\x79\x00\x00\x00\x00\x06\x00\x07\x00\x1F\x00\x00\x00\x15\x00\x00\x00\x77\x5F\x66\x6F\x67\x64\x65\x70" +"\x74\x68\x62\x69\x61\x73\x00\x00\x06\x00\x05\x00\x1F\x00\x00\x00\x16\x00\x00\x00\x65\x70\x61\x64\x37\x00\x00\x00\x05\x00\x03\x00" +"\x21\x00\x00\x00\x00\x00\x00\x00\x05\x00\x06\x00\x27\x00\x00\x00\x67\x6C\x5F\x46\x72\x61\x67\x43\x6F\x6F\x72\x64\x00\x00\x00\x00" +"\x05\x00\x07\x00\x38\x00\x00\x00\x5F\x63\x76\x61\x72\x5F\x72\x5F\x66\x6F\x67\x5F\x65\x78\x70\x32\x00\x00\x00\x00\x05\x00\x03\x00" +"\x3F\x00\x00\x00\x66\x61\x63\x00\x05\x00\x04\x00\x5B\x00\x00\x00\x73\x6B\x79\x62\x6F\x78\x00\x00\x05\x00\x06\x00\x5F\x00\x00\x00" +"\x73\x5F\x72\x65\x66\x6C\x65\x63\x74\x63\x75\x62\x65\x00\x00\x00\x05\x00\x03\x00\x62\x00\x00\x00\x70\x6F\x73\x00\x05\x00\x05\x00" +"\x66\x00\x00\x00\x6F\x75\x74\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x05\x00\x04\x00\x67\x00\x00\x00\x70\x61\x72\x61\x6D\x00\x00\x00" +"\x05\x00\x05\x00\x6F\x00\x00\x00\x6C\x69\x67\x68\x74\x62\x6C\x6F\x63\x6B\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x00\x00\x00\x00" +"\x6C\x5F\x63\x75\x62\x65\x6D\x61\x74\x72\x69\x78\x00\x00\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x01\x00\x00\x00\x6C\x5F\x6C\x69" +"\x67\x68\x74\x70\x6F\x73\x69\x74\x69\x6F\x6E\x00\x06\x00\x05\x00\x6F\x00\x00\x00\x02\x00\x00\x00\x6C\x70\x61\x64\x31\x00\x00\x00" +"\x06\x00\x07\x00\x6F\x00\x00\x00\x03\x00\x00\x00\x6C\x5F\x6C\x69\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x00\x00\x00\x06\x00\x05\x00" +"\x6F\x00\x00\x00\x04\x00\x00\x00\x6C\x70\x61\x64\x32\x00\x00\x00\x06\x00\x08\x00\x6F\x00\x00\x00\x05\x00\x00\x00\x6C\x5F\x6C\x69" +"\x67\x68\x74\x63\x6F\x6C\x6F\x75\x72\x73\x63\x61\x6C\x65\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x06\x00\x00\x00\x6C\x5F\x6C\x69" +"\x67\x68\x74\x72\x61\x64\x69\x75\x73\x00\x00\x00\x06\x00\x07\x00\x6F\x00\x00\x00\x07\x00\x00\x00\x6C\x5F\x73\x68\x61\x64\x6F\x77" +"\x6D\x61\x70\x70\x72\x6F\x6A\x00\x06\x00\x08\x00\x6F\x00\x00\x00\x08\x00\x00\x00\x6C\x5F\x73\x68\x61\x64\x6F\x77\x6D\x61\x70\x73" +"\x63\x61\x6C\x65\x00\x00\x00\x00\x06\x00\x05\x00\x6F\x00\x00\x00\x09\x00\x00\x00\x6C\x70\x61\x64\x33\x00\x00\x00\x05\x00\x03\x00" +"\x71\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x0E\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x47\x00\x04\x00\x1D\x00\x00\x00" +"\x06\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00\x1F\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" +"\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00" +"\x48\x00\x04\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x23\x00\x00\x00" +"\x40\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x04\x00\x1F\x00\x00\x00" +"\x02\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x23\x00\x00\x00\x80\x00\x00\x00\x48\x00\x05\x00" +"\x1F\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x03\x00\x00\x00\x23\x00\x00\x00" +"\xC0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00\xCC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" +"\x05\x00\x00\x00\x23\x00\x00\x00\xD0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x06\x00\x00\x00\x23\x00\x00\x00\xDC\x00\x00\x00" +"\x48\x00\x05\x00\x1F\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\xE0\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x08\x00\x00\x00" +"\x23\x00\x00\x00\xEC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x09\x00\x00\x00\x23\x00\x00\x00\xF0\x00\x00\x00\x48\x00\x05\x00" +"\x1F\x00\x00\x00\x0A\x00\x00\x00\x23\x00\x00\x00\xFC\x00\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0B\x00\x00\x00\x23\x00\x00\x00" +"\x00\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0C\x00\x00\x00\x23\x00\x00\x00\x40\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" +"\x0D\x00\x00\x00\x23\x00\x00\x00\x4C\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x0E\x00\x00\x00\x23\x00\x00\x00\x50\x01\x00\x00" +"\x48\x00\x05\x00\x1F\x00\x00\x00\x0F\x00\x00\x00\x23\x00\x00\x00\x5C\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x10\x00\x00\x00" +"\x23\x00\x00\x00\x60\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x11\x00\x00\x00\x23\x00\x00\x00\x6C\x01\x00\x00\x48\x00\x05\x00" +"\x1F\x00\x00\x00\x12\x00\x00\x00\x23\x00\x00\x00\x70\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x13\x00\x00\x00\x23\x00\x00\x00" +"\x80\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x14\x00\x00\x00\x23\x00\x00\x00\x90\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00" +"\x15\x00\x00\x00\x23\x00\x00\x00\x94\x01\x00\x00\x48\x00\x05\x00\x1F\x00\x00\x00\x16\x00\x00\x00\x23\x00\x00\x00\x98\x01\x00\x00" +"\x47\x00\x03\x00\x1F\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x21\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00" +"\x21\x00\x00\x00\x21\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x27\x00\x00\x00\x0B\x00\x00\x00\x0F\x00\x00\x00\x47\x00\x04\x00" +"\x38\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x47\x00\x04\x00\x5F\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00" +"\x5F\x00\x00\x00\x21\x00\x00\x00\x02\x00\x00\x00\x47\x00\x04\x00\x62\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00" +"\x66\x00\x00\x00\x1E\x00\x00\x00\x00\x00\x00\x00\x48\x00\x04\x00\x6F\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x48\x00\x05\x00" +"\x6F\x00\x00\x00\x00\x00\x00\x00\x23\x00\x00\x00\x00\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00" +"\x10\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x01\x00\x00\x00\x23\x00\x00\x00\x40\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00" +"\x02\x00\x00\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x03\x00\x00\x00\x23\x00\x00\x00\x50\x00\x00\x00" +"\x48\x00\x05\x00\x6F\x00\x00\x00\x04\x00\x00\x00\x23\x00\x00\x00\x5C\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x05\x00\x00\x00" +"\x23\x00\x00\x00\x60\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x06\x00\x00\x00\x23\x00\x00\x00\x6C\x00\x00\x00\x48\x00\x05\x00" +"\x6F\x00\x00\x00\x07\x00\x00\x00\x23\x00\x00\x00\x70\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x08\x00\x00\x00\x23\x00\x00\x00" +"\x80\x00\x00\x00\x48\x00\x05\x00\x6F\x00\x00\x00\x09\x00\x00\x00\x23\x00\x00\x00\x88\x00\x00\x00\x47\x00\x03\x00\x6F\x00\x00\x00" +"\x02\x00\x00\x00\x47\x00\x04\x00\x71\x00\x00\x00\x22\x00\x00\x00\x00\x00\x00\x00\x47\x00\x04\x00\x71\x00\x00\x00\x21\x00\x00\x00" +"\x01\x00\x00\x00\x13\x00\x02\x00\x02\x00\x00\x00\x21\x00\x03\x00\x03\x00\x00\x00\x02\x00\x00\x00\x16\x00\x03\x00\x06\x00\x00\x00" +"\x20\x00\x00\x00\x17\x00\x04\x00\x07\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x20\x00\x04\x00\x08\x00\x00\x00\x07\x00\x00\x00" +"\x07\x00\x00\x00\x21\x00\x04\x00\x09\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\x15\x00\x04\x00\x0D\x00\x00\x00\x20\x00\x00\x00" +"\x01\x00\x00\x00\x32\x00\x04\x00\x0D\x00\x00\x00\x0E\x00\x00\x00\x15\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00\x0F\x00\x00\x00" +"\x00\x00\x00\x00\x14\x00\x02\x00\x10\x00\x00\x00\x34\x00\x06\x00\x10\x00\x00\x00\x11\x00\x00\x00\xAB\x00\x00\x00\x0E\x00\x00\x00" +"\x0F\x00\x00\x00\x34\x00\x05\x00\x10\x00\x00\x00\x12\x00\x00\x00\xA8\x00\x00\x00\x11\x00\x00\x00\x20\x00\x04\x00\x17\x00\x00\x00" +"\x07\x00\x00\x00\x06\x00\x00\x00\x17\x00\x04\x00\x19\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x18\x00\x04\x00\x1A\x00\x00\x00" +"\x19\x00\x00\x00\x04\x00\x00\x00\x15\x00\x04\x00\x1B\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x2B\x00\x04\x00\x1B\x00\x00\x00" +"\x1C\x00\x00\x00\x04\x00\x00\x00\x1C\x00\x04\x00\x1D\x00\x00\x00\x19\x00\x00\x00\x1C\x00\x00\x00\x17\x00\x04\x00\x1E\x00\x00\x00" +"\x06\x00\x00\x00\x02\x00\x00\x00\x1E\x00\x19\x00\x1F\x00\x00\x00\x1A\x00\x00\x00\x1A\x00\x00\x00\x1A\x00\x00\x00\x07\x00\x00\x00" +"\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x1D\x00\x00\x00" +"\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x19\x00\x00\x00\x19\x00\x00\x00" +"\x06\x00\x00\x00\x06\x00\x00\x00\x1E\x00\x00\x00\x20\x00\x04\x00\x20\x00\x00\x00\x02\x00\x00\x00\x1F\x00\x00\x00\x3B\x00\x04\x00" +"\x20\x00\x00\x00\x21\x00\x00\x00\x02\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00\x22\x00\x00\x00\x14\x00\x00\x00\x20\x00\x04\x00" +"\x23\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x20\x00\x04\x00\x26\x00\x00\x00\x01\x00\x00\x00\x19\x00\x00\x00\x3B\x00\x04\x00" +"\x26\x00\x00\x00\x27\x00\x00\x00\x01\x00\x00\x00\x2B\x00\x04\x00\x1B\x00\x00\x00\x28\x00\x00\x00\x02\x00\x00\x00\x20\x00\x04\x00" +"\x29\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x2B\x00\x04\x00\x1B\x00\x00\x00\x2D\x00\x00\x00\x03\x00\x00\x00\x2B\x00\x04\x00" +"\x06\x00\x00\x00\x31\x00\x00\x00\x00\x00\x00\x00\x2B\x00\x04\x00\x0D\x00\x00\x00\x33\x00\x00\x00\x15\x00\x00\x00\x32\x00\x04\x00" +"\x0D\x00\x00\x00\x38\x00\x00\x00\x00\x01\x00\x00\x34\x00\x06\x00\x10\x00\x00\x00\x39\x00\x00\x00\xAB\x00\x00\x00\x38\x00\x00\x00" +"\x0F\x00\x00\x00\x2B\x00\x04\x00\x06\x00\x00\x00\x41\x00\x00\x00\x3B\xAA\xB8\x3F\x2B\x00\x04\x00\x06\x00\x00\x00\x45\x00\x00\x00" +"\x00\x00\x80\x3F\x2B\x00\x04\x00\x0D\x00\x00\x00\x46\x00\x00\x00\x13\x00\x00\x00\x20\x00\x04\x00\x50\x00\x00\x00\x02\x00\x00\x00" +"\x19\x00\x00\x00\x20\x00\x04\x00\x5A\x00\x00\x00\x07\x00\x00\x00\x19\x00\x00\x00\x19\x00\x09\x00\x5C\x00\x00\x00\x06\x00\x00\x00" +"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x1B\x00\x03\x00\x5D\x00\x00\x00" +"\x5C\x00\x00\x00\x20\x00\x04\x00\x5E\x00\x00\x00\x00\x00\x00\x00\x5D\x00\x00\x00\x3B\x00\x04\x00\x5E\x00\x00\x00\x5F\x00\x00\x00" +"\x00\x00\x00\x00\x20\x00\x04\x00\x61\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x3B\x00\x04\x00\x61\x00\x00\x00\x62\x00\x00\x00" +"\x01\x00\x00\x00\x20\x00\x04\x00\x65\x00\x00\x00\x03\x00\x00\x00\x19\x00\x00\x00\x3B\x00\x04\x00\x65\x00\x00\x00\x66\x00\x00\x00" +"\x03\x00\x00\x00\x1E\x00\x0C\x00\x6F\x00\x00\x00\x1A\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x06\x00\x00\x00" +"\x07\x00\x00\x00\x06\x00\x00\x00\x19\x00\x00\x00\x1E\x00\x00\x00\x1E\x00\x00\x00\x20\x00\x04\x00\x70\x00\x00\x00\x02\x00\x00\x00" +"\x6F\x00\x00\x00\x3B\x00\x04\x00\x70\x00\x00\x00\x71\x00\x00\x00\x02\x00\x00\x00\x36\x00\x05\x00\x02\x00\x00\x00\x04\x00\x00\x00" +"\x00\x00\x00\x00\x03\x00\x00\x00\xF8\x00\x02\x00\x05\x00\x00\x00\x3B\x00\x04\x00\x5A\x00\x00\x00\x5B\x00\x00\x00\x07\x00\x00\x00" +"\x3B\x00\x04\x00\x08\x00\x00\x00\x67\x00\x00\x00\x07\x00\x00\x00\x3D\x00\x04\x00\x5D\x00\x00\x00\x60\x00\x00\x00\x5F\x00\x00\x00" +"\x3D\x00\x04\x00\x07\x00\x00\x00\x63\x00\x00\x00\x62\x00\x00\x00\x57\x00\x05\x00\x19\x00\x00\x00\x64\x00\x00\x00\x60\x00\x00\x00" +"\x63\x00\x00\x00\x3E\x00\x03\x00\x5B\x00\x00\x00\x64\x00\x00\x00\x3D\x00\x04\x00\x19\x00\x00\x00\x68\x00\x00\x00\x5B\x00\x00\x00" +"\x4F\x00\x08\x00\x07\x00\x00\x00\x69\x00\x00\x00\x68\x00\x00\x00\x68\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" +"\x3E\x00\x03\x00\x67\x00\x00\x00\x69\x00\x00\x00\x39\x00\x05\x00\x07\x00\x00\x00\x6A\x00\x00\x00\x0B\x00\x00\x00\x67\x00\x00\x00" +"\x51\x00\x05\x00\x06\x00\x00\x00\x6B\x00\x00\x00\x6A\x00\x00\x00\x00\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x6C\x00\x00\x00" +"\x6A\x00\x00\x00\x01\x00\x00\x00\x51\x00\x05\x00\x06\x00\x00\x00\x6D\x00\x00\x00\x6A\x00\x00\x00\x02\x00\x00\x00\x50\x00\x07\x00" +"\x19\x00\x00\x00\x6E\x00\x00\x00\x6B\x00\x00\x00\x6C\x00\x00\x00\x6D\x00\x00\x00\x45\x00\x00\x00\x3E\x00\x03\x00\x66\x00\x00\x00" +"\x6E\x00\x00\x00\xFD\x00\x01\x00\x38\x00\x01\x00\x36\x00\x05\x00\x07\x00\x00\x00\x0B\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00" +"\x37\x00\x03\x00\x08\x00\x00\x00\x0A\x00\x00\x00\xF8\x00\x02\x00\x0C\x00\x00\x00\x3B\x00\x04\x00\x17\x00\x00\x00\x18\x00\x00\x00" +"\x07\x00\x00\x00\x3B\x00\x04\x00\x17\x00\x00\x00\x3F\x00\x00\x00\x07\x00\x00\x00\xF7\x00\x03\x00\x14\x00\x00\x00\x00\x00\x00\x00" +"\xFA\x00\x04\x00\x12\x00\x00\x00\x13\x00\x00\x00\x14\x00\x00\x00\xF8\x00\x02\x00\x13\x00\x00\x00\x3D\x00\x04\x00\x07\x00\x00\x00" +"\x15\x00\x00\x00\x0A\x00\x00\x00\xFE\x00\x02\x00\x15\x00\x00\x00\xF8\x00\x02\x00\x14\x00\x00\x00\x41\x00\x05\x00\x23\x00\x00\x00" +"\x24\x00\x00\x00\x21\x00\x00\x00\x22\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x25\x00\x00\x00\x24\x00\x00\x00\x41\x00\x05\x00" +"\x29\x00\x00\x00\x2A\x00\x00\x00\x27\x00\x00\x00\x28\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x2B\x00\x00\x00\x2A\x00\x00\x00" +"\x85\x00\x05\x00\x06\x00\x00\x00\x2C\x00\x00\x00\x25\x00\x00\x00\x2B\x00\x00\x00\x41\x00\x05\x00\x29\x00\x00\x00\x2E\x00\x00\x00" +"\x27\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x2F\x00\x00\x00\x2E\x00\x00\x00\x88\x00\x05\x00\x06\x00\x00\x00" +"\x30\x00\x00\x00\x2C\x00\x00\x00\x2F\x00\x00\x00\x3E\x00\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00" +"\x32\x00\x00\x00\x18\x00\x00\x00\x41\x00\x05\x00\x23\x00\x00\x00\x34\x00\x00\x00\x21\x00\x00\x00\x33\x00\x00\x00\x3D\x00\x04\x00" +"\x06\x00\x00\x00\x35\x00\x00\x00\x34\x00\x00\x00\x83\x00\x05\x00\x06\x00\x00\x00\x36\x00\x00\x00\x32\x00\x00\x00\x35\x00\x00\x00" +"\x0C\x00\x07\x00\x06\x00\x00\x00\x37\x00\x00\x00\x01\x00\x00\x00\x28\x00\x00\x00\x31\x00\x00\x00\x36\x00\x00\x00\x3E\x00\x03\x00" +"\x18\x00\x00\x00\x37\x00\x00\x00\xF7\x00\x03\x00\x3B\x00\x00\x00\x00\x00\x00\x00\xFA\x00\x04\x00\x39\x00\x00\x00\x3A\x00\x00\x00" +"\x3B\x00\x00\x00\xF8\x00\x02\x00\x3A\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x3C\x00\x00\x00\x18\x00\x00\x00\x3D\x00\x04\x00" +"\x06\x00\x00\x00\x3D\x00\x00\x00\x18\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x3E\x00\x00\x00\x3D\x00\x00\x00\x3C\x00\x00\x00" +"\x3E\x00\x03\x00\x18\x00\x00\x00\x3E\x00\x00\x00\xF9\x00\x02\x00\x3B\x00\x00\x00\xF8\x00\x02\x00\x3B\x00\x00\x00\x3D\x00\x04\x00" +"\x06\x00\x00\x00\x40\x00\x00\x00\x18\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x42\x00\x00\x00\x40\x00\x00\x00\x41\x00\x00\x00" +"\x7F\x00\x04\x00\x06\x00\x00\x00\x43\x00\x00\x00\x42\x00\x00\x00\x0C\x00\x06\x00\x06\x00\x00\x00\x44\x00\x00\x00\x01\x00\x00\x00" +"\x1D\x00\x00\x00\x43\x00\x00\x00\x3E\x00\x03\x00\x3F\x00\x00\x00\x44\x00\x00\x00\x41\x00\x06\x00\x23\x00\x00\x00\x47\x00\x00\x00" +"\x21\x00\x00\x00\x46\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x48\x00\x00\x00\x47\x00\x00\x00\x83\x00\x05\x00" +"\x06\x00\x00\x00\x49\x00\x00\x00\x45\x00\x00\x00\x48\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x4A\x00\x00\x00\x3F\x00\x00\x00" +"\x0C\x00\x08\x00\x06\x00\x00\x00\x4B\x00\x00\x00\x01\x00\x00\x00\x2B\x00\x00\x00\x4A\x00\x00\x00\x31\x00\x00\x00\x45\x00\x00\x00" +"\x41\x00\x06\x00\x23\x00\x00\x00\x4C\x00\x00\x00\x21\x00\x00\x00\x46\x00\x00\x00\x2D\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00" +"\x4D\x00\x00\x00\x4C\x00\x00\x00\x85\x00\x05\x00\x06\x00\x00\x00\x4E\x00\x00\x00\x4B\x00\x00\x00\x4D\x00\x00\x00\x81\x00\x05\x00" +"\x06\x00\x00\x00\x4F\x00\x00\x00\x49\x00\x00\x00\x4E\x00\x00\x00\x3E\x00\x03\x00\x3F\x00\x00\x00\x4F\x00\x00\x00\x41\x00\x05\x00" +"\x50\x00\x00\x00\x51\x00\x00\x00\x21\x00\x00\x00\x46\x00\x00\x00\x3D\x00\x04\x00\x19\x00\x00\x00\x52\x00\x00\x00\x51\x00\x00\x00" +"\x4F\x00\x08\x00\x07\x00\x00\x00\x53\x00\x00\x00\x52\x00\x00\x00\x52\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" +"\x3D\x00\x04\x00\x07\x00\x00\x00\x54\x00\x00\x00\x0A\x00\x00\x00\x3D\x00\x04\x00\x06\x00\x00\x00\x55\x00\x00\x00\x3F\x00\x00\x00" +"\x50\x00\x06\x00\x07\x00\x00\x00\x56\x00\x00\x00\x55\x00\x00\x00\x55\x00\x00\x00\x55\x00\x00\x00\x0C\x00\x08\x00\x07\x00\x00\x00" +"\x57\x00\x00\x00\x01\x00\x00\x00\x2E\x00\x00\x00\x53\x00\x00\x00\x54\x00\x00\x00\x56\x00\x00\x00\xFE\x00\x02\x00\x57\x00\x00\x00" +"\x38\x00\x01\x00"}, #endif #ifdef D3D9QUAKE {QR_DIRECT3D9, 9, "defaultskybox", @@ -4953,7 +4949,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "float4 main (v2f inp) : COLOR0\n" "{\n" "float3 tc = inp.texc - e_eyepos.xyz;\n" -"tc.y = -tc.y;\n" "return texCUBE(s_reflectcube, tc);\n" "}\n" "#endif\n" @@ -4984,7 +4979,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "v2f outp;\n" "outp.pos = mul(m_model, inp.pos);\n" "outp.texc= outp.pos.xyz - v_eyepos;\n" -"outp.texc.y = -outp.texc.y;\n" "outp.pos = mul(m_view, outp.pos);\n" "outp.pos = mul(m_projection, outp.pos);\n" "return outp;\n" @@ -5642,7 +5636,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!samps =SPECULAR specular\n" "!!samps lightmap\n" "!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n" -"!!samps =DELUXE deluxmap\n" +"!!samps =DELUXE deluxemap\n" "!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\n" "!!samps =FAKESHADOWS shadowmap\n" diff --git a/engine/gl/shader.h b/engine/gl/shader.h index f03e22cb..f94b6fcc 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -719,7 +719,7 @@ shader_t *QDECL R_RegisterSkin (const char *shadername, const char *modname); shader_t *R_RegisterCustom (const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args); //once loaded, most shaders should have one of the following two calls used upon it void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader, unsigned int imageflags); -void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *mipdata[4], qbyte *palette); +void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *mipdata, qbyte *palette); void R_RemapShader(const char *sourcename, const char *destname, float timeoffset); cin_t *R_ShaderGetCinematic(shader_t *s); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index cc38423b..698108fc 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10624,7 +10624,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"changelevel", PF_changelevel, 70, 70, 70, 0, D("void(string mapname, optional string newmapstartspot)", "Attempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged.")}, //70 - {"cvar_set", PF_cvar_set, 72, 72, 72, 0, D("void(string cvarname, string valuetoset)", "Instantly sets a cvar to the given string value.")}, //72 + {"cvar_set", PF_cvar_set, 72, 72, 72, 0, D("void(string cvarname, string valuetoset)", "Instantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead.")}, //72 {"centerprint", PF_centerprint, 73, 73, 73, 0, "void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7)"}, //73 {"ambientsound", PF_ambientsound, 74, 74, 74, 0, "void (vector pos, string samp, float vol, float atten)"}, //74 @@ -10866,12 +10866,13 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"rotatevectorsbyvectors",PF_rotatevectorsbymatrix,0,0, 0, 236, "void(vector fwd, vector right, vector up)"}, // #236 {"skinforname", PF_skinforname, 0, 0, 0, 237, "float(float mdlindex, string skinname)"}, // #237 {"shaderforname", PF_Fixme, 0, 0, 0, 238, D("float(string shadername, optional string defaultshader, ...)", "Caches the named shader and returns a handle to it.\nIf the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used.\ndefaultshader if not empty should include the outer {} that you would ordinarily find in a shader.")}, + {"remapshader", PF_Fixme, 0, 0, 0, 0, D("void(string shadername, string replacement, float timeoffset)", "All surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted.")}, {"te_bloodqw", PF_te_bloodqw, 0, 0, 0, 239, "void(vector org, optional float count)"}, {"te_muzzleflash", PF_te_muzzleflash, 0, 0, 0, 0, "void(entity ent)"}, {"checkpvs", PF_checkpvs, 0, 0, 0, 240, "float(vector viewpos, entity entity)"}, {"matchclientname", PF_matchclient, 0, 0, 0, 241, "entity(string match, optional float matchnum)"}, - {"sendpacket", PF_SendPacket, 0, 0, 0, 242, D("void(string destaddress, string content)", "Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature.")},// (FTE_QC_SENDPACKET) + {"sendpacket", PF_SendPacket, 0, 0, 0, 242, D("float(string destaddress, string content)", "Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature.")},// (FTE_QC_SENDPACKET) // {"bulleten", PF_bulleten, 0, 0, 0, 243}, (removed builtin) @@ -12349,7 +12350,7 @@ void PR_DumpPlatform_f(void) {"CONTENTBIT_BODY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_BODY)"i"}, {"CONTENTBIT_CORPSE", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_CORPSE)"i"}, {"CONTENTBIT_Q2LADDER", "const int", QW|NQ|CS, D("Content bit specific to q2bsp"), 0,STRINGIFY(Q2CONTENTS_LADDER)"i"}, - {"CONTENTBIT_SKY", "const int", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SKY)"i"}, + {"CONTENTBIT_SKY", "const int", QW|NQ|CS, D("Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too."), 0,STRINGIFY(FTECONTENTS_SKY)"i"}, {"CONTENTBITS_POINTSOLID", "const int", QW|NQ|CS, D("Bits that traceline would normally consider solid"), 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"i|CONTENTBIT_BODY"}, {"CONTENTBITS_BOXSOLID", "const int", QW|NQ|CS, D("Bits that tracebox would normally consider solid"), 0,"CONTENTBIT_SOLID|"STRINGIFY(Q2CONTENTS_WINDOW)"i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP"}, {"CONTENTBITS_FLUID", "const int", QW|NQ|CS, NULL, 0,"CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY"}, diff --git a/engine/server/server.h b/engine/server/server.h index 11cced63..d49e2df3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -113,13 +113,15 @@ typedef struct qboolean csqcdebug; unsigned int csqcchecksum; qboolean mapchangelocked; + qboolean restarting; #ifdef SAVEDGAMES char loadgame_on_restart[MAX_QPATH]; //saved game to load on map_restart double autosave_time; #endif - double time; - double starttime; + double time; //current map time + double restartedtime; //sv.time from last map restart + double starttime; //system time we changed map. int framenum; int logindatabase; @@ -1283,8 +1285,10 @@ void SVQ2_BuildBaselines(void); //q3 stuff #ifdef Q3SERVER -void SVQ3_ShutdownGame(void); -qboolean SVQ3_InitGame(void); +void SVQ3_ShutdownGame(qboolean restarting); +qboolean SVQ3_InitGame(qboolean restarting); +void SVQ3_ServerinfoChanged(const char *key); +qboolean SVQ3_RestartGamecode(void); qboolean SVQ3_ConsoleCommand(void); qboolean SVQ3_HandleClient(void); void SVQ3_DirectConnect(void); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 65f0ad67..a1754e33 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -556,6 +556,13 @@ void SV_Map_f (void) if (!Q_strcasecmp(Cmd_Argv(0), "map_restart")) { const char *arg = Cmd_Argv(1); + +#ifdef Q3SERVER + if (sv.state==ss_active && svs.gametype==GT_QUAKE3) + if (SVQ3_RestartGamecode()) + return; +#endif + #ifdef SAVEDGAMES if (!strcmp(arg, "restore")) //hexen2 reload-saved-game ; @@ -800,6 +807,8 @@ void SV_Map_f (void) { cvar_t *gametype; + Cvar_ApplyLatches(CVAR_LATCH); + gametype = Cvar_Get("mapname", "", CVAR_LATCH|CVAR_SERVERINFO, "Q3 compatability"); gametype->flags |= CVAR_SERVERINFO; Cvar_ForceSet(gametype, level); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 62bc1e68..85951e50 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -625,7 +625,7 @@ void SV_UnspawnServer (void) //terminate the running server. } PR_Deinit(); #ifdef Q3SERVER - SVQ3_ShutdownGame(); + SVQ3_ShutdownGame(false); #endif #ifdef Q2SERVER SVQ2_ShutdownGameProgs(); @@ -921,7 +921,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) - SVQ3_ShutdownGame(); //botlib kinda mandates this. :( + SVQ3_ShutdownGame(false); //botlib kinda mandates this. :( #endif Mod_ClearAll (); @@ -981,7 +981,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, CL_CheckServerInfo(); #endif - + sv.restarting = false; sv.state = ss_loading; #if defined(Q2BSPS) if (usecinematic) @@ -1132,7 +1132,7 @@ MSV_OpenUserDatabase(); else #endif #ifdef Q3SERVER - if (SVQ3_InitGame()) + if (SVQ3_InitGame(false)) newgametype = GT_QUAKE3; else #endif @@ -1167,7 +1167,7 @@ MSV_OpenUserDatabase(); #endif #ifdef Q3SERVER if (newgametype != GT_QUAKE3) - SVQ3_ShutdownGame(); + SVQ3_ShutdownGame(false); #endif #ifdef Q2SERVER if (newgametype != GT_QUAKE2) //we don't want the q2 stuff anymore. @@ -1380,7 +1380,7 @@ MSV_OpenUserDatabase(); #endif #ifdef Q3SERVER case GT_QUAKE3: - SV_UpdateMaxPlayers(32); + SV_UpdateMaxPlayers(max(8,maxclients.ival)); break; #endif #ifdef HLSERVER diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 16265797..e0c25bb0 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -111,7 +111,15 @@ extern cvar_t sv_allow_splitscreen; #ifdef SUPPORT_ICE static void QDECL SV_Public_Callback(struct cvar_s *var, char *oldvalue) { - if (var->ival == 2) + char name[64], *e; + COM_ParseOut(var->string, name, sizeof(name)); + strtol(name, &e, 0); + if (*name&&e==name) //failed to read any number out of it. + { + FTENET_AddToCollection(svs.sockets, var->name, va("/%s", name), NA_INVALID, NP_RTC_TLS); + var->value = var->ival = 2; //so other stuff sees us as holepunched. + } + else if (var->ival == 2) FTENET_AddToCollection(svs.sockets, var->name, "/", NA_INVALID, NP_RTC_TLS); else FTENET_AddToCollection(svs.sockets, var->name, "", NA_INVALID, NP_INVALID); @@ -5281,6 +5289,11 @@ float SV_Frame (void) static void SV_InfoChanged(void *context, const char *key) { size_t i; + +#ifdef Q3SERVER + SVQ3_ServerinfoChanged(key); +#endif + if (context != &svs.info && *key == '_') return; //these keys are considered private to originating client/server, and are not broadcast to anyone else diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index b5839473..5567172d 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1941,6 +1941,18 @@ void SVQW_Spawn_f (void) // when that is completed, a begin command will be issued ClientReliableWrite_Begin (host_client, svc_stufftext, 8); ClientReliableWrite_String (host_client, "skins\n" ); + + if (sv.allocated_client_slots > 1) + { //okay, so nq player physics don't suppot prediction. + //if we use qw physics in nq mods then we risk breaking things. + //the only progs many players will have is the vanilla nq one. + //so prediction is broken on most people's quicky servers. + //which really sucks. + //so let multiplayer people know what's going on so that they don't think its an actual bug, and can harass the admin to get it fixed in mods that allow for it. + if (!strcmp(sv_nqplayerphysics.string, "auto") || !strcmp(sv_nqplayerphysics.string, "")) + if (sv_nqplayerphysics.ival) + SV_PrintToClient(host_client, PRINT_HIGH, CON_WARNING"Movement prediction is disabled in favour of non-quakeworld mod compatibilty\n"); + } } /* diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 2c8ffd03..a18c581b 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -61,6 +61,7 @@ static vm_t *q3gamevm; #define MAX_CONFIGSTRINGS 1024 static char *svq3_configstrings[MAX_CONFIGSTRINGS]; +static qboolean q3_serverinfo_dirty; static q3sharedEntity_t *q3_entarray; static int numq3entities; static int sizeofq3gentity; @@ -799,14 +800,21 @@ void SVQ3_SendConfigString(client_t *dest, int num, char *string) void SVQ3_SetConfigString(int num, char *string) { int len; + if (num < 0 || num >= MAX_Q3_CONFIGSTRINGS) + return; //no exploits please + if (!string) string = ""; + if (!strcmp(svq3_configstrings[num]?svq3_configstrings[num]:"", string)) + return; len = strlen(string); if (svq3_configstrings[num]) Z_Free(svq3_configstrings[num]); svq3_configstrings[num] = Z_Malloc(len+1); strcpy(svq3_configstrings[num], string); + if (sv.state == ss_loading && !sv.restarting) + return; //don't spam these, the svcq3_gamestate will have a copy anyway and the gamecode can get confused. SVQ3_SendConfigString(NULL, num, string); } @@ -1676,33 +1684,37 @@ static qintptr_t EXPORT_FN Q3G_SystemCallsNative(qintptr_t arg, ...) return Q3G_SystemCalls(NULL, ~(quintptr_t)0, arg, args); } -void SVQ3_ShutdownGame(void) +void SVQ3_ShutdownGame(qboolean restarting) { int i; if (!q3gamevm) return; + + if (!restarting) + { #ifdef USEBOTLIB - if (botlib) - { //it crashes otherwise, probably due to our huck clearage - botlib->BotLibShutdown(); - Z_FreeTags(Z_TAG_BOTLIB); - } + if (botlib) + { //it crashes otherwise, probably due to our huck clearage + botlib->BotLibShutdown(); + Z_FreeTags(Z_TAG_BOTLIB); + } #endif - for (i = 0; i < MAX_CONFIGSTRINGS; i++) - { - if (svq3_configstrings[i]) + for (i = 0; i < MAX_CONFIGSTRINGS; i++) { - Z_Free(svq3_configstrings[i]); - svq3_configstrings[i] = NULL; + if (svq3_configstrings[i]) + { + Z_Free(svq3_configstrings[i]); + svq3_configstrings[i] = NULL; + } } + + Z_Free(q3_sentities); + q3_sentities = NULL; + BZ_Free(q3_snapshot_entities); + q3_snapshot_entities = NULL; } - Z_Free(q3_sentities); - q3_sentities = NULL; - BZ_Free(q3_snapshot_entities); - q3_snapshot_entities = NULL; - VM_Destroy(q3gamevm); q3gamevm = NULL; @@ -1926,12 +1938,27 @@ static void SV_InitBotLib(void) #endif } -qboolean SVQ3_InitGame(void) +void SVQ3_ServerinfoChanged(const char *key) +{ //roll up multiple updates into a single splurge. SVQ3_UpdateServerinfo will be called after the next frame. + q3_serverinfo_dirty = true; +} +static void SVQ3_UpdateServerinfo(void) +{ + char buffer[8192]; + /*update serverinfo - qw serverinfo settings are not normally visible in the q3 serverinfo, so strip them from the configstring*/ + static const char *ignores[] = {"maxclients", "map", "sv_maxclients", "*z_ext", "*bspversion", "*gamedir", NULL}; + extern cvar_t maxclients; + InfoBuf_ToString(&svs.info, buffer, sizeof(buffer), NULL, ignores, NULL, NULL, NULL); + //add in maxclients.. the q3 version + Q_strncatz(buffer, va("\\sv_maxclients\\%s", maxclients.string), sizeof(buffer)); + SVQ3_SetConfigString(0, buffer); + + q3_serverinfo_dirty = false; +} + +qboolean SVQ3_InitGame(qboolean restart) { int i; - char buffer[8192]; - char *str; - char sysinfo[8192]; if (sv.world.worldmodel->type == mod_heightmap) { @@ -1946,7 +1973,7 @@ qboolean SVQ3_InitGame(void) return false; - SVQ3_ShutdownGame(); + SVQ3_ShutdownGame(restart); q3gamevm = VM_Create("qagame", com_nogamedirnativecode.ival?NULL:Q3G_SystemCallsNative, "vm/qagame", Q3G_SystemCallsVM); @@ -1965,68 +1992,67 @@ qboolean SVQ3_InitGame(void) q3_sentities = Z_Malloc(sizeof(q3serverEntity_t)*MAX_GENTITIES); - { /*qw serverinfo settings are not normally visible in the q3 serverinfo, so strip them from the configstring*/ - static const char *ignores[] = {"maxclients", "map", "sv_maxclients", "*z_ext", "*bspversion", "*gamedir", NULL}; - extern cvar_t maxclients; - InfoBuf_ToString(&svs.info, buffer, sizeof(buffer), NULL, ignores, NULL, NULL, NULL); - //add in maxclients.. the q3 version - Q_strncatz(buffer, va("\\sv_maxclients\\%s", maxclients.string), sizeof(buffer)); - } - SVQ3_SetConfigString(0, buffer); - Cvar_Set(Cvar_Get("sv_running", "0", 0, "Q3 compatability"), "1"); - sysinfo[0] = '\0'; - Info_SetValueForKey(sysinfo, "sv_serverid", va("%i", svs.spawncount), MAX_SERVERINFO_STRING); + if (!restart) + { + char buffer[8192]; + char sysinfo[8192]; + /*update the system info*/ + sysinfo[0] = '\0'; + Info_SetValueForKey(sysinfo, "sv_serverid", va("%i", svs.spawncount), MAX_SERVERINFO_STRING); - str = FS_GetPackHashes(buffer, sizeof(buffer), false); - Info_SetValueForKey(sysinfo, "sv_paks", str, MAX_SERVERINFO_STRING); - - str = FS_GetPackNames(buffer, sizeof(buffer), false, false); - Info_SetValueForKey(sysinfo, "sv_pakNames", str, MAX_SERVERINFO_STRING); - - str = FS_GetPackHashes(buffer, sizeof(buffer), true); - Info_SetValueForKey(sysinfo, "sv_referencedPaks", str, MAX_SERVERINFO_STRING); - - str = FS_GetPackNames(buffer, sizeof(buffer), true, false); - Info_SetValueForKey(sysinfo, "sv_referencedPakNames", str, MAX_SERVERINFO_STRING); - - Info_SetValueForKey(sysinfo, "sv_pure", sv_pure.string, MAX_SERVERINFO_STRING); - - SVQ3_SetConfigString(1, sysinfo); + Info_SetValueForKey(sysinfo, "sv_paks", FS_GetPackHashes(buffer, sizeof(buffer), false ), MAX_SERVERINFO_STRING); + Info_SetValueForKey(sysinfo, "sv_pakNames", FS_GetPackNames (buffer, sizeof(buffer), false, false), MAX_SERVERINFO_STRING); + Info_SetValueForKey(sysinfo, "sv_referencedPaks", FS_GetPackHashes(buffer, sizeof(buffer), true ), MAX_SERVERINFO_STRING); + Info_SetValueForKey(sysinfo, "sv_referencedPakNames", FS_GetPackNames (buffer, sizeof(buffer), true, false ), MAX_SERVERINFO_STRING); + Info_SetValueForKey(sysinfo, "sv_pure", sv_pure.string, MAX_SERVERINFO_STRING); + SVQ3_SetConfigString(1, sysinfo); + q3_serverinfo_dirty = true; + } mapentspointer = Mod_GetEntitiesString(sv.world.worldmodel); - VM_Call(q3gamevm, GAME_INIT, 0, (int)rand(), false); + VM_Call(q3gamevm, GAME_INIT, (intptr_t)(sv.time*1000), (int)rand(), restart); CM_InitBoxHull(); - SVQ3_CreateBaseline(); + if (!restart) + { + SVQ3_CreateBaseline(); - q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32; - if (q3_snapshot_entities) - BZ_Free(q3_snapshot_entities); - q3_next_snapshot_entities = 0; - q3_snapshot_entities = BZ_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities); + q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32; + if (q3_snapshot_entities) + BZ_Free(q3_snapshot_entities); + q3_next_snapshot_entities = 0; + q3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities); + } - - // run a few frames to allow everything to settle + // run a few frames to allow everything to settle for (i = 0; i < 3; i++) { SVQ3_RunFrame(); sv.time += 0.1; } +#ifdef HAVE_CLIENT + //there's a whole load of ugly debug crap there. make sure it stays hidden. + Con_ClearNotify(); +#endif + return true; } void SVQ3_RunFrame(void) { - VM_Call(q3gamevm, GAME_RUN_FRAME, (int)(sv.time*1000)); #ifdef USEBOTLIB if (botlib) VM_Call(q3gamevm, BOTAI_START_FRAME, (int)(sv.time*1000)); #endif + VM_Call(q3gamevm, GAME_RUN_FRAME, (int)(sv.time*1000)); + + if (q3_serverinfo_dirty) + SVQ3_UpdateServerinfo(); } void SVQ3_ClientCommand(client_t *cl) @@ -2038,6 +2064,7 @@ void SVQ3_ClientBegin(client_t *cl) { VM_Call(q3gamevm, GAME_CLIENT_BEGIN, (int)(cl-svs.clients)); sv.spawned_client_slots++; + cl->spawned = true; } void SVQ3_ClientThink(client_t *cl) @@ -2464,7 +2491,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) { q3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32; q3_next_snapshot_entities = 0; - q3_snapshot_entities = BZ_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities); + q3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities); } clientNum = client - svs.clients; @@ -2484,7 +2511,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) // this is the frame we are creating snap = &client->frameunion.q3frames[client->netchan.outgoing_sequence & Q3UPDATE_MASK]; - snap->serverTime = sv.time*1000;//svs.levelTime; // save it for ping calc later + snap->serverTime = sv.restartedtime*1000 + sv.time*1000;//svs.levelTime; // save it for ping calc later snap->flags = 0; if( client->state < cs_spawned ) @@ -2818,8 +2845,8 @@ void SVQ3_SendGameState(client_t *client) // write baselines for( i=0; inetchan)) { return false; } +#ifndef Q3_NOENCRYPT // archive buffer state bit = net_message.currentbit; readcount = msg_readcount; @@ -2965,7 +2995,6 @@ static qboolean SVQ3_Netchan_Process(client_t *client) bitmask = (serverid ^ lastSequence ^ client->challenge) & 0xff; string = client->server_commands[lastServerCommandNum & Q3TEXTCMD_MASK]; -#ifndef Q3_NOENCRYPT // decrypt the packet for(i=msg_readcount+12,j=0; inetchan.outgoing_sequence ^ client->challenge) & 0xff; string = client->last_client_command; -#ifndef Q3_NOENCRYPT //first four bytes are not encrypted. for(i=0; i<4 ; i++) buffer[i] = data[i]; @@ -3145,7 +3175,7 @@ void SVQ3_UpdateUserinfo_f(client_t *cl) InfoBuf_FromString(&cl->userinfo, Cmd_Argv(1), false); SV_ExtractFromUserinfo (cl, true); - if (svs.gametype == GT_QUAKE3) + if (svs.gametype == GT_QUAKE3 && cl->spawned) VM_Call(q3gamevm, GAME_CLIENT_USERINFO_CHANGED, (int)(cl-svs.clients)); } @@ -3385,6 +3415,69 @@ qboolean SVQ3_HandleClient(void) SVQ3_ParseClientMessage(&svs.clients[i]); return true; } + +//Q3 gamecode does map_restart weirdly. +//it simply reloads the gamecode without changing any maps/models/sounds +//this won't work for q1/q2, but q3 expects it. +//note that time continues from its prior value without any kind of reset. +qboolean SVQ3_RestartGamecode(void) +{ + int i; + extern cvar_t maxclients; + int newmaxclients = max(8,maxclients.ival); + if (sv.allocated_client_slots != newmaxclients) + return false; //can't do it if maxclients needs to change. + + Cvar_ApplyLatches(CVAR_LATCH); + + //reload the gamecode + sv.state = ss_loading; + sv.restarting = true; + if (!SVQ3_InitGame(true)) + return false; + +// svs.spawncount++; //so new snapshots get sent + sv.restartedtime = 0; + + //and then reconnect the players as appropriate +// SVQ3_NewMapConnects(); + + for (i = 0; i < sv.allocated_client_slots; i++) + { + if (svs.clients[i].state < cs_connected) + continue; + + if (VM_Call(q3gamevm, GAME_CLIENT_CONNECT, i, false, svs.clients[i].protocol == SCP_BAD)) + { + SV_DropClient(&svs.clients[i]); + continue; + } + if (svs.clients[i].spawned) + { + sv.spawned_client_slots--; + SVQ3_ClientBegin(&svs.clients[i]); + } + } + + sv.starttime = Sys_DoubleTime() - sv.time; +#ifdef SAVEDGAMES + sv.autosave_time = sv.time + sv_autosave.value*60; +#endif + + //basically done + sv.state = ss_active; + sv.restarting = false; + + //and an extra physics frame for luck + sv.time+=0.1; + sv.world.physicstime=sv.time; + SVQ3_RunFrame(); + + SVQ3_SendServerCommand(NULL, "map_restart"); + + return true; //yup, we did it. +} + void SVQ3_NewMapConnects(void) { int i; @@ -3467,10 +3560,13 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and else { InfoBuf_FromString(&cl->userinfo, userinfo, false); - reason = NET_AdrToString(adr, sizeof(adr), &net_from); + if (net_from.type == NA_LOOPBACK) + reason = "localhost"; //Q3 uses this specific string for listen servers. + else + reason = NET_AdrToString(adr, sizeof(adr), &net_from); InfoBuf_SetKey(&cl->userinfo, "ip", reason); //q3 gamecode needs to know the client's ip (server's perception of the client, NOT QW client's perception of the server/proxy) - ret = VM_Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), false, false); + ret = VM_Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), true, false); if (!ret) reason = NULL; else diff --git a/engine/shaders/glsl/defaultskybox.glsl b/engine/shaders/glsl/defaultskybox.glsl index f93cd30b..ab582dbd 100644 --- a/engine/shaders/glsl/defaultskybox.glsl +++ b/engine/shaders/glsl/defaultskybox.glsl @@ -24,7 +24,6 @@ mat3 rotateAroundAxis(vec4 axis) //xyz axis, with angle in w void main () { pos = v_position.xyz - e_eyepos; - pos.y = -pos.y; if (r_glsl_skybox_orientation.xyz != vec3(0.0)) pos = pos*rotateAroundAxis(r_glsl_skybox_orientation); diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index c71cf930..15ee7817 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -20,7 +20,7 @@ !!samps =SPECULAR specular !!samps lightmap !!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxmap +!!samps =DELUXE deluxemap !!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 !!samps =FAKESHADOWS shadowmap diff --git a/engine/shaders/hlsl11/defaultskybox.hlsl b/engine/shaders/hlsl11/defaultskybox.hlsl index 55fb3267..afa7dfa4 100644 --- a/engine/shaders/hlsl11/defaultskybox.hlsl +++ b/engine/shaders/hlsl11/defaultskybox.hlsl @@ -21,7 +21,6 @@ struct v2f v2f outp; outp.pos = mul(m_model, inp.pos); outp.texc= outp.pos.xyz - v_eyepos; - outp.texc.y = -outp.texc.y; outp.pos = mul(m_view, outp.pos); outp.pos = mul(m_projection, outp.pos); return outp; diff --git a/engine/shaders/hlsl9/defaultskybox.hlsl b/engine/shaders/hlsl9/defaultskybox.hlsl index 3e3e14bf..e6a2173b 100644 --- a/engine/shaders/hlsl9/defaultskybox.hlsl +++ b/engine/shaders/hlsl9/defaultskybox.hlsl @@ -29,7 +29,6 @@ float4 main (v2f inp) : COLOR0 { float3 tc = inp.texc - e_eyepos.xyz; - tc.y = -tc.y; return texCUBE(s_reflectcube, tc); } #endif diff --git a/engine/shaders/vulkan/defaultskybox.glsl b/engine/shaders/vulkan/defaultskybox.glsl index 29882a0c..32832658 100644 --- a/engine/shaders/vulkan/defaultskybox.glsl +++ b/engine/shaders/vulkan/defaultskybox.glsl @@ -11,7 +11,6 @@ layout(location=0) varying vec3 pos; void main () { pos = v_position.xyz - e_eyepos; - pos.y = -pos.y; gl_Position = ftetransform(); } #endif diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 96f2dec4..6c1b7dbb 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1381,10 +1381,10 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay case PTI_BC2_RGBA_SRGB: format = VK_FORMAT_BC2_SRGB_BLOCK; break; case PTI_BC3_RGBA: format = VK_FORMAT_BC3_UNORM_BLOCK; break; case PTI_BC3_RGBA_SRGB: format = VK_FORMAT_BC3_SRGB_BLOCK; break; - case PTI_BC4_R8: format = VK_FORMAT_BC4_UNORM_BLOCK; break; - case PTI_BC4_R8_SNORM: format = VK_FORMAT_BC4_SNORM_BLOCK; break; - case PTI_BC5_RG8: format = VK_FORMAT_BC5_UNORM_BLOCK; break; - case PTI_BC5_RG8_SNORM: format = VK_FORMAT_BC5_SNORM_BLOCK; break; + case PTI_BC4_R: format = VK_FORMAT_BC4_UNORM_BLOCK; break; + case PTI_BC4_R_SNORM: format = VK_FORMAT_BC4_SNORM_BLOCK; break; + case PTI_BC5_RG: format = VK_FORMAT_BC5_UNORM_BLOCK; break; + case PTI_BC5_RG_SNORM: format = VK_FORMAT_BC5_SNORM_BLOCK; break; case PTI_BC6_RGB_UFLOAT: format = VK_FORMAT_BC6H_UFLOAT_BLOCK; break; case PTI_BC6_RGB_SFLOAT: format = VK_FORMAT_BC6H_SFLOAT_BLOCK; break; case PTI_BC7_RGBA: format = VK_FORMAT_BC7_UNORM_BLOCK; break; @@ -1828,7 +1828,7 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips) { if (mips->mip[i].width != max(1,(mips->mip[i-1].width>>1)) || mips->mip[i].height != max(1,(mips->mip[i-1].height>>1))) - { //okay, this mip looks like it was sized wrongly. this can easily happen with dds files. + { //okay, this mip looks like it was sized wrongly. mipcount = i; break; } @@ -4147,10 +4147,10 @@ void VK_CheckTextureFormats(void) {PTI_BC1_RGBA_SRGB, VK_FORMAT_BC1_RGBA_SRGB_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_BC2_RGBA_SRGB, VK_FORMAT_BC2_SRGB_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_BC3_RGBA_SRGB, VK_FORMAT_BC3_SRGB_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, - {PTI_BC4_R8, VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, - {PTI_BC4_R8_SNORM, VK_FORMAT_BC4_SNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, - {PTI_BC5_RG8, VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, - {PTI_BC5_RG8_SNORM, VK_FORMAT_BC5_SNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, + {PTI_BC4_R, VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, + {PTI_BC4_R_SNORM, VK_FORMAT_BC4_SNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, + {PTI_BC5_RG, VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, + {PTI_BC5_RG_SNORM, VK_FORMAT_BC5_SNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_BC6_RGB_UFLOAT, VK_FORMAT_BC6H_UFLOAT_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_BC6_RGB_SFLOAT, VK_FORMAT_BC6H_SFLOAT_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_BC7_RGBA, VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, @@ -4231,7 +4231,7 @@ void VK_CheckTextureFormats(void) sh_config.texfmt[texfmt[i].pti] = true; } - if (sh_config.texfmt[PTI_BC1_RGBA] && sh_config.texfmt[PTI_BC2_RGBA] && sh_config.texfmt[PTI_BC3_RGBA] && sh_config.texfmt[PTI_BC5_RG8] && sh_config.texfmt[PTI_BC7_RGBA]) + if (sh_config.texfmt[PTI_BC1_RGBA] && sh_config.texfmt[PTI_BC2_RGBA] && sh_config.texfmt[PTI_BC3_RGBA] && sh_config.texfmt[PTI_BC5_RG] && sh_config.texfmt[PTI_BC7_RGBA]) sh_config.hw_bc = 3; if (sh_config.texfmt[PTI_ETC2_RGB8] && sh_config.texfmt[PTI_ETC2_RGB8A1] && sh_config.texfmt[PTI_ETC2_RGB8A8] && sh_config.texfmt[PTI_EAC_RG11]) sh_config.hw_etc = 2; diff --git a/imgtool.c b/imgtool.c index d85a12f4..90693119 100644 --- a/imgtool.c +++ b/imgtool.c @@ -10,6 +10,7 @@ #ifdef _WIN32 #include #endif +static qboolean verbose; void VARGS Sys_Error (const char *fmt, ...) { va_list argptr; @@ -33,6 +34,8 @@ void VARGS Con_Printf (const char *fmt, ...) void VARGS Con_DPrintf (const char *fmt, ...) { va_list argptr; + if (!verbose) + return; va_start (argptr,fmt); vfprintf (stderr,fmt,argptr); @@ -113,6 +116,19 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out Q_strncpyz(out, fname, outlen); return true; } +char *COM_SkipPath (const char *pathname) +{ + const char *last; + + last = pathname; + while (*pathname) + { + if (*pathname=='/' || *pathname == '\\') + last = pathname+1; + pathname++; + } + return (char *)last; +} #ifdef __unix__ #include @@ -249,7 +265,15 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela } qboolean COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len) { - return false; + vfsfile_t *f = FS_OpenVFS(filename, "wb", fsroot); + qboolean ret = false; + if (f) + { + ret = len==VFS_WRITE(f, data, len); + if (!VFS_CLOSE(f)) + ret = false; + } + return ret; } void QDECL Q_strncpyz(char *d, const char *s, int n) { @@ -312,10 +336,11 @@ qbyte GetPaletteIndexNoFB(int red, int green, int blue) int dist; for (i = 0; i < 256-32; i++) { - dist = - abs(host_basepal[i*3+0]-red)+ - abs(host_basepal[i*3+1]-green)+ - abs(host_basepal[i*3+2]-blue); + const int diff[3] = { + host_basepal[i*3+0]-red, + host_basepal[i*3+1]-green, + host_basepal[i*3+2]-blue}; + dist = DotProduct(diff,diff); if (dist < bestdist) { bestdist = dist; @@ -377,8 +402,12 @@ struct opts_s unsigned int flags; //image flags to use (affects how textures get interpreted a little) unsigned int mipnum; //when exporting to a mipless format, this is the mip level that is actually written. default 0. uploadfmt_t newpixelformat; //try to convert to this pixel format on export. + + int width, height; }; +static qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struct pendingtextureinfo *in, const char *mipname, int wadtype); +static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, miptex_t *mip); void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags); int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, qintptr_t bufferstride, int width, int height, enum uploadfmt fmt, qboolean writemetadata); qboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *fte_restrict rgb_buffer, qintptr_t bytestride, int width, int height, enum uploadfmt fmt); @@ -468,6 +497,29 @@ static qboolean ImgTool_HasAlpha(struct pendingtextureinfo *mips) return Image_FormatHasAlpha(mips->encoding); } +//copys all the data out and everything! +static struct pendingtextureinfo *ImgTool_DupeMipchain(struct pendingtextureinfo *src) +{ + struct pendingtextureinfo *dest; + qbyte *data; + size_t size = 0; + size_t m; + for(m = 0; m < src->mipcount; m++) + size += src->mip[m].datasize; + dest = Z_Malloc(sizeof(*dest)+size); + *dest = *src; + data = (qbyte*)(dest+1); + for(m = 0; m < src->mipcount; m++) + { + dest->mip[m].data = data; + dest->mip[m].needfree = false; + memcpy(data, src->mip[m].data, src->mip[m].datasize); + data += src->mip[m].datasize; + } + dest->extrafree = NULL; //part of the texinfo itself. + return dest; +} + static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, struct pendingtextureinfo *mips) { struct pendingtextureinfo tmp, *ret; @@ -508,9 +560,9 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna Q_snprintfz(command, sizeof(command), "nvcompress -bc3%s", (args->flags&IF_TRYBUMP)?"n":""); else if (targfmt == PTI_BC3_RGBA_SRGB) Q_snprintfz(command, sizeof(command), "nvcompress -bc3%s -srgb -dds10", (args->flags&IF_TRYBUMP)?"n":""); - else if (targfmt == PTI_BC4_R8) + else if (targfmt == PTI_BC4_R) Q_snprintfz(command, sizeof(command), "nvcompress -bc4"); - else if (targfmt == PTI_BC5_RG8) + else if (targfmt == PTI_BC5_RG) Q_snprintfz(command, sizeof(command), "nvcompress -bc5"); else if (targfmt == PTI_BC6_RGB_SFLOAT || targfmt == PTI_BC6_RGB_UFLOAT) Q_snprintfz(command, sizeof(command), "nvcompress -bc6"); @@ -531,7 +583,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna //switch to common formats... for (m = 0; m < PTI_MAX; m++) - forceformats[m] = (m == targfmt) || (m==PTI_RGBA8); + forceformats[m] = (m == targfmt) || (m==PTI_RGBA8) || (m==PTI_RGBA32F); Image_ChangeFormat(mips, forceformats, PTI_INVALID, inname); //and try again... for (m = 0; m < PTI_MAX; m++) @@ -540,7 +592,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna return (mips->encoding == targfmt); } - return false; + return true; } if (canktx) FS_MakeTempName(raw, sizeof(raw), "itr", ".ktx"); @@ -587,6 +639,8 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna Image_ChangeFormat(mips, allowformats, PTI_INVALID, inname); } +// Con_Printf("%s: Compressing %u mips\n", inname, mips->mipcount); + Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); for (m = 0; m < mips->mipcount; m++) { @@ -606,7 +660,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna } for (l = 0; l < layers; l++) { - Con_DPrintf("Compressing %s mip %u, layer %u\n", inname, (unsigned)m, l); +// Con_DPrintf("Compressing %s mip %u, layer %u\n", inname, (unsigned)m, l); tmp.mip[0] = mips->mip[m]; tmp.mip[0].needfree = false; tmp.mip[0].depth = d; @@ -659,6 +713,12 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna } continue; } + else if (!ret) + Con_Printf("Failed to read intermediate file %s\n", comp); + else + Con_Printf("intermediate file %s has unexpected size/depth/format %s:%i*%i*%i vs %s:%i*%i*%i\n", comp, + Image_FormatName(ret->encoding), ret->mip[0].width, ret->mip[0].height, ret->mip[0].depth, + Image_FormatName(targfmt), mips->mip[m].width, mips->mip[m].height, d); break; } if (l != layers) @@ -702,18 +762,22 @@ static struct pendingtextureinfo *ImgTool_Read(struct opts_s *args, const char * struct pendingtextureinfo *in; indata = FS_LoadMallocFile(inname, &fsize); if (!indata) - printf("%s: unable to read\n", inname); + Con_Printf("%s: unable to read\n", inname); else { - in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize); + const char *ex = COM_GetFileExtension(inname, NULL); + if (!strcasecmp(ex, ".mip")) + in = ImgTool_DecodeMiptex(args, (miptex_t*)indata); + else + in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize); if (!in) { - printf("%s: unsupported format\n", inname); + Con_Printf("%s: unsupported format\n", inname); BZ_Free(indata); } else { - printf("%s: %s %s, %i*%i, %i mips\n", inname, imagetypename[in->type], Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); + Con_DPrintf("%s: %s %s, %i*%i, %i mips\n", inname, imagetypename[in->type], Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount); return in; } } @@ -944,6 +1008,8 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, outname = newout; } + + outext = COM_GetFileExtension(outname, NULL); if (!strcmp(outext, ".dds") || !strcmp(outext, ".ktx")) allowcompressed = true; @@ -970,22 +1036,34 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, in->mipcount -= k; memmove(in->mip, &in->mip[k], sizeof(in->mip[0])*in->mipcount); - printf("%s(%s)->", inname, Image_FormatName(in->encoding)); + Con_Printf("%s(%s)->", inname, Image_FormatName(in->encoding)); if (args->newpixelformat != PTI_INVALID && (args->newpixelformat < PTI_BC1_RGB || allowcompressed) && ImgTool_ConvertPixelFormat(args, inname, in)) - printf("(%s)->\n", Image_FormatName(in->encoding)); + Con_Printf("(%s)->", Image_FormatName(in->encoding)); if (!in->mipcount) - printf("%s: unable to convert any mips\n", inname); + Con_Printf("%s: no image data\n", inname); + else if (!strcasecmp(outext, ".mip")) + { + vfsfile_t *fs = FS_OpenVFS(outname, "wb", FS_SYSTEM); + if (!fs) + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + else + { + if (!ImgTool_MipExport(args, fs, in, outname, 1)) + Con_Printf("%s: export failed\n", outname); + VFS_CLOSE(fs); + } + } #ifdef IMAGEFMT_KTX - else if (!strcmp(outext, ".ktx")) + else if (!strcasecmp(outext, ".ktx")) { if (!Image_WriteKTXFile(outname, FS_SYSTEM, in)) Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } #endif #ifdef IMAGEFMT_DDS - else if (!strcmp(outext, ".dds")) + else if (!strcasecmp(outext, ".dds")) { if (!Image_WriteDDSFile(outname, FS_SYSTEM, in)) Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); @@ -998,7 +1076,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, if (in->type != PTI_2D) Con_Printf("%s: Unable to write %s file to 2d image format\n", outname, imagetypename[in->type]); #ifdef IMAGEFMT_PNG - else if (!strcmp(outext, ".png")) + else if (!strcasecmp(outext, ".png")) { #ifdef AVAIL_PNGLIB qboolean outformats[PTI_MAX]; @@ -1041,6 +1119,24 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, if (!WriteTGA(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding)) Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); } +#endif +#ifdef IMAGEFMT_PCX + else if (!strcmp(outext, ".pcx")) + { + qboolean outformats[PTI_MAX]; + for (k = 0; k < PTI_MAX; k++) + outformats[k] = + (k == PTI_P8) || + (k == TF_SOLID8) || + (k == TF_TRANS8) || + (k == TF_H2_TRANS8_0) || + 0; + if (!outformats[in->encoding]) + Image_ChangeFormat(in, outformats, PTI_INVALID, outname); + Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh); + if (!WritePCXfile(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width, in->mip[0].height, in->mip[0].width*bb, host_basepal, false)) + Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); + } #endif else Con_Printf("%s: Unknown output file format\n", outname); @@ -1048,17 +1144,147 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, if (in->mipcount > 1) { - printf("%s(%s): %s %i*%i*%i, %i mips\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount); + Con_Printf("%s(%s): %s %i*%i*%i, %i mips\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount); for (k = 0; k < in->mipcount; k++) - printf("\t%u: %i*%i*%i, %u\n", (unsigned)k, in->mip[k].width, in->mip[k].height, in->mip[k].depth, (unsigned)in->mip[k].datasize); + if (in->mip[k].depth == 1 && in->type == PTI_2D) + Con_DPrintf("\t%u: %i*%i, %u\n", (unsigned)k, in->mip[k].width, in->mip[k].height, (unsigned)in->mip[k].datasize); + else + Con_DPrintf("\t%u: %i*%i*%i, %u\n", (unsigned)k, in->mip[k].width, in->mip[k].height, in->mip[k].depth, (unsigned)in->mip[k].datasize); } else - printf("%s(%s): %s %i*%i*%i, %u bytes\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize); + Con_Printf("%s(%s): %s %i*%i*%i, %u bytes\n", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize); ImgTool_FreeMips(in); } fflush(stdout); } + +static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, miptex_t *mip) +{ + qbyte *data = (qbyte*)mip + (mip->offsets[2]?mip->offsets[2] + (mip->width>>2)*(mip->height>>2):sizeof(miptex_t)); + qbyte *dataend = (qbyte*)mip + (mip->offsets[3]); + struct pendingtextureinfo *out = Z_Malloc(sizeof(*out)); + qbyte *newdata = NULL; + int neww=0, newh=0, sz; + unsigned int bw,bh,bb; + out->type = PTI_2D; + + out->encoding = PTI_INVALID; + + Con_Printf("%s: Invalid miptex extension\n", mip->name); + + //header [legacymip0 legacymip1 legacymip2] [extsize extcode extdata]*n [legacymip3] + //extcode NAME: extdata-8 bytes of replacement name + //extdata pixelformats, extdata is: Width Height newmip0...N where N is 1*1 mip, using round-down logic. + //compressed and legacy data ommitted means all 4 offsets are 0. + //compressed-only data has offset[3] state the termination position, but offset[0,1,2] MUST be 0 still (extension data starts right after the header, offset3 points to the end of the miptex and has no data there. + //legacy-only data is densely packed or whatever. + //half-life palette data might be glued onto the end. we don't care to handle that. + Con_DPrintf("%i bytes of extended data\n", (unsigned)(dataend-data)); + Con_DPrintf("offset[0]: %i\n", mip->offsets[0]); + Con_DPrintf("offset[1]: %i\n", mip->offsets[1]); + Con_DPrintf("offset[2]: %i\n", mip->offsets[2]); + Con_DPrintf("offset[3]: %i\n", mip->offsets[3]); + for (; data+4 < dataend; data += sz) + { //we could recognise more, + uploadfmt_t fmt = PTI_INVALID; + size_t csz, w, h; + sz = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + + if (sz < 4 || sz > dataend-data) { Con_Printf("%s: Invalid miptex extension\n", mip->name); break;} + else if (sz > 8 && !strncmp(data+4, "NAME", 4)) continue; //FIXME + else if (sz > 16 && !strncmp(data+4, "RGBA", 4)) fmt = PTI_RGBA8; + else if (sz > 16 && !strncmp(data+4, "RGB", 4)) fmt = PTI_RGB8; + else if (sz > 16 && !strncmp(data+4, "565", 4)) fmt = PTI_RGB565; + else if (sz > 16 && !strncmp(data+4, "5551", 4)) fmt = PTI_RGBA5551; + else if (sz > 16 && !strncmp(data+4, "4444", 4)) fmt = PTI_RGBA4444; + else if (sz > 16 && !strncmp(data+4, "LUM8", 4)) fmt = PTI_L8; //greyscale. because why not. + else if (sz > 16 && !strncmp(data+4, "BC1", 4)) fmt = PTI_BC1_RGBA; + else if (sz > 16 && !strncmp(data+4, "BC2", 4)) fmt = PTI_BC2_RGBA; + else if (sz > 16 && !strncmp(data+4, "BC3", 4)) fmt = PTI_BC3_RGBA; + else if (sz > 16 && !strncmp(data+4, "BC4", 4)) fmt = PTI_BC4_R; + else if (sz > 16 && !strncmp(data+4, "BC5", 4)) fmt = PTI_BC5_RG; + else if (sz > 16 && !strncmp(data+4, "BC6", 4)) fmt = PTI_BC6_RGB_UFLOAT; + else if (sz > 16 && !strncmp(data+4, "BC7", 4)) fmt = PTI_BC7_RGBA; + else if (sz > 16 && !strncmp(data+4, "ETC1", 4)) fmt = PTI_ETC1_RGB8; + else if (sz > 16 && !strncmp(data+4, "ETC2", 4)) fmt = PTI_ETC2_RGB8; + else if (sz > 16 && !strncmp(data+4, "ETCP", 4)) fmt = PTI_ETC2_RGB8A1; + else if (sz > 16 && !strncmp(data+4, "ETCA", 4)) fmt = PTI_ETC2_RGB8A8; + else if (sz > 16 && !strncmp(data+4, "AST4", 4)) fmt = PTI_ASTC_4X4_LDR; + else if (sz > 16 && !strncmp(data+4, "AST5", 4)) fmt = PTI_ASTC_5X5_LDR; + else if (sz > 16 && !strncmp(data+4, "AST6", 4)) fmt = PTI_ASTC_6X6_LDR; + else if (sz > 16 && !strncmp(data+4, "AST8", 4)) fmt = PTI_ASTC_8X8_LDR; + else if (sz > 16 && !strncmp(data+4, "EXP5", 4)) fmt = PTI_E5BGR9; + else {Con_Printf("%s: Unknown miptex extension %4s\n", mip->name, data+4);continue;} + + if (out->encoding != PTI_INVALID) //use the first format we support, allowing prioritisation. + continue; + + Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); + w = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24); + h = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24); + for (csz = 16; w || h; w>>=1, h>>=1) + { + w = max(1, w); + h = max(1, h); + csz += bb*((w+bw-1)/bw)*((h+bh-1)/bh); + } + if (sz == csz) + { //mip chain is complete etc + out->encoding = fmt; + newdata = data+16; + neww = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24); + newh = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24); + } + else + Con_Printf("%s: Chain size of %u doesn't match expected %u\n", mip->name, (unsigned)csz-16, sz-16); + } + + //only use our if there were no corrupt sections. + if (data == dataend && newdata && neww && newh) + { + Image_BlockSizeForEncoding(out->encoding, &bb, &bw, &bh); + for (out->mipcount = 0; out->mipcount < countof(out->mip) && neww && newh; out->mipcount++, neww>>=1, newh>>=1) + { + neww = max(1, neww); + newh = max(1, newh); + out->mip[out->mipcount].width = neww; + out->mip[out->mipcount].height = newh; + out->mip[out->mipcount].depth = 1; + out->mip[out->mipcount].datasize = bb; + out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].width + bw-1)/bw; + out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].height + bh-1)/bh; + out->mip[out->mipcount].data = newdata; + newdata += out->mip[out->mipcount].datasize; + } + } + else + { + if (*mip->name == '{') + out->encoding = TF_TRANS8; + else if (!strncasecmp(mip->name, "sky", 3)) + out->encoding = TF_H2_TRANS8_0; + else + out->encoding = PTI_P8; + for (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++) + { + out->mip[out->mipcount].width = mip->width>>out->mipcount; + out->mip[out->mipcount].height = mip->height>>out->mipcount; + out->mip[out->mipcount].depth = 1; + out->mip[out->mipcount].datasize = out->mip[out->mipcount].width*out->mip[out->mipcount].height*out->mip[out->mipcount].depth; + out->mip[out->mipcount].data = (char*)mip + mip->offsets[out->mipcount]; + } + } + if (*mip->name == '*') + *mip->name = '#'; //convert from * to #, so its a valid file name. + + if (args) + { + ImgTool_Convert(args, out, mip->name, NULL); + return NULL; + } + return out; +} static void ImgTool_Info(struct opts_s *args, const char *inname) { qbyte *indata; @@ -1080,12 +1306,20 @@ static void ImgTool_Info(struct opts_s *args, const char *inname) case 67: //hl... case TYP_MIPTEX: { - const miptex_t *mip = (const miptex_t *)(indata+e->offset); + miptex_t *mip = (miptex_t *)(indata+e->offset); + in = ImgTool_DecodeMiptex(NULL, mip); + /*mip name SHOULD match entry name... but gah!*/ if (strcasecmp(e->name, mip->name)) - printf("\t%16.16s (%s): %u*%u%s\n", e->name, mip->name, mip->width, mip->height, mip->offsets[0]?"":" (external data)"); + printf("\t%16.16s (%s): ", e->name, mip->name); else - printf("\t%16.16s: %u*%u%s\n", mip->name, mip->width, mip->height, mip->offsets[0]?"":" (external data)"); + printf("\t%16.16s: ", mip->name); + printf("%u*%u%s", mip->width, mip->height, mip->offsets[0]?"":" (omitted)"); + + if (in->encoding != PTI_P8) + printf(" (%s %u*%u)", Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height); + printf("\n"); + ImgTool_FreeMips(in); } break; case TYP_PALETTE: @@ -1115,12 +1349,22 @@ static void ImgTool_Info(struct opts_s *args, const char *inname) continue; miptex = (miptex_t*)((qbyte*)texlump + texlump->dataofs[i]); - printf("\t%16.16s: %u*%u%s\n", miptex->name, miptex->width, miptex->height, miptex->offsets[0]?"":" (external data)"); + + in = ImgTool_DecodeMiptex(NULL, miptex); + if (in->encoding != PTI_P8) + printf("\t%16.16s: %u*%u%s (%s: %i*%i)\n", miptex->name, miptex->width, miptex->height, miptex->offsets[0]?"":" (external data)", Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height); + else + printf("\t%16.16s: %u*%u%s\n", miptex->name, miptex->width, miptex->height, miptex->offsets[0]?"":" (external data)"); + ImgTool_FreeMips(in); } } else { - in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize); + const char *ex = COM_GetFileExtension(inname, NULL); + if (!strcasecmp(ex, ".mip")) + in = ImgTool_DecodeMiptex(NULL, (miptex_t*)indata); + else + in = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize); if (!in) printf("%-20s: unsupported format\n", inname); else if (in->mipcount == 1 && in->type == PTI_2D && in->mip[0].depth == 1) @@ -1159,7 +1403,7 @@ static void FileList_Release(struct filelist_s *list) list->numfiles = 0; list->maxfiles = 0; } -static void FileList_Add(struct filelist_s *list, const char *rootpath, char *fname) +static void FileList_Add(struct filelist_s *list, const char *rootpath, const char *fname) { size_t i; size_t baselen; @@ -1235,44 +1479,56 @@ static void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, cons DIR *dir; char file[MAX_OSPATH]; struct dirent *ent; + struct stat sb; if (subpath && *subpath) Q_snprintfz(file, sizeof(file), "%s/%s", basepath, subpath); else Q_snprintfz(file, sizeof(file), "%s", basepath); - dir = opendir(file); - if (!dir) + stat(file, &sb); + if ((sb.st_mode & S_IFMT) == S_IFDIR) { - Con_Printf("Failed to open dir %s\n", file); - return; - } - for (;;) - { - ent = readdir(dir); - if (!ent) - break; - if (*ent->d_name == '.') - continue; - else if (ent->d_type == DT_DIR) + dir = opendir(file); + if (!dir) { - if (!subpath) + Con_Printf("Failed to open dir %s\n", file); + return; + } + for (;;) + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name == '.') continue; - if (*subpath) - Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); - else - Q_snprintfz(file, sizeof(file), "%s", ent->d_name); - ImgTool_TreeScan(list, basepath, file); - } - else if (ent->d_type == DT_REG) - { - if (subpath && *subpath) - Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); - else - Q_snprintfz(file, sizeof(file), "%s", ent->d_name); - FileList_Add(list, basepath, file); + else if (ent->d_type == DT_DIR) + { + if (!subpath) + continue; + if (*subpath) + Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); + else + Q_snprintfz(file, sizeof(file), "%s", ent->d_name); + ImgTool_TreeScan(list, basepath, file); + } + else if (ent->d_type == DT_REG) + { + if (subpath && *subpath) + Q_snprintfz(file, sizeof(file), "%s/%s", subpath, ent->d_name); + else + Q_snprintfz(file, sizeof(file), "%s", ent->d_name); + FileList_Add(list, basepath, file); + } } + closedir(dir); + } + else + { + if (*file == '/') + FileList_Add(list, "", file); + else + FileList_Add(list, ".", file); } - closedir(dir); } #endif static void ImgTool_TreeConvert(struct opts_s *args, const char *destpath, const char *srcpath) @@ -1323,9 +1579,6 @@ static void ImgTool_TreeConvert(struct opts_s *args, const char *destpath, const return; } - - - static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) { qbyte *indata; @@ -1349,10 +1602,10 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) case TYP_MIPTEX: { miptex_t *mip = (miptex_t *)(indata+e->offset); - struct pendingtextureinfo *out = Z_Malloc(sizeof(*out)); if (!strcmp(e->name, "CONCHARS") && e->size==128*128) { //special hack for conchars, which is listed as a miptex for some reason, with no qpic header (it not being a qpic lump) + struct pendingtextureinfo *out = Z_Malloc(sizeof(*out)); out->encoding = TF_H2_TRANS8_0; out->type = PTI_2D; out->mip[0].width = 128; @@ -1365,19 +1618,7 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) break; } - out->encoding = PTI_P8; - out->type = PTI_2D; - for (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++) - { - out->mip[out->mipcount].width = mip->width>>out->mipcount; - out->mip[out->mipcount].height = mip->height>>out->mipcount; - out->mip[out->mipcount].depth = 1; - out->mip[out->mipcount].datasize = out->mip[out->mipcount].width*out->mip[out->mipcount].height*out->mip[out->mipcount].depth; - out->mip[out->mipcount].data = (char*)mip + mip->offsets[out->mipcount]; - } - if (*mip->name == '*') - *mip->name = '#'; //convert from * to #, so its a valid file name. - ImgTool_Convert(args, out, mip->name, NULL); + ImgTool_DecodeMiptex(args, mip); } break; case TYP_QPIC: @@ -1435,7 +1676,7 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) dheader_t *bsp = (dheader_t*)indata; dmiptexlump_t *texlump = (dmiptexlump_t*)(indata + bsp->lumps[LUMP_TEXTURES].fileofs); miptex_t *miptex; - size_t i, j; + size_t i; for (i = 0; i < texlump->nummiptex; i++) { if (texlump->dataofs[i] < 0 || texlump->dataofs[i] >= bsp->lumps[LUMP_TEXTURES].filelen) @@ -1443,35 +1684,245 @@ static void ImgTool_WadExtract(struct opts_s *args, const char *wadname) miptex = (miptex_t*)((qbyte*)texlump + texlump->dataofs[i]); if (*miptex->name && miptex->width && miptex->height && miptex->offsets[0]>0) - { - struct pendingtextureinfo *out = Z_Malloc(sizeof(*out)); - out->type = PTI_2D; - out->encoding = PTI_P8; - for (j = 0; j < 4; j++) - { - if (!miptex->offsets[j]) - break; - out->mip[j].width = miptex->width>>j; - out->mip[j].height = miptex->height>>j; - out->mip[j].depth = 1; - out->mip[j].datasize = out->mip[j].width*out->mip[j].height; - out->mip[j].data = (qbyte*)miptex + miptex->offsets[0]; - } - out->mipcount = j; - - if (*miptex->name == '*') - *miptex->name = '#'; //change the prefix of turbs, so it can actually exist as a file on windows (and doesn't cause confusion). - ImgTool_Convert(args, out, miptex->name, NULL); - } + ImgTool_DecodeMiptex(args, miptex); } } else printf("%s: does not appear to be a wad file\n", wadname); } +//spits out our extended .mip format +static qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struct pendingtextureinfo *in, const char *mipname, int wadtype) +{ + struct pendingtextureinfo *highcolour = NULL; + char *highcode = NULL, *ext; + size_t u; + unsigned int m, tsz; + miptex_t mip; + + static qboolean mippixelformats[PTI_MAX] = {[PTI_P8]=true}; + + if (in) + { + if (in->mipcount == 1) + Image_GenerateMips(in, args->flags); + + if (!in->mipcount) + { + Con_Printf("%s: unable to load any mips\n", mipname); + return false; + } + } + + if (in->encoding == PTI_P8 || args->newpixelformat == PTI_INVALID) + { + in = ImgTool_DupeMipchain(in); + highcode = NULL; //no, don't store it weirdly... + } + else + { + highcolour = in; + in = ImgTool_DupeMipchain(in); + Image_GenerateMips(highcolour, args->flags); + for (u = 1; u < countof(sh_config.texfmt); u++) + sh_config.texfmt[u] = true; + if (!ImgTool_ConvertPixelFormat(args, mipname, highcolour)) + { + Con_Printf("%s: Unable to convert to requested pixel format\n", mipname); + ImgTool_FreeMips(highcolour); + highcolour = NULL; + } + else if (highcolour->mip[highcolour->mipcount-1].width != 1 || highcolour->mip[highcolour->mipcount-1].height != 1) + { + Con_Printf("%s: Mipchain truncated\n", mipname); + ImgTool_FreeMips(highcolour); + highcolour = NULL; + } + else for (u = 1; u < highcolour->mipcount; u++) + { //mip chain must round down consistently. + if (highcolour->mip[u].width != max(1,highcolour->mip[u-1].width>>1) || + highcolour->mip[u].height!= max(1,highcolour->mip[u-1].height>>1)) + { + Con_Printf("%s: Mipchain sized wrongly\n", mipname); + ImgTool_FreeMips(highcolour); + highcolour = NULL; + break; + } + } + + if (highcolour) switch(highcolour->encoding) + { + case PTI_BC1_RGB: + case PTI_BC1_RGBA: highcode = "BC1"; break; //not in any core gl, but uniquitous on desktop, but not mobile. + case PTI_BC2_RGBA: highcode = "BC2"; break; + case PTI_BC3_RGBA: highcode = "BC3"; break; + case PTI_BC4_R: highcode = "BC4"; break; + case PTI_BC5_RG: highcode = "BC5"; break; + case PTI_BC6_RGB_UFLOAT:highcode = "BC6"; break; //aka bptc, core in gl4.2 (not gles) + case PTI_BC7_RGBA: highcode = "BC7"; break; //aka bptc, core in gl4.2 (not gles) + case PTI_ETC1_RGB8: highcode = "ETC1"; break; //available on most gles2 devices. + case PTI_ETC2_RGB8: highcode = "ETC2"; break; //core in gles3 (or gl4.3) + case PTI_ETC2_RGB8A1: highcode = "ETCP"; break; //core in gles3 (or gl4.3) + case PTI_ETC2_RGB8A8: highcode = "ETCA"; break; //core in gles3 (or gl4.3) + case PTI_ASTC_4X4_LDR: highcode = "AST4"; break; //core in gles3.2 + case PTI_ASTC_5X4_LDR: highcode = "AS54"; break; //core in gles3.2 + case PTI_ASTC_5X5_LDR: highcode = "AST5"; break; //core in gles3.2 + case PTI_ASTC_6X5_LDR: highcode = "AS65"; break; //core in gles3.2 + case PTI_ASTC_6X6_LDR: highcode = "AST6"; break; //core in gles3.2 + case PTI_ASTC_8X5_LDR: highcode = "AS85"; break; //core in gles3.2 + case PTI_ASTC_8X6_LDR: highcode = "AS86"; break; //core in gles3.2 + case PTI_ASTC_10X5_LDR: highcode = "AS05"; break; //core in gles3.2 + case PTI_ASTC_10X6_LDR: highcode = "AS06"; break; //core in gles3.2 + case PTI_ASTC_8X8_LDR: highcode = "AST8"; break; //core in gles3.2 + case PTI_ASTC_10X8_LDR: highcode = "AS08"; break; //core in gles3.2 + case PTI_ASTC_10X10_LDR:highcode = "AST0"; break; //core in gles3.2 + case PTI_ASTC_12X10_LDR:highcode = "AS20"; break; //core in gles3.2 + case PTI_ASTC_12X12_LDR:highcode = "AST2"; break; //core in gles3.2 + case PTI_RGB565: highcode = "565"; break; + case PTI_RGBA5551: highcode = "5551"; break; + case PTI_RGBA4444: highcode = "4444"; break; + case PTI_RGB8: highcode = "RGB"; break; //generally needs reformatting to rgbx. + case PTI_RGBA8: highcode = "RGBA"; break; //bloaty + case PTI_L8: highcode = "LUM8"; break; + case PTI_E5BGR9: highcode = "EXP5"; break; //gl3+ + default: + Con_Printf("%s: unsupported pixel format(%s) for miptex\n", mipname, Image_FormatName(highcolour->encoding)); + ImgTool_FreeMips(highcolour); + highcolour = NULL; + break; + } + } + + if (args->width && args->height && in->mipcount >= 1) + { + qbyte *newimg; + unsigned int bb, bw, bh; + Image_BlockSizeForEncoding(in->encoding, &bb, &bw, &bh); + newimg = Image_ResampleTexture(in->encoding, in->mip[0].data, in->mip[0].width, in->mip[0].height, NULL, args->width, args->height); + if (newimg) + { + in->mipcount = 1; //urgh + if (in->mip[0].needfree) + BZ_Free(in->mip[0].data); + in->mip[0].data = newimg; + in->mip[0].needfree = true; + in->mip[0].width = args->width; + in->mip[0].height = args->height; + in->mip[0].datasize = bb*((in->mip[0].width+bw-1)/bw)*((in->mip[0].height+bh-1)/bh); + + Image_GenerateMips(in, args->flags); + } + else + Con_Printf("%s: unable to resize %s\n", mipname, Image_FormatName(in->encoding)); + } + + if (args->mipnum >= in->mipcount) + { + Con_Printf("%s: not enough mips\n", mipname); + return false; + } + + //strip out all but the 4 mip levels we care about. + for (u = 0; u < in->mipcount; u++) + { + if (u >= args->mipnum && u < args->mipnum+4) + { + if (!wadtype) + { //if we're stripping out the wad data (so that the engine ends up requiring external textures) then do it now before palettizing, for efficiency. + if (in->mip[u].needfree) + BZ_Free(in->mip[u].data); + in->mip[u].data = NULL; + in->mip[u].datasize = 0; + } + } + else + { + if (in->mip[u].needfree) + BZ_Free(in->mip[u].data); + memset(&in->mip[u], 0, sizeof(in->mip[u])); + } + } + in->mipcount -= args->mipnum; + if (in->mipcount > 4) + in->mipcount = 4; + memmove(&in->mip[0], &in->mip[args->mipnum], sizeof(in->mip[0])*in->mipcount); + memset(&in->mip[in->mipcount], 0, sizeof(in->mip[0])*((args->mipnum+4)-in->mipcount)); //null it out, just in case. + + if (in->mip[0].data) + { + if (in->encoding != PTI_P8) + Image_ChangeFormat(in, mippixelformats, (*mipname=='{')?TF_TRANS8:PTI_INVALID, mipname); + if (in->encoding != PTI_P8) + { //erk! we failed to palettize... + ImgTool_FreeMips(in); + ImgTool_FreeMips(highcolour); + Con_Printf("%s: paletizing error (source format %s)\n", mipname, Image_FormatName(in->encoding)); + return false; + } + } + + if (!in->mip[0].width || (in->mip[0].width & 15)) + Con_Printf("%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\n", mipname, args->mipnum, in->mip[0].width, in->mip[0].height); + if (!in->mip[0].height || (in->mip[0].height & 15)) + Con_Printf("%s(%i): WARNING: miptex height is not a multiple of 16 - %i*%i\n", mipname, args->mipnum, in->mip[0].width, in->mip[0].height); + + memset(mip.name, 0, sizeof(mip.name)); + Q_strncpyz(mip.name, mipname, sizeof(mip.name)); + ext = (char*)COM_GetFileExtension (mip.name, NULL); + while (*ext) *ext++=0; + if (*mip.name == '#') + *mip.name = '*'; //make it a proper turb + mip.width = in->mip[0].width; + mip.height = in->mip[0].height; + mip.offsets[0] = in->mip[0].datasize?sizeof(mip):0; + mip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0; + mip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0; + mip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0; + + tsz = sizeof(mip)+in->mip[0].datasize+in->mip[1].datasize+in->mip[2].datasize+in->mip[3].datasize; + VFS_WRITE(outfile, &mip, sizeof(mip)); + VFS_WRITE(outfile, in->mip[0].data, in->mip[0].datasize); + VFS_WRITE(outfile, in->mip[1].data, in->mip[1].datasize); + VFS_WRITE(outfile, in->mip[2].data, in->mip[2].datasize); + VFS_WRITE(outfile, in->mip[3].data, in->mip[3].datasize); + if (wadtype == 2) + { + tsz += 2 + 256*3; + VFS_WRITE(outfile, "\x00\x01", 2); + VFS_WRITE(outfile, host_basepal, 256*3); + } + + if (highcolour) + { + unsigned int highsize; + VFS_WRITE(outfile, "\x00\xfb\x2b\xaf", 4); //magic id to say that there's actually extensions here... + tsz += 4; + //spit out our high-colour lump here + for (highsize = 16, m = 0; m < highcolour->mipcount; m++) + highsize += highcolour->mip[m].datasize; + VFS_WRITE(outfile, &highsize, 4); + VFS_WRITE(outfile, highcode, 4); + VFS_WRITE(outfile, &highcolour->mip[0].width, 4); + VFS_WRITE(outfile, &highcolour->mip[0].height, 4); + for (m = 0; m < highcolour->mipcount; m++) + VFS_WRITE(outfile, highcolour->mip[m].data, highcolour->mip[m].datasize); + tsz += highsize; + + Con_Printf("%s: %ix%i (%s: %ix%i %i)\n", mip.name, mip.width, mip.height, highcode, highcolour->mip[0].width, highcolour->mip[0].height, highcolour->mipcount); + } + else + Con_Printf("%s: %ix%i\n", mip.name, mip.width, mip.height); + + //and pad it, just in case. + if (tsz & 3) + VFS_WRITE(outfile, "\0\0\0\0", 4-(tsz&3)); + + ImgTool_FreeMips(in); + return true; +} static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const char **srcpaths, size_t numpaths, int wadtype/*x,2,3*/) { char file[MAX_OSPATH]; - const char *exts[] = {".png", ".bmp", ".tga", ".exr", ".hdr", ".dds", ".ktx", ".xcf", ".pcx", ".jpg", NULL}; + const char *exts[] = {".mip", ".png", ".bmp", ".tga", ".exr", ".hdr", ".dds", ".ktx", ".xcf", ".pcx", ".jpg", NULL}; struct filelist_s list = {exts}; size_t i, u; vfsfile_t *f; @@ -1481,9 +1932,8 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const wad2_t wad2; wad2entry_t *wadentries = NULL, *entry; size_t maxentries = 0; - miptex_t mip; - qboolean wadpixelformats[PTI_MAX] = {0}; - wadpixelformats[PTI_P8] = true; + qboolean qpics; + struct pendingtextureinfo *in; if (!numpaths) ImgTool_TreeScan(&list, ".", NULL); else while(numpaths --> 0) @@ -1495,6 +1945,8 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const return; } + qpics = !strcasecmp("gfx.wad", COM_SkipPath(destpath)); + f = FS_OpenVFS(destpath, "wb", FS_SYSTEM); wad2.magic[0] = 'W'; wad2.magic[1] = 'A'; @@ -1504,11 +1956,7 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const wad2.offset = 0; VFS_WRITE(f, &wad2, 12); - //try to decompress everything to a nice friendly palletizable range. - for (u = 1; u < countof(sh_config.texfmt); u++) - sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8); - - if (wadtype == 2) + if (wadtype == 1) { //WAD2 texture files generally have a palette lump. if (wad2.num == maxentries) { @@ -1523,6 +1971,8 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const //and the lump data. VFS_WRITE(f, host_basepal, 256*3); + + entry->size = entry->dsize = VFS_TELL(f)-entry->offset; } for (i = 0; i < list.numfiles; i++) @@ -1536,93 +1986,55 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const } indata = FS_LoadMallocFile(file, &fsize); if (!indata) - printf("Unable to open %s\n", inname); + { + Con_Printf("Unable to open %s\n", inname); + continue; + } + + if (wad2.num == maxentries) + { + maxentries += 64; + wadentries = realloc(wadentries, sizeof(*wadentries)*maxentries); + } + entry = &wadentries[wad2.num]; + memset(entry, 0, sizeof(*entry)); + Q_strncpyz(entry->name, inname, 16); + if (list.file[i].baselen < sizeof(entry->name)) + entry->name[list.file[i].baselen] = 0; //kill any .tga + if (*entry->name == '#') + *entry->name = '*'; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char. + entry->type = TYP_MIPTEX; + entry->offset = VFS_TELL(f); + + if (!strcasecmp(COM_GetFileExtension(file, NULL), ".mip")) + { //.mip files can just be loaded directly + //I just hope they are actually q1 format and not hl, for instance. + if (wadtype == 3) + { + Con_Printf("refusing to inject q1 miptex into halflife wad-3 file\n"); + Z_Free(indata); + continue; + } + VFS_WRITE(f, indata, fsize); + wad2.num++; + entry->size = entry->dsize = VFS_TELL(f)-entry->offset; + Z_Free(indata); + } else { - struct pendingtextureinfo *in = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize); - Image_GenerateMips(in, args->flags); - if (in) - { - if (in->mipcount == 1) - Image_GenerateMips(in, args->flags); - - if (!in->mipcount) - { - ImgTool_FreeMips(in); - Con_Printf("%s: unable to load any mips\n", inname); - continue; - } - } - - if (args->mipnum >= in->mipcount) - { - ImgTool_FreeMips(in); - Con_Printf("%s: not enough mips\n", inname); - continue; - } - - //strip out all but the 4 mip levels we care about. - for (u = 0; u < in->mipcount; u++) - { - if (u >= args->mipnum && u < args->mipnum+4) - { - if (!wadtype) - { //if we're stripping out the wad data (so that the engine ends up requiring external textures) then do it now before palettizing, for efficiency. - if (in->mip[u].needfree) - BZ_Free(in->mip[u].data); - in->mip[u].data = NULL; - in->mip[u].datasize = 0; - } - } - else - { - if (in->mip[u].needfree) - BZ_Free(in->mip[u].data); - memset(&in->mip[u], 0, sizeof(in->mip[u])); - } - } - in->mipcount -= args->mipnum; - if (in->mipcount > 4) - in->mipcount = 4; - memmove(&in->mip[0], &in->mip[args->mipnum], sizeof(in->mip[0])*in->mipcount); - memset(&in->mip[in->mipcount], 0, sizeof(in->mip[0])*((args->mipnum+4)-in->mipcount)); //null it out, just in case. - - if (in->encoding != PTI_P8) - Image_ChangeFormat(in, wadpixelformats, (*inname=='{')?TF_TRANS8:PTI_INVALID, inname); - if (in->encoding != PTI_P8) - { //erk! we failed to palettize... - ImgTool_FreeMips(in); - continue; - } - - if (wad2.num == maxentries) - { - maxentries += 64; - wadentries = realloc(wadentries, sizeof(*wadentries)*maxentries); - } - entry = &wadentries[wad2.num++]; - Q_strncpyz(entry->name, inname, 16); - entry->name[list.file[i].baselen] = 0; //kill any .tga - if (*entry->name == '#') - *entry->name = '*'; //* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char. if (wadtype == 3) { for (u = 0; u < sizeof(entry->name); u++) entry->name[u] = toupper(entry->name[u]); - entry->type = 67; + entry->type = 67; //halflife's mips actually use a different type from q1 ones. } - else - entry->type = TYP_MIPTEX; - entry->cmprs = 0; - entry->dummy = 0; - entry->offset = VFS_TELL(f); - if (!in->mip[0].width || (in->mip[0].width & 15)) - Con_Printf("%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height); - if (!in->mip[0].height || (in->mip[0].height & 15)) - Con_Printf("%s(%i): WARNING: miptex height is not a not multiple of 16 - %i*%i\n", inname, args->mipnum, in->mip[0].width, in->mip[0].height); + //try to decompress everything to a nice friendly palletizable range. + for (u = 1; u < countof(sh_config.texfmt); u++) + sh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8)||(u==args->newpixelformat); + in = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize); - if (0) + if (qpics) { if (!strcasecmp(entry->name, "CONCHARS") && in->mip[0].width==128&&in->mip[0].height==128) entry->type = TYP_MIPTEX; //yes, weird. match vanilla quake. explicitly avoid qpic to avoid corruption in the first 8 bytes (due to the engine's early endian swapping) @@ -1637,32 +2049,11 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const //and now the 8bit pixel data itself VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize); } - else - { - memcpy(mip.name, entry->name, sizeof(mip.name)); - mip.width = in->mip[0].width; - mip.height = in->mip[0].height; - mip.offsets[0] = in->mip[0].datasize?sizeof(mip):0; - mip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0; - mip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0; - mip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0; - - Con_Printf("%s: %ix%i\n", mip.name, mip.width, mip.height); - - VFS_WRITE(f, &mip, sizeof(mip)); - VFS_WRITE(f, in->mip[0].data, in->mip[0].datasize); - VFS_WRITE(f, in->mip[1].data, in->mip[1].datasize); - VFS_WRITE(f, in->mip[2].data, in->mip[2].datasize); - VFS_WRITE(f, in->mip[3].data, in->mip[3].datasize); - if (wad2.magic[3] == '3') - { - VFS_WRITE(f, "\x00\x01", 2); - VFS_WRITE(f, host_basepal, 256*3); - } - } + else if (!ImgTool_MipExport(args, f, in, entry->name, wadtype)) + continue; + wad2.num++; entry->size = entry->dsize = VFS_TELL(f)-entry->offset; - ImgTool_FreeMips(in); } } wad2.offset = VFS_TELL(f); @@ -1671,14 +2062,50 @@ static void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const VFS_WRITE(f, &wad2, sizeof(wad2)); VFS_CLOSE(f); + free(wadentries); FileList_Release(&list); } int main(int argc, const char **argv) { + static const struct + { + const char *alias; + uploadfmt_t fmt; + } fmtaliases[] = { + {"BC1", PTI_BC1_RGBA}, + {"BC2", PTI_BC2_RGBA}, + {"BC3", PTI_BC3_RGBA}, + {"BC4", PTI_BC4_R}, + {"BC5", PTI_BC5_RG}, + {"BC6", PTI_BC6_RGB_UFLOAT}, + {"BC7", PTI_BC7_RGBA}, + {"ETC1", PTI_ETC1_RGB8}, + {"ETC2", PTI_ETC2_RGB8}, + {"ETCP", PTI_ETC2_RGB8A1}, + {"ETCA", PTI_ETC2_RGB8A8}, + {"ASTC4x4", PTI_ASTC_4X4_LDR}, + {"ASTC5x4", PTI_ASTC_5X4_LDR}, + {"ASTC5x5", PTI_ASTC_5X5_LDR}, + {"ASTC6x5", PTI_ASTC_6X5_LDR}, + {"ASTC6x6", PTI_ASTC_6X6_LDR}, + {"ASTC8x5", PTI_ASTC_8X5_LDR}, + {"ASTC8x6", PTI_ASTC_8X6_LDR}, + {"ASTC10x5",PTI_ASTC_10X5_LDR}, + {"ASTC10x6",PTI_ASTC_10X6_LDR}, + {"ASTC8x8", PTI_ASTC_8X8_LDR}, + {"ASTC10x8",PTI_ASTC_10X8_LDR}, + {"ASTC10x10",PTI_ASTC_10X10_LDR}, + {"ASTC12x10",PTI_ASTC_12X10_LDR}, + {"ASTC12x12",PTI_ASTC_12X12_LDR}, + {"LUM8", PTI_L8}, + {"RGBA", PTI_RGBA8}, + {"RGB", PTI_RGB8}, + }; enum { + mode_unspecified, mode_info, mode_convert, mode_autotree, @@ -1686,19 +2113,21 @@ int main(int argc, const char **argv) mode_genwad2, mode_genwad3, mode_extractwad, - } mode = mode_info; + } mode = mode_unspecified; size_t u, f; qboolean nomoreopts = false; struct opts_s args; size_t files = 0; + const char *outname = NULL; for (u = 1; u < countof(sh_config.texfmt); u++) sh_config.texfmt[u] = true; args.flags = 0; args.newpixelformat = PTI_INVALID; args.mipnum = 0; - args.textype = -1; + args.textype = PTI_ANY; args.defaultext = NULL; + args.width = args.height = 0; sh_config.texture2d_maxsize = 1u<<31; sh_config.texture3d_maxsize = 1u<<31; @@ -1727,12 +2156,15 @@ int main(int argc, const char **argv) showhelp: Con_Printf(DISTRIBUTION " Image Tool\n"); Con_Printf("show info : %s -i *.ktx\n", argv[0]); - Con_Printf("compress : %s --astc_6x6_ldr [--nomips] in.png out.ktx [in2.png out2.ktx]\n", argv[0]); - Con_Printf("compress : %s --bc3_rgba [--premul] [--nomips] in.png out.dds\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]); - Con_Printf("convert : %s --convert in.exr out.dds\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); - Con_Printf("recursive : %s --auto --astc_6x6_ldr destdir srcdir\n\tCompresses the files to dds (writing to an optionally different directory)", argv[0]); + Con_Printf("compress : %s --astc_6x6_ldr [--nomips] --ext ktx in.png [in2.png ...]\n", argv[0]); + Con_Printf("compress : %s --bc3 [--premul] [--nomips] in.png\n\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\n", argv[0]); + Con_Printf("convert : %s --convert in.exr\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); + Con_Printf("merge : %s -o output [--cube|--3d|--2darray|--cubearray] [--bc1] foo_*.png\n\tConvert to different file format, while trying to preserve pixel formats.\n", argv[0]); + Con_Printf("recursive : %s --auto --astc_6x6_ldr destdir srcdir\n\tCompresses the files to dds (writing to an optionally different directory)\n", argv[0]); Con_Printf("decompress : %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\n\tDecompresses any block-compressed pixel data.\n", argv[0]); - Con_Printf("create wad : %s -w [--exportmip 2] out.wad srcdir\n", argv[0]); + Con_Printf("create mips: %s --ext mip [--bc1] [--resize width height] [--exportmip 2] *.dds\n", argv[0]); + Con_Printf("create xwad: %s --genwadx [--exportmip 2] [--bc1] out.wad srcdir\n", argv[0]); + Con_Printf("create wad : %s -w [--exportmip 2] out.wad *.mipsrcdir\n", argv[0]); Con_Printf("extract wad: %s -x [--ext png] src.wad\n", argv[0]); Con_Printf("extract bsp: %s -x [--ext png] src.bsp\n", argv[0]); @@ -1743,8 +2175,12 @@ showhelp: int bb,bw,bh; Image_BlockSizeForEncoding(f, &bb,&bw,&bh); if (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST) + { + if (f >= PTI_ASTC_4X4_SRGB) + continue; Con_Printf(" --%-16s %5.3g-bpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); - else if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R8||f==PTI_BC5_RG8) + } + else if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R||f==PTI_BC5_RG) Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); else if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA) Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh)); @@ -1784,14 +2220,39 @@ showhelp: mode = mode_autotree; else if (!files && (!strcmp(argv[u], "-i") || !strcmp(argv[u], "--info"))) mode = mode_info; - else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad3"))) - mode = mode_genwad3; else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad2"))) mode = mode_genwad2; + else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwad3"))) + mode = mode_genwad3; else if (!files && (!strcmp(argv[u], "-w") || !strcmp(argv[u], "--genwadx"))) mode = mode_genwadx; else if (!files && (!strcmp(argv[u], "-x") || !strcmp(argv[u], "--extractwad"))) mode = mode_extractwad; + else if (!files && (!strcmp(argv[u], "-v") || !strcmp(argv[u], "--verbose"))) + verbose = true; + else if (!files && (!strcmp(argv[u], "-o") || !strcmp(argv[u], "--outfile"))) + { + if (u+1 < argc) + outname = argv[++u]; + else + { + Con_Printf("--outfile requires output filename\n"); + return 1; + } + } + else if (!strcmp(argv[u], "--resize")) + { + if (u+2 < argc) + { + args.width = atoi(argv[++u]); + args.height = atoi(argv[++u]); + } + else + { + Con_Printf("--resize requires width+height values\n"); + return 1; + } + } else if (!strcmp(argv[u], "--2d")) args.textype = PTI_2D; else if (!strcmp(argv[u], "--3d")) @@ -1812,11 +2273,13 @@ showhelp: args.flags &= ~IF_PREMULTIPLYALPHA; else if (!strcmp(argv[u], "--ext")) { + if (mode == mode_unspecified) + mode = mode_convert; if (u+1 < argc) args.defaultext = argv[++u]; else { - Con_Printf("--exportmip requires trailing numeric argument\n"); + Con_Printf("--ext requires output extension\n"); return 1; } } @@ -1835,17 +2298,35 @@ showhelp: { if (argv[u][1] == '-') { + //try aliases first. + for (f = 0; f < countof(fmtaliases); f++) + { + if (!strcasecmp(argv[u]+2, fmtaliases[f].alias)) + { + args.newpixelformat = fmtaliases[f].fmt; + if (mode == mode_unspecified) + mode = mode_convert; + break; + } + } + if (f < countof(fmtaliases)) + continue; + + //now try our formal format names for (f = 0; f < PTI_MAX; f++) { if (!strcasecmp(argv[u]+2, Image_FormatName(f))) { args.newpixelformat = f; - mode = mode_convert; + if (mode == mode_unspecified) + mode = mode_convert; break; } } if (f < PTI_MAX) continue; + + //nope, not a format name } Con_Printf("Unknown arg %s\n", argv[u]); goto showhelp; @@ -1856,31 +2337,41 @@ showhelp: argv[files++] = argv[u]; } + if (mode == mode_unspecified && args.textype!=PTI_ANY) + mode = mode_convert; + if (!args.defaultext) { + if (mode == mode_unspecified) + mode = mode_info; + if (mode == mode_extractwad) args.defaultext = "png"; //something the user expects to be able to view easily (and lossless) + else if (args.newpixelformat >= PTI_BC1_RGB && args.newpixelformat < PTI_BC4_R) + args.defaultext = "dds"; else args.defaultext = "ktx"; } + else + { + if (mode == mode_unspecified) + mode = mode_convert; + } if (mode == mode_info) { //just print info about each listed file. for (u = 0; u < files; u++) ImgTool_Info(&args, argv[u]); } - else if (mode == mode_convert && files > 1 && args.textype>=0) //overwrite input + else if (mode == mode_convert && args.textype!=PTI_ANY && outname) //overwrite input { - files--; - ImgTool_Convert(&args, ImgTool_Combine(&args, argv, files), "combined", argv[files]); + ImgTool_Convert(&args, ImgTool_Combine(&args, argv, files), "combined", outname); } - else if (mode == mode_convert && files == 1 && args.textype<0) //overwrite input - ImgTool_Convert(&args, ImgTool_Read(&args, argv[0]), argv[0], NULL); - else if (mode == mode_convert && !(files&1) && args.textype<0) //list of pairs + else if (mode == mode_convert && args.textype==PTI_ANY && (!outname||files==1)) //list of files (output filenames will be generated according to -ext arg) { - //-c src1 dst1 src2 dst2 - for (u = 0; u+1 < files; u+=2) - ImgTool_Convert(&args, ImgTool_Read(&args, argv[u]), argv[u], argv[u+1]); + //-c src1 src2 src3 + for (u = 0; u < files; u++) + ImgTool_Convert(&args, ImgTool_Read(&args, argv[u]), argv[u], NULL); } else if (mode == mode_autotree && files == 2) ImgTool_TreeConvert(&args, argv[0], argv[1]);