wined3d: Relative addressing offsets are limited to [-64; 63] in arb.
diff --git a/dlls/wined3d/arb_program_shader.c b/dlls/wined3d/arb_program_shader.c
index 20d338e..d6ca4a8 100644
--- a/dlls/wined3d/arb_program_shader.c
+++ b/dlls/wined3d/arb_program_shader.c
@@ -467,7 +467,15 @@
     strcat(hwLine, tmpReg);
     break;
   case WINED3DSPR_CONST:
-    sprintf(tmpReg, "C[%s%u]", (param & WINED3DSHADER_ADDRMODE_RELATIVE) ? "A0.x + " : "", reg);
+      if(param & WINED3DSHADER_ADDRMODE_RELATIVE) {
+          if(reg - This->rel_offset >= 0) {
+              sprintf(tmpReg, "C[A0.x + %u]", reg - This->rel_offset);
+          } else {
+              sprintf(tmpReg, "C[A0.x - %u]", -reg + This->rel_offset);
+          }
+      } else {
+          sprintf(tmpReg, "C[%u]", reg);
+      }
     strcat(hwLine, tmpReg);
     break;
   case WINED3DSPR_ADDR: /*case D3DSPR_TEXTURE:*/
@@ -1571,6 +1579,7 @@
 /* Map the opcode 1-to-1 to the GL code */
 void vshader_hw_map2gl(SHADER_OPCODE_ARG* arg) {
 
+    IWineD3DVertexShaderImpl *shader = (IWineD3DVertexShaderImpl*) arg->shader;
     CONST SHADER_OPCODE* curOpcode = arg->opcode;
     SHADER_BUFFER* buffer = arg->buffer;
     DWORD dst = arg->dst;
@@ -1580,9 +1589,17 @@
     char tmpLine[256];
     unsigned int i;
 
-    if ((curOpcode->opcode == WINED3DSIO_MOV && dst_regtype == WINED3DSPR_ADDR) || curOpcode->opcode == WINED3DSIO_MOVA)
-        strcpy(tmpLine, "ARL");
-    else
+    if ((curOpcode->opcode == WINED3DSIO_MOV && dst_regtype == WINED3DSPR_ADDR) || curOpcode->opcode == WINED3DSIO_MOVA) {
+        if(shader->rel_offset) {
+            memset(tmpLine, 0, sizeof(tmpLine));
+            vshader_program_add_param(arg, src[0], TRUE, tmpLine);
+            shader_addline(buffer, "ADD TMP.x, %s, helper_const.z;\n", tmpLine);
+            shader_addline(buffer, "ARL A0.x, TMP.x;\n");
+            return;
+        } else {
+            strcpy(tmpLine, "ARL");
+        }
+    } else
         strcpy(tmpLine, curOpcode->glname);
 
     if (curOpcode->num_params > 0) {
diff --git a/dlls/wined3d/baseshader.c b/dlls/wined3d/baseshader.c
index df0ac0c..520554f 100644
--- a/dlls/wined3d/baseshader.c
+++ b/dlls/wined3d/baseshader.c
@@ -446,6 +446,15 @@
 
                 else if (WINED3DSPR_MISCTYPE == regtype && reg == 0 && pshader)
                     reg_maps->vpos = 1;
+
+                else if(WINED3DSPR_CONST == regtype && !pshader &&
+                        param & WINED3DSHADER_ADDRMODE_RELATIVE) {
+                    if(reg <= ((IWineD3DVertexShaderImpl *) This)->min_rel_offset) {
+                        ((IWineD3DVertexShaderImpl *) This)->min_rel_offset = reg;
+                    } else if(reg >= ((IWineD3DVertexShaderImpl *) This)->max_rel_offset) {
+                        ((IWineD3DVertexShaderImpl *) This)->max_rel_offset = reg;
+                    }
+                }
             }
         }
     }
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index a77ea96..ada76e2 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -407,6 +407,38 @@
  **********************************************************/
 
 #define GLINFO_LOCATION (*gl_info)
+static inline BOOL test_arb_vs_offset_limit(WineD3D_GL_Info *gl_info) {
+    GLuint prog;
+    BOOL ret = FALSE;
+    const char *testcode =
+        "!!ARBvp1.0\n"
+        "PARAM C[66] = { program.env[0..65] };\n"
+        "ADDRESS A0;"
+        "ARL A0.x, 0.0;\n"
+        "MOV result.position, C[A0.x + 65];\n"
+        "END\n";
+
+    while(glGetError());
+    GL_EXTCALL(glGenProgramsARB(1, &prog));
+    if(!prog) {
+        ERR("Failed to create an ARB offset limit test program\n");
+    }
+    GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, prog));
+    GL_EXTCALL(glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
+                                  strlen(testcode), testcode));
+    if(glGetError() != 0) {
+        TRACE("OpenGL implementation does not allow indirect addressing offsets > 63\n");
+        TRACE("error: %s\n", debugstr_a((const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB)));
+        ret = TRUE;
+    } else TRACE("OpenGL implementation allows offsets > 63\n");
+
+    GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0));
+    GL_EXTCALL(glDeleteProgramsARB(1, &prog));
+    checkGLcall("ARB vp offset limit test cleanup\n");
+
+    return ret;
+}
+
 BOOL IWineD3DImpl_FillGLCaps(WineD3D_GL_Info *gl_info) {
     const char *GL_Extensions    = NULL;
     const char *WGL_Extensions   = NULL;
@@ -691,7 +723,7 @@
             if (gl_info->supported[ARB_FRAGMENT_PROGRAM]) {
                 GLint tmp;
                 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &tmp);
-                gl_info->max_fragment_samplers = min(MAX_FRAGMENT_SAMPLERS, tmp);
+                gl_info->max_fragment_samplers = min(8, tmp);
             } else {
                 gl_info->max_fragment_samplers = max(gl_info->max_fragment_samplers, gl_max);
             }
@@ -747,6 +779,8 @@
             GL_EXTCALL(glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &gl_max));
             gl_info->vs_arb_max_instructions = gl_max;
             TRACE_(d3d_caps)("Max ARB_VERTEX_PROGRAM native instructions: %d\n", gl_info->vs_arb_max_instructions);
+
+            gl_info->arb_vs_offset_limit = test_arb_vs_offset_limit(gl_info);
         }
         if (gl_info->supported[ARB_VERTEX_SHADER]) {
             glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &gl_max);
diff --git a/dlls/wined3d/vertexshader.c b/dlls/wined3d/vertexshader.c
index 5c9da19..e49e34a 100644
--- a/dlls/wined3d/vertexshader.c
+++ b/dlls/wined3d/vertexshader.c
@@ -353,7 +353,9 @@
          * before the homogenous divide, so we have to take the w into account: z = ((z / w) * 2 - 1) * w,
          * which is the same as z = z / 2 - w.
          */
-        shader_addline(&buffer, "gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n");
+        shader_addline(&buffer, "tmp0 = gl_Position;\n");
+        shader_addline(&buffer, "gl_Position.z = tmp0.z * 2.0;\n");
+        shader_addline(&buffer, "gl_Position.z = gl_Position.z - gl_Position.w;\n");
 
         shader_addline(&buffer, "}\n");
 
@@ -369,7 +371,7 @@
 
         /*  Create the hw ARB shader */
         shader_addline(&buffer, "!!ARBvp1.0\n");
-        shader_addline(&buffer, "PARAM zfixup = { 2.0, -1.0, 0.0, 0.0 };\n");
+        shader_addline(&buffer, "PARAM helper_const = { 2.0, -1.0, %d.0, 0.0 };\n", This->rel_offset);
 
         /* Mesa supports only 95 constants */
         if (GL_VEND(MESA) || GL_VEND(WINE))
@@ -377,7 +379,7 @@
                 min(95, This->baseShader.limits.constant_float);
 
         /* Some instructions need a temporary register. Add it if needed, but only if it is really needed */
-        if(reg_maps->usesnrm) {
+        if(reg_maps->usesnrm || This->rel_offset) {
             shader_addline(&buffer, "TEMP TMP;\n");
         }
 
@@ -409,7 +411,7 @@
         /* Z coord [0;1]->[-1;1] mapping, see comment in transform_projection in state.c
          * and the glsl equivalent
          */
-        shader_addline(&buffer, "MAD TMP_OUT.z, TMP_OUT.z, zfixup.x, -TMP_OUT.w;\n");
+        shader_addline(&buffer, "MAD TMP_OUT.z, TMP_OUT.z, helper_const.x, -TMP_OUT.w;\n");
 
         shader_addline(&buffer, "MOV result.position, TMP_OUT;\n");
         
@@ -566,6 +568,8 @@
     list_init(&This->baseShader.constantsI);
 
     /* Second pass: figure out registers used, semantics, etc.. */
+    This->min_rel_offset = GL_LIMITS(vshader_constantsF);
+    This->max_rel_offset = 0;
     memset(reg_maps, 0, sizeof(shader_reg_maps));
     hr = shader_get_registers_used((IWineD3DBaseShader*) This, reg_maps,
        This->semantics_in, This->semantics_out, pFunction, NULL);
@@ -573,6 +577,23 @@
 
     This->baseShader.shader_mode = deviceImpl->vs_selected_mode;
 
+    if(deviceImpl->vs_selected_mode == SHADER_ARB &&
+       (GLINFO_LOCATION).arb_vs_offset_limit      &&
+       This->min_rel_offset <= This->max_rel_offset) {
+
+        if(This->max_rel_offset - This->min_rel_offset > 127) {
+            FIXME("The difference between the minimum and maximum relative offset is > 127\n");
+            FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
+            FIXME("Min: %d, Max: %d\n", This->min_rel_offset, This->max_rel_offset);
+        } else if(This->max_rel_offset - This->min_rel_offset > 63) {
+            This->rel_offset = This->min_rel_offset + 63;
+        } else if(This->max_rel_offset > 63) {
+            This->rel_offset = This->min_rel_offset;
+        } else {
+            This->rel_offset = 0;
+        }
+    }
+
     /* copy the function ... because it will certainly be released by application */
     if (NULL != pFunction) {
         void *function;
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 56ba569..baa9152 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -2051,6 +2051,9 @@
 
     /* run time datas...  */
     VSHADERDATA                *data;
+    UINT                       min_rel_offset, max_rel_offset;
+    UINT                       rel_offset;
+
 #if 0 /* needs reworking */
     /* run time datas */
     VSHADERINPUTDATA input;
diff --git a/include/wine/wined3d_gl.h b/include/wine/wined3d_gl.h
index 8694f37..7fbe25c 100644
--- a/include/wine/wined3d_gl.h
+++ b/include/wine/wined3d_gl.h
@@ -3744,6 +3744,8 @@
   GL_VSVersion vs_nv_version;
   GL_VSVersion vs_ati_version;
 
+  BOOL arb_vs_offset_limit;
+
   BOOL supported[OPENGL_SUPPORTED_EXT_END + 1];
 
   /** OpenGL EXT and ARB functions ptr */