- Add ability to build MojoShader as a shared library
authorEthan Lee <flibitijibibo@flibitijibibo.com>
Fri, 01 Jan 2016 02:12:19 -0500
changeset 1150 02c0f0afb39a
parent 1149 265ab4117525
child 1151 5e0ebc5366f3
- Add ability to build MojoShader as a shared library - Off by default, turn on BUILD_SHARED in CMake - Add ability to disable shader profiles from CMake - Add MOJOSHADER_glSetVertexAttribDivisor - Behaves like glSetVertexAttribute, but for glVertexAttribDivisor - Add MOJOSHADER_glGetVertexAttribLocation - Allows user to directly call glVertexAttribPointer in their program - Full Effect framework support - API is in a separate header, see mojoshader_effects.h for details - Off by default, turn on EFFECT_SUPPORT in CMake - #define MOJOSHADER_EFFECT_SUPPORT before including mojoshader.h - Add MOJOSHADER_PROFILE_GLSLES shader profile - Basically just GLSL120 without using builtins - Add ability to flip gl_Position via MOJOSHADER_glProgramViewportFlip - You probably shouldn't use this unless you're a project like FNA. - Off by default, turn on FLIP_VIEWPORT in CMake - #define MOJOSHADER_FLIP_RENDERTARGET before including mojoshader.h - Add ability to adjust Z coordinates for [0, 1]-like depth clipping - You probably shouldn't use this unless you're a project like FNA. - Off by default, turn on DEPTH_CLIPPING in CMake - Add ability to build MojoShader with XNA4 vertex texturing behavior - You probably shouldn't use this unless you're reimplementing XNA. - Off by default, turn on XNA4_VERTEXTEXTURE in CMake - Add DECLSPEC/MOJOSHADERCALL to functions/function pointers - This allows for easier interop with other languages, i.e. C# P/Invoke - Fix printing float values for locales where radix is not '.' - Various minor bugfixes, optimizations, Win32 buildfixes, etc.
CMakeLists.txt
mojoshader.c
mojoshader.h
mojoshader_common.c
mojoshader_effects.c
mojoshader_effects.h
mojoshader_internal.h
mojoshader_opengl.c
utils/testparse.c
--- a/CMakeLists.txt	Tue Oct 13 12:08:55 2015 -0400
+++ b/CMakeLists.txt	Fri Jan 01 02:12:19 2016 -0500
@@ -1,6 +1,18 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
 PROJECT(MojoShader)
 
+OPTION(BUILD_SHARED "Build MojoShader as a shared library" OFF)
+OPTION(PROFILE_D3D "Build MojoShader with support for the D3D profile" ON)
+OPTION(PROFILE_BYTECODE "Build MojoShader with support for the BYTECODE profile" ON)
+OPTION(PROFILE_GLSL120 "Build MojoShader with support for the GLSL120 profile" ON)
+OPTION(PROFILE_GLSL "Build MojoShader with support for the GLSL profile" ON)
+OPTION(PROFILE_ARB1 "Build MojoShader with support for the ARB1 profile" ON)
+OPTION(PROFILE_ARB1_NV "Build MojoShader with support for the ARB1_NV profile" ON)
+OPTION(EFFECT_SUPPORT "Build MojoShader with support for Effect framework files" OFF)
+OPTION(FLIP_VIEWPORT "Build MojoShader with the ability to flip the GL viewport" OFF)
+OPTION(DEPTH_CLIPPING "Build MojoShader with the ability to simulate [0, 1] depth clipping" OFF)
+OPTION(XNA4_VERTEXTEXTURE "Build MojoShader with XNA4 vertex texturing behavior" OFF)
+
 INCLUDE_DIRECTORIES(.)
 
 FIND_PROGRAM(HG hg DOC "Path to hg command line app: http://www.selenic.com/mercurial/")
@@ -61,7 +73,55 @@
     ARGS -q "-T${CMAKE_CURRENT_SOURCE_DIR}/misc/lempar.c" "${CMAKE_CURRENT_SOURCE_DIR}/mojoshader_parser_hlsl.lemon"
 )
 
-ADD_LIBRARY(mojoshader STATIC
+IF(APPLE)
+    find_library(CARBON_FRAMEWORK Carbon)  # Stupid Gestalt.
+ENDIF(APPLE)
+
+IF(NOT PROFILE_D3D)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_D3D=0)
+ENDIF(NOT PROFILE_D3D)
+IF(NOT PROFILE_BYTECODE)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_BYTECODE=0)
+ENDIF(NOT PROFILE_BYTECODE)
+IF(NOT PROFILE_GLSL120)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_GLSL120=0)
+ENDIF(NOT PROFILE_GLSL120)
+IF(NOT PROFILE_GLSL)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_GLSL=0)
+ENDIF(NOT PROFILE_GLSL)
+IF(NOT PROFILE_ARB1)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_ARB1=0)
+ENDIF(NOT PROFILE_ARB1)
+IF(NOT PROFILE_ARB1_NV)
+    ADD_DEFINITIONS(-DSUPPORT_PROFILE_ARB1_NV=0)
+ENDIF(NOT PROFILE_ARB1_NV)
+
+IF(EFFECT_SUPPORT)
+    IF(UNIX)
+        SET(LIBM -lm)
+    ENDIF(UNIX)
+    ADD_DEFINITIONS(-DMOJOSHADER_EFFECT_SUPPORT)
+ENDIF(EFFECT_SUPPORT)
+
+IF(FLIP_VIEWPORT)
+    ADD_DEFINITIONS(-DMOJOSHADER_FLIP_RENDERTARGET)
+ENDIF(FLIP_VIEWPORT)
+
+IF(DEPTH_CLIPPING)
+    ADD_DEFINITIONS(-DMOJOSHADER_DEPTH_CLIPPING)
+ENDIF(DEPTH_CLIPPING)
+
+IF(XNA4_VERTEXTEXTURE)
+    ADD_DEFINITIONS(-DMOJOSHADER_XNA4_VERTEX_TEXTURES)
+ENDIF(XNA4_VERTEXTEXTURE)
+
+IF(BUILD_SHARED)
+    SET(LIBRARY_FORMAT SHARED)
+ELSE(BUILD_SHARED)
+    SET(LIBRARY_FORMAT STATIC)
+ENDIF(BUILD_SHARED)
+
+ADD_LIBRARY(mojoshader ${LIBRARY_FORMAT}
     mojoshader.c
     mojoshader_common.c
     mojoshader_effects.c
@@ -71,10 +131,9 @@
     mojoshader_assembler.c
     mojoshader_opengl.c
 )
-
-IF(UNIX)
-    SET(LIBM -lm)
-ENDIF(UNIX)
+IF(BUILD_SHARED)
+    TARGET_LINK_LIBRARIES(mojoshader ${LIBM} ${CARBON_FRAMEWORK})
+ENDIF(BUILD_SHARED)
 
 SET_SOURCE_FILES_PROPERTIES(
     mojoshader_compiler.c
@@ -94,10 +153,6 @@
     )
 ENDIF(NOT RE2C)
 
-IF(APPLE)
-    find_library(CARBON_FRAMEWORK Carbon)  # Stupid Gestalt.
-ENDIF(APPLE)
-
 find_path(SDL2_INCLUDE_DIR SDL.h PATH_SUFFIXES include/SDL2)
 find_library(SDL2 NAMES SDL2 PATH_SUFFIXES lib)
 IF(SDL2)
--- a/mojoshader.c	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader.c	Fri Jan 01 02:12:19 2016 -0500
@@ -178,6 +178,9 @@
 #if SUPPORT_PROFILE_GLSL120
     int profile_supports_glsl120;
 #endif
+#if SUPPORT_PROFILE_GLSLES
+    int profile_supports_glsles;
+#endif
 } Context;
 
 
@@ -198,6 +201,12 @@
 #define support_glsl120(ctx) (0)
 #endif
 
+#if SUPPORT_PROFILE_GLSLES
+#define support_glsles(ctx) ((ctx)->profile_supports_glsles)
+#else
+#define support_glsles(ctx) (0)
+#endif
+
 
 // Profile entry points...
 
@@ -300,12 +309,12 @@
     ctx->free(ptr, ctx->malloc_data);
 } // Free
 
-static void *MallocBridge(int bytes, void *data)
+static void * MOJOSHADERCALL MallocBridge(int bytes, void *data)
 {
     return Malloc((Context *) data, (size_t) bytes);
 } // MallocBridge
 
-static void FreeBridge(void *ptr, void *data)
+static void MOJOSHADERCALL FreeBridge(void *ptr, void *data)
 {
     Free((Context *) data, ptr);
 } // FreeBridge
@@ -446,7 +455,7 @@
 static void floatstr(Context *ctx, char *buf, size_t bufsize, float f,
                      int leavedecimal)
 {
-    const size_t len = snprintf(buf, bufsize, "%f", f);
+    const size_t len = MOJOSHADER_printFloat(buf, bufsize, f);
     if ((len+2) >= bufsize)
         fail(ctx, "BUG: internal buffer is too small");
     else
@@ -2185,6 +2194,24 @@
     } // else if
     #endif
 
+    #if SUPPORT_PROFILE_GLSLES
+    else if (strcmp(profilestr, MOJOSHADER_PROFILE_GLSLES) == 0)
+    {
+        ctx->profile_supports_glsles = 1;
+        push_output(ctx, &ctx->preflight);
+        output_line(ctx, "#version 100");
+        if (shader_is_vertex(ctx))
+            output_line(ctx, "precision highp float;");
+        else
+            output_line(ctx, "precision mediump float;");
+        output_line(ctx, "precision mediump int;");
+        output_line(ctx, "varying vec4 v_FrontColor;");
+        output_line(ctx, "varying vec4 v_FrontSecondaryColor;");
+        output_line(ctx, "varying vec4 v_TexCoord[10];"); // 10 according to SM3
+        pop_output(ctx);
+    } // else if
+    #endif
+
     else
     {
         failf(ctx, "Profile '%s' unsupported or unknown.", profilestr);
@@ -2212,6 +2239,15 @@
         set_used_register(ctx, REG_TYPE_COLOROUT, 0, 1);
         output_line(ctx, "%s_oC0 = %s_r0;", shstr, shstr);
     } // if
+    else if (shader_is_vertex(ctx))
+    {
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+        output_line(ctx, "gl_Position.y = gl_Position.y * vpFlip;");
+#endif
+#ifdef MOJOSHADER_DEPTH_CLIPPING
+        output_line(ctx, "gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;");
+#endif
+    } // else if
 
     // force a RET opcode if we're at the end of the stream without one.
     if (ctx->previous_opcode != OPCODE_RET)
@@ -2230,7 +2266,19 @@
     {
         char buf[64];
         get_GLSL_uniform_array_varname(ctx, regtype, buf, sizeof (buf));
-        output_line(ctx, "uniform vec4 %s[%d];", buf, size);
+        const char *typ;
+        switch (regtype)
+        {
+            case REG_TYPE_CONST: typ = "vec4"; break;
+            case REG_TYPE_CONSTINT: typ ="ivec4"; break;
+            case REG_TYPE_CONSTBOOL: typ = "bool"; break;
+            default:
+            {
+                fail(ctx, "BUG: used a uniform we don't know how to define.");
+                return;
+            } // default
+        } // switch
+        output_line(ctx, "uniform %s %s[%d];", typ, buf, size);
     } // if
 } // output_GLSL_uniform_array
 
@@ -2252,6 +2300,10 @@
     output_GLSL_uniform_array(ctx, REG_TYPE_CONST, ctx->uniform_float4_count);
     output_GLSL_uniform_array(ctx, REG_TYPE_CONSTINT, ctx->uniform_int4_count);
     output_GLSL_uniform_array(ctx, REG_TYPE_CONSTBOOL, ctx->uniform_bool_count);
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+    if (shader_is_vertex(ctx))
+        output_line(ctx, "uniform float vpFlip;");
+#endif
     pop_output(ctx);
 } // emit_GLSL_finalize
 
@@ -2535,15 +2587,34 @@
                 case MOJOSHADER_USAGE_COLOR:
                     index_str[0] = '\0';  // no explicit number.
                     if (index == 0)
+                    {
+#if SUPPORT_PROFILE_GLSLES
+                        if (support_glsles(ctx))
+                            usage_str = "v_FrontColor";
+                        else
+#endif
                         usage_str = "gl_FrontColor";
+                    } // if
                     else if (index == 1)
+                    {
+#if SUPPORT_PROFILE_GLSLES
+                        if (support_glsles(ctx))
+                            usage_str = "v_FrontSecondaryColor";
+                        else
+#endif
                         usage_str = "gl_FrontSecondaryColor";
+                    } // else if
                     break;
                 case MOJOSHADER_USAGE_FOG:
                     usage_str = "gl_FogFragCoord";
                     break;
                 case MOJOSHADER_USAGE_TEXCOORD:
                     snprintf(index_str, sizeof (index_str), "%u", (uint) index);
+#if SUPPORT_PROFILE_GLSLES
+                    if (support_glsles(ctx))
+                        usage_str = "v_TexCoord";
+                    else
+#endif
                     usage_str = "gl_TexCoord";
                     arrayleft = "[";
                     arrayright = "]";
@@ -2609,6 +2680,11 @@
                 if (shader_version_atleast(ctx, 1, 4))
                 {
                     snprintf(index_str, sizeof (index_str), "%u", (uint) index);
+#if SUPPORT_PROFILE_GLSLES
+                    if (support_glsles(ctx))
+                        usage_str = "v_TexCoord";
+                    else
+#endif
                     usage_str = "gl_TexCoord";
                     arrayleft = "[";
                     arrayright = "]";
@@ -2619,12 +2695,33 @@
             {
                 index_str[0] = '\0';  // no explicit number.
                 if (index == 0)
+                {
+#if SUPPORT_PROFILE_GLSLES
+                    if (support_glsles(ctx))
+                        usage_str = "v_FrontColor";
+                    else
+#endif
                     usage_str = "gl_Color";
+                } // if
                 else if (index == 1)
+                {
+#if SUPPORT_PROFILE_GLSLES
+                    if (support_glsles(ctx))
+                        usage_str = "v_FrontSecondaryColor";
+                    else
+#endif
                     usage_str = "gl_SecondaryColor";
+                } // else if
                 else
                     fail(ctx, "unsupported color index");
             } // else if
+
+            else if (usage == MOJOSHADER_USAGE_DEPTH) // !!! FIXME: Possibly more! -flibit
+            {
+                push_output(ctx, &ctx->globals);
+                output_line(ctx, "attribute vec4 %s;", var);
+                pop_output(ctx);
+            } // else if
         } // else if
 
         else if (regtype == REG_TYPE_MISCTYPE)
@@ -6155,6 +6252,7 @@
 // This is for profiles that extend other profiles...
 static const struct { const char *from; const char *to; } profileMap[] =
 {
+    { MOJOSHADER_PROFILE_GLSLES, MOJOSHADER_PROFILE_GLSL },
     { MOJOSHADER_PROFILE_GLSL120, MOJOSHADER_PROFILE_GLSL },
     { MOJOSHADER_PROFILE_NV2, MOJOSHADER_PROFILE_ARB1 },
     { MOJOSHADER_PROFILE_NV3, MOJOSHADER_PROFILE_ARB1 },
@@ -6843,6 +6941,35 @@
             ctx->dwords[0] = usage;
             ctx->dwords[1] = index;
         } // if
+        else if (regtype == REG_TYPE_TEXTURE)
+        {
+            const uint32 usage = (token & 0xF);
+            const uint32 index = ((token >> 16) & 0xF);
+            if (usage == MOJOSHADER_USAGE_TEXCOORD)
+            {
+                if (index > 7)
+                    fail(ctx, "DCL texcoord usage must have 0-7 index");
+            } // if
+            else if (usage == MOJOSHADER_USAGE_COLOR)
+            {
+                if (index != 0)
+                    fail(ctx, "DCL texcoord usage must have 0 index");
+            } // else if
+            else
+                fail(ctx, "Invalid DCL texture usage");
+
+            reserved_mask = 0x7FF0FFE0;
+            ctx->dwords[0] = usage;
+            ctx->dwords[1] = index;
+        } // else if
+        else if (regtype == REG_TYPE_SAMPLER)
+        {
+            const uint32 ttype = ((token >> 27) & 0xF);
+            if (!valid_texture_type(ttype))
+                fail(ctx, "Unknown sampler texture type");
+            reserved_mask = 0x6FFFFFFF;
+            ctx->dwords[0] = ttype;
+        } // else if
         else
         {
             unsupported = 1;
@@ -7086,14 +7213,19 @@
 
     else if (shader_is_vertex(ctx))
     {
-        const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0];
-        const int index = ctx->dwords[1];
-        if (usage >= MOJOSHADER_USAGE_TOTAL)
-        {
-            fail(ctx, "unknown DCL usage");
-            return;
-        } // if
-        add_attribute_register(ctx, regtype, regnum, usage, index, wmask, mods);
+        if (regtype == REG_TYPE_SAMPLER)
+            add_sampler(ctx, regnum, (TextureType) ctx->dwords[0], 0);
+        else
+        {
+            const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0];
+            const int index = ctx->dwords[1];
+            if (usage >= MOJOSHADER_USAGE_TOTAL)
+            {
+                fail(ctx, "unknown DCL usage");
+                return;
+            } // if
+            add_attribute_register(ctx, regtype, regnum, usage, index, wmask, mods);
+        } // else
     } // if
 
     else if (shader_is_pixel(ctx))
@@ -7470,8 +7602,6 @@
     const RegisterType regtype = ctx->source_args[0].regtype;
     if ((regtype != REG_TYPE_PREDICATE) && (regtype != REG_TYPE_CONSTBOOL))
         fail(ctx, "IF src0 must be CONSTBOOL or PREDICATE");
-    else if (!replicate_swizzle(ctx->source_args[0].swizzle))
-        fail(ctx, "IF src0 must have replicate swizzle");
     // !!! FIXME: track if nesting depth.
 } // state_IF
 
@@ -8061,7 +8191,7 @@
     // !!! FIXME: check that (start+target) points to "ps_3_0", etc.
 
     ctab->symbol_count = constants;
-    ctab->symbols = Malloc(ctx, sizeof (MOJOSHADER_symbol) * constants);
+    ctab->symbols = (MOJOSHADER_symbol *) Malloc(ctx, sizeof (MOJOSHADER_symbol) * constants);
     if (ctab->symbols == NULL)
         return;
     memset(ctab->symbols, '\0', sizeof (MOJOSHADER_symbol) * constants);
@@ -8173,8 +8303,8 @@
     if ((tokcount < 2) || (SWAP32(tokens[1]) != PRES_ID))
         return;  // not a preshader.
 
-#if !SUPPORT_PRESHADERS
-    fail(ctx, "Preshader found, but preshader support is disabled!");
+#ifndef MOJOSHADER_EFFECT_SUPPORT
+    fail(ctx, "Preshader found, but effect support is disabled!");
 #else
 
     assert(ctx->have_preshader == 0);  // !!! FIXME: can you have more than one?
@@ -8183,10 +8313,12 @@
     // !!! FIXME: I don't know what specific versions signify, but we need to
     // !!! FIXME:  save this to test against the CTAB version field, if
     // !!! FIXME:  nothing else.
-    // !!! FIXME: 0x02 0x01 is probably the version (fx_2_1),
+    // !!! FIXME: 0x02 0x0? is probably the version (fx_2_?),
     // !!! FIXME:  and 0x4658 is the magic, like a real shader's version token.
-    const uint32 okay_version = 0x46580201;
-    if (SWAP32(tokens[2]) != okay_version)
+    const uint32 min_version = 0x46580200;
+    const uint32 max_version = 0x46580201;
+    const uint32 version = SWAP32(tokens[2]);
+    if (version < min_version || version > max_version)
     {
         fail(ctx, "Unsupported preshader version.");
         return;  // fail because the shader will malfunction w/o this.
@@ -8313,7 +8445,7 @@
     // Now we'll figure out the CTAB...
     CtabData ctabdata = { 0, 0, 0 };
     parse_constant_table(ctx, ctab.tokens - 1, ctab.tokcount * 4,
-                         okay_version, 0, &ctabdata);
+                         version, 0, &ctabdata);
 
     // preshader owns this now. Don't free it in this function.
     preshader->symbol_count = ctabdata.symbol_count;
@@ -8412,7 +8544,7 @@
             {
                 case 1:  // literal from CLIT block.
                 {
-                    if (item >= preshader->literal_count)
+                    if (item > preshader->literal_count)
                     {
                         fail(ctx, "Bogus preshader literal index.");
                         break;
@@ -8480,6 +8612,27 @@
 
         inst++;
     } // while
+
+    // Registers need to be vec4, round up to nearest 4
+    preshader->temp_count = (preshader->temp_count + 3) & ~3;
+
+    unsigned int largest = 0;
+    const MOJOSHADER_symbol *sym = preshader->symbols;
+    int i;
+    for (i = 0; i < preshader->symbol_count; i++, sym++)
+    {
+        const unsigned int val = sym->register_index + sym->register_count;
+        if (val > largest)
+            largest = val;
+    } // for
+
+    if (largest > 0)
+    {
+        const size_t len = largest * sizeof (float) * 4;
+        preshader->registers = (float *) Malloc(ctx, len);
+        memset(preshader->registers, '\0', len);
+        preshader->register_count = largest;
+    } // if
 #endif
 } // parse_preshader
 
@@ -8702,19 +8855,6 @@
 } // free_symbols
 
 
-static void free_preshader(MOJOSHADER_free f, void *d,
-                           MOJOSHADER_preshader *preshader)
-{
-    if (preshader != NULL)
-    {
-        f((void *) preshader->literals, d);
-        f((void *) preshader->instructions, d);
-        free_symbols(f, d, preshader->symbols, preshader->symbol_count);
-        f((void *) preshader, d);
-    } // if
-} // free_preshader
-
-
 static void destroy_context(Context *ctx)
 {
     if (ctx != NULL)
@@ -8737,7 +8877,7 @@
         free_variable_list(f, d, ctx->variables);
         errorlist_destroy(ctx->errors);
         free_symbols(f, d, ctx->ctab.symbols, ctx->ctab.symbol_count);
-        free_preshader(f, d, ctx->preshader);
+        MOJOSHADER_freePreshader(ctx->preshader, f, d);
         f(ctx, d);
     } // if
 } // destroy_context
@@ -9514,7 +9654,7 @@
     f((void *) data->samplers, d);
 
     free_symbols(f, d, data->symbols, data->symbol_count);
-    free_preshader(f, d, data->preshader);
+    MOJOSHADER_freePreshader(data->preshader, f, d);
 
     f(data, d);
 } // MOJOSHADER_freeParseData
@@ -9539,6 +9679,7 @@
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_BYTECODE, 3);
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL, 3);
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL120, 3);
+    PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSLES, 3);
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_ARB1, 2);
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV2, 2);
     PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV3, 2);
@@ -9547,5 +9688,378 @@
     return -1;  // unknown profile?
 } // MOJOSHADER_maxShaderModel
 
+
+const MOJOSHADER_preshader *MOJOSHADER_parsePreshader(const unsigned char *buf,
+                                                      const unsigned int _len,
+                                                      MOJOSHADER_malloc m,
+                                                      MOJOSHADER_free f,
+                                                      void *d)
+{
+    // !!! FIXME: This is copypasta ripped from parse_preshader -flibit
+    int i;
+
+    const uint32 *tokens = (const uint32 *) buf;
+    uint32 tokcount = _len / 4;
+
+    // !!! FIXME: This is stupid. -flibit
+    Context fillerContext;
+    fillerContext.malloc = m;
+    fillerContext.free = f;
+    fillerContext.malloc_data = d;
+    fillerContext.out_of_memory = 0;
+
+    MOJOSHADER_preshader *preshader = NULL;
+
+    const uint32 version_magic = 0x46580000;
+    const uint32 min_version = 0x00000200 | version_magic;
+    const uint32 max_version = 0x00000201 | version_magic;
+    const uint32 version = SWAP32(tokens[0]);
+    if (version < min_version || version > max_version)
+    {
+        // fail because the shader will malfunction w/o this.
+        goto parsePreshader_notAPreshader;
+    } // if
+
+    tokens++;
+    tokcount++;
+
+    // All sections of a preshader are packed into separate comment tokens,
+    //  inside the containing comment token block. Find them all before
+    //  we start, so we don't care about the order they appear in the file.
+    PreshaderBlockInfo ctab = { 0, 0, 0 };
+    PreshaderBlockInfo prsi = { 0, 0, 0 };
+    PreshaderBlockInfo fxlc = { 0, 0, 0 };
+    PreshaderBlockInfo clit = { 0, 0, 0 };
+
+    while (tokcount > 0)
+    {
+        uint32 subtokcount = 0;
+        // !!! FIXME: Passing a NULL ctx! -flibit
+        if ( (!is_comment_token(&fillerContext, *tokens, &subtokcount)) ||
+             (subtokcount > tokcount) )
+        {
+            goto parsePreshader_notAPreshader;
+        } // if
+
+        tokens++;
+        tokcount--;
+
+        const uint32 *nexttokens = tokens + subtokcount;
+        const uint32 nexttokcount = tokcount - subtokcount;
+
+        if (subtokcount > 0)
+        {
+            switch (SWAP32(*tokens))
+            {
+                #define PRESHADER_BLOCK_CASE(id, var) \
+                    case id##_ID: { \
+                        if (var.seen) \
+                            goto parsePreshader_notAPreshader; \
+                        var.tokens = tokens; \
+                        var.tokcount = subtokcount; \
+                        var.seen = 1; \
+                        break; \
+                    }
+                PRESHADER_BLOCK_CASE(CTAB, ctab);
+                PRESHADER_BLOCK_CASE(PRSI, prsi);
+                PRESHADER_BLOCK_CASE(FXLC, fxlc);
+                PRESHADER_BLOCK_CASE(CLIT, clit);
+                default:
+                    // Bogus preshader section
+                    goto parsePreshader_notAPreshader;
+                #undef PRESHADER_BLOCK_CASE
+            } // switch
+        } // if
+
+        tokens = nexttokens;
+        tokcount = nexttokcount;
+    } // while
+
+    if (!ctab.seen) // No CTAB block in preshader
+        goto parsePreshader_notAPreshader;
+    if (!fxlc.seen) // No FXLC block in preshader
+        goto parsePreshader_notAPreshader;
+    if (!clit.seen) // No CLIT block in preshader
+        goto parsePreshader_notAPreshader;
+
+    preshader = (MOJOSHADER_preshader *) m(sizeof (MOJOSHADER_preshader), d);
+    if (preshader == NULL)
+        goto parsePreshader_outOfMemory;
+    memset(preshader, '\0', sizeof (MOJOSHADER_preshader));
+
+    // Let's set up the constant literals first...
+    if (clit.tokcount == 0) // Bogus CLIT block in preshader
+        goto parsePreshader_notAPreshader;
+    else
+    {
+        const uint32 lit_count = SWAP32(clit.tokens[1]);
+        if (lit_count > ((clit.tokcount - 2) / 2))
+            // Bogus CLIT block in preshader
+            goto parsePreshader_notAPreshader;
+        else if (lit_count > 0)
+        {
+            preshader->literal_count = (unsigned int) lit_count;
+            assert(sizeof (double) == 8);  // just in case.
+            const size_t len = sizeof (double) * lit_count;
+            preshader->literals = (double *) m(len, d);
+            if (preshader->literals == NULL)
+                goto parsePreshader_notAPreshader;  // oh well.
+            const double *litptr = (const double *) (clit.tokens + 2);
+            for (i = 0; i < lit_count; i++)
+                preshader->literals[i] = SWAPDBL(litptr[i]);
+        } // else if
+    } // else
+
+    // Parse out the PRSI block. This is used to map the output registers.
+    uint32 output_map_count = 0;
+    const uint32 *output_map = NULL;
+    if (prsi.seen)
+    {
+        if (prsi.tokcount < 8) // Bogus preshader PRSI data
+            goto parsePreshader_notAPreshader;
+
+        //const uint32 first_output_reg = SWAP32(prsi.tokens[1]);
+        // !!! FIXME: there are a lot of fields here I don't know about.
+        // !!! FIXME:  maybe [2] and [3] are for int4 and bool registers?
+        //const uint32 output_reg_count = SWAP32(prsi.tokens[4]);
+        // !!! FIXME:  maybe [5] and [6] are for int4 and bool registers?
+        output_map_count = SWAP32(prsi.tokens[7]);
+
+        prsi.tokcount -= 8;
+        prsi.tokens += 8;
+
+        if (prsi.tokcount < ((output_map_count + 1) * 2))
+            // Bogus preshader PRSI data
+            goto parsePreshader_notAPreshader;
+
+        output_map = prsi.tokens;
+    }
+
+    // Now we'll figure out the CTAB...
+    CtabData ctabdata = { 0, 0, 0 };
+    parse_constant_table(&fillerContext, ctab.tokens - 1, ctab.tokcount * 4,
+                         version, 0, &ctabdata);
+
+    // preshader owns this now. Don't free it in this function.
+    preshader->symbol_count = ctabdata.symbol_count;
+    preshader->symbols = ctabdata.symbols;
+
+    if (!ctabdata.have_ctab) // Bogus preshader CTAB data
+        goto parsePreshader_notAPreshader;
+
+    // The FXLC block has the actual instructions...
+    uint32 opcode_count = SWAP32(fxlc.tokens[1]);
+
+    size_t len = sizeof (MOJOSHADER_preshaderInstruction) * opcode_count;
+    preshader->instruction_count = (unsigned int) opcode_count;
+    preshader->instructions = (MOJOSHADER_preshaderInstruction *)
+                                m(len, d);
+    if (preshader->instructions == NULL)
+        goto parsePreshader_outOfMemory;
+    memset(preshader->instructions, '\0', len);
+
+    fxlc.tokens += 2;
+    fxlc.tokcount -= 2;
+    if (opcode_count > (fxlc.tokcount / 2)) // Bogus preshader FXLC block
+        goto parsePreshader_notAPreshader;
+
+    MOJOSHADER_preshaderInstruction *inst = preshader->instructions;
+    while (opcode_count--)
+    {
+        const uint32 opcodetok = SWAP32(fxlc.tokens[0]);
+        MOJOSHADER_preshaderOpcode opcode = MOJOSHADER_PRESHADEROP_NOP;
+        switch ((opcodetok >> 16) & 0xFFFF)
+        {
+            case 0x1000: opcode = MOJOSHADER_PRESHADEROP_MOV; break;
+            case 0x1010: opcode = MOJOSHADER_PRESHADEROP_NEG; break;
+            case 0x1030: opcode = MOJOSHADER_PRESHADEROP_RCP; break;
+            case 0x1040: opcode = MOJOSHADER_PRESHADEROP_FRC; break;
+            case 0x1050: opcode = MOJOSHADER_PRESHADEROP_EXP; break;
+            case 0x1060: opcode = MOJOSHADER_PRESHADEROP_LOG; break;
+            case 0x1070: opcode = MOJOSHADER_PRESHADEROP_RSQ; break;
+            case 0x1080: opcode = MOJOSHADER_PRESHADEROP_SIN; break;
+            case 0x1090: opcode = MOJOSHADER_PRESHADEROP_COS; break;
+            case 0x10A0: opcode = MOJOSHADER_PRESHADEROP_ASIN; break;
+            case 0x10B0: opcode = MOJOSHADER_PRESHADEROP_ACOS; break;
+            case 0x10C0: opcode = MOJOSHADER_PRESHADEROP_ATAN; break;
+            case 0x2000: opcode = MOJOSHADER_PRESHADEROP_MIN; break;
+            case 0x2010: opcode = MOJOSHADER_PRESHADEROP_MAX; break;
+            case 0x2020: opcode = MOJOSHADER_PRESHADEROP_LT; break;
+            case 0x2030: opcode = MOJOSHADER_PRESHADEROP_GE; break;
+            case 0x2040: opcode = MOJOSHADER_PRESHADEROP_ADD; break;
+            case 0x2050: opcode = MOJOSHADER_PRESHADEROP_MUL; break;
+            case 0x2060: opcode = MOJOSHADER_PRESHADEROP_ATAN2; break;
+            case 0x2080: opcode = MOJOSHADER_PRESHADEROP_DIV; break;
+            case 0x3000: opcode = MOJOSHADER_PRESHADEROP_CMP; break;
+            case 0x3010: opcode = MOJOSHADER_PRESHADEROP_MOVC; break;
+            case 0x5000: opcode = MOJOSHADER_PRESHADEROP_DOT; break;
+            case 0x5020: opcode = MOJOSHADER_PRESHADEROP_NOISE; break;
+            case 0xA000: opcode = MOJOSHADER_PRESHADEROP_MIN_SCALAR; break;
+            case 0xA010: opcode = MOJOSHADER_PRESHADEROP_MAX_SCALAR; break;
+            case 0xA020: opcode = MOJOSHADER_PRESHADEROP_LT_SCALAR; break;
+            case 0xA030: opcode = MOJOSHADER_PRESHADEROP_GE_SCALAR; break;
+            case 0xA040: opcode = MOJOSHADER_PRESHADEROP_ADD_SCALAR; break;
+            case 0xA050: opcode = MOJOSHADER_PRESHADEROP_MUL_SCALAR; break;
+            case 0xA060: opcode = MOJOSHADER_PRESHADEROP_ATAN2_SCALAR; break;
+            case 0xA080: opcode = MOJOSHADER_PRESHADEROP_DIV_SCALAR; break;
+            case 0xD000: opcode = MOJOSHADER_PRESHADEROP_DOT_SCALAR; break;
+            case 0xD020: opcode = MOJOSHADER_PRESHADEROP_NOISE_SCALAR; break;
+            default:
+                // Unknown preshader opcode
+                goto parsePreshader_notAPreshader;
+        } // switch
+
+        uint32 operand_count = SWAP32(fxlc.tokens[1]) + 1;  // +1 for dest.
+
+        inst->opcode = opcode;
+        inst->element_count = (unsigned int) (opcodetok & 0xFF);
+        inst->operand_count = (unsigned int) operand_count;
+
+        fxlc.tokens += 2;
+        fxlc.tokcount -= 2;
+        if ((operand_count * 3) > fxlc.tokcount)
+            // Bogus preshader FXLC block
+            goto parsePreshader_notAPreshader;
+
+        MOJOSHADER_preshaderOperand *operand = inst->operands;
+        while (operand_count--)
+        {
+            const unsigned int item = (unsigned int) SWAP32(fxlc.tokens[2]);
+
+            // !!! FIXME: Is this used anywhere other than INPUT? -flibit
+            const uint32 numArrays = SWAP32(fxlc.tokens[0]);
+
+            switch (SWAP32(fxlc.tokens[1]))
+            {
+                case 1:  // literal from CLIT block.
+                {
+                    if ((item + inst->element_count) >= preshader->literal_count)
+                        // Bogus preshader literal index
+                        goto parsePreshader_notAPreshader;
+                    operand->type = MOJOSHADER_PRESHADEROPERAND_LITERAL;
+                    break;
+                } // case
+
+                case 2:  // item from ctabdata.
+                {
+                    MOJOSHADER_symbol *sym = ctabdata.symbols;
+                    for (i = 0; i < ctabdata.symbol_count; i++, sym++)
+                    {
+                        const uint32 base = sym->register_index * 4;
+                        const uint32 count = sym->register_count * 4;
+                        assert(sym->register_set==MOJOSHADER_SYMREGSET_FLOAT4);
+                        if ( (base <= item) && ((base + count) >= (item + inst->element_count)) )
+                            break;
+                    } // for
+                    if (i == ctabdata.symbol_count)
+                        // Bogus preshader input index
+                        goto parsePreshader_notAPreshader;
+                    operand->type = MOJOSHADER_PRESHADEROPERAND_INPUT;
+                    if (numArrays > 0)
+                    {
+                        // malloc the array symbol name array
+                        const uint32 siz = numArrays * sizeof (uint32);
+                        operand->array_register_count = numArrays;
+                        operand->array_registers = (uint32 *) m(siz, d);
+                        memset(operand->array_registers, '\0', siz);
+                        // Get each register base, indicating the arrays used.
+                        for (i = 0; i < numArrays; i++)
+                        {
+                            const uint32 jmp = SWAP32(fxlc.tokens[4]);
+                            const uint32 bigjmp = (jmp >> 4) * 4;
+                            const uint32 ltljmp = (jmp >> 2) & 3;
+                            operand->array_registers[i] = bigjmp + ltljmp;
+                            fxlc.tokens += 2;
+                            fxlc.tokcount -= 2;
+                        } // for
+                    } // if
+                    break;
+                } // case
+
+                case 4:
+                {
+                    operand->type = MOJOSHADER_PRESHADEROPERAND_OUTPUT;
+                    if (!prsi.seen)
+                        // No PRSI tokens, no output map.
+                        continue;
+                    for (i = 0; i < output_map_count; i++)
+                    {
+                        const uint32 base = output_map[(i*2)] * 4;
+                        const uint32 count = output_map[(i*2)+1] * 4;
+                        if ( (base <= item) && ((base + count) >= (item + inst->element_count)) )
+                            break;
+                    } // for
+                    if (i == output_map_count)
+                        // Bogus preshader output index
+                        goto parsePreshader_notAPreshader;
+                    break;
+                } // case
+
+                case 7:
+                {
+                    operand->type = MOJOSHADER_PRESHADEROPERAND_TEMP;
+                    if (item >= preshader->temp_count)
+                        preshader->temp_count = item + inst->element_count;
+                    break;
+                } // case
+            } // switch
+
+            operand->index = item;
+
+            fxlc.tokens += 3;
+            fxlc.tokcount -= 3;
+            operand++;
+        } // while
+
+        inst++;
+    } // while
+
+    // Registers need to be vec4, round up to nearest 4
+    preshader->temp_count = (preshader->temp_count + 3) & ~3;
+
+    unsigned int largest = 0;
+    const MOJOSHADER_symbol *sym = preshader->symbols;
+    for (i = 0; i < preshader->symbol_count; i++, sym++)
+    {
+        const unsigned int val = sym->register_index + sym->register_count;
+        if (val > largest)
+            largest = val;
+    } // for
+
+    if (largest > 0)
+    {
+        const size_t len = largest * sizeof (float) * 4;
+        preshader->registers = (float *) m(len, d);
+        memset(preshader->registers, '\0', len);
+        preshader->register_count = largest;
+    } // if
+
+    return preshader;
+
+parsePreshader_notAPreshader:
+parsePreshader_outOfMemory:
+    MOJOSHADER_freePreshader(preshader, f, d);
+    return NULL;
+} // MOJOSHADER_parsePreshader
+
+
+void MOJOSHADER_freePreshader(const MOJOSHADER_preshader *preshader,
+                              MOJOSHADER_free f,
+                              void *d)
+{
+    int i, j;
+    if (preshader != NULL)
+    {
+        f((void *) preshader->literals, d);
+        for (i = 0; i < preshader->instruction_count; i++)
+            for (j = 0; j < preshader->instructions[i].operand_count; j++)
+                f((void *) preshader->instructions[i].operands[j].array_registers, d);
+        f((void *) preshader->instructions, d);
+        f((void *) preshader->registers, d);
+        free_symbols(f, d, preshader->symbols, preshader->symbol_count);
+        f((void *) preshader, d);
+    } // if
+} // MOJOSHADER_freePreshader
+
 // end of mojoshader.c ...
 
--- a/mojoshader.h	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader.h	Fri Jan 01 02:12:19 2016 -0500
@@ -27,6 +27,22 @@
 #define MOJOSHADER_CHANGESET "???"
 #endif
 
+#ifndef DECLSPEC
+#ifdef _WIN32
+#define DECLSPEC __declspec(dllexport)
+#else
+#define DECLSPEC
+#endif
+#endif
+
+#ifndef MOJOSHADERCALL
+#ifdef _WIN32
+#define MOJOSHADERCALL __stdcall
+#else
+#define MOJOSHADERCALL
+#endif
+#endif
+
 /*
  * For determining the version of MojoShader you are using:
  *    const int compiled_against = MOJOSHADER_VERSION;
@@ -34,7 +50,7 @@
  *
  * The version is a single integer that increments, not a major/minor value.
  */
-int MOJOSHADER_version(void);
+DECLSPEC int MOJOSHADER_version(void);
 
 /*
  * For determining the revision control changeset of MojoShader you are using:
@@ -47,7 +63,7 @@
  *
  * Do not attempt to free this string; it's statically allocated.
  */
-const char *MOJOSHADER_changeset(void);
+DECLSPEC const char *MOJOSHADER_changeset(void);
 
 /*
  * These allocators work just like the C runtime's malloc() and free()
@@ -57,8 +73,8 @@
  *  callbacks, in case you need instance-specific data...it is passed through
  *  to your allocator unmolested, and can be NULL if you like.
  */
-typedef void *(*MOJOSHADER_malloc)(int bytes, void *data);
-typedef void (*MOJOSHADER_free)(void *ptr, void *data);
+typedef void *(MOJOSHADERCALL *MOJOSHADER_malloc)(int bytes, void *data);
+typedef void (MOJOSHADERCALL *MOJOSHADER_free)(void *ptr, void *data);
 
 
 /*
@@ -428,6 +444,8 @@
 {
     MOJOSHADER_preshaderOperandType type;
     unsigned int index;
+    unsigned int array_register_count;
+    unsigned int *array_registers;
 } MOJOSHADER_preshaderOperand;
 
 typedef struct MOJOSHADER_preshaderInstruction
@@ -435,7 +453,7 @@
     MOJOSHADER_preshaderOpcode opcode;
     unsigned int element_count;
     unsigned int operand_count;
-    MOJOSHADER_preshaderOperand operands[3];
+    MOJOSHADER_preshaderOperand operands[4];
 } MOJOSHADER_preshaderInstruction;
 
 typedef struct MOJOSHADER_preshader
@@ -447,6 +465,8 @@
     MOJOSHADER_symbol *symbols;
     unsigned int instruction_count;
     MOJOSHADER_preshaderInstruction *instructions;
+    unsigned int register_count;
+    float *registers;
 } MOJOSHADER_preshader;
 
 /*
@@ -651,6 +671,11 @@
 #define MOJOSHADER_PROFILE_GLSL120 "glsl120"
 
 /*
+ * Profile string for GLSL ES: minor changes to GLSL output for ES compliance.
+ */
+#define MOJOSHADER_PROFILE_GLSLES "glsles"
+
+/*
  * Profile string for OpenGL ARB 1.0 shaders: GL_ARB_(vertex|fragment)_program.
  */
 #define MOJOSHADER_PROFILE_ARB1 "arb1"
@@ -676,7 +701,7 @@
 /*
  * Determine the highest supported Shader Model for a profile.
  */
-int MOJOSHADER_maxShaderModel(const char *profile);
+DECLSPEC int MOJOSHADER_maxShaderModel(const char *profile);
 
 
 /*
@@ -732,16 +757,17 @@
  *  (tokenbuf) remains intact for the duration of the call. This allows you
  *  to parse several shaders on separate CPU cores at the same time.
  */
-const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
-                                             const unsigned char *tokenbuf,
-                                             const unsigned int bufsize,
-                                             const MOJOSHADER_swizzle *swiz,
-                                             const unsigned int swizcount,
-                                             const MOJOSHADER_samplerMap *smap,
-                                             const unsigned int smapcount,
-                                             MOJOSHADER_malloc m,
-                                             MOJOSHADER_free f,
-                                             void *d);
+DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
+                                                      const unsigned char *tokenbuf,
+                                                      const unsigned int bufsize,
+                                                      const MOJOSHADER_swizzle *swiz,
+                                                      const unsigned int swizcount,
+                                                      const MOJOSHADER_samplerMap *smap,
+                                                      const unsigned int smapcount,
+                                                      MOJOSHADER_malloc m,
+                                                      MOJOSHADER_free f,
+                                                      void *d);
+
 
 /*
  * Call this to dispose of parsing results when you are done with them.
@@ -752,152 +778,25 @@
  * This function is thread safe, so long as any allocator you passed into
  *  MOJOSHADER_parse() is, too.
  */
-void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data);
-
-
-/* Effects interface... */  /* !!! FIXME: THIS API IS NOT STABLE YET! */
-
-typedef struct MOJOSHADER_effectParam
-{
-    const char *name;
-    const char *semantic;
-} MOJOSHADER_effectParam;
-
-typedef struct MOJOSHADER_effectState
-{
-    unsigned int type;
-} MOJOSHADER_effectState;
-
-typedef struct MOJOSHADER_effectPass
-{
-    const char *name;
-    unsigned int state_count;
-    MOJOSHADER_effectState *states;
-} MOJOSHADER_effectPass;
-
-typedef struct MOJOSHADER_effectTechnique
-{
-    const char *name;
-    unsigned int pass_count;
-    MOJOSHADER_effectPass *passes;
-} MOJOSHADER_effectTechnique;
-
-typedef struct MOJOSHADER_effectTexture
-{
-    unsigned int param;
-    const char *name;
-} MOJOSHADER_effectTexture;
-
-typedef struct MOJOSHADER_effectShader
-{
-    unsigned int technique;
-    unsigned int pass;
-    const MOJOSHADER_parseData *shader;
-} MOJOSHADER_effectShader;
-
-/*
- * Structure used to return data from parsing of an effect file...
- */
-/* !!! FIXME: most of these ints should be unsigned. */
-typedef struct MOJOSHADER_effect
-{
-    /*
-     * The number of elements pointed to by (errors).
-     */
-    int error_count;
-
-    /*
-     * (error_count) elements of data that specify errors that were generated
-     *  by parsing this shader.
-     * This can be NULL if there were no errors or if (error_count) is zero.
-     */
-    MOJOSHADER_error *errors;
-
-    /*
-     * The name of the profile used to parse the shader. Will be NULL on error.
-     */
-    const char *profile;
-
-    /*
-     * The number of params pointed to by (params).
-     */
-    int param_count;
-
-    /*
-     * (param_count) elements of data that specify parameter bind points for
-     *  this effect.
-     * This can be NULL on error or if (param_count) is zero.
-     */
-    MOJOSHADER_effectParam *params;
-
-    /*
-     * The number of elements pointed to by (techniques).
-     */
-    int technique_count;
-
-    /*
-     * (technique_count) elements of data that specify techniques used in
-     *  this effect. Each technique contains a series of passes, and each pass
-     *  specifies state and shaders that affect rendering.
-     * This can be NULL on error or if (technique_count) is zero.
-     */
-    MOJOSHADER_effectTechnique *techniques;
-
-    /*
-     * The number of elements pointed to by (textures).
-     */
-    int texture_count;
-
-    /*
-     * (texture_count) elements of data that specify textures used in
-     *  this effect.
-     * This can be NULL on error or if (texture_count) is zero.
-     */
-    MOJOSHADER_effectTexture *textures;
-
-    /*
-     * The number of elements pointed to by (shaders).
-     */
-    int shader_count;
-
-    /*
-     * (shader_count) elements of data that specify shaders used in
-     *  this effect.
-     * This can be NULL on error or if (shader_count) is zero.
-     */
-    MOJOSHADER_effectShader *shaders;
-
-    /*
-     * This is the malloc implementation you passed to MOJOSHADER_parseEffect().
-     */
-    MOJOSHADER_malloc malloc;
-
-    /*
-     * This is the free implementation you passed to MOJOSHADER_parseEffect().
-     */
-    MOJOSHADER_free free;
-
-    /*
-     * This is the pointer you passed as opaque data for your allocator.
-     */
-    void *malloc_data;
-} MOJOSHADER_effect;
-
-/* !!! FIXME: document me. */
-const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile,
-                                                const unsigned char *buf,
-                                                const unsigned int _len,
-                                                const MOJOSHADER_swizzle *swiz,
-                                                const unsigned int swizcount,
-                                                const MOJOSHADER_samplerMap *smap,
-                                                const unsigned int smapcount,
-                                                MOJOSHADER_malloc m,
-                                                MOJOSHADER_free f,
-                                                void *d);
+DECLSPEC void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data);
 
 
 /* !!! FIXME: document me. */
-void MOJOSHADER_freeEffect(const MOJOSHADER_effect *effect);
+DECLSPEC const MOJOSHADER_preshader *MOJOSHADER_parsePreshader(const unsigned char *buf,
+                                                               const unsigned int len,
+                                                               MOJOSHADER_malloc m,
+                                                               MOJOSHADER_free f,
+                                                               void *d);
+
+
+/* !!! FIXME: document me. */
+DECLSPEC void MOJOSHADER_freePreshader(const MOJOSHADER_preshader *preshader,
+                                       MOJOSHADER_free f,
+                                       void *d);
+
+
+/* Effects interface... */
+#include "mojoshader_effects.h"
 
 
 /* Preprocessor interface... */
@@ -1001,7 +900,7 @@
  *
  * If you supply an includeOpen callback, you must supply includeClose, too.
  */
-typedef int (*MOJOSHADER_includeOpen)(MOJOSHADER_includeType inctype,
+typedef int (MOJOSHADERCALL *MOJOSHADER_includeOpen)(MOJOSHADER_includeType inctype,
                             const char *fname, const char *parent,
                             const char **outdata, unsigned int *outbytes,
                             MOJOSHADER_malloc m, MOJOSHADER_free f, void *d);
@@ -1019,7 +918,7 @@
  *
  * If you supply an includeClose callback, you must supply includeOpen, too.
  */
-typedef void (*MOJOSHADER_includeClose)(const char *data,
+typedef void (MOJOSHADERCALL *MOJOSHADER_includeClose)(const char *data,
                             MOJOSHADER_malloc m, MOJOSHADER_free f, void *d);
 
 
@@ -1079,7 +978,7 @@
  *  call. This allows you to preprocess several shaders on separate CPU cores
  *  at the same time.
  */
-const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *filename,
+DECLSPEC const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *filename,
                              const char *source, unsigned int sourcelen,
                              const MOJOSHADER_preprocessorDefine *defines,
                              unsigned int define_count,
@@ -1097,7 +996,7 @@
  * This function is thread safe, so long as any allocator you passed into
  *  MOJOSHADER_preprocess() is, too.
  */
-void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *data);
+DECLSPEC void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *data);
 
 
 /* Assembler interface... */
@@ -1159,7 +1058,7 @@
  *  call. This allows you to assemble several shaders on separate CPU cores
  *  at the same time.
  */
-const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename,
+DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename,
                              const char *source, unsigned int sourcelen,
                              const char **comments, unsigned int comment_count,
                              const MOJOSHADER_symbol *symbols,
@@ -2010,7 +1909,7 @@
  *  call. This allows you to parse several shaders on separate CPU cores
  *  at the same time.
  */
-const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile,
+DECLSPEC const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile,
                                     const char *filename, const char *source,
                                     unsigned int sourcelen,
                                     const MOJOSHADER_preprocessorDefine *defs,
@@ -2033,7 +1932,7 @@
  * This function is thread safe, so long as any allocator you passed into
  *  MOJOSHADER_parseAst() is, too.
  */
-void MOJOSHADER_freeAstData(const MOJOSHADER_astData *data);
+DECLSPEC void MOJOSHADER_freeAstData(const MOJOSHADER_astData *data);
 
 
 /* Intermediate Representation interface... */
@@ -2450,7 +2349,7 @@
  *  call. This allows you to compile several shaders on separate CPU cores
  *  at the same time.
  */
-const MOJOSHADER_compileData *MOJOSHADER_compile(const char *srcprofile,
+DECLSPEC const MOJOSHADER_compileData *MOJOSHADER_compile(const char *srcprofile,
                                     const char *filename, const char *source,
                                     unsigned int sourcelen,
                                     const MOJOSHADER_preprocessorDefine *defs,
@@ -2470,7 +2369,7 @@
  * This function is thread safe, so long as any allocator you passed into
  *  MOJOSHADER_compile() is, too.
  */
-void MOJOSHADER_freeCompileData(const MOJOSHADER_compileData *data);
+DECLSPEC void MOJOSHADER_freeCompileData(const MOJOSHADER_compileData *data);
 
 
 /* OpenGL interface... */
@@ -2493,7 +2392,7 @@
  *  to check two places to find the desired entry point, depending on your
  *  platform (Windows might need to look in OpenGL32.dll and use WGL, etc).
  */
-typedef void *(*MOJOSHADER_glGetProcAddress)(const char *fnname, void *data);
+typedef void *(MOJOSHADERCALL *MOJOSHADER_glGetProcAddress)(const char *fnname, void *data);
 
 
 /*
@@ -2544,11 +2443,11 @@
  *  safe, you should probably only call this from the same thread that created
  *  the GL context.
  */
-int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup,
-                                   void *lookup_d,
-                                   const char **profs, const int size,
-                                   MOJOSHADER_malloc m, MOJOSHADER_free f,
-                                   void *malloc_d);
+DECLSPEC int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup,
+                                            void *lookup_d,
+                                            const char **profs, const int size,
+                                            MOJOSHADER_malloc m, MOJOSHADER_free f,
+                                            void *malloc_d);
 
 
 /*
@@ -2581,7 +2480,7 @@
  *  safe, you should probably only call this from the same thread that created
  *  the GL context.
  */
-const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup,
+DECLSPEC const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup,
                                    void *lookup_d,
                                    MOJOSHADER_malloc m, MOJOSHADER_free f,
                                    void *malloc_d);
@@ -2623,7 +2522,7 @@
  *  are not thread safe, you should probably only call this from the same
  *  thread that created the GL context.
  */
-MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile,
+DECLSPEC MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile,
                                         MOJOSHADER_glGetProcAddress lookup,
                                         void *lookup_d,
                                         MOJOSHADER_malloc m, MOJOSHADER_free f,
@@ -2640,7 +2539,7 @@
  * It is legal to call this with a NULL pointer to make no context current,
  *  but you need a valid context to be current to use most of MojoShader.
  */
-void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *ctx);
+DECLSPEC void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *ctx);
 
 /*
  * Get any error state we might have picked up. MojoShader will NOT call
@@ -2667,7 +2566,7 @@
  *  current. The error buffer is shared between contexts, so you can get
  *  error results from a failed MOJOSHADER_glCreateContext().
  */
-const char *MOJOSHADER_glGetError(void);
+DECLSPEC const char *MOJOSHADER_glGetError(void);
 
 /*
  * Get the maximum uniforms a shader can support for the current GL context,
@@ -2684,7 +2583,7 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type);
+DECLSPEC int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type);
 
 /*
  * Compile a buffer of Direct3D shader bytecode into an OpenGL shader.
@@ -2706,12 +2605,12 @@
  *
  * Compiled shaders from this function may not be shared between contexts.
  */
-MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf,
-                                                const unsigned int bufsize,
-                                                const MOJOSHADER_swizzle *swiz,
-                                                const unsigned int swizcount,
-                                                const MOJOSHADER_samplerMap *smap,
-                                                const unsigned int smapcount);
+DECLSPEC MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf,
+                                                         const unsigned int bufsize,
+                                                         const MOJOSHADER_swizzle *swiz,
+                                                         const unsigned int swizcount,
+                                                         const MOJOSHADER_samplerMap *smap,
+                                                         const unsigned int smapcount);
 
 
 /*
@@ -2721,7 +2620,7 @@
  * This data is read-only, and you should NOT attempt to free it. This
  *  pointer remains valid until the shader is deleted.
  */
-const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData(
+DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData(
                                                 MOJOSHADER_glShader *shader);
 /*
  * Link a vertex and pixel shader into an OpenGL program.
@@ -2746,8 +2645,8 @@
  *
  * Linked programs from this function may not be shared between contexts.
  */
-MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader,
-                                               MOJOSHADER_glShader *pshader);
+DECLSPEC MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader,
+                                                        MOJOSHADER_glShader *pshader);
 
 /*
  * This binds the program (using, for example, glUseProgramObjectARB()), and
@@ -2770,7 +2669,7 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program);
+DECLSPEC void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program);
 
 /*
  * This binds individual shaders as if you had linked them with
@@ -2797,8 +2696,8 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *vshader,
-                              MOJOSHADER_glShader *pshader);
+DECLSPEC void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *vshader,
+                                       MOJOSHADER_glShader *pshader);
 
 /*
  * Set a floating-point uniform value (what Direct3D calls a "constant").
@@ -2821,8 +2720,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data,
-                                          unsigned int vec4count);
+DECLSPEC void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data,
+                                                   unsigned int vec4count);
 
 /*
  * Retrieve a floating-point uniform value (what Direct3D calls a "constant").
@@ -2851,8 +2750,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data,
-                                          unsigned int vec4count);
+DECLSPEC void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data,
+                                                   unsigned int vec4count);
 
 
 /*
@@ -2876,8 +2775,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data,
-                                          unsigned int ivec4count);
+DECLSPEC void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data,
+                                                   unsigned int ivec4count);
 
 /*
  * Retrieve an integer uniform value (what Direct3D calls a "constant").
@@ -2906,8 +2805,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data,
-                                          unsigned int ivec4count);
+DECLSPEC void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data,
+                                                   unsigned int ivec4count);
 
 /*
  * Set a boolean uniform value (what Direct3D calls a "constant").
@@ -2935,8 +2834,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data,
-                                          unsigned int bcount);
+DECLSPEC void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data,
+                                                   unsigned int bcount);
 
 /*
  * Retrieve a boolean uniform value (what Direct3D calls a "constant").
@@ -2972,8 +2871,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data,
-                                          unsigned int bcount);
+DECLSPEC void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data,
+                                                   unsigned int bcount);
 
 /*
  * The equivalent of MOJOSHADER_glSetVertexShaderUniformF() for pixel
@@ -2989,8 +2888,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data,
-                                         unsigned int vec4count);
+DECLSPEC void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data,
+                                                  unsigned int vec4count);
 
 
 /*
@@ -3007,8 +2906,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data,
-                                         unsigned int vec4count);
+DECLSPEC void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data,
+                                                  unsigned int vec4count);
 
 
 /*
@@ -3025,8 +2924,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data,
-                                         unsigned int ivec4count);
+DECLSPEC void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data,
+                                                  unsigned int ivec4count);
 
 
 /*
@@ -3043,8 +2942,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data,
-                                         unsigned int ivec4count);
+DECLSPEC void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data,
+                                                  unsigned int ivec4count);
 
 /*
  * The equivalent of MOJOSHADER_glSetVertexShaderUniformB() for pixel
@@ -3060,8 +2959,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data,
-                                         unsigned int bcount);
+DECLSPEC void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data,
+                                                  unsigned int bcount);
 
 /*
  * The equivalent of MOJOSHADER_glGetVertexShaderUniformB() for pixel
@@ -3077,8 +2976,8 @@
  *
  * Uniforms are not shared between contexts.
  */
-void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data,
-                                         unsigned int bcount);
+DECLSPEC void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data,
+                                                  unsigned int bcount);
 
 /*
  * Set up the vector for the TEXBEM opcode. Most apps can ignore this API.
@@ -3115,9 +3014,23 @@
  *
  * These values are not shared between contexts.
  */
-void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00,
-                                      float mat01, float mat10, float mat11,
-                                      float lscale, float loffset);
+DECLSPEC void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00,
+                                               float mat01, float mat10, float mat11,
+                                               float lscale, float loffset);
+
+/*
+ * Return the location of a vertex attribute for the currently-bound program.
+ *
+ * (usage) and (index) map to Direct3D vertex declaration values: COLOR1 would
+ *  be MOJOSHADER_USAGE_COLOR and 1.
+ *
+ * The return value is the index of the attribute to be sent to
+ *  glVertexAttribPointer, or -1 if the stream is not used.
+ *
+ * This call requires a valid MOJOSHADER_glContext to have been made current,
+ *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
+ */
+DECLSPEC int MOJOSHADER_glGetVertexAttribLocation(MOJOSHADER_usage usage, int index);
 
 /*
  * Connect a client-side array to the currently-bound program.
@@ -3146,30 +3059,30 @@
  */
  /* !!! FIXME: this should probably be "input" and not "attribute" */
  /* !!! FIXME: or maybe "vertex array" or something. */
-void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage,
-                                     int index, unsigned int size,
-                                     MOJOSHADER_attributeType type,
-                                     int normalized, unsigned int stride,
-                                     const void *ptr);
-
-
-
+DECLSPEC void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage,
+                                              int index, unsigned int size,
+                                              MOJOSHADER_attributeType type,
+                                              int normalized, unsigned int stride,
+                                              const void *ptr);
 
-/* These below functions are temporary and will be removed from the API once
-    the real Effects API is written. Do not use! */
-void MOJOSHADER_glSetVertexPreshaderUniformF(unsigned int idx, const float *data,
-                                             unsigned int vec4n);
-void MOJOSHADER_glGetVertexPreshaderUniformF(unsigned int idx, float *data,
-                                             unsigned int vec4n);
-void MOJOSHADER_glSetPixelPreshaderUniformF(unsigned int idx, const float *data,
-                                            unsigned int vec4n);
-void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data,
-                                            unsigned int vec4n);
-/* These above functions are temporary and will be removed from the API once
-    the real Effects API is written. Do not use! */
-
-
-
+/*
+ *  Modify the rate at which this vertex attribute advances during instanced
+ *  rendering.
+ *
+ * This should be called alongside glSetVertexAttribute, as this does not flag
+ *  the vertex array as being in use. This just calls glVertexAttribDivisorARB.
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ *  safe, you should probably only call this from the same thread that created
+ *  the GL context.
+ *
+ * This call requires a valid MOJOSHADER_glContext to have been made current,
+ *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
+ *
+ * Vertex attributes are not shared between contexts.
+ */
+DECLSPEC void MOJOSHADER_glSetVertexAttribDivisor(MOJOSHADER_usage usage,
+                                                  int index, unsigned int divisor);
 
 /*
  * Inform MojoShader that it should commit any pending state to the GL. This
@@ -3184,7 +3097,12 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-void MOJOSHADER_glProgramReady(void);
+DECLSPEC void MOJOSHADER_glProgramReady(void);
+
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+// !!! FIXME: Document me.
+DECLSPEC void MOJOSHADER_glProgramViewportFlip(int flip);
+#endif
 
 /*
  * Free the resources of a linked program. This will delete the GL object
@@ -3200,7 +3118,7 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program);
+DECLSPEC void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program);
 
 /*
  * Free the resources of a compiled shader. This will delete the GL object
@@ -3217,7 +3135,7 @@
  * This call requires a valid MOJOSHADER_glContext to have been made current,
  *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
  */
-void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader);
+DECLSPEC void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader);
 
 /*
  * Deinitialize MojoShader's OpenGL shader management.
@@ -3241,7 +3159,7 @@
  *  are not thread safe, you should probably only call this from the same
  *  thread that created the GL context.
  */
-void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *ctx);
+DECLSPEC void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *ctx);
 
 #ifdef __cplusplus
 }
--- a/mojoshader_common.c	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader_common.c	Fri Jan 01 02:12:19 2016 -0500
@@ -3,8 +3,8 @@
 
 // Convenience functions for allocators...
 #if !MOJOSHADER_FORCE_ALLOCATOR
-void *MOJOSHADER_internal_malloc(int bytes, void *d) { return malloc(bytes); }
-void MOJOSHADER_internal_free(void *ptr, void *d) { free(ptr); }
+void * MOJOSHADERCALL MOJOSHADER_internal_malloc(int bytes, void *d) { return malloc(bytes); }
+void MOJOSHADERCALL MOJOSHADER_internal_free(void *ptr, void *d) { free(ptr); }
 #endif
 
 MOJOSHADER_error MOJOSHADER_out_of_mem_error = {
@@ -78,7 +78,7 @@
 int hash_iter(const HashTable *table, const void *key,
               const void **_value, void **iter)
 {
-    HashItem *item = *iter;
+    HashItem *item = (HashItem *) *iter;
     if (item == NULL)
         item = table->table[calc_hash(table, key)];
     else
@@ -103,7 +103,7 @@
 
 int hash_iter_keys(const HashTable *table, const void **_key, void **iter)
 {
-    HashItem *item = *iter;
+    HashItem *item = (HashItem *) *iter;
     int idx = 0;
 
     if (item != NULL)
@@ -1011,5 +1011,73 @@
     return -1;  // no match found.
 } // buffer_find
 
+
+// Based on SDL_string.c's SDL_PrintFloat function
+size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg)
+{
+    size_t len;
+    size_t left = maxlen;
+    char *textstart = text;
+
+    int precision = 9;
+
+    if (arg)
+    {
+        /* This isn't especially accurate, but hey, it's easy. :) */
+        unsigned long value;
+
+        if (arg < 0)
+        {
+            if (left > 1)
+            {
+                *text = '-';
+                --left;
+            } // if
+            ++text;
+            arg = -arg;
+        } // if
+        value = (unsigned long) arg;
+        len = snprintf(text, left, "%lu", value);
+        text += len;
+        if (len >= left)
+            left = (left < 1) ? left : 1;
+        else
+            left -= len;
+        arg -= value;
+
+        int mult = 10;
+        if (left > 1)
+        {
+            *text = '.';
+            --left;
+        } // if
+        ++text;
+        while (precision-- > 0)
+        {
+            value = (unsigned long) (arg * mult);
+            len = snprintf(text, left, "%lu", value);
+            text += len;
+            if (len >= left)
+                left = (left < 1) ? left : 1;
+            else
+                left -= len;
+            arg -= (double) value / mult;
+            if (arg < 0) arg = -arg; // Sometimes that bit gets flipped...
+            mult *= 10;
+        } // while
+    } // if
+    else
+    {
+        if (left > 3)
+        {
+            snprintf(text, left, "0.0");
+            left -= 3;
+        } // if
+        text += 3;
+    } // else
+
+    return (text - textstart);
+} // MOJOSHADER_printFloat
+
 // end of mojoshader_common.c ...
 
--- a/mojoshader_effects.c	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader_effects.c	Fri Jan 01 02:12:19 2016 -0500
@@ -10,12 +10,15 @@
 #define __MOJOSHADER_INTERNAL__ 1
 #include "mojoshader_internal.h"
 
+#ifdef MOJOSHADER_EFFECT_SUPPORT
+
 #include <math.h>
 
-#if SUPPORT_PRESHADERS
 void MOJOSHADER_runPreshader(const MOJOSHADER_preshader *preshader,
-                             const float *inregs, float *outregs)
+                             float *outregs)
 {
+    const float *inregs = preshader->registers;
+
     // this is fairly straightforward, as there aren't any branching
     //  opcodes in the preshader instruction set (at the moment, at least).
     const int scalarstart = (int) MOJOSHADER_PRESHADEROP_SCALAR_OPS;
@@ -56,21 +59,32 @@
             {
                 case MOJOSHADER_PRESHADEROPERAND_LITERAL:
                 {
-                    const double *lit = &preshader->literals[index];
-                    assert((index + elems) <= preshader->literal_count);
                     if (!isscalar)
-                        memcpy(&src[opiter][0], lit, elemsbytes);
+                    {
+                        assert((index + elems) <= preshader->literal_count);
+                        memcpy(&src[opiter][0], &preshader->literals[index], elemsbytes);
+                    } // if
                     else
                     {
-                        const double val = *lit;
                         for (elemiter = 0; elemiter < elems; elemiter++)
-                            src[opiter][elemiter] = val;
+                            src[opiter][elemiter] = preshader->literals[index];
                     } // else
                     break;
                 } // case
 
                 case MOJOSHADER_PRESHADEROPERAND_INPUT:
-                    if (isscalar)
+                    if (operand->array_register_count > 0)
+                    {
+                        int i;
+                        const int *regsi = (const int *) inregs;
+                        int arrIndex = regsi[((index >> 4) * 4) + ((index >> 2) & 3)];
+                        for (i = 0; i < operand->array_register_count; i++)
+                        {
+                            arrIndex = regsi[operand->array_registers[i] + arrIndex];
+                        }
+                        src[opiter][0] = arrIndex;
+                    } // if
+                    else if (isscalar)
                         src[opiter][0] = inregs[index];
                     else
                     {
@@ -159,6 +173,7 @@
                     final += src0[i] * src1[i];
                 for (i = 0; i < elems; i++)
                     dst[i] = final;  // !!! FIXME: is this right?
+                break;
             } // case
 
             default:
@@ -181,7 +196,6 @@
         } // else
     } // for
 } // MOJOSHADER_runPreshader
-#endif
 
 static MOJOSHADER_effect MOJOSHADER_out_of_mem_effect = {
     1, &MOJOSHADER_out_of_mem_error, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -202,357 +216,671 @@
     return retval;
 } // readui32
 
-// !!! FIXME: this is sort of a big, ugly function.
-const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile,
-                                                const unsigned char *buf,
-                                                const unsigned int _len,
-                                                const MOJOSHADER_swizzle *swiz,
-                                                const unsigned int swizcount,
-                                                const MOJOSHADER_samplerMap *smap,
-                                                const unsigned int smapcount,
-                                                MOJOSHADER_malloc m,
-                                                MOJOSHADER_free f,
-                                                void *d)
+static char *readstring(const uint8 *base,
+                        const uint32 offset,
+                        MOJOSHADER_malloc m,
+                        void *d)
+{
+    // !!! FIXME: sanity checks!
+    // !!! FIXME: verify this doesn't go past EOF looking for a null.
+    const char *str = ((const char *) base) + offset;
+    const uint32 len = *((const uint32 *) str);
+    char *strptr = NULL;
+    if (len == 0) return NULL; /* No length? No string. */
+    strptr = (char *) m(len, d);
+    memcpy(strptr, str + 4, len);
+    return strptr;
+} // readstring
+
+static int findparameter(const MOJOSHADER_effectParam *params,
+                         const uint32 param_count,
+                         const char *name)
+{
+    int i;
+    for (i = 0; i < param_count; i++)
+        if (strcmp(name, params[i].value.name) == 0)
+            return i;
+    assert(0 && "Parameter not found!");
+}
+
+static void readvalue(const uint8 *base,
+                      const uint32 typeoffset,
+                      const uint32 valoffset,
+                      MOJOSHADER_effectValue *value,
+                      MOJOSHADER_effectObject *objects,
+                      MOJOSHADER_malloc m,
+                      void *d)
+{
+    int i;
+    const uint8 *typeptr = base + typeoffset;
+    const uint8 *valptr = base + valoffset;
+    unsigned int typelen = 9999999;  // !!! FIXME
+    const uint32 type = readui32(&typeptr, &typelen);
+    const uint32 valclass = readui32(&typeptr, &typelen);
+    const uint32 name = readui32(&typeptr, &typelen);
+    const uint32 semantic = readui32(&typeptr, &typelen);
+    const uint32 numelements = readui32(&typeptr, &typelen);
+
+    value->value_type = (MOJOSHADER_symbolType) type;
+    value->value_class = (MOJOSHADER_symbolClass) valclass;
+    value->name = readstring(base, name, m, d);
+    value->semantic = readstring(base, semantic, m, d);
+    value->element_count = numelements;
+
+    /* Class sanity check */
+    assert(valclass >= MOJOSHADER_SYMCLASS_SCALAR && valclass <= MOJOSHADER_SYMCLASS_STRUCT);
+
+    if (valclass == MOJOSHADER_SYMCLASS_SCALAR
+     || valclass == MOJOSHADER_SYMCLASS_VECTOR
+     || valclass == MOJOSHADER_SYMCLASS_MATRIX_ROWS
+     || valclass == MOJOSHADER_SYMCLASS_MATRIX_COLUMNS)
+    {
+        /* These classes only ever contain scalar values */
+        assert(type >= MOJOSHADER_SYMTYPE_BOOL && type <= MOJOSHADER_SYMTYPE_FLOAT);
+
+        const uint32 columncount = readui32(&typeptr, &typelen);
+        const uint32 rowcount = readui32(&typeptr, &typelen);
+
+        value->column_count = columncount;
+        value->row_count = rowcount;
+
+        uint32 siz = columncount * rowcount;
+        if (numelements > 0)
+            siz *= numelements;
+        value->value_count = siz;
+        siz *= 4;
+        value->values = m(siz, d);
+        memcpy(value->values, valptr, siz);
+    } // if
+    else if (valclass == MOJOSHADER_SYMCLASS_OBJECT)
+    {
+        /* This class contains either samplers or "objects" */
+        assert(type >= MOJOSHADER_SYMTYPE_STRING && type <= MOJOSHADER_SYMTYPE_VERTEXSHADER);
+
+        if (type == MOJOSHADER_SYMTYPE_SAMPLER
+         || type == MOJOSHADER_SYMTYPE_SAMPLER1D
+         || type == MOJOSHADER_SYMTYPE_SAMPLER2D
+         || type == MOJOSHADER_SYMTYPE_SAMPLER3D
+         || type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        {
+            unsigned int vallen = 9999999; // !!! FIXME
+            const uint32 numstates = readui32(&valptr, &vallen);
+
+            value->value_count = numstates;
+
+            const uint32 siz = sizeof(MOJOSHADER_effectSamplerState) * numstates;
+            value->values = m(siz, d);
+            memset(value->values, '\0', siz);
+
+            for (i = 0; i < numstates; i++)
+            {
+                MOJOSHADER_effectSamplerState *state = &value->valuesSS[i];
+                const uint32 stype = readui32(&valptr, &vallen) & ~0xA0;
+                /*const uint32 FIXME =*/ readui32(&valptr, &vallen);
+                const uint32 statetypeoffset = readui32(&valptr, &vallen);
+                const uint32 statevaloffset = readui32(&valptr, &vallen);
+
+                state->type = (MOJOSHADER_samplerStateType) stype;
+                readvalue(base, statetypeoffset, statevaloffset,
+                          &state->value, objects,
+                          m, d);
+                if (stype == MOJOSHADER_SAMP_TEXTURE)
+                    objects[state->value.valuesI[0]].type = (MOJOSHADER_symbolType) type;
+            } // for
+        } // if
+        else
+        {
+            uint32 numobjects = 1;
+            if (numelements > 0)
+                numobjects = numelements;
+
+            value->value_count = numobjects;
+
+            const uint32 siz = 4 * numobjects;
+            value->values = m(siz, d);
+            memcpy(value->values, valptr, siz);
+
+            for (i = 0; i < value->value_count; i++)
+                objects[value->valuesI[i]].type = (MOJOSHADER_symbolType) type;
+        } // else
+    } // else if
+    else if (valclass == MOJOSHADER_SYMCLASS_STRUCT)
+    {
+        /* TODO: Maybe this is like parse_ctab_typeinfo? -flibit */
+        assert(0 && "Effect struct value parsing not implemented!");
+    } // else if
+} // readvalue
+
+static void readannotations(const uint32 numannos,
+                            const uint8 *base,
+                            const uint8 **ptr,
+                            uint32 *len,
+                            MOJOSHADER_effectAnnotation **annotations,
+                            MOJOSHADER_effectObject *objects,
+                            MOJOSHADER_malloc m,
+                            void *d)
+{
+    int i;
+    if (numannos == 0) return;
+
+    const uint32 siz = sizeof(MOJOSHADER_effectAnnotation) * numannos;
+    *annotations = (MOJOSHADER_effectAnnotation *) m(siz, d);
+    memset(*annotations, '\0', siz);
+
+    for (i = 0; i < numannos; i++)
+    {
+        MOJOSHADER_effectAnnotation *anno = &(*annotations)[i];
+
+        const uint32 typeoffset = readui32(ptr, len);
+        const uint32 valoffset = readui32(ptr, len);
+
+        readvalue(base, typeoffset, valoffset,
+                  anno, objects,
+                  m, d);
+    } // for
+} // readannotation
+
+static void readparameters(const uint32 numparams,
+                           const uint8 *base,
+                           const uint8 **ptr,
+                           uint32 *len,
+                           MOJOSHADER_effectParam **params,
+                           MOJOSHADER_effectObject *objects,
+                           MOJOSHADER_malloc m,
+                           void *d)
+{
+    int i;
+    if (numparams == 0) return;
+
+    uint32 siz = sizeof(MOJOSHADER_effectParam) * numparams;
+    *params = (MOJOSHADER_effectParam *) m(siz, d);
+    memset(*params, '\0', siz);
+
+    for (i = 0; i < numparams; i++)
+    {
+        MOJOSHADER_effectParam *param = &(*params)[i];
+
+        const uint32 typeoffset = readui32(ptr, len);
+        const uint32 valoffset = readui32(ptr, len);
+        /*const uint32 flags =*/ readui32(ptr, len);
+        const uint32 numannos = readui32(ptr, len);
+
+        param->annotation_count = numannos;
+        readannotations(numannos, base, ptr, len,
+                        &param->annotations, objects,
+                        m, d);
+
+        readvalue(base, typeoffset, valoffset,
+                  &param->value, objects,
+                  m, d);
+    } // for
+} // readparameters
+
+static void readstates(const uint32 numstates,
+                       const uint8 *base,
+                       const uint8 **ptr,
+                       uint32 *len,
+                       MOJOSHADER_effectState **states,
+                       MOJOSHADER_effectObject *objects,
+                       MOJOSHADER_malloc m,
+                       void *d)
+{
+    int i;
+    if (numstates == 0) return;
+
+    const uint32 siz = sizeof (MOJOSHADER_effectState) * numstates;
+    *states = (MOJOSHADER_effectState *) m(siz, d);
+    memset(*states, '\0', siz);
+
+    for (i = 0; i < numstates; i++)
+    {
+        MOJOSHADER_effectState *state = &(*states)[i];
+
+        const uint32 type = readui32(ptr, len);
+        /*const uint32 FIXME =*/ readui32(ptr, len);
+        const uint32 typeoffset = readui32(ptr, len);
+        const uint32 valoffset = readui32(ptr, len);
+
+        state->type = (MOJOSHADER_renderStateType) type;
+        readvalue(base, typeoffset, valoffset,
+                  &state->value, objects,
+                  m, d);
+    } // for
+} // readstates
+
+static void readpasses(const uint32 numpasses,
+                       const uint8 *base,
+                       const uint8 **ptr,
+                       uint32 *len,
+                       MOJOSHADER_effectPass **passes,
+                       MOJOSHADER_effectObject *objects,
+                       MOJOSHADER_malloc m,
+                       void *d)
+{
+    int i;
+    if (numpasses == 0) return;
+
+    const uint32 siz = sizeof (MOJOSHADER_effectPass) * numpasses;
+    *passes = (MOJOSHADER_effectPass *) m(siz, d);
+    memset(*passes, '\0', siz);
+
+    for (i = 0; i < numpasses; i++)
+    {
+        MOJOSHADER_effectPass *pass = &(*passes)[i];
+
+        const uint32 passnameoffset = readui32(ptr, len);
+        const uint32 numannos = readui32(ptr, len);
+        const uint32 numstates = readui32(ptr, len);
+
+        pass->name = readstring(base, passnameoffset, m, d);
+
+        pass->annotation_count = numannos;
+        readannotations(numannos, base, ptr, len,
+                        &pass->annotations, objects,
+                        m, d);
+
+        pass->state_count = numstates;
+        readstates(numstates, base, ptr, len,
+                   &pass->states, objects,
+                   m, d);
+    } // for
+} // readpasses
+
+static void readtechniques(const uint32 numtechniques,
+                           const uint8 *base,
+                           const uint8 **ptr,
+                           uint32 *len,
+                           MOJOSHADER_effectTechnique **techniques,
+                           MOJOSHADER_effectObject *objects,
+                           MOJOSHADER_malloc m,
+                           void *d)
 {
+    int i;
+    if (numtechniques == 0) return;
+
+    const uint32 siz = sizeof (MOJOSHADER_effectTechnique) * numtechniques;
+    *techniques = (MOJOSHADER_effectTechnique *) m(siz, d);
+    memset(*techniques, '\0', siz);
+
+    for (i = 0; i < numtechniques; i++)
+    {
+        MOJOSHADER_effectTechnique *technique = &(*techniques)[i];
+
+        const uint32 nameoffset = readui32(ptr, len);
+        const uint32 numannos = readui32(ptr, len);
+        const uint32 numpasses = readui32(ptr, len);
+
+        technique->name = readstring(base, nameoffset, m, d);
+
+        technique->annotation_count = numannos;
+        readannotations(numannos, base, ptr, len,
+                        &technique->annotations, objects,
+                        m, d);
+
+        technique->pass_count = numpasses;
+        readpasses(numpasses, base, ptr, len,
+                   &technique->passes, objects,
+                   m, d);
+    } // for
+} // readtechniques
+
+static void readsmallobjects(const uint32 numsmallobjects,
+                             const uint8 **ptr,
+                             uint32 *len,
+                             MOJOSHADER_effect *effect,
+                             const char *profile,
+                             const MOJOSHADER_swizzle *swiz,
+                             const unsigned int swizcount,
+                             const MOJOSHADER_samplerMap *smap,
+                             const unsigned int smapcount,
+                             MOJOSHADER_malloc m,
+                             MOJOSHADER_free f,
+                             void *d)
+{
+    int i, j;
+    if (numsmallobjects == 0) return;
+
+    for (i = 1; i < numsmallobjects + 1; i++)
+    {
+        const uint32 index = readui32(ptr, len);
+        const uint32 length = readui32(ptr, len);
+
+        MOJOSHADER_effectObject *object = &effect->objects[index];
+        if (object->type == MOJOSHADER_SYMTYPE_STRING)
+        {
+            if (length > 0)
+            {
+                char *str = (char *) m(length, d);
+                memcpy(str, *ptr, length);
+                object->string.string = str;
+            } // if
+        } // if
+        else if (object->type == MOJOSHADER_SYMTYPE_TEXTURE
+              || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D
+              || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D
+              || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D
+              || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE)
+        {
+            // No-op. Why is this even here?
+        } // else if
+        else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        {
+            if (length > 0)
+            {
+                char *str = (char *) m(length, d);
+                memcpy(str, *ptr, length);
+                object->mapping.name = str;
+            } // if
+        } // else if
+        else if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+              || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            object->shader.technique = -1;
+            object->shader.pass = -1;
+            object->shader.shader = MOJOSHADER_parse(profile, *ptr, length,
+                                                     swiz, swizcount, smap, smapcount,
+                                                     m, f, d);
+            // !!! FIXME: check for errors.
+            for (j = 0; j < object->shader.shader->symbol_count; j++)
+                if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER)
+                    object->shader.sampler_count++;
+            object->shader.param_count = object->shader.shader->symbol_count;
+            object->shader.params = (uint32 *) m(object->shader.param_count * sizeof (uint32), d);
+            object->shader.samplers = (MOJOSHADER_samplerStateRegister *) m(object->shader.sampler_count * sizeof (MOJOSHADER_samplerStateRegister), d);
+            uint32 curSampler = 0;
+            for (j = 0; j < object->shader.shader->symbol_count; j++)
+            {
+                int par = findparameter(effect->params,
+                                        effect->param_count,
+                                        object->shader.shader->symbols[j].name);
+                object->shader.params[j] = par;
+                if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER)
+                {
+                    object->shader.samplers[curSampler].sampler_name = object->shader.shader->symbols[j].name;
+                    object->shader.samplers[curSampler].sampler_register = object->shader.shader->symbols[j].register_index;
+                    object->shader.samplers[curSampler].sampler_state_count = effect->params[par].value.value_count;
+                    object->shader.samplers[curSampler].sampler_states = effect->params[par].value.valuesSS;
+                    curSampler++;
+                } // if
+            } // for
+            if (object->shader.shader->preshader)
+            {
+                object->shader.preshader_param_count = object->shader.shader->preshader->symbol_count;
+                object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d);
+                for (j = 0; j < object->shader.shader->preshader->symbol_count; j++)
+                {
+                    object->shader.preshader_params[j] = findparameter(effect->params,
+                                                                       effect->param_count,
+                                                                       object->shader.shader->preshader->symbols[j].name);
+                } // for
+            } // if
+        } // else if
+        else
+        {
+            assert(0 && "Small object type unknown!");
+        } // else
+
+        /* Object block is always a multiple of four */
+        const uint32 blocklen = (length + 3) - ((length - 1) % 4);
+        *ptr += blocklen;
+        *len -= blocklen;
+    } // for
+} // readstrings
+
+static void readlargeobjects(const uint32 numlargeobjects,
+                             const uint32 numsmallobjects,
+                             const uint8 **ptr,
+                             uint32 *len,
+                             MOJOSHADER_effect *effect,
+                             const char *profile,
+                             const MOJOSHADER_swizzle *swiz,
+                             const unsigned int swizcount,
+                             const MOJOSHADER_samplerMap *smap,
+                             const unsigned int smapcount,
+                             MOJOSHADER_malloc m,
+                             MOJOSHADER_free f,
+                             void *d)
+{
+    int i, j;
+    if (numlargeobjects == 0) return;
+
+    int numobjects = numsmallobjects + numlargeobjects + 1;
+    for (i = numsmallobjects + 1; i < numobjects; i++)
+    {
+        const uint32 technique = readui32(ptr, len);
+        const uint32 index = readui32(ptr, len);
+        /*const uint32 FIXME =*/ readui32(ptr, len);
+        const uint32 state = readui32(ptr, len);
+        const uint32 type = readui32(ptr, len);
+        const uint32 length = readui32(ptr, len);
+
+        uint32 objectIndex;
+        if (technique == -1)
+            objectIndex = effect->params[index].value.valuesSS[state].value.valuesI[0];
+        else
+            objectIndex = effect->techniques[technique].passes[index].states[state].value.valuesI[0];
+
+        MOJOSHADER_effectObject *object = &effect->objects[objectIndex];
+        if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+         || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            object->shader.technique = technique;
+            object->shader.pass = index;
+
+            const char *emitter = profile;
+            if (type == 2)
+            {
+                /* This is a standalone preshader!
+                 * It exists solely for effect passes that do not use a single
+                 * vertex/fragment shader.
+                 */
+                object->shader.is_preshader = 1;
+                const uint32 start = *((uint32 *) *ptr) + 4;
+                const uint32 end = 24; // FIXME: Why? -flibit
+                const char *array = readstring(*ptr, 0, m, d);
+                object->shader.param_count = 1;
+                object->shader.params = (uint32 *) m(sizeof (uint32), d);
+                object->shader.params[0] = findparameter(effect->params,
+                                                         effect->param_count,
+                                                         array);
+                f((void *) array, d);
+                object->shader.preshader = MOJOSHADER_parsePreshader(*ptr + start, length - end,
+                                                                     m, f, d);
+                // !!! FIXME: check for errors.
+                object->shader.preshader_param_count = object->shader.preshader->symbol_count;
+                object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d);
+                for (j = 0; j < object->shader.preshader->symbol_count; j++)
+                {
+                    object->shader.preshader_params[j] = findparameter(effect->params,
+                                                                       effect->param_count,
+                                                                       object->shader.preshader->symbols[j].name);
+                } // for
+            } // if
+            else
+            {
+                object->shader.shader = MOJOSHADER_parse(emitter, *ptr, length,
+                                                         swiz, swizcount, smap, smapcount,
+                                                         m, f, d);
+                // !!! FIXME: check for errors.
+                for (j = 0; j < object->shader.shader->symbol_count; j++)
+                    if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER)
+                        object->shader.sampler_count++;
+                object->shader.param_count = object->shader.shader->symbol_count;
+                object->shader.params = (uint32 *) m(object->shader.param_count * sizeof (uint32), d);
+                object->shader.samplers = (MOJOSHADER_samplerStateRegister *) m(object->shader.sampler_count * sizeof (MOJOSHADER_samplerStateRegister), d);
+                uint32 curSampler = 0;
+                for (j = 0; j < object->shader.shader->symbol_count; j++)
+                {
+                    int par = findparameter(effect->params,
+                                            effect->param_count,
+                                            object->shader.shader->symbols[j].name);
+                    object->shader.params[j] = par;
+                    if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER)
+                    {
+                        object->shader.samplers[curSampler].sampler_name = object->shader.shader->symbols[j].name;
+                        object->shader.samplers[curSampler].sampler_register = object->shader.shader->symbols[j].register_index;
+                        object->shader.samplers[curSampler].sampler_state_count = effect->params[par].value.value_count;
+                        object->shader.samplers[curSampler].sampler_states = effect->params[par].value.valuesSS;
+                        curSampler++;
+                    } // if
+                } // for
+                if (object->shader.shader->preshader)
+                {
+                    object->shader.preshader_param_count = object->shader.shader->preshader->symbol_count;
+                    object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d);
+                    for (j = 0; j < object->shader.shader->preshader->symbol_count; j++)
+                    {
+                        object->shader.preshader_params[j] = findparameter(effect->params,
+                                                                           effect->param_count,
+                                                                           object->shader.shader->preshader->symbols[j].name);
+                    } // for
+                } // if
+            }
+        } // if
+        else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        {
+            if (length > 0)
+            {
+                char *str = (char *) m(length, d);
+                memcpy(str, *ptr, length);
+                object->mapping.name = str;
+            } // if
+        } // else if
+        else if (object->type != MOJOSHADER_SYMTYPE_VOID) // FIXME: Why? -flibit
+        {
+            assert(0 && "Large object type unknown!");
+        } // else
+
+        /* Object block is always a multiple of four */
+        const uint32 blocklen = (length + 3) - ((length - 1) % 4);
+        *ptr += blocklen;
+        *len -= blocklen;
+    } // for
+} // readobjects
+
+MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile,
+                                          const unsigned char *buf,
+                                          const unsigned int _len,
+                                          const MOJOSHADER_swizzle *swiz,
+                                          const unsigned int swizcount,
+                                          const MOJOSHADER_samplerMap *smap,
+                                          const unsigned int smapcount,
+                                          MOJOSHADER_malloc m,
+                                          MOJOSHADER_free f,
+                                          void *d)
+{
+    const uint8 *ptr = (const uint8 *) buf;
+    uint32 len = (uint32) _len;
+
+    /* Supply both m and f, or neither */
     if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
-        return &MOJOSHADER_out_of_mem_effect;  // supply both or neither.
+        return &MOJOSHADER_out_of_mem_effect;
 
+    /* Use default malloc/free if m/f were not passed */
     if (m == NULL) m = MOJOSHADER_internal_malloc;
     if (f == NULL) f = MOJOSHADER_internal_free;
 
-    MOJOSHADER_effect *retval = m(sizeof (MOJOSHADER_effect), d);
+    /* malloc base effect structure */
+    MOJOSHADER_effect *retval = (MOJOSHADER_effect *) m(sizeof (MOJOSHADER_effect), d);
     if (retval == NULL)
-        return &MOJOSHADER_out_of_mem_effect;  // supply both or neither.
+        return &MOJOSHADER_out_of_mem_effect;
     memset(retval, '\0', sizeof (*retval));
 
+    /* Store m/f/d in effect structure */
     retval->malloc = m;
     retval->free = f;
     retval->malloc_data = d;
 
-    const uint8 *ptr = (const uint8 *) buf;
-    uint32 len = (uint32) _len;
-    size_t siz = 0;
-    int i, j, k;
-
     if (len < 8)
         goto parseEffect_unexpectedEOF;
 
+    /* Read in header magic, seek to initial offset */
     const uint8 *base = NULL;
-    if (readui32(&ptr, &len) != 0xFEFF0901) // !!! FIXME: is this always magic?
+    uint32 header = readui32(&ptr, &len);
+    if (header == 0xBCF00BCF)
+    {
+        /* The Effect compiler provided with XNA4 adds some extra mess at the
+         * beginning of the file. It's useless though, so just skip it.
+         * -flibit
+         */
+        const uint32 skip = readui32(&ptr, &len) - 8;
+        ptr += skip;
+        len += skip;
+        header = readui32(&ptr, &len);
+    } // if
+    if (header != 0xFEFF0901)
         goto parseEffect_notAnEffectsFile;
     else
     {
         const uint32 offset = readui32(&ptr, &len);
         base = ptr;
-//printf("base offset == %u\n", offset);
         if (offset > len)
             goto parseEffect_unexpectedEOF;
         ptr += offset;
         len -= offset;
     } // else
 
-    // params...
-
     if (len < 16)
         goto parseEffect_unexpectedEOF;
 
+    /* Parse structure counts */
     const uint32 numparams = readui32(&ptr, &len);
     const uint32 numtechniques = readui32(&ptr, &len);
-
-    readui32(&ptr, &len); // !!! FIXME: there are 8 unknown bytes here. Annotations?
-    /*const uint32 numobjects = */ readui32(&ptr, &len);
-
-    if (numparams > 0)
-    {
-        siz = sizeof (MOJOSHADER_effectParam) * numparams;
-        retval->params = (MOJOSHADER_effectParam *) m(siz, d);
-        if (retval->params == NULL)
-            goto parseEffect_outOfMemory;
-        memset(retval->params, '\0', siz);
-
-        retval->param_count = numparams;
-
-        for (i = 0; i < numparams; i++)
-        {
-            if (len < 16)
-                goto parseEffect_unexpectedEOF;
-
-            const uint32 typeoffset = readui32(&ptr, &len);
-            /*const uint32 valoffset =*/ readui32(&ptr, &len);
-            /*const uint32 flags =*/ readui32(&ptr, &len);
-            const uint32 numannos = readui32(&ptr, &len);
-            for (j = 0; j < numannos; j++)
-            {
-                if (len < 8)
-                    goto parseEffect_unexpectedEOF;
-                // !!! FIXME: parse annotations.
-                readui32(&ptr, &len);
-                readui32(&ptr, &len);
-            } // for
-
-            const uint8 *typeptr = base + typeoffset;
-            unsigned int typelen = 9999999;  // !!! FIXME
-            /*const uint32 paramtype =*/ readui32(&typeptr, &typelen);
-            /*const uint32 paramclass =*/ readui32(&typeptr, &typelen);
-            const uint32 paramname = readui32(&typeptr, &typelen);
-            const uint32 paramsemantic = readui32(&typeptr, &typelen);
+    /*const uint32 FIXME =*/ readui32(&ptr, &len);
+    const uint32 numobjects = readui32(&ptr, &len);
 
-            // !!! FIXME: sanity checks!
-            const char *namestr = ((const char *) base) + paramname;
-            const char *semstr = ((const char *) base) + paramsemantic;
-            uint32 len;
-            char *strptr;
-            len = *((const uint32 *) namestr);
-            strptr = (char *) m(len + 1, d);
-            memcpy(strptr, namestr + 4, len);
-            strptr[len] = '\0';
-            retval->params[i].name = strptr;
-            len = *((const uint32 *) semstr);
-            strptr = (char *) m(len + 1, d);
-            memcpy(strptr, semstr + 4, len);
-            strptr[len] = '\0';
-            retval->params[i].semantic = strptr;
-        } // for
-    } // if
-
-    uint32 numshaders = 0;  // we'll calculate this later.
-
-    // techniques...
-
-    if (numtechniques > 0)
-    {
-        siz = sizeof (MOJOSHADER_effectTechnique) * numtechniques;
-        retval->techniques = (MOJOSHADER_effectTechnique *) m(siz, d);
-        if (retval->techniques == NULL)
-            goto parseEffect_outOfMemory;
-        memset(retval->techniques, '\0', siz);
-
-        retval->technique_count = numtechniques;
-
-        for (i = 0; i < numtechniques; i++)
-        {
-            if (len < 12)
-                goto parseEffect_unexpectedEOF;
-            
-            MOJOSHADER_effectTechnique *technique = &retval->techniques[i];
-
-            const uint32 nameoffset = readui32(&ptr, &len);
-            const uint32 numannos = readui32(&ptr, &len);
-            const uint32 numpasses = readui32(&ptr, &len);
-
-            if (nameoffset >= _len)
-                goto parseEffect_unexpectedEOF;
+    /* Alloc structures now, so object types can be stored */
+    retval->object_count = numobjects;
+    const uint32 siz = sizeof (MOJOSHADER_effectObject) * numobjects;
+    retval->objects = (MOJOSHADER_effectObject *) m(siz, d);
+    if (retval->objects == NULL)
+        goto parseEffect_outOfMemory;
+    memset(retval->objects, '\0', siz);
 
-            if (numannos > 0)
-            {
-                // !!! FIXME: expose these to the caller?
-                for (j = 0; j < numannos; j++)
-                {
-                    if (len < 8)
-                        goto parseEffect_unexpectedEOF;
-                    readui32(&ptr, &len);  // typedef offset
-                    readui32(&ptr, &len);  // value offset
-                } // for
-            } // if
-
-            // !!! FIXME: verify this doesn't go past EOF looking for a null.
-            {
-                const char *namestr = ((char *) base) + nameoffset;
-                uint32 len = *((const uint32 *) namestr);
-                char *strptr = (char *) m(len + 1, d);
-                memcpy(strptr, namestr + 4, len);
-                strptr[len] = '\0';
-                technique->name = strptr;
-            }
-
-            if (numpasses > 0)
-            {
-                technique->pass_count = numpasses;
-
-                siz = sizeof (MOJOSHADER_effectPass) * numpasses;
-                technique->passes = (MOJOSHADER_effectPass *) m(siz, d);
-                if (technique->passes == NULL)
-                    goto parseEffect_outOfMemory;
-                memset(technique->passes, '\0', siz);
-
-                for (j = 0; j < numpasses; j++)
-                {
-                    if (len < 12)
-                        goto parseEffect_unexpectedEOF;
-
-                    MOJOSHADER_effectPass *pass = &technique->passes[j];
-
-                    const uint32 passnameoffset = readui32(&ptr, &len);
-                    const uint32 numannos = readui32(&ptr, &len);
-                    const uint32 numstates = readui32(&ptr, &len);
-
-                    if (passnameoffset >= _len)
-                        goto parseEffect_unexpectedEOF;
+    /* Parse effect parameters */
+    retval->param_count = numparams;
+    readparameters(numparams, base, &ptr, &len,
+                   &retval->params, retval->objects,
+                   m, d);
 
-                    // !!! FIXME: verify this doesn't go past EOF looking for a null.
-                    {
-                        const char *namestr = ((char *) base) + passnameoffset;
-                        uint32 len = *((const uint32 *) namestr);
-                        char *strptr = (char *) m(len + 1, d);
-                        memcpy(strptr, namestr + 4, len);
-                        strptr[len] = '\0';
-                        pass->name = strptr;
-                    }
-
-                    if (numannos > 0)
-                    {
-                        for (k = 0; k < numannos; k++)
-                        {
-                            if (len < 8)
-                                goto parseEffect_unexpectedEOF;
-                            // !!! FIXME: do something with this.
-                            readui32(&ptr, &len);
-                            readui32(&ptr, &len);
-                        } // for
-                    } // if
-
-                    if (numstates > 0)
-                    {
-                        pass->state_count = numstates;
+    /* Parse effect techniques */
+    retval->technique_count = numtechniques;
+    readtechniques(numtechniques, base, &ptr, &len,
+                   &retval->techniques, retval->objects,
+                   m, d);
 
-                        siz = sizeof (MOJOSHADER_effectState) * numstates;
-                        pass->states = (MOJOSHADER_effectState *) m(siz, d);
-                        if (pass->states == NULL)
-                            goto parseEffect_outOfMemory;
-                        memset(pass->states, '\0', siz);
-
-                        for (k = 0; k < numstates; k++)
-                        {
-                            if (len < 16)
-                                goto parseEffect_unexpectedEOF;
-
-                            MOJOSHADER_effectState *state = &pass->states[k];
-                            const uint32 type = readui32(&ptr, &len);
-                            readui32(&ptr, &len);  // !!! FIXME: don't know what this field does.
-                            /*const uint32 offsetend = */ readui32(&ptr, &len);
-                            /*const uint32 offsetstart = */ readui32(&ptr, &len);
-                            state->type = type;
-
-                            if ((type == 0x92) || (type == 0x93))
-                                numshaders++;
-                        } // for
-                    } // if
-                } // for
-            } // if
-        } // for
-    } // if
-
-    // textures...
+    /* Initial effect technique/pass */
+    retval->current_technique = &retval->techniques[0];
+    retval->current_pass = -1;
 
     if (len < 8)
         goto parseEffect_unexpectedEOF;
 
-    const int numtextures = readui32(&ptr, &len);
-    const int numobjects = readui32(&ptr, &len);  // !!! FIXME: "objects" for lack of a better word.
-
-    if (numtextures > 0)
-    {
-        siz = sizeof (MOJOSHADER_effectTexture) * numtextures;
-        retval->textures = m(siz, d);
-        if (retval->textures == NULL)
-            goto parseEffect_outOfMemory;
-        memset(retval->textures, '\0', siz);
-
-        for (i = 0; i < numtextures; i++)
-        {
-            if (len < 8)
-                goto parseEffect_unexpectedEOF;
-
-            MOJOSHADER_effectTexture *texture = &retval->textures[i];
-            const uint32 texparam = readui32(&ptr, &len);
-            const uint32 texsize = readui32(&ptr, &len);
-            // apparently texsize will pad out to 32 bits.
-            const uint32 readsize = (((texsize + 3) / 4) * 4);
-            if (len < readsize)
-                goto parseEffect_unexpectedEOF;
-
-            texture->param = texparam;
-            char *str = m(texsize + 1, d);
-            if (str == NULL)
-                goto parseEffect_outOfMemory;
-            memcpy(str, ptr, texsize);
-            str[texsize] = '\0';
-            texture->name = str;
-
-            ptr += readsize;
-            len -= readsize;
-        } // for
-    } // if
-
-    // shaders...
-
-    if (numshaders > 0)
-    {
-        siz = sizeof (MOJOSHADER_effectShader) * numshaders;
-        retval->shaders = (MOJOSHADER_effectShader *) m(siz, d);
-        if (retval->shaders == NULL)
-            goto parseEffect_outOfMemory;
-        memset(retval->shaders, '\0', siz);
-
-        retval->shader_count = numshaders;
+    /* Parse object counts */
+    const int numsmallobjects = readui32(&ptr, &len);
+    const int numlargeobjects = readui32(&ptr, &len);
 
-        // !!! FIXME: I wonder if we should pull these from offsets and not
-        // !!! FIXME:  count on them all being in a line like this.
-        for (i = 0; i < numshaders; i++)
-        {
-            if (len < 24)
-                goto parseEffect_unexpectedEOF;
-
-            MOJOSHADER_effectShader *shader = &retval->shaders[i];
-            const uint32 technique = readui32(&ptr, &len);
-            const uint32 pass = readui32(&ptr, &len);
-            readui32(&ptr, &len);  // !!! FIXME: don't know what this does.
-            readui32(&ptr, &len);  // !!! FIXME: don't know what this does (vertex/pixel/geometry?)
-            readui32(&ptr, &len);  // !!! FIXME: don't know what this does.
-            const uint32 shadersize = readui32(&ptr, &len);
-
-            if (len < shadersize)
-                goto parseEffect_unexpectedEOF;
-
-            shader->technique = technique;
-            shader->pass = pass;
-            shader->shader = MOJOSHADER_parse(profile, ptr, shadersize,
-                                              swiz, swizcount, smap, smapcount,
-                                              m, f, d);
-
-            // !!! FIXME: check for errors.
+    /* Parse "small" object table */
+    readsmallobjects(numsmallobjects, &ptr, &len,
+                     retval,
+                     profile, swiz, swizcount, smap, smapcount,
+                     m, f, d);
 
-            ptr += shadersize;
-            len -= shadersize;
-        } // for
-    } // if
-
-    // !!! FIXME: we parse this, but don't expose the data, yet.
-    // mappings ...
-    assert(numshaders <= numobjects);
-    const uint32 nummappings = numobjects - numshaders;
-    if (nummappings > 0)
-    {
-        for (i = 0; i < nummappings; i++)
-        {
-            if (len < 24)
-                goto parseEffect_unexpectedEOF;
+    /* Parse "large" object table. */
+    readlargeobjects(numlargeobjects, numsmallobjects, &ptr, &len,
+                     retval,
+                     profile, swiz, swizcount, smap, smapcount,
+                     m, f, d);
 
-            /*const uint32 magic = */ readui32(&ptr, &len);
-            /*const uint32 index = */ readui32(&ptr, &len);
-            readui32(&ptr, &len);  // !!! FIXME: what is this field?
-            readui32(&ptr, &len);  // !!! FIXME: what is this field?
-            /*const uint32 type = */ readui32(&ptr, &len);
-            const uint32 mapsize = readui32(&ptr, &len);
-            if (mapsize > 0)
-            {
-                const uint32 readsize = (((mapsize + 3) / 4) * 4);
-                if (len < readsize)
-                    goto parseEffect_unexpectedEOF;
-            } // if
-        } // for
-    } // if
-
+    /* Store MojoShader profile in effect structure */
     retval->profile = (char *) m(strlen(profile) + 1, d);
     if (retval->profile == NULL)
         goto parseEffect_outOfMemory;
@@ -560,7 +888,6 @@
 
     return retval;
 
-
 // !!! FIXME: do something with this.
 parseEffect_notAnEffectsFile:
 parseEffect_unexpectedEOF:
@@ -570,6 +897,22 @@
 } // MOJOSHADER_parseEffect
 
 
+void freevalue(MOJOSHADER_effectValue *value, MOJOSHADER_free f, void *d)
+{
+    int i;
+    f((void *) value->name, d);
+    f((void *) value->semantic, d);
+    if (value->value_type == MOJOSHADER_SYMTYPE_SAMPLER
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        for (i = 0; i < value->value_count; i++)
+            freevalue(&value->valuesSS[i].value, f, d);
+    f(value->values, d);
+} // freevalue
+
+
 void MOJOSHADER_freeEffect(const MOJOSHADER_effect *_effect)
 {
     MOJOSHADER_effect *effect = (MOJOSHADER_effect *) _effect;
@@ -578,8 +921,9 @@
 
     MOJOSHADER_free f = effect->free;
     void *d = effect->malloc_data;
-    int i, j;
+    int i, j, k;
 
+    /* Free errors */
     for (i = 0; i < effect->error_count; i++)
     {
         f((void *) effect->errors[i].error, d);
@@ -587,39 +931,702 @@
     } // for
     f((void *) effect->errors, d);
 
+    /* Free profile string */
     f((void *) effect->profile, d);
 
+    /* Free parameters, including annotations */
     for (i = 0; i < effect->param_count; i++)
     {
-        f((void *) effect->params[i].name, d);
-        f((void *) effect->params[i].semantic, d);
+        MOJOSHADER_effectParam *param = &effect->params[i];
+        freevalue(&param->value, f, d);
+        for (j = 0; j < param->annotation_count; j++)
+        {
+            freevalue(&param->annotations[j], f, d);
+        } // for
+        f((void *) param->annotations, d);
     } // for
-    f(effect->params, d);
+    f((void *) effect->params, d);
 
+    /* Free techniques, including passes and all annotations */
     for (i = 0; i < effect->technique_count; i++)
     {
         MOJOSHADER_effectTechnique *technique = &effect->techniques[i];
         f((void *) technique->name, d);
         for (j = 0; j < technique->pass_count; j++)
         {
-            f((void *) technique->passes[j].name, d);
-            f(technique->passes[j].states, d);
+            MOJOSHADER_effectPass *pass = &technique->passes[j];
+            f((void *) pass->name, d);
+            for (k = 0; k < pass->state_count; k++)
+            {
+                freevalue(&pass->states[k].value, f, d);
+            } // for
+            f((void *) pass->states, d);
+            for (k = 0; k < pass->annotation_count; k++)
+            {
+                freevalue(&pass->annotations[k], f, d);
+            } // for
+            f((void *) pass->annotations, d);
+        } // for
+        f((void *) technique->passes, d);
+        for (j = 0; j < technique->annotation_count; j++)
+        {
+            freevalue(&technique->annotations[j], f, d);
+        } // for
+        f((void *) technique->annotations, d);
+    } // for
+    f((void *) effect->techniques, d);
+
+    /* Free object table */
+    for (i = 0; i < effect->object_count; i++)
+    {
+        MOJOSHADER_effectObject *object = &effect->objects[i];
+        if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+         || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            if (object->shader.is_preshader)
+                MOJOSHADER_freePreshader(object->shader.preshader, f, d);
+            else
+                MOJOSHADER_freeParseData(object->shader.shader);
+            f((void *) object->shader.params, d);
+            f((void *) object->shader.samplers, d);
+            f((void *) object->shader.preshader_params, d);
+        } // if
+        else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
+              || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+            f((void *) object->mapping.name, d);
+        else if (object->type == MOJOSHADER_SYMTYPE_STRING)
+            f((void *) object->string.string, d);
+    } // for
+    f((void *) effect->objects, d);
+
+    /* Free base effect structure */
+    f((void *) effect, d);
+} // MOJOSHADER_freeEffect
+
+
+void copyvalue(MOJOSHADER_effectValue *dst,
+               MOJOSHADER_effectValue *src,
+               MOJOSHADER_malloc m,
+               void *d)
+{
+    int i;
+    uint32 siz = 0;
+    char *stringcopy = NULL;
+
+    // !!! FIXME: Out of memory check!
+    #define COPY_STRING(location) \
+        if (src->location != NULL) \
+        { \
+            siz = strlen(src->location) + 1; \
+            stringcopy = (char *) m(siz, d); \
+            strcpy(stringcopy, src->location); \
+            dst->location = stringcopy; \
+        } // if
+
+    COPY_STRING(name)
+    COPY_STRING(semantic)
+    dst->element_count = src->element_count;
+    dst->row_count = src->row_count;
+    dst->column_count = src->column_count;
+    dst->value_class = src->value_class;
+    dst->value_type = src->value_type;
+    dst->value_count = src->value_count;
+
+    if (dst->value_class == MOJOSHADER_SYMCLASS_SCALAR
+     || dst->value_class == MOJOSHADER_SYMCLASS_VECTOR
+     || dst->value_class == MOJOSHADER_SYMCLASS_MATRIX_ROWS
+     || dst->value_class == MOJOSHADER_SYMCLASS_MATRIX_COLUMNS)
+    {
+        siz = dst->value_count * 4;
+        dst->values = m(siz, d);
+        // !!! FIXME: Out of memory check!
+        memcpy(dst->values, src->values, siz);
+    } // if
+    else if (dst->value_class == MOJOSHADER_SYMCLASS_OBJECT)
+    {
+        if (dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER
+         || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D
+         || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D
+         || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D
+         || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        {
+            siz = dst->value_count * sizeof (MOJOSHADER_effectSamplerState);
+            dst->values = m(siz, d);
+            // !!! FIXME: Out of memory check!
+            memset(dst->values, '\0', siz);
+            for (i = 0; i < dst->value_count; i++)
+            {
+                dst->valuesSS[i].type = src->valuesSS[i].type;
+                copyvalue(&dst->valuesSS[i].value,
+                          &src->valuesSS[i].value,
+                          m, d);
+            } // for
+        } // if
+        else
+        {
+            siz = dst->value_count * 4;
+            dst->values = m(siz, d);
+            // !!! FIXME: Out of memory check!
+            memcpy(dst->values, src->values, siz);
+        } // else
+    } // else if
+    else if (dst->value_class == MOJOSHADER_SYMCLASS_STRUCT)
+    {
+        /* TODO: See readvalue! -flibit */
+    } // else if
+
+    #undef COPY_STRING
+} // copyvalue
+
+
+void copysymbolinfo(MOJOSHADER_symbolTypeInfo *dst,
+                    MOJOSHADER_symbolTypeInfo *src,
+                    MOJOSHADER_malloc m,
+                    void *d)
+{
+    int i;
+    uint32 siz;
+    char *stringcopy;
+
+    dst->parameter_class = src->parameter_class;
+    dst->parameter_type = src->parameter_type;
+    dst->rows = src->rows;
+    dst->columns = src->columns;
+    dst->elements = src->elements;
+    dst->member_count = src->member_count;
+
+    if (dst->member_count > 0)
+    {
+        siz = sizeof (MOJOSHADER_symbolStructMember) * dst->member_count;
+        dst->members = (MOJOSHADER_symbolStructMember *) m(siz, d);
+        // !!! FIXME: Out of memory check!
+        for (i = 0; i < dst->member_count; i++)
+        {
+            if (src->members[i].name != NULL)
+            {
+                siz = strlen(src->members[i].name) + 1;
+                stringcopy = (char *) m(siz, d);
+                strcpy(stringcopy, src->members[i].name);
+                dst->members[i].name = stringcopy;
+            } // if
+            copysymbolinfo(&dst->members[i].info, &src->members[i].info, m, d);
         } // for
-        f(technique->passes, d);
+    } // if
+} // copysymbolinfo
+
+
+void copysymbol(MOJOSHADER_symbol *dst,
+                MOJOSHADER_symbol *src,
+                MOJOSHADER_malloc m,
+                void *d)
+{
+    uint32 siz = strlen(src->name) + 1;
+    char *stringcopy = (char *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    strcpy(stringcopy, src->name);
+    dst->name = stringcopy;
+    dst->register_set = src->register_set;
+    dst->register_index = src->register_index;
+    dst->register_count = src->register_count;
+    copysymbolinfo(&dst->info, &src->info, m, d);
+} // copysymbol
+
+
+MOJOSHADER_preshader *copypreshader(const MOJOSHADER_preshader *src,
+                                    MOJOSHADER_malloc m,
+                                    void *d)
+{
+    int i, j;
+    uint32 siz;
+    MOJOSHADER_preshader *retval;
+
+    retval = (MOJOSHADER_preshader *) m(sizeof (MOJOSHADER_preshader), d);
+    // !!! FIXME: Out of memory check!
+    memset(retval, '\0', sizeof (MOJOSHADER_preshader));
+
+    siz = sizeof (double) * src->literal_count;
+    retval->literal_count = src->literal_count;
+    retval->literals = (double *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memcpy(retval->literals, src->literals, siz);
+
+    retval->temp_count = src->temp_count;
+
+    siz = sizeof (MOJOSHADER_symbol) * src->symbol_count;
+    retval->symbol_count = src->symbol_count;
+    retval->symbols = (MOJOSHADER_symbol *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->symbols, '\0', siz);
+
+    for (i = 0; i < retval->symbol_count; i++)
+        copysymbol(&retval->symbols[i], &src->symbols[i], m, d);
+
+    siz = sizeof (MOJOSHADER_preshaderInstruction) * src->instruction_count;
+    retval->instruction_count = src->instruction_count;
+    retval->instructions = (MOJOSHADER_preshaderInstruction *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memcpy(retval->instructions, src->instructions, siz);
+    for (i = 0; i < retval->instruction_count; i++)
+        for (j = 0; j < retval->instructions[i].operand_count; j++)
+        {
+            siz = sizeof (unsigned int) * retval->instructions[i].operands[j].array_register_count;
+            retval->instructions[i].operands[j].array_registers = (unsigned int *) m(siz, d);
+            // !!! FIXME: Out of memory check!
+            memcpy(retval->instructions[i].operands[j].array_registers,
+                   src->instructions[i].operands[j].array_registers,
+                   siz);
+        } // for
+
+    siz = sizeof (float) * 4 * src->register_count;
+    retval->register_count = src->register_count;
+    retval->registers = (float *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memcpy(retval->registers, src->registers, siz);
+
+    return retval;
+} // copypreshader
+
+
+MOJOSHADER_parseData *copyparsedata(const MOJOSHADER_parseData *src,
+                                    MOJOSHADER_malloc m,
+                                    void *d)
+{
+    int i;
+    uint32 siz;
+    char *stringcopy;
+    MOJOSHADER_parseData *retval;
+
+    retval = (MOJOSHADER_parseData *) m(sizeof (MOJOSHADER_parseData), d);
+    memset(retval, '\0', sizeof (MOJOSHADER_parseData));
+
+    /* Copy malloc/free */
+    retval->malloc = src->malloc;
+    retval->free = src->free;
+    retval->malloc_data = src->malloc_data;
+
+    // !!! FIXME: Out of memory check!
+    #define COPY_STRING(location) \
+        siz = strlen(src->location) + 1; \
+        stringcopy = (char *) m(siz, d); \
+        strcpy(stringcopy, src->location); \
+        retval->location = stringcopy; \
+
+    /* Copy errors */
+    siz = sizeof (MOJOSHADER_error) * src->error_count;
+    retval->error_count = src->error_count;
+    retval->errors = (MOJOSHADER_error *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->errors, '\0', siz);
+    for (i = 0; i < retval->error_count; i++)
+    {
+        COPY_STRING(errors[i].error)
+        COPY_STRING(errors[i].filename)
+        retval->errors[i].error_position = src->errors[i].error_position;
+    } // for
+
+    /* Copy profile string constant */
+    retval->profile = src->profile;
+
+    /* Copy shader output */
+    retval->output_len = src->output_len;
+    stringcopy = (char *) m(src->output_len, d);
+    memcpy(stringcopy, src->output, src->output_len);
+    retval->output = stringcopy;
+
+    /* Copy miscellaneous shader info */
+    retval->instruction_count = src->instruction_count;
+    retval->shader_type = src->shader_type;
+    retval->major_ver = src->major_ver;
+    retval->minor_ver = src->minor_ver;
+
+    /* Copy uniforms */
+    siz = sizeof (MOJOSHADER_uniform) * src->uniform_count;
+    retval->uniform_count = src->uniform_count;
+    retval->uniforms = (MOJOSHADER_uniform *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->uniforms, '\0', siz);
+    for (i = 0; i < retval->uniform_count; i++)
+    {
+        retval->uniforms[i].type = src->uniforms[i].type;
+        retval->uniforms[i].index = src->uniforms[i].index;
+        retval->uniforms[i].array_count = src->uniforms[i].array_count;
+        retval->uniforms[i].constant = src->uniforms[i].constant;
+        COPY_STRING(uniforms[i].name)
+    } // for
+
+    /* Copy constants */
+    siz = sizeof (MOJOSHADER_constant) * src->constant_count;
+    retval->constant_count = src->constant_count;
+    retval->constants = (MOJOSHADER_constant *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memcpy(retval->constants, src->constants, siz);
+
+    /* Copy samplers */
+    siz = sizeof (MOJOSHADER_sampler) * src->sampler_count;
+    retval->sampler_count = src->sampler_count;
+    retval->samplers = (MOJOSHADER_sampler *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->samplers, '\0', siz);
+    for (i = 0; i < retval->sampler_count; i++)
+    {
+        retval->samplers[i].type = src->samplers[i].type;
+        retval->samplers[i].index = src->samplers[i].index;
+        COPY_STRING(samplers[i].name)
+        retval->samplers[i].texbem = src->samplers[i].texbem;
+    } // for
+
+    /* Copy attributes */
+    siz = sizeof (MOJOSHADER_attribute) * src->attribute_count;
+    retval->attribute_count = src->attribute_count;
+    retval->attributes = (MOJOSHADER_attribute *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->attributes, '\0', siz);
+    for (i = 0; i < retval->attribute_count; i++)
+    {
+        retval->attributes[i].usage = src->attributes[i].usage;
+        retval->attributes[i].index = src->attributes[i].index;
+        COPY_STRING(attributes[i].name)
     } // for
 
-    f(effect->techniques, d);
+    /* Copy outputs */
+    siz = sizeof (MOJOSHADER_attribute) * src->output_count;
+    retval->output_count = src->output_count;
+    retval->outputs = (MOJOSHADER_attribute *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->outputs, '\0', siz);
+    for (i = 0; i < retval->output_count; i++)
+    {
+        retval->outputs[i].usage = src->outputs[i].usage;
+        retval->outputs[i].index = src->outputs[i].index;
+        COPY_STRING(outputs[i].name)
+    } // for
+
+    #undef COPY_STRING
+
+    /* Copy swizzles */
+    siz = sizeof (MOJOSHADER_swizzle) * src->swizzle_count;
+    retval->swizzle_count = src->swizzle_count;
+    retval->swizzles = (MOJOSHADER_swizzle *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memcpy(retval->swizzles, src->swizzles, siz);
+
+    /* Copy symbols */
+    siz = sizeof (MOJOSHADER_symbol) * src->symbol_count;
+    retval->symbol_count = src->symbol_count;
+    retval->symbols = (MOJOSHADER_symbol *) m(siz, d);
+    // !!! FIXME: Out of memory check!
+    memset(retval->symbols, '\0', siz);
+    for (i = 0; i < retval->symbol_count; i++)
+        copysymbol(&retval->symbols[i], &src->symbols[i], m, d);
+
+    /* Copy preshader */
+    if (src->preshader != NULL)
+        retval->preshader = copypreshader(src->preshader, m, d);
+
+    return retval;
+} // copyparsedata
+
+
+MOJOSHADER_effect *MOJOSHADER_cloneEffect(const MOJOSHADER_effect *effect)
+{
+    int i, j, k;
+    MOJOSHADER_effect *clone;
+    MOJOSHADER_malloc m = effect->malloc;
+    void *d = effect->malloc_data;
+    uint32 siz = 0;
+    char *stringcopy = NULL;
+    uint32 curSampler;
+
+    if ((effect == NULL) || (effect == &MOJOSHADER_out_of_mem_effect))
+        return NULL;  // no-op.
+
+    clone = (MOJOSHADER_effect *) m(sizeof (MOJOSHADER_effect), d);
+    if (clone == NULL)
+        return NULL; // Maybe out_of_mem_effect instead?
+    memset(clone, '\0', sizeof (MOJOSHADER_effect));
+
+    /* Copy malloc/free */
+    clone->malloc = effect->malloc;
+    clone->free = effect->free;
+    clone->malloc_data = effect->malloc_data;
+
+    #define COPY_STRING(location) \
+        siz = strlen(effect->location) + 1; \
+        stringcopy = (char *) m(siz, d); \
+        if (stringcopy == NULL) \
+            goto cloneEffect_outOfMemory; \
+        strcpy(stringcopy, effect->location); \
+        clone->location = stringcopy; \
 
-    for (i = 0; i < effect->texture_count; i++)
-        f((void *) effect->textures[i].name, d);
-    f(effect->textures, d);
+    /* Copy errors */
+    siz = sizeof (MOJOSHADER_error) * effect->error_count;
+    clone->error_count = effect->error_count;
+    clone->errors = (MOJOSHADER_error *) m(siz, d);
+    if (clone->errors == NULL)
+        goto cloneEffect_outOfMemory;
+    memset(clone->errors, '\0', siz);
+    for (i = 0; i < clone->error_count; i++)
+    {
+        COPY_STRING(errors[i].error)
+        COPY_STRING(errors[i].filename)
+        clone->errors[i].error_position = effect->errors[i].error_position;
+    } // for
+
+    /* Copy profile string */
+    COPY_STRING(profile)
+
+    /* Copy parameters */
+    siz = sizeof (MOJOSHADER_effectParam) * effect->param_count;
+    clone->param_count = effect->param_count;
+    clone->params = (MOJOSHADER_effectParam *) m(siz, d);
+    if (clone->params == NULL)
+        goto cloneEffect_outOfMemory;
+    memset(clone->params, '\0', siz);
+    for (i = 0; i < clone->param_count; i++)
+    {
+        copyvalue(&clone->params[i].value, &effect->params[i].value, m, d);
+
+        /* Copy parameter annotations */
+        siz = sizeof (MOJOSHADER_effectAnnotation) * effect->params[i].annotation_count;
+        clone->params[i].annotation_count = effect->params[i].annotation_count;
+        clone->params[i].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d);
+        if (clone->params[i].annotations == NULL)
+            goto cloneEffect_outOfMemory;
+        memset(clone->params[i].annotations, '\0', siz);
+        for (j = 0; j < clone->params[i].annotation_count; j++)
+            copyvalue(&clone->params[i].annotations[j],
+                      &effect->params[i].annotations[j],
+                      m, d);
+    } // for
+
+    /* Copy techniques */
+    siz = sizeof (MOJOSHADER_effectTechnique) * effect->technique_count;
+    clone->technique_count = effect->technique_count;
+    clone->techniques = (MOJOSHADER_effectTechnique *) m(siz, d);
+    if (clone->techniques == NULL)
+        goto cloneEffect_outOfMemory;
+    memset(clone->techniques, '\0', siz);
+    for (i = 0; i < clone->technique_count; i++)
+    {
+        COPY_STRING(techniques[i].name)
+
+        /* Copy passes */
+        siz = sizeof (MOJOSHADER_effectPass) * effect->techniques[i].pass_count;
+        clone->techniques[i].pass_count = effect->techniques[i].pass_count;
+        clone->techniques[i].passes = (MOJOSHADER_effectPass *) m(siz, d);
+        if (clone->techniques[i].passes == NULL)
+            goto cloneEffect_outOfMemory;
+        memset(clone->techniques[i].passes, '\0', siz);
+        for (j = 0; j < clone->techniques[i].pass_count; j++)
+        {
+            COPY_STRING(techniques[i].passes[j].name)
+
+            /* Copy pass states */
+            siz = sizeof (MOJOSHADER_effectState) * effect->techniques[i].passes[j].state_count;
+            clone->techniques[i].passes[j].state_count = effect->techniques[i].passes[j].state_count;
+            clone->techniques[i].passes[j].states = (MOJOSHADER_effectState *) m(siz, d);
+            if (clone->techniques[i].passes[j].states == NULL)
+                goto cloneEffect_outOfMemory;
+            memset(clone->techniques[i].passes[j].states, '\0', siz);
+            for (k = 0; k < clone->techniques[i].passes[j].state_count; k++)
+            {
+                clone->techniques[i].passes[j].states[k].type = effect->techniques[i].passes[j].states[k].type;
+                copyvalue(&clone->techniques[i].passes[j].states[k].value,
+                          &effect->techniques[i].passes[j].states[k].value,
+                          m, d);
+            } // for
+
+            /* Copy pass annotations */
+            siz = sizeof (MOJOSHADER_effectAnnotation) * effect->techniques[i].passes[j].annotation_count;
+            clone->techniques[i].passes[j].annotation_count = effect->techniques[i].passes[j].annotation_count;
+            clone->techniques[i].passes[j].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d);
+            if (clone->techniques[i].passes[j].annotations == NULL)
+                goto cloneEffect_outOfMemory;
+            memset(clone->techniques[i].passes[j].annotations, '\0', siz);
+            for (k = 0; k < clone->techniques[i].passes[j].annotation_count; k++)
+                copyvalue(&clone->techniques[i].passes[j].annotations[k],
+                          &effect->techniques[i].passes[j].annotations[k],
+                          m, d);
+        } // for
 
-    for (i = 0; i < effect->shader_count; i++)
-        MOJOSHADER_freeParseData(effect->shaders[i].shader);
-    f(effect->shaders, d);
+        /* Copy technique annotations */
+        siz = sizeof (MOJOSHADER_effectAnnotation) * effect->techniques[i].annotation_count;
+        clone->techniques[i].annotation_count = effect->techniques[i].annotation_count;
+        clone->techniques[i].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d);
+        if (clone->techniques[i].annotations == NULL)
+            goto cloneEffect_outOfMemory;
+        memset(clone->techniques[i].annotations, '\0', siz);
+        for (j = 0; j < clone->techniques[i].annotation_count; j++)
+            copyvalue(&clone->techniques[i].annotations[j],
+                      &effect->techniques[i].annotations[j],
+                      m, d);
+    } // for
+
+    /* Copy the current technique/pass */
+    for (i = 0; i < effect->technique_count; i++)
+        if (&effect->techniques[i] == effect->current_technique)
+        {
+            clone->current_technique = &clone->techniques[i];
+            break;
+        } // if
+    assert(clone->current_technique != NULL);
+    clone->current_pass = effect->current_pass;
+    assert(clone->current_pass == -1);
+
+    /* Copy object table */
+    siz = sizeof (MOJOSHADER_effectObject) * effect->object_count;
+    clone->object_count = effect->object_count;
+    clone->objects = (MOJOSHADER_effectObject *) m(siz, d);
+    if (clone->objects == NULL)
+        goto cloneEffect_outOfMemory;
+    memset(clone->objects, '\0', siz);
+    for (i = 0; i < clone->object_count; i++)
+    {
+        clone->objects[i].type = effect->objects[i].type;
+        if (clone->objects[i].type == MOJOSHADER_SYMTYPE_PIXELSHADER
+         || clone->objects[i].type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            clone->objects[i].shader.technique = effect->objects[i].shader.technique;
+            clone->objects[i].shader.pass = effect->objects[i].shader.pass;
+            clone->objects[i].shader.is_preshader = effect->objects[i].shader.is_preshader;
+            siz = sizeof (uint32) * effect->objects[i].shader.preshader_param_count;
+            clone->objects[i].shader.preshader_param_count = effect->objects[i].shader.preshader_param_count;
+            clone->objects[i].shader.preshader_params = (uint32 *) m(siz, d);
+            memcpy(clone->objects[i].shader.preshader_params,
+                   effect->objects[i].shader.preshader_params,
+                   siz);
+            siz = sizeof (uint32) * effect->objects[i].shader.param_count;
+            clone->objects[i].shader.param_count = effect->objects[i].shader.param_count;
+            clone->objects[i].shader.params = (uint32 *) m(siz, d);
+            memcpy(clone->objects[i].shader.params,
+                   effect->objects[i].shader.params,
+                   siz);
+
+            if (clone->objects[i].shader.is_preshader)
+            {
+                clone->objects[i].shader.preshader = copypreshader(effect->objects[i].shader.preshader,
+                                                                   m, d);
+                continue;
+            } // if
+
+            clone->objects[i].shader.shader = copyparsedata(effect->objects[i].shader.shader,
+                                                            m, d);
 
-    f(effect, d);
-} // MOJOSHADER_freeEffect
+            siz = sizeof (MOJOSHADER_samplerStateRegister) * effect->objects[i].shader.sampler_count;
+            clone->objects[i].shader.sampler_count = effect->objects[i].shader.sampler_count;
+            clone->objects[i].shader.samplers = (MOJOSHADER_samplerStateRegister *) m(siz, d);
+            if (clone->objects[i].shader.samplers == NULL)
+                goto cloneEffect_outOfMemory;
+            curSampler = 0;
+            for (j = 0; j < clone->objects[i].shader.shader->symbol_count; j++)
+                if (clone->objects[i].shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER)
+                {
+                    clone->objects[i].shader.samplers[curSampler].sampler_name = clone->objects[i].shader.shader->symbols[j].name;
+                    clone->objects[i].shader.samplers[curSampler].sampler_register = clone->objects[i].shader.shader->symbols[j].register_index;
+                    clone->objects[i].shader.samplers[curSampler].sampler_state_count = clone->params[clone->objects[i].shader.params[j]].value.value_count;
+                    clone->objects[i].shader.samplers[curSampler].sampler_states = clone->params[clone->objects[i].shader.params[j]].value.valuesSS;
+                    curSampler++;
+                } // if
+        } // if
+        else if (clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER
+              || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER1D
+              || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER2D
+              || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER3D
+              || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+        {
+            COPY_STRING(objects[i].mapping.name)
+        } // else if
+        else if (clone->objects[i].type == MOJOSHADER_SYMTYPE_STRING)
+        {
+            COPY_STRING(objects[i].string.string)
+        } // else if
+    } // for
+
+    #undef COPY_STRING
+
+    return clone;
+
+cloneEffect_outOfMemory:
+    MOJOSHADER_freeEffect(clone);
+    return NULL;
+} // MOJOSHADER_cloneEffect
+
+
+void MOJOSHADER_effectSetRawValueHandle(const MOJOSHADER_effectParam *parameter,
+                                        const void *data,
+                                        const unsigned int offset,
+                                        const unsigned int len)
+{
+    // !!! FIXME: uint32* case is arbitary, for Win32 -flibit
+    memcpy((uint32 *) parameter->value.values + offset, data, len);
+} // MOJOSHADER_effectSetRawValueHandle
+
+
+void MOJOSHADER_effectSetRawValueName(const MOJOSHADER_effect *effect,
+                                      const char *name,
+                                      const void *data,
+                                      const unsigned int offset,
+                                      const unsigned int len)
+{
+    int i;
+    for (i = 0; i < effect->param_count; i++)
+    {
+        if (strcmp(name, effect->params[i].value.name) == 0)
+        {
+            // !!! FIXME: uint32* case is arbitary, for Win32 -flibit
+            memcpy((uint32 *) effect->params[i].value.values + offset, data, len);
+            return;
+        } // if
+    } // for
+    assert(0 && "Effect parameter not found!");
+} // MOJOSHADER_effectSetRawValueName
+
+
+const MOJOSHADER_effectTechnique *MOJOSHADER_effectGetCurrentTechnique(const MOJOSHADER_effect *effect)
+{
+    return effect->current_technique;
+} // MOJOSHADER_effectGetCurrentTechnique
+
+
+void MOJOSHADER_effectSetTechnique(MOJOSHADER_effect *effect,
+                                   const MOJOSHADER_effectTechnique *technique)
+{
+    int i;
+    for (i = 0; i < effect->technique_count; i++)
+    {
+        if (technique == &effect->techniques[i])
+        {
+            effect->current_technique = technique;
+            return;
+        } // if
+    } // for
+    assert(0 && "Technique is not part of this effect!");
+} // MOJOSHADER_effectSetTechnique
+
+
+const MOJOSHADER_effectTechnique *MOJOSHADER_effectFindNextValidTechnique(const MOJOSHADER_effect *effect,
+                                                                          const MOJOSHADER_effectTechnique *technique
+)
+{
+    int i;
+    if (technique == NULL)
+        return &effect->techniques[0];
+    for (i = 0; i < effect->technique_count; i++)
+    {
+        if (technique == &effect->techniques[i])
+        {
+            if (i == effect->technique_count - 1)
+                return NULL; /* We were passed the last technique! */
+            return &effect->techniques[i + 1];
+        } // if
+    } // for
+    assert(0 && "Technique is not part of this effect!");
+} // MOJOSHADER_effectFindNextValidTechnique
+
+#endif // MOJOSHADER_EFFECT_SUPPORT
 
 // end of mojoshader_effects.c ...
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mojoshader_effects.h	Fri Jan 01 02:12:19 2016 -0500
@@ -0,0 +1,809 @@
+/**
+ * MojoShader; generate shader programs from bytecode of compiled
+ *  Direct3D shaders.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon.
+ */
+
+#ifndef MOJOSHADER_EFFECTS_H
+#define MOJOSHADER_EFFECTS_H
+
+#ifdef MOJOSHADER_EFFECT_SUPPORT
+
+/* MOJOSHADER_effectState types... */
+
+typedef enum MOJOSHADER_renderStateType
+{
+    /* Note that we are NOT using the actual RS values from D3D here.
+     * For some reason, in the binary data, it's 0-based.
+     * Even worse, it doesn't even seem to be in order.
+     * Here is the list of changes compared to the real D3DRS enum:
+     * - All of the RS_WRAP values are in a row, not separate!
+     *
+     * -flibit
+     */
+    MOJOSHADER_RS_ZENABLE,
+    MOJOSHADER_RS_FILLMODE,
+    MOJOSHADER_RS_SHADEMODE,
+    MOJOSHADER_RS_ZWRITEENABLE,
+    MOJOSHADER_RS_ALPHATESTENABLE,
+    MOJOSHADER_RS_LASTPIXEL,
+    MOJOSHADER_RS_SRCBLEND,
+    MOJOSHADER_RS_DESTBLEND,
+    MOJOSHADER_RS_CULLMODE,
+    MOJOSHADER_RS_ZFUNC,
+    MOJOSHADER_RS_ALPHAREF,
+    MOJOSHADER_RS_ALPHAFUNC,
+    MOJOSHADER_RS_DITHERENABLE,
+    MOJOSHADER_RS_ALPHABLENDENABLE,
+    MOJOSHADER_RS_FOGENABLE,
+    MOJOSHADER_RS_SPECULARENABLE,
+    MOJOSHADER_RS_FOGCOLOR,
+    MOJOSHADER_RS_FOGTABLEMODE,
+    MOJOSHADER_RS_FOGSTART,
+    MOJOSHADER_RS_FOGEND,
+    MOJOSHADER_RS_FOGDENSITY,
+    MOJOSHADER_RS_RANGEFOGENABLE,
+    MOJOSHADER_RS_STENCILENABLE,
+    MOJOSHADER_RS_STENCILFAIL,
+    MOJOSHADER_RS_STENCILZFAIL,
+    MOJOSHADER_RS_STENCILPASS,
+    MOJOSHADER_RS_STENCILFUNC,
+    MOJOSHADER_RS_STENCILREF,
+    MOJOSHADER_RS_STENCILMASK,
+    MOJOSHADER_RS_STENCILWRITEMASK,
+    MOJOSHADER_RS_TEXTUREFACTOR,
+    MOJOSHADER_RS_WRAP0,
+    MOJOSHADER_RS_WRAP1,
+    MOJOSHADER_RS_WRAP2,
+    MOJOSHADER_RS_WRAP3,
+    MOJOSHADER_RS_WRAP4,
+    MOJOSHADER_RS_WRAP5,
+    MOJOSHADER_RS_WRAP6,
+    MOJOSHADER_RS_WRAP7,
+    MOJOSHADER_RS_WRAP8,
+    MOJOSHADER_RS_WRAP9,
+    MOJOSHADER_RS_WRAP10,
+    MOJOSHADER_RS_WRAP11,
+    MOJOSHADER_RS_WRAP12,
+    MOJOSHADER_RS_WRAP13,
+    MOJOSHADER_RS_WRAP14,
+    MOJOSHADER_RS_WRAP15,
+    MOJOSHADER_RS_CLIPPING,
+    MOJOSHADER_RS_LIGHTING,
+    MOJOSHADER_RS_AMBIENT,
+    MOJOSHADER_RS_FOGVERTEXMODE,
+    MOJOSHADER_RS_COLORVERTEX,
+    MOJOSHADER_RS_LOCALVIEWER,
+    MOJOSHADER_RS_NORMALIZENORMALS,
+    MOJOSHADER_RS_DIFFUSEMATERIALSOURCE,
+    MOJOSHADER_RS_SPECULARMATERIALSOURCE,
+    MOJOSHADER_RS_AMBIENTMATERIALSOURCE,
+    MOJOSHADER_RS_EMISSIVEMATERIALSOURCE,
+    MOJOSHADER_RS_VERTEXBLEND,
+    MOJOSHADER_RS_CLIPPLANEENABLE,
+    MOJOSHADER_RS_POINTSIZE,
+    MOJOSHADER_RS_POINTSIZE_MIN,
+    MOJOSHADER_RS_POINTSPRITEENABLE,
+    MOJOSHADER_RS_POINTSCALEENABLE,
+    MOJOSHADER_RS_POINTSCALE_A,
+    MOJOSHADER_RS_POINTSCALE_B,
+    MOJOSHADER_RS_POINTSCALE_C,
+    MOJOSHADER_RS_MULTISAMPLEANTIALIAS,
+    MOJOSHADER_RS_MULTISAMPLEMASK,
+    MOJOSHADER_RS_PATCHEDGESTYLE,
+    MOJOSHADER_RS_DEBUGMONITORTOKEN,
+    MOJOSHADER_RS_POINTSIZE_MAX,
+    MOJOSHADER_RS_INDEXEDVERTEXBLENDENABLE,
+    MOJOSHADER_RS_COLORWRITEENABLE,
+    MOJOSHADER_RS_TWEENFACTOR,
+    MOJOSHADER_RS_BLENDOP,
+    MOJOSHADER_RS_POSITIONDEGREE,
+    MOJOSHADER_RS_NORMALDEGREE,
+    MOJOSHADER_RS_SCISSORTESTENABLE,
+    MOJOSHADER_RS_SLOPESCALEDEPTHBIAS,
+    MOJOSHADER_RS_ANTIALIASEDLINEENABLE,
+    MOJOSHADER_RS_MINTESSELLATIONLEVEL,
+    MOJOSHADER_RS_MAXTESSELLATIONLEVEL,
+    MOJOSHADER_RS_ADAPTIVETESS_X,
+    MOJOSHADER_RS_ADAPTIVETESS_Y,
+    MOJOSHADER_RS_ADAPTIVETESS_Z,
+    MOJOSHADER_RS_ADAPTIVETESS_W,
+    MOJOSHADER_RS_ENABLEADAPTIVETESSELLATION,
+    MOJOSHADER_RS_TWOSIDEDSTENCILMODE,
+    MOJOSHADER_RS_CCW_STENCILFAIL,
+    MOJOSHADER_RS_CCW_STENCILZFAIL,
+    MOJOSHADER_RS_CCW_STENCILPASS,
+    MOJOSHADER_RS_CCW_STENCILFUNC,
+    MOJOSHADER_RS_COLORWRITEENABLE1,
+    MOJOSHADER_RS_COLORWRITEENABLE2,
+    MOJOSHADER_RS_COLORWRITEENABLE3,
+    MOJOSHADER_RS_BLENDFACTOR,
+    MOJOSHADER_RS_SRGBWRITEENABLE,
+    MOJOSHADER_RS_DEPTHBIAS,
+    MOJOSHADER_RS_SEPARATEALPHABLENDENABLE,
+    MOJOSHADER_RS_SRCBLENDALPHA,
+    MOJOSHADER_RS_DESTBLENDALPHA,
+    MOJOSHADER_RS_BLENDOPALPHA,
+
+    /* These aren't really "states", but these numbers are
+     * referred to by MOJOSHADER_effectStateType as such.
+     */
+    MOJOSHADER_RS_VERTEXSHADER = 146,
+    MOJOSHADER_RS_PIXELSHADER = 147
+} MOJOSHADER_renderStateType;
+
+typedef enum MOJOSHADER_zBufferType
+{
+    MOJOSHADER_ZB_FALSE,
+    MOJOSHADER_ZB_TRUE,
+    MOJOSHADER_ZB_USEW
+} MOJOSHADER_zBufferType;
+
+typedef enum MOJOSHADER_fillMode
+{
+    MOJOSHADER_FILL_POINT     = 1,
+    MOJOSHADER_FILL_WIREFRAME = 2,
+    MOJOSHADER_FILL_SOLID     = 3
+} MOJOSHADER_fillMode;
+
+typedef enum MOJOSHADER_shadeMode
+{
+    MOJOSHADER_SHADE_FLAT    = 1,
+    MOJOSHADER_SHADE_GOURAUD = 2,
+    MOJOSHADER_SHADE_PHONG   = 3,
+} MOJOSHADER_shadeMode;
+
+typedef enum MOJOSHADER_blendMode
+{
+    MOJOSHADER_BLEND_ZERO            = 1,
+    MOJOSHADER_BLEND_ONE             = 2,
+    MOJOSHADER_BLEND_SRCCOLOR        = 3,
+    MOJOSHADER_BLEND_INVSRCCOLOR     = 4,
+    MOJOSHADER_BLEND_SRCALPHA        = 5,
+    MOJOSHADER_BLEND_INVSRCALPHA     = 6,
+    MOJOSHADER_BLEND_DESTALPHA       = 7,
+    MOJOSHADER_BLEND_INVDESTALPHA    = 8,
+    MOJOSHADER_BLEND_DESTCOLOR       = 9,
+    MOJOSHADER_BLEND_INVDESTCOLOR    = 10,
+    MOJOSHADER_BLEND_SRCALPHASAT     = 11,
+    MOJOSHADER_BLEND_BOTHSRCALPHA    = 12,
+    MOJOSHADER_BLEND_BOTHINVSRCALPHA = 13,
+    MOJOSHADER_BLEND_BLENDFACTOR     = 14,
+    MOJOSHADER_BLEND_INVBLENDFACTOR  = 15,
+    MOJOSHADER_BLEND_SRCCOLOR2       = 16,
+    MOJOSHADER_BLEND_INVSRCCOLOR2    = 17
+} MOJOSHADER_blendMode;
+
+typedef enum MOJOSHADER_cullMode
+{
+    MOJOSHADER_CULL_NONE = 1,
+    MOJOSHADER_CULL_CW   = 2,
+    MOJOSHADER_CULL_CCW  = 3
+} MOJOSHADER_cullMode;
+
+typedef enum MOJOSHADER_compareFunc
+{
+    MOJOSHADER_CMP_NEVER        = 1,
+    MOJOSHADER_CMP_LESS         = 2,
+    MOJOSHADER_CMP_EQUAL        = 3,
+    MOJOSHADER_CMP_LESSEQUAL    = 4,
+    MOJOSHADER_CMP_GREATER      = 5,
+    MOJOSHADER_CMP_NOTEQUAL     = 6,
+    MOJOSHADER_CMP_GREATEREQUAL = 7,
+    MOJOSHADER_CMP_ALWAYS       = 8
+} MOJOSHADER_compareFunc;
+
+typedef enum MOJOSHADER_fogMode
+{
+    MOJOSHADER_FOG_NONE,
+    MOJOSHADER_FOG_EXP,
+    MOJOSHADER_FOG_EXP2,
+    MOJOSHADER_FOG_LINEAR
+} MOJOSHADER_fogMode;
+
+typedef enum MOJOSHADER_stencilOp
+{
+    MOJOSHADER_STENCILOP_KEEP    = 1,
+    MOJOSHADER_STENCILOP_ZERO    = 2,
+    MOJOSHADER_STENCILOP_REPLACE = 3,
+    MOJOSHADER_STENCILOP_INCRSAT = 4,
+    MOJOSHADER_STENCILOP_DECRSAT = 5,
+    MOJOSHADER_STENCILOP_INVERT  = 6,
+    MOJOSHADER_STENCILOP_INCR    = 7,
+    MOJOSHADER_STENCILOP_DECR    = 8
+} MOJOSHADER_stencilOp;
+
+typedef enum MOJOSHADER_materialColorSource
+{
+    MOJOSHADER_MCS_MATERIAL,
+    MOJOSHADER_MCS_COLOR1,
+    MOJOSHADER_MCS_COLOR2
+} MOJOSHADER_materialColorSource;
+
+typedef enum MOJOSHADER_vertexBlendFlags
+{
+    MOJOSHADER_VBF_DISABLE  = 0,
+    MOJOSHADER_VBF_1WEIGHTS = 1,
+    MOJOSHADER_VBF_2WEIGHTS = 2,
+    MOJOSHADER_VBF_3WEIGHTS = 3,
+    MOJOSHADER_VBF_TWEENING = 255,
+    MOJOSHADER_VBF_0WEIGHTS = 256,
+} MOJOSHADER_vertexBlendFlags;
+
+typedef enum MOJOSHADER_patchedEdgeStyle
+{
+    MOJOSHADER_PATCHEDGE_DISCRETE,
+    MOJOSHADER_PATCHEDGE_CONTINUOUS
+} MOJOSHADER_patchedEdgeStyle;
+
+typedef enum MOJOSHADER_debugMonitorTokens
+{
+    MOJOSHADER_DMT_ENABLE,
+    MOJOSHADER_DMT_DISABLE
+} MOJOSHADER_debugMonitorTokens;
+
+typedef enum MOJOSHADER_blendOp
+{
+    MOJOSHADER_BLENDOP_ADD         = 1,
+    MOJOSHADER_BLENDOP_SUBTRACT    = 2,
+    MOJOSHADER_BLENDOP_REVSUBTRACT = 3,
+    MOJOSHADER_BLENDOP_MIN         = 4,
+    MOJOSHADER_BLENDOP_MAX         = 5
+} MOJOSHADER_blendOp;
+
+typedef enum MOJOSHADER_degreeType
+{
+    MOJOSHADER_DEGREE_LINEAR    = 1,
+    MOJOSHADER_DEGREE_QUADRATIC = 2,
+    MOJOSHADER_DEGREE_CUBIC     = 3,
+    MOJOSHADER_DEGREE_QUINTIC   = 5
+} MOJOSHADER_degreeType;
+
+
+/* MOJOSHADER_effectSamplerState types... */
+
+typedef enum MOJOSHADER_samplerStateType
+{
+    MOJOSHADER_SAMP_UNKNOWN0      = 0,
+    MOJOSHADER_SAMP_UNKNOWN1      = 1,
+    MOJOSHADER_SAMP_UNKNOWN2      = 2,
+    MOJOSHADER_SAMP_UNKNOWN3      = 3,
+    MOJOSHADER_SAMP_TEXTURE       = 4,
+    MOJOSHADER_SAMP_ADDRESSU      = 5,
+    MOJOSHADER_SAMP_ADDRESSV      = 6,
+    MOJOSHADER_SAMP_ADDRESSW      = 7,
+    MOJOSHADER_SAMP_BORDERCOLOR   = 8,
+    MOJOSHADER_SAMP_MAGFILTER     = 9,
+    MOJOSHADER_SAMP_MINFILTER     = 10,
+    MOJOSHADER_SAMP_MIPFILTER     = 11,
+    MOJOSHADER_SAMP_MIPMAPLODBIAS = 12,
+    MOJOSHADER_SAMP_MAXMIPLEVEL   = 13,
+    MOJOSHADER_SAMP_MAXANISOTROPY = 14,
+    MOJOSHADER_SAMP_SRGBTEXTURE   = 15,
+    MOJOSHADER_SAMP_ELEMENTINDEX  = 16,
+    MOJOSHADER_SAMP_DMAPOFFSET    = 17
+} MOJOSHADER_samplerStateType;
+
+typedef enum MOJOSHADER_textureAddress
+{
+    MOJOSHADER_TADDRESS_WRAP       = 1,
+    MOJOSHADER_TADDRESS_MIRROR     = 2,
+    MOJOSHADER_TADDRESS_CLAMP      = 3,
+    MOJOSHADER_TADDRESS_BORDER     = 4,
+    MOJOSHADER_TADDRESS_MIRRORONCE = 5
+} MOJOSHADER_textureAddress;
+
+typedef enum MOJOSHADER_textureFilterType
+{
+    MOJOSHADER_TEXTUREFILTER_NONE,
+    MOJOSHADER_TEXTUREFILTER_POINT,
+    MOJOSHADER_TEXTUREFILTER_LINEAR,
+    MOJOSHADER_TEXTUREFILTER_ANISOTROPIC,
+    MOJOSHADER_TEXTUREFILTER_PYRAMIDALQUAD,
+    MOJOSHADER_TEXTUREFILTER_GAUSSIANQUAD,
+    MOJOSHADER_TEXTUREFILTER_CONVOLUTIONMONO
+} MOJOSHADER_textureFilterType;
+
+
+/* Effect value types... */
+
+typedef struct MOJOSHADER_effectSamplerState MOJOSHADER_effectSamplerState;
+
+typedef struct MOJOSHADER_effectValue
+{
+    const char *name;
+    const char *semantic;
+    unsigned int element_count;
+    unsigned int row_count;
+    unsigned int column_count;
+    MOJOSHADER_symbolClass value_class;
+    MOJOSHADER_symbolType value_type;
+    unsigned int value_count;
+    union
+    {
+         /* Raw value types */
+        void                           *values;
+        int                            *valuesI;
+        float                          *valuesF;
+        /* As used by MOJOSHADER_effectState */
+        MOJOSHADER_zBufferType         *valuesZBT;
+        MOJOSHADER_fillMode            *valuesFiM;
+        MOJOSHADER_shadeMode           *valuesSM;
+        MOJOSHADER_blendMode           *valuesBM;
+        MOJOSHADER_cullMode            *valuesCM;
+        MOJOSHADER_compareFunc         *valuesCF;
+        MOJOSHADER_fogMode             *valuesFoM;
+        MOJOSHADER_stencilOp           *valuesSO;
+        MOJOSHADER_materialColorSource *valuesMCS;
+        MOJOSHADER_vertexBlendFlags    *valuesVBF;
+        MOJOSHADER_patchedEdgeStyle    *valuesPES;
+        MOJOSHADER_debugMonitorTokens  *valuesDMT;
+        MOJOSHADER_blendOp             *valuesBO;
+        MOJOSHADER_degreeType          *valuesDT;
+        /* As used by MOJOSHADER_effectSamplerState */
+        MOJOSHADER_textureAddress      *valuesTA;
+        MOJOSHADER_textureFilterType   *valuesTFT;
+        /* As used by MOJOSHADER_effectParameter */
+        MOJOSHADER_effectSamplerState  *valuesSS;
+    };
+} MOJOSHADER_effectValue;
+
+typedef struct MOJOSHADER_effectState
+{
+    MOJOSHADER_renderStateType type;
+    MOJOSHADER_effectValue value;
+} MOJOSHADER_effectState;
+
+struct MOJOSHADER_effectSamplerState
+{
+    MOJOSHADER_samplerStateType type;
+    MOJOSHADER_effectValue value;
+};
+
+typedef MOJOSHADER_effectValue MOJOSHADER_effectAnnotation;
+
+
+/* Effect interface structures... */
+
+typedef struct MOJOSHADER_effectParam
+{
+    MOJOSHADER_effectValue value;
+    unsigned int annotation_count;
+    MOJOSHADER_effectAnnotation *annotations;
+} MOJOSHADER_effectParam;
+
+typedef struct MOJOSHADER_effectPass
+{
+    const char *name;
+    unsigned int state_count;
+    MOJOSHADER_effectState *states;
+    unsigned int annotation_count;
+    MOJOSHADER_effectAnnotation* annotations;
+} MOJOSHADER_effectPass;
+
+typedef struct MOJOSHADER_effectTechnique
+{
+    const char *name;
+    unsigned int pass_count;
+    MOJOSHADER_effectPass *passes;
+    unsigned int annotation_count;
+    MOJOSHADER_effectAnnotation* annotations;
+} MOJOSHADER_effectTechnique;
+
+
+/* Effect "objects"... */
+
+/* Defined later in the state change types... */
+typedef struct MOJOSHADER_samplerStateRegister MOJOSHADER_samplerStateRegister;
+
+typedef struct MOJOSHADER_effectShader
+{
+    MOJOSHADER_symbolType type;
+    unsigned int technique;
+    unsigned int pass;
+    unsigned int is_preshader;
+    unsigned int preshader_param_count;
+    unsigned int *preshader_params;
+    unsigned int param_count;
+    unsigned int *params;
+    unsigned int sampler_count;
+    MOJOSHADER_samplerStateRegister *samplers;
+    union
+    {
+        const MOJOSHADER_parseData *shader;
+        const MOJOSHADER_preshader *preshader;
+    };
+} MOJOSHADER_effectShader;
+
+typedef struct MOJOSHADER_effectSamplerMap
+{
+    MOJOSHADER_symbolType type;
+    const char *name;
+} MOJOSHADER_effectSamplerMap;
+
+typedef struct MOJOSHADER_effectString
+{
+    MOJOSHADER_symbolType type;
+    const char *string;
+} MOJOSHADER_effectString;
+
+typedef struct MOJOSHADER_effectTexture
+{
+    MOJOSHADER_symbolType type;
+    /* FIXME: Does this even do anything? */
+} MOJOSHADER_effectTexture;
+
+typedef union MOJOSHADER_effectObject
+{
+    MOJOSHADER_symbolType type;
+    union
+    {
+        MOJOSHADER_effectShader shader;
+        MOJOSHADER_effectSamplerMap mapping;
+        MOJOSHADER_effectString string;
+        MOJOSHADER_effectTexture texture;
+    };
+} MOJOSHADER_effectObject;
+
+
+/* Effect state change types... */
+
+/* Used to store sampler states with accompanying sampler registers */
+struct MOJOSHADER_samplerStateRegister
+{
+    const char *sampler_name;
+    unsigned int sampler_register;
+    unsigned int sampler_state_count;
+    const MOJOSHADER_effectSamplerState *sampler_states;
+};
+
+/*
+ * Used to acquire the desired render state by the effect pass.
+ */
+typedef struct MOJOSHADER_effectStateChanges
+{
+    /* Render state changes caused by effect technique */
+    unsigned int render_state_change_count;
+    const MOJOSHADER_effectState *render_state_changes;
+
+    /* Sampler state changes caused by effect technique */
+    unsigned int sampler_state_change_count;
+    const MOJOSHADER_samplerStateRegister *sampler_state_changes;
+
+    /* Vertex sampler state changes caused by effect technique */
+    unsigned int vertex_sampler_state_change_count;
+    const MOJOSHADER_samplerStateRegister *vertex_sampler_state_changes;
+} MOJOSHADER_effectStateChanges;
+
+
+/*
+ * Structure used to return data from parsing of an effect file...
+ */
+/* !!! FIXME: most of these ints should be unsigned. */
+typedef struct MOJOSHADER_effect
+{
+    /*
+     * The number of elements pointed to by (errors).
+     */
+    int error_count;
+
+    /*
+     * (error_count) elements of data that specify errors that were generated
+     *  by parsing this shader.
+     * This can be NULL if there were no errors or if (error_count) is zero.
+     */
+    MOJOSHADER_error *errors;
+
+    /*
+     * The name of the profile used to parse the shader. Will be NULL on error.
+     */
+    const char *profile;
+
+    /*
+     * The number of params pointed to by (params).
+     */
+    int param_count;
+
+    /*
+     * (param_count) elements of data that specify parameter bind points for
+     *  this effect.
+     * This can be NULL on error or if (param_count) is zero.
+     */
+    MOJOSHADER_effectParam *params;
+
+    /*
+     * The number of elements pointed to by (techniques).
+     */
+    int technique_count;
+
+    /*
+     * (technique_count) elements of data that specify techniques used in
+     *  this effect. Each technique contains a series of passes, and each pass
+     *  specifies state and shaders that affect rendering.
+     * This can be NULL on error or if (technique_count) is zero.
+     */
+    MOJOSHADER_effectTechnique *techniques;
+
+    /*
+     * The technique currently being rendered by this effect.
+     */
+    const MOJOSHADER_effectTechnique *current_technique;
+
+    /*
+     * The index of the current pass being rendered by this effect.
+     */
+    int current_pass;
+
+    /*
+     * The number of elements pointed to by (objects).
+     */
+    int object_count;
+
+    /*
+     * (object_count) elements of data that specify objects used in
+     *  this effect.
+     * This can be NULL on error or if (object_count) is zero.
+     */
+    MOJOSHADER_effectObject *objects;
+
+    /*
+     * Value used to determine whether or not to restore the previous shader
+     * state after rendering an effect, as requested by application.
+     */
+    int restore_shader_state;
+
+    /*
+     * The structure provided by the appliation to store the state changes.
+     */
+    MOJOSHADER_effectStateChanges *state_changes;
+
+    /*
+     * This is the malloc implementation you passed to MOJOSHADER_parseEffect().
+     */
+    MOJOSHADER_malloc malloc;
+
+    /*
+     * This is the free implementation you passed to MOJOSHADER_parseEffect().
+     */
+    MOJOSHADER_free free;
+
+    /*
+     * This is the pointer you passed as opaque data for your allocator.
+     */
+    void *malloc_data;
+} MOJOSHADER_effect;
+
+
+/* Effect parsing interface... */
+
+/* !!! FIXME: document me. */
+DECLSPEC MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile,
+                                                   const unsigned char *buf,
+                                                   const unsigned int _len,
+                                                   const MOJOSHADER_swizzle *swiz,
+                                                   const unsigned int swizcount,
+                                                   const MOJOSHADER_samplerMap *smap,
+                                                   const unsigned int smapcount,
+                                                   MOJOSHADER_malloc m,
+                                                   MOJOSHADER_free f,
+                                                   void *d);
+
+
+/* !!! FIXME: document me. */
+DECLSPEC void MOJOSHADER_freeEffect(const MOJOSHADER_effect *effect);
+
+
+/* !!! FIXME: document me. */
+DECLSPEC MOJOSHADER_effect *MOJOSHADER_cloneEffect(const MOJOSHADER_effect *effect);
+
+
+/* Effect parameter interface... */
+
+/* Set the constant value for the specified effect parameter.
+ *
+ * This function maps to ID3DXEffect::SetRawValue.
+ *
+ * (parameter) is a parameter obtained from a MOJOSHADER_effect*.
+ * (data) is the constant values to be applied to the parameter.
+ * (offset) is the offset, in bytes, of the parameter data being modified.
+ * (len) is the size, in bytes, of the data buffer being applied.
+ *
+ * This function is thread safe.
+ */
+DECLSPEC void MOJOSHADER_effectSetRawValueHandle(const MOJOSHADER_effectParam *parameter,
+                                                 const void *data,
+                                                 const unsigned int offset,
+                                                 const unsigned int len);
+
+/* Set the constant value for the effect parameter, specified by name.
+ *  Note: this function is slower than MOJOSHADER_effectSetRawValueHandle(),
+ *  but we still provide it to fully map to ID3DXEffect.
+ *
+ * This function maps to ID3DXEffect::SetRawValue.
+ *
+ * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect().
+ * (name) is the human-readable name of the parameter being modified.
+ * (data) is the constant values to be applied to the parameter.
+ * (offset) is the offset, in bytes, of the parameter data being modified.
+ * (len) is the size, in bytes, of the data buffer being applied.
+ *
+ * This function is thread safe.
+ */
+DECLSPEC void MOJOSHADER_effectSetRawValueName(const MOJOSHADER_effect *effect,
+                                               const char *name,
+                                               const void *data,
+                                               const unsigned int offset,
+                                               const unsigned int len);
+
+
+/* Effect technique interface... */
+
+/* Get the current technique in use by an effect.
+ *
+ * This function maps to ID3DXEffect::GetCurrentTechnique.
+ *
+ * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect().
+ *
+ * This function returns the technique currently used by the given effect.
+ *
+ * This function is thread safe.
+ */
+DECLSPEC const MOJOSHADER_effectTechnique *MOJOSHADER_effectGetCurrentTechnique(const MOJOSHADER_effect *effect);
+
+/* Set the current technique to be used an effect.
+ *
+ * This function maps to ID3DXEffect::SetTechnique.
+ *
+ * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect().
+ * (technique) is the technique to be used by the effect when rendered.
+ *
+ * This function is thread safe.
+ */
+DECLSPEC void MOJOSHADER_effectSetTechnique(MOJOSHADER_effect *effect,
+                                            const MOJOSHADER_effectTechnique *technique);
+
+/* Get the next technique in an effect's list.
+ *
+ * This function maps to ID3DXEffect::FindNextValidTechnique.
+ *
+ * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect().
+ * (technique) can either be a technique found in the given effect, or NULL to
+ *  find the first technique in the given effect.
+ *
+ * This function returns either the next technique after the passed technique,
+ *  or the first technique if the passed technique is NULL.
+ *
+ * This function is thread safe.
+ */
+DECLSPEC const MOJOSHADER_effectTechnique *MOJOSHADER_effectFindNextValidTechnique(const MOJOSHADER_effect *effect,
+                                                                                   const MOJOSHADER_effectTechnique *technique);
+
+
+/* OpenGL effect interface... */
+
+typedef struct MOJOSHADER_glEffect MOJOSHADER_glEffect;
+
+/* Fully compile/link the shaders found within the effect.
+ *
+ * The MOJOSHADER_glEffect* is solely for use within the OpenGL-specific calls.
+ *  In all other cases you will be using the MOJOSHADER_effect* instead.
+ *
+ * In a typical use case, you will be calling this immediately after obtaining
+ *  the MOJOSHADER_effect*.
+ *
+ * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect().
+ *
+ * This function returns a MOJOSHADER_glEffect*, containing OpenGL-specific
+ *  data for an accompanying MOJOSHADER_effect*.
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC MOJOSHADER_glEffect *MOJOSHADER_glCompileEffect(MOJOSHADER_effect *effect);
+
+/* Delete the shaders that were allocated for an effect.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glDeleteEffect(MOJOSHADER_glEffect *glEffect);
+
+/* Prepare the effect for rendering with the currently applied technique.
+ *
+ * This function maps to ID3DXEffect::Begin.
+ *
+ * In addition to the expected Begin parameters, we also include a parameter
+ *  to pass in a MOJOSHADER_effectRenderState. Rather than change the render
+ *  state within MojoShader itself we will simply provide what the effect wants
+ *  and allow you to use this information with your own renderer.
+ *  MOJOSHADER_glEffectBeginPass will update with the render state desired by
+ *  the current effect pass.
+ *
+ * Note that we only provide the ability to preserve the shader state, but NOT
+ * the ability to preserve the render/sampler states. You are expected to
+ * track your own GL state and restore these states as needed for your
+ * application.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ * (numPasses) will be filled with the number of passes that this technique
+ *  will need to fully render.
+ * (saveShaderState) is a boolean value informing the effect whether or not to
+ *  restore the shader bindings after calling MOJOSHADER_glEffectEnd.
+ * (renderState) will be filled by the effect to inform you of the render state
+ *  changes introduced by the technique and its passes.
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glEffectBegin(MOJOSHADER_glEffect *glEffect,
+                                       unsigned int *numPasses,
+                                       int saveShaderState,
+                                       MOJOSHADER_effectStateChanges *stateChanges);
+
+/* Begin an effect pass from the currently applied technique.
+ *
+ * This function maps to ID3DXEffect::BeginPass.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ * (pass) is the index of the effect pass as found in the current technique.
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glEffectBeginPass(MOJOSHADER_glEffect *glEffect,
+                                           unsigned int pass);
+
+/* Push render state changes that occurred within an actively rendering pass.
+ *
+ * This function maps to ID3DXEffect::CommitChanges.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glEffectCommitChanges(MOJOSHADER_glEffect *glEffect);
+
+/* End an effect pass from the currently applied technique.
+ *
+ * This function maps to ID3DXEffect::EndPass.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glEffectEndPass(MOJOSHADER_glEffect *glEffect);
+
+/* Complete rendering the effect technique, and restore the render state.
+ *
+ * This function maps to ID3DXEffect::End.
+ *
+ * (glEffect) is a MOJOSHADER_glEffect* obtained from
+ *  MOJOSHADER_glCompileEffect().
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ * safe, you should probably only call this from the same thread that created
+ * the GL context.
+ */
+DECLSPEC void MOJOSHADER_glEffectEnd(MOJOSHADER_glEffect *glEffect);
+
+#endif /* MOJOSHADER_EFFECT_SUPPORT */
+
+#endif /* MOJOSHADER_EFFECTS_H */
--- a/mojoshader_internal.h	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader_internal.h	Fri Jan 01 02:12:19 2016 -0500
@@ -52,6 +52,10 @@
 #define SUPPORT_PROFILE_GLSL120 1
 #endif
 
+#ifndef SUPPORT_PROFILE_GLSLES
+#define SUPPORT_PROFILE_GLSLES 1
+#endif
+
 #ifndef SUPPORT_PROFILE_ARB1
 #define SUPPORT_PROFILE_ARB1 1
 #endif
@@ -68,6 +72,10 @@
 #error glsl120 profile requires glsl profile. Fix your build.
 #endif
 
+#if SUPPORT_PROFILE_GLSLES && !SUPPORT_PROFILE_GLSL
+#error glsles profile requires glsl profile. Fix your build.
+#endif
+
 // Microsoft's preprocessor has some quirks. In some ways, it doesn't work
 //  like you'd expect a C preprocessor to function.
 #ifndef MATCH_MICROSOFT_PREPROCESSOR
@@ -76,16 +84,8 @@
 
 // Other stuff you can disable...
 
-// This removes the preshader parsing and execution code. You can save some
-//  bytes if you have normal shaders and not Effect files.
-#ifndef SUPPORT_PRESHADERS
-#define SUPPORT_PRESHADERS 1
-#endif
-
-#if SUPPORT_PRESHADERS
-void MOJOSHADER_runPreshader(const MOJOSHADER_preshader*, const float*, float*);
-#else
-#define MOJOSHADER_runPreshader(a, b)
+#ifdef MOJOSHADER_EFFECT_SUPPORT
+void MOJOSHADER_runPreshader(const MOJOSHADER_preshader*, float*);
 #endif
 
 
@@ -99,6 +99,9 @@
 
 typedef unsigned int uint;  // this is a printf() helper. don't use for code.
 
+// Locale-independent float printing replacement for snprintf
+size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg);
+
 #ifdef _MSC_VER
 #include <malloc.h>
 #define va_copy(a, b) a = b
@@ -293,8 +296,8 @@
 #define MOJOSHADER_internal_malloc NULL
 #define MOJOSHADER_internal_free NULL
 #else
-void *MOJOSHADER_internal_malloc(int bytes, void *d);
-void MOJOSHADER_internal_free(void *ptr, void *d);
+void * MOJOSHADERCALL MOJOSHADER_internal_malloc(int bytes, void *d);
+void MOJOSHADERCALL MOJOSHADER_internal_free(void *ptr, void *d);
 #endif
 
 #if MOJOSHADER_FORCE_INCLUDE_CALLBACKS
--- a/mojoshader_opengl.c	Tue Oct 13 12:08:55 2015 -0400
+++ b/mojoshader_opengl.c	Fri Jan 01 02:12:19 2016 -0500
@@ -7,9 +7,6 @@
  *  This file written by Ryan C. Gordon.
  */
 
-// !!! FIXME: preshaders shouldn't be handled in here at all. This should
-// !!! FIXME:  be in the Effects API, once that's actually written.
-
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -98,15 +95,13 @@
     size_t ps_uniforms_bool_count;
     GLint *ps_uniforms_bool;
 
-    size_t vs_preshader_reg_count;
-    GLfloat *vs_preshader_regs;
-    size_t ps_preshader_reg_count;
-    GLfloat *ps_preshader_regs;
-
     uint32 refcount;
 
     int uses_pointsize;
 
+    // 10 is apparently the resource limit according to SM3 -flibit
+    GLint vertex_attrib_loc[MOJOSHADER_USAGE_TOTAL][10];
+
     // GLSL uses these...location of uniform arrays.
     GLint vs_float4_loc;
     GLint vs_int4_loc;
@@ -114,6 +109,10 @@
     GLint ps_float4_loc;
     GLint ps_int4_loc;
     GLint ps_bool_loc;
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+    GLint vs_flip_loc;
+    int current_flip;
+#endif
 };
 
 #ifndef WINGDIAPI
@@ -163,6 +162,9 @@
     uint8 want_attr[32];
     uint8 have_attr[32];
 
+    // This shadows vertex attribute and divisor states.
+    GLuint attr_divisor[32];
+
     // rarely used, so we don't touch when we don't have to.
     int pointsize_enabled;
 
@@ -174,10 +176,16 @@
     MOJOSHADER_glProgram *bound_program;
     char profile[16];
 
+#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES
+    // Vertex texture sampler offset...
+    int vertex_sampler_offset;
+#endif
+
     // Extensions...
     int have_core_opengl;
     int have_opengl_2;  // different entry points than ARB extensions.
     int have_opengl_3;  // different extension query.
+    int have_opengl_es; // different extension requirements
     int have_GL_ARB_vertex_program;
     int have_GL_ARB_fragment_program;
     int have_GL_NV_vertex_program2_option;
@@ -191,6 +199,7 @@
     int have_GL_NV_half_float;
     int have_GL_ARB_half_float_vertex;
     int have_GL_OES_vertex_half_float;
+    int have_GL_ARB_instanced_arrays;
 
     // Entry points...
     PFNGLGETSTRINGPROC glGetString;
@@ -217,6 +226,9 @@
     PFNGLSHADERSOURCEPROC glShaderSource;
     PFNGLUNIFORM1IPROC glUniform1i;
     PFNGLUNIFORM1IVPROC glUniform1iv;
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+    PFNGLUNIFORM1FPROC glUniform1f;
+#endif
     PFNGLUNIFORM4FVPROC glUniform4fv;
     PFNGLUNIFORM4IVPROC glUniform4iv;
     PFNGLUSEPROGRAMPROC glUseProgram;
@@ -247,6 +259,7 @@
     PFNGLGENPROGRAMSARBPROC glGenProgramsARB;
     PFNGLBINDPROGRAMARBPROC glBindProgramARB;
     PFNGLPROGRAMSTRINGARBPROC glProgramStringARB;
+    PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisorARB;
 
     // interface for profile-specific things.
     int (*profileMaxUniforms)(MOJOSHADER_shaderType shader_type);
@@ -562,6 +575,9 @@
     program->ps_float4_loc = glsl_uniform_loc(program, "ps_uniforms_vec4");
     program->ps_int4_loc = glsl_uniform_loc(program, "ps_uniforms_ivec4");
     program->ps_bool_loc = glsl_uniform_loc(program, "ps_uniforms_bool");
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+    program->vs_flip_loc = glsl_uniform_loc(program, "vpFlip");
+#endif
 } // impl_GLSL_FinalInitProgram
 
 
@@ -951,6 +967,9 @@
     DO_LOOKUP(opengl_2, PFNGLSHADERSOURCEPROC, glShaderSource);
     DO_LOOKUP(opengl_2, PFNGLUNIFORM1IPROC, glUniform1i);
     DO_LOOKUP(opengl_2, PFNGLUNIFORM1IVPROC, glUniform1iv);
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+    DO_LOOKUP(opengl_2, PFNGLUNIFORM1FPROC, glUniform1f);
+#endif
     DO_LOOKUP(opengl_2, PFNGLUNIFORM4FVPROC, glUniform4fv);
     DO_LOOKUP(opengl_2, PFNGLUNIFORM4IVPROC, glUniform4iv);
     DO_LOOKUP(opengl_2, PFNGLUSEPROGRAMPROC, glUseProgram);
@@ -982,6 +1001,7 @@
     DO_LOOKUP(GL_ARB_vertex_program, PFNGLBINDPROGRAMARBPROC, glBindProgramARB);
     DO_LOOKUP(GL_ARB_vertex_program, PFNGLPROGRAMSTRINGARBPROC, glProgramStringARB);
     DO_LOOKUP(GL_NV_gpu_program4, PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC, glProgramLocalParameterI4ivNV);
+    DO_LOOKUP(GL_ARB_instanced_arrays, PFNGLVERTEXATTRIBDIVISORARBPROC, glVertexAttribDivisorARB);
 
     #undef DO_LOOKUP
 } // lookup_entry_points
@@ -1054,6 +1074,8 @@
         const char *str = (const char *) ctx->glGetString(enumval);
         if (ctx->glGetError() == GL_INVALID_ENUM)
             str = NULL;
+        if (strstr(str, "OpenGL ES GLSL ES "))
+            str += 18;
         parse_opengl_version_str(str, &ctx->glsl_major, &ctx->glsl_minor);
     } // if
 #endif
@@ -1095,6 +1117,7 @@
     ctx->have_GL_NV_half_float = 1;
     ctx->have_GL_ARB_half_float_vertex = 1;
     ctx->have_GL_OES_vertex_half_float = 1;
+    ctx->have_GL_ARB_instanced_arrays = 1;
 
     lookup_entry_points(lookup, d);
 
@@ -1103,6 +1126,11 @@
     else
     {
         const char *str = (const char *) ctx->glGetString(GL_VERSION);
+        if (strstr(str, "OpenGL ES "))
+        {
+            ctx->have_opengl_es = 1;
+            str += 10;
+        }
         parse_opengl_version_str(str, &ctx->opengl_major, &ctx->opengl_minor);
 
         if ((ctx->have_opengl_3) && (opengl_version_atleast(3, 0)))
@@ -1187,6 +1215,7 @@
     VERIFY_EXT(GL_NV_half_float, -1, -1);
     VERIFY_EXT(GL_ARB_half_float_vertex, 3, 0);
     VERIFY_EXT(GL_OES_vertex_half_float, -1, -1);
+    VERIFY_EXT(GL_ARB_instanced_arrays, 3, 3);
 
     #undef VERIFY_EXT
 
@@ -1247,6 +1276,13 @@
     } // else if
     #endif
 
+    #if SUPPORT_PROFILE_GLSLES
+    else if (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0)
+    {
+        MUST_HAVE_GLSL(MOJOSHADER_PROFILE_GLSLES, 1, 10);
+    } // else if
+    #endif
+
     #if SUPPORT_PROFILE_GLSL120
     else if (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0)
     {
@@ -1311,6 +1347,14 @@
 
     load_extensions(lookup, lookup_d);
 
+#if SUPPORT_PROFILE_GLSLES
+    if (ctx->have_opengl_es)
+    {
+        profs[0] = MOJOSHADER_PROFILE_GLSLES;
+        return 1;
+    } // if
+#endif
+
     if (ctx->have_core_opengl)
     {
         size_t i;
@@ -1381,15 +1425,24 @@
     if (!valid_profile(profile))
         goto init_fail;
 
+#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES
+        GLint maxTextures;
+        GLint maxVertexTextures;
+        ctx->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures);
+        maxVertexTextures = ((maxTextures - 16) < 4) ? (maxTextures - 16) : 4;
+        ctx->vertex_sampler_offset = maxTextures - maxVertexTextures;
+#endif
+
     MOJOSHADER_glBindProgram(NULL);
 
     // !!! FIXME: generalize this part.
     if (profile == NULL) {}
 
-    // We don't check SUPPORT_PROFILE_GLSL120 here, since valid_profile() does.
+    // We don't check SUPPORT_PROFILE_GLSL120/ES here, since valid_profile() does.
 #if SUPPORT_PROFILE_GLSL
     else if ( (strcmp(profile, MOJOSHADER_PROFILE_GLSL) == 0) ||
-              (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) )
+              (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) ||
+              (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0) )
     {
         ctx->profileMaxUniforms = impl_GLSL_MaxUniforms;
         ctx->profileCompileShader = impl_GLSL_CompileShader;
@@ -1555,8 +1608,6 @@
             ctx->profileDeleteProgram(program->handle);
             shader_unref(program->vertex);
             shader_unref(program->fragment);
-            Free(program->vs_preshader_regs);
-            Free(program->ps_preshader_regs);
             Free(program->vs_uniforms_float4);
             Free(program->vs_uniforms_int4);
             Free(program->vs_uniforms_bool);
@@ -1685,38 +1736,6 @@
 
     #undef MAKE_ARRAY
 
-    if (pd->preshader)
-    {
-        unsigned int largest = 0;
-        const MOJOSHADER_symbol *sym = pd->preshader->symbols;
-        for (i = 0; i < pd->preshader->symbol_count; i++, sym++)
-        {
-            const unsigned int val = sym->register_index + sym->register_count;
-            if (val > largest)
-                largest = val;
-        } // for
-
-        if (largest > 0)
-        {
-            const size_t len = largest * sizeof (GLfloat) * 4;
-            GLfloat *buf = (GLfloat *) Malloc(len);
-            if (buf == NULL)
-                return 0;
-            memset(buf, '\0', len);
-
-            if (shader_type == MOJOSHADER_TYPE_VERTEX)
-            {
-                program->vs_preshader_reg_count = largest;
-                program->vs_preshader_regs = buf;
-            } // if
-            else if (shader_type == MOJOSHADER_TYPE_PIXEL)
-            {
-                program->ps_preshader_reg_count = largest;
-                program->ps_preshader_regs = buf;
-            } // else if
-        } // if
-    } // if
-
     return 1;
 } // lookup_uniforms
 
@@ -1744,7 +1763,14 @@
     {
         const GLint loc = ctx->profileGetSamplerLocation(program, shader, i);
         if (loc >= 0)  // maybe the Sampler was optimized out?
-            ctx->profilePushSampler(loc, s[i].index);
+        {
+#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES
+            if (pd->shader_type == MOJOSHADER_TYPE_VERTEX)
+                ctx->profilePushSampler(loc, s[i].index + ctx->vertex_sampler_offset);
+            else
+#endif
+                ctx->profilePushSampler(loc, s[i].index);
+        } // if
     } // for
 } // lookup_samplers
 
@@ -1784,6 +1810,7 @@
             AttributeMap *map = &program->attributes[program->attribute_count];
             map->attribute = &a[i];
             map->location = loc;
+            program->vertex_attrib_loc[map->attribute->usage][map->attribute->index] = loc;
             program->attribute_count++;
 
             if (((size_t)loc) > STATICARRAYLEN(ctx->want_attr))
@@ -1844,6 +1871,7 @@
     if (retval == NULL)
         goto link_program_fail;
     memset(retval, '\0', sizeof (MOJOSHADER_glProgram));
+    memset(retval->vertex_attrib_loc, 0xFF, sizeof(retval->vertex_attrib_loc));
 
     numregs = 0;
     if (vshader != NULL) numregs += vshader->parseData->uniform_count;
@@ -2278,6 +2306,15 @@
 } // opengl_attr_type
 
 
+int MOJOSHADER_glGetVertexAttribLocation(MOJOSHADER_usage usage, int index)
+{
+    if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL))
+        return -1;
+
+    return ctx->bound_program->vertex_attrib_loc[usage][index];
+} // MOJOSHADER_glGetVertexAttribLocation
+
+
 // !!! FIXME: shouldn't (index) be unsigned?
 void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage,
                                      int index, unsigned int size,
@@ -2290,27 +2327,10 @@
 
     const GLenum gl_type = opengl_attr_type(type);
     const GLboolean norm = (normalized) ? GL_TRUE : GL_FALSE;
-    const int count = ctx->bound_program->attribute_count;
-    GLint gl_index = 0;
-    int i;
-
-    for (i = 0; i < count; i++)
-    {
-        const AttributeMap *map = &ctx->bound_program->attributes[i];
-        const MOJOSHADER_attribute *a = map->attribute;
-
-        // !!! FIXME: is this array guaranteed to be sorted by usage?
-        // !!! FIXME:  if so, we can break if a->usage > usage.
-
-        if ((a->usage == usage) && (a->index == index))
-        {
-            gl_index = map->location;
-            break;
-        } // if
-    } // for
-
-    if (i == count)
-        return;  // nothing to do, this shader doesn't use this stream.
+    const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index];
+
+    if (gl_index == -1)
+        return; // Nothing to do, this shader doesn't use this stream.
 
     // this happens to work in both ARB1 and GLSL, but if something alien
     //  shows up, we'll have to split these into profile*() functions.
@@ -2323,76 +2343,26 @@
 } // MOJOSHADER_glSetVertexAttribute
 
 
-void MOJOSHADER_glSetVertexPreshaderUniformF(unsigned int idx,
-                                             const float *data,
-                                             unsigned int vec4n)
+// !!! FIXME: shouldn't (index) be unsigned?
+void MOJOSHADER_glSetVertexAttribDivisor(MOJOSHADER_usage usage,
+                                         int index, unsigned int divisor)
 {
-    MOJOSHADER_glProgram *program = ctx->bound_program;
-    if (program == NULL)
-        return;  // nothing to do.
-
-    const uint maxregs = program->vs_preshader_reg_count;
-    if (idx < maxregs)
+    assert(ctx->have_GL_ARB_instanced_arrays);
+
+    if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL))
+        return;
+
+    const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index];
+
+    if (gl_index == -1)
+        return; // Nothing to do, this shader doesn't use this stream.
+
+    if (divisor != ctx->attr_divisor[gl_index])
     {
-        assert(sizeof (GLfloat) == sizeof (float));
-        const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4;
-        memcpy(program->vs_preshader_regs + (idx * 4), data, cpy);
-        program->generation = ctx->generation-1;
-    } // if
-} // MOJOSHADER_glSetVertexPreshaderUniformF
-
-
-void MOJOSHADER_glGetVertexPreshaderUniformF(unsigned int idx, float *data,
-                                             unsigned int vec4n)
-{
-    MOJOSHADER_glProgram *program = ctx->bound_program;
-    if (program == NULL)
-        return;  // nothing to do.
-
-    const uint maxregs = program->vs_preshader_reg_count;
-    if (idx < maxregs)
-    {
-        assert(sizeof (GLfloat) == sizeof (float));
-        const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4;
-        memcpy(data, program->vs_preshader_regs + (idx * 4), cpy);
+        ctx->glVertexAttribDivisorARB(gl_index, divisor);
+        ctx->attr_divisor[gl_index] = divisor;
     } // if
-} // MOJOSHADER_glGetVertexPreshaderUniformF
-
-
-void MOJOSHADER_glSetPixelPreshaderUniformF(unsigned int idx,
-                                             const float *data,
-                                             unsigned int vec4n)
-{
-    MOJOSHADER_glProgram *program = ctx->bound_program;
-    if (program == NULL)
-        return;  // nothing to do.
-
-    const uint maxregs = program->ps_preshader_reg_count;
-    if (idx < maxregs)
-    {
-        assert(sizeof (GLfloat) == sizeof (float));
-        const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4;
-        memcpy(program->ps_preshader_regs + (idx * 4), data, cpy);
-        program->generation = ctx->generation-1;
-    } // if
-} // MOJOSHADER_glSetPixelPreshaderUniformF
-
-
-void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data,
-                                             unsigned int vec4n)
-{
-    MOJOSHADER_glProgram *program = ctx->bound_program;
-    if (program == NULL)
-        return;  // nothing to do.
-
-    const uint maxregs = program->ps_preshader_reg_count;
-    if (idx < maxregs)
-    {
-        assert(sizeof (GLfloat) == sizeof (float));
-        const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4;
-        memcpy(data, program->ps_preshader_regs + (idx * 4), cpy);
-    } // if
-} // MOJOSHADER_glGetPixelPreshaderUniformF
+} // MOJOSHADER_glSetVertexAttribDivisor
 
 
 void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00,
@@ -2445,38 +2415,9 @@
         GLfloat *dstf = program->vs_uniforms_float4;
         GLint *dsti = program->vs_uniforms_int4;
         GLint *dstb = program->vs_uniforms_bool;
-        const MOJOSHADER_preshader *preshader = NULL;
+        uint8 uniforms_changed = 0;
         uint32 i;
 
-        // !!! FIXME: shouldn't this run even if the generation hasn't changed?
-        #if SUPPORT_PRESHADERS
-        int ran_preshader = 0;
-        if (program->vertex)
-        {
-            preshader = program->vertex->parseData->preshader;
-            if (preshader)
-            {
-                MOJOSHADER_runPreshader(preshader, program->vs_preshader_regs,
-                                        ctx->vs_reg_file_f);
-                ran_preshader = 1;
-            } // if
-        } // if
-
-        if (program->fragment)
-        {
-            preshader = program->fragment->parseData->preshader;
-            if (preshader)
-            {
-                MOJOSHADER_runPreshader(preshader, program->ps_preshader_regs,
-                                        ctx->ps_reg_file_f);
-                ran_preshader = 1;
-            } // if
-        } // if
-
-        if (ran_preshader)
-            ctx->generation++;
-        #endif
-
         for (i = 0; i < count; i++)
         {
             UniformMap *map = &program->uniforms[i];
@@ -2516,14 +2457,22 @@
             {
                 const size_t count = 4 * size;
                 const GLfloat *f = &srcf[index * 4];
-                memcpy(dstf, f, sizeof (GLfloat) * count);
+                if (memcmp(dstf, f, sizeof (GLfloat) * count) != 0)
+                {
+                    memcpy(dstf, f, sizeof (GLfloat) * count);
+                    uniforms_changed = 1;
+                }
                 dstf += count;
             } // if
             else if (type == MOJOSHADER_UNIFORM_INT)
             {
                 const size_t count = 4 * size;
                 const GLint *i = &srci[index * 4];
-                memcpy(dsti, i, sizeof (GLint) * count);
+                if (memcmp(dsti, i, sizeof (GLint) * count) != 0)
+                {
+                    memcpy(dsti, i, sizeof (GLint) * count);
+                    uniforms_changed = 1;
+                } // if
                 dsti += count;
             } // else if
             else if (type == MOJOSHADER_UNIFORM_BOOL)
@@ -2532,7 +2481,11 @@
                 const uint8 *b = &srcb[index];
                 size_t i;
                 for (i = 0; i < count; i++)
-                    dstb[i] = (GLint) b[i];
+                    if (dstb[i] != b[i])
+                    {
+                        dstb[i] = (GLint) b[i];
+                        uniforms_changed = 1;
+                    } // if
                 dstb += count;
             } // else if
 
@@ -2572,7 +2525,8 @@
 
         program->generation = ctx->generation;
 
-        ctx->profilePushUniforms();
+        if (uniforms_changed)
+            ctx->profilePushUniforms();
     } // if
 } // MOJOSHADER_glProgramReady
 
@@ -2620,5 +2574,510 @@
     ctx = ((current_ctx == _ctx) ? NULL : current_ctx);
 } // MOJOSHADER_glDestroyContext
 
+
+#ifdef MOJOSHADER_FLIP_RENDERTARGET
+
+
+void MOJOSHADER_glProgramViewportFlip(int flip)
+{
+    assert(ctx->bound_program->vs_flip_loc != -1);
+
+    /* Some compilers require that vpFlip be a float value, rather than int.
+     * However, there's no real reason for it to be a float in the API, so we
+     * do a cast in here. That's not so bad, right...?
+     * -flibit
+     */
+    if (flip != ctx->bound_program->current_flip)
+    {
+        ctx->glUniform1f(ctx->bound_program->vs_flip_loc, (float) flip);
+        ctx->bound_program->current_flip = flip;
+    } // if
+}
+
+
+#endif
+
+
+#ifdef MOJOSHADER_EFFECT_SUPPORT
+
+
+struct MOJOSHADER_glEffect
+{
+    MOJOSHADER_effect *effect;
+    unsigned int num_shaders;
+    MOJOSHADER_glShader *shaders;
+    unsigned int *shader_indices;
+    unsigned int num_preshaders;
+    unsigned int *preshader_indices;
+    MOJOSHADER_glShader *current_vert;
+    MOJOSHADER_glShader *current_frag;
+    MOJOSHADER_effectShader *current_vert_raw;
+    MOJOSHADER_effectShader *current_frag_raw;
+    MOJOSHADER_glProgram *prev_program;
+};
+
+
+MOJOSHADER_glEffect *MOJOSHADER_glCompileEffect(MOJOSHADER_effect *effect)
+{
+    int i;
+    MOJOSHADER_malloc m = effect->malloc;
+    MOJOSHADER_free f = effect->free;
+    void *d = effect->malloc_data;
+    int current_shader = 0;
+    int current_preshader = 0;
+    GLuint shader = 0;
+
+    MOJOSHADER_glEffect *retval = (MOJOSHADER_glEffect *) m(sizeof (MOJOSHADER_glEffect), d);
+    if (retval == NULL)
+    {
+        out_of_memory();
+        return NULL;
+    } // if
+    memset(retval, '\0', sizeof (MOJOSHADER_glEffect));
+
+    // Count the number of shaders before allocating
+    for (i = 0; i < effect->object_count; i++)
+    {
+        MOJOSHADER_effectObject *object = &effect->objects[i];
+        if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+         || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            if (object->shader.is_preshader)
+                retval->num_preshaders++;
+            else
+                retval->num_shaders++;
+        } // if
+    } // for
+
+    // Alloc shader information
+    retval->shaders = (MOJOSHADER_glShader *) m(retval->num_shaders * sizeof (MOJOSHADER_glShader), d);
+    if (retval->shaders == NULL)
+    {
+        f(retval, d);
+        out_of_memory();
+        return NULL;
+    } // if
+    memset(retval->shaders, '\0', retval->num_shaders * sizeof (MOJOSHADER_glShader));
+    retval->shader_indices = (unsigned int *) m(retval->num_shaders * sizeof (unsigned int), d);
+    if (retval->shader_indices == NULL)
+    {
+        f(retval->shaders, d);
+        f(retval, d);
+        out_of_memory();
+        return NULL;
+    } // if
+    memset(retval->shader_indices, '\0', retval->num_shaders * sizeof (unsigned int));
+
+    // Alloc preshader information
+    if (retval->num_preshaders > 0)
+    {
+        retval->preshader_indices = (unsigned int *) m(retval->num_preshaders * sizeof (unsigned int), d);
+        if (retval->preshader_indices == NULL)
+        {
+            f(retval->shaders, d);
+            f(retval->shader_indices, d);
+            f(retval, d);
+            out_of_memory();
+            return NULL;
+        } // if
+        memset(retval->preshader_indices, '\0', retval->num_preshaders * sizeof (unsigned int));
+    } // if
+
+    // Run through the shaders again, compiling and tracking the object indices
+    for (i = 0; i < effect->object_count; i++)
+    {
+        MOJOSHADER_effectObject *object = &effect->objects[i];
+        if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+         || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+        {
+            if (object->shader.is_preshader)
+            {
+                retval->preshader_indices[current_preshader++] = i;
+                continue;
+            } // if
+            if (!ctx->profileCompileShader(object->shader.shader, &shader))
+                goto compile_shader_fail;
+            retval->shaders[current_shader].parseData = object->shader.shader;
+            retval->shaders[current_shader].handle = shader;
+            retval->shaders[current_shader].refcount = 1;
+            retval->shader_indices[current_shader] = i;
+            current_shader++;
+        } // if
+    } // for
+
+    retval->effect = effect;
+    return retval;
+
+compile_shader_fail:
+    for (i = 0; i < retval->num_shaders; i++)
+        if (retval->shaders[i].handle != 0)
+            ctx->profileDeleteShader(retval->shaders[i].handle);
+    f(retval->shader_indices, d);
+    f(retval->shaders, d);
+    f(retval, d);
+    return NULL;
+} // MOJOSHADER_glCompileEffect
+
+
+void MOJOSHADER_glDeleteEffect(MOJOSHADER_glEffect *glEffect)
+{
+    int i;
+    MOJOSHADER_free f = glEffect->effect->free;
+    void *d = glEffect->effect->malloc_data;
+
+    for (i = 0; i < glEffect->num_shaders; i++)
+    {
+        /* Arbitarily add a reference to the refcount.
+         * We're going to be calling glDeleteShader so we can clean out the
+         * program cache, but we can NOT let it free() the array elements!
+         * We'll do that ourselves, as we malloc()'d in CompileEffect.
+         * -flibit
+         */
+        glEffect->shaders[i].refcount++;
+        MOJOSHADER_glDeleteShader(&glEffect->shaders[i]);
+
+        /* Delete the shader, but do NOT delete the parse data!
+         * The parse data belongs to the parent effect.
+         * -flibit
+         */
+        ctx->profileDeleteShader(glEffect->shaders[i].handle);
+    } // for
+
+    f(glEffect->shader_indices, d);
+    f(glEffect->preshader_indices, d);
+    f(glEffect, d);
+} // MOJOSHADER_glDeleteEffect
+
+
+void MOJOSHADER_glEffectBegin(MOJOSHADER_glEffect *glEffect,
+                              unsigned int *numPasses,
+                              int saveShaderState,
+                              MOJOSHADER_effectStateChanges *stateChanges)
+{
+    *numPasses = glEffect->effect->current_technique->pass_count;
+    glEffect->effect->restore_shader_state = saveShaderState;
+    glEffect->effect->state_changes = stateChanges;
+
+    if (glEffect->effect->restore_shader_state)
+        glEffect->prev_program = ctx->bound_program;
+} // MOJOSHADER_glEffectBegin
+
+
+void MOJOSHADER_glEffectBeginPass(MOJOSHADER_glEffect *glEffect,
+                                  unsigned int pass)
+{
+    int i, j;
+    MOJOSHADER_effectPass *curPass;
+    MOJOSHADER_effectState *state;
+    MOJOSHADER_effectShader *rawVert = glEffect->current_vert_raw;
+    MOJOSHADER_effectShader *rawFrag = glEffect->current_frag_raw;
+    int has_preshader = 0;
+
+    if (ctx->bound_program != NULL)
+    {
+        glEffect->current_vert = ctx->bound_program->vertex;
+        glEffect->current_frag = ctx->bound_program->fragment;
+    } // if
+
+    assert(glEffect->effect->current_pass == -1);
+    glEffect->effect->current_pass = pass;
+    curPass = &glEffect->effect->current_technique->passes[pass];
+
+    // !!! FIXME: I bet this could be stored at parse/compile time. -flibit
+    for (i = 0; i < curPass->state_count; i++)
+    {
+        state = &curPass->states[i];
+        #define ASSIGN_SHADER(stype, raw, gls) \
+            (state->type == stype) \
+            { \
+                j = 0; \
+                do \
+                { \
+                    if (*state->value.valuesI == glEffect->shader_indices[j]) \
+                    { \
+                        raw = &glEffect->effect->objects[*state->value.valuesI].shader; \
+                        glEffect->gls = &glEffect->shaders[j]; \
+                        break; \
+                    } \
+                    else if (glEffect->num_preshaders > 0 \
+                          && *state->value.valuesI == glEffect->preshader_indices[j]) \
+                    { \
+                        raw = &glEffect->effect->objects[*state->value.valuesI].shader; \
+                        has_preshader = 1; \
+                        break; \
+                    } \
+                } while (++j < glEffect->num_shaders); \
+            }
+        if ASSIGN_SHADER(MOJOSHADER_RS_VERTEXSHADER, rawVert, current_vert)
+        else if ASSIGN_SHADER(MOJOSHADER_RS_PIXELSHADER, rawFrag, current_frag)
+        #undef ASSIGN_SHADER
+    } // for
+
+    glEffect->effect->state_changes->render_state_changes = curPass->states;
+    glEffect->effect->state_changes->render_state_change_count = curPass->state_count;
+
+    glEffect->current_vert_raw = rawVert;
+    glEffect->current_frag_raw = rawFrag;
+
+    /* If this effect pass has an array of shaders, we get to wait until
+     * CommitChanges to actually bind the final shaders.
+     * -flibit
+     */
+    if (!has_preshader)
+    {
+        MOJOSHADER_glBindShaders(glEffect->current_vert,
+                                 glEffect->current_frag);
+        if (glEffect->current_vert_raw != NULL)
+        {
+            glEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers;
+            glEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count;
+        } // if
+        if (glEffect->current_frag_raw != NULL)
+        {
+            glEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers;
+            glEffect->effect->state_changes->sampler_state_change_count = rawFrag->sampler_count;
+        } // if
+    } // if
+
+    MOJOSHADER_glEffectCommitChanges(glEffect);
+} // MOJOSHADER_glEffectBeginPass
+
+
+static inline void copy_parameter_data(MOJOSHADER_effectParam *params,
+                                       unsigned int *param_loc,
+                                       MOJOSHADER_symbol *symbols,
+                                       unsigned int symbol_count,
+                                       GLfloat *regf, GLint *regi, uint8 *regb)
+{
+    int i, j, r, c;
+
+    i = 0;
+    for (i = 0; i < symbol_count; i++)
+    {
+        const MOJOSHADER_symbol *sym = &symbols[i];
+        const MOJOSHADER_effectValue *param = &params[param_loc[i]].value;
+
+        // float/int registers are vec4, so they have 4 elements each
+        const uint32 start = sym->register_index << 2;
+
+        if (param->value_type == MOJOSHADER_SYMTYPE_FLOAT)
+        {
+            // Matrices have to be transposed from row-major to column-major!
+            if (param->value_class == MOJOSHADER_SYMCLASS_MATRIX_ROWS)
+            {
+                if (param->element_count > 1)
+                {
+                    const uint32 regcount = sym->register_count / param->element_count;
+                    j = 0;
+                    do
+                    {
+                        r = 0;
+                        do
+                        {
+                            c = 0;
+                            do
+                            {
+                                const uint32 dest = start + c +
+                                                   (r << 2) +
+                                                  ((j << 2) * regcount);
+                                const uint32 src = r +
+                                                  (c * param->row_count) +
+                                                  (j * param->row_count * param->column_count);
+                                regf[dest] = param->valuesF[src];
+                            } while (++c < param->column_count);
+                        } while (++r < regcount);
+                    } while (++j < param->element_count);
+                } // if
+                else
+                {
+                    r = 0;
+                    do
+                    {
+                        c = 0;
+                        do
+                        {
+                            regf[start + (r << 2 ) + c] = param->valuesF[r + (c * param->row_count)];
+                        } while (++c < param->column_count);
+                    } while (++r < sym->register_count);
+                } // else
+            } // if
+            else if (sym->register_count > 1)
+            {
+                j = 0;
+                do
+                {
+                    memcpy(regf + start + (j << 2),
+                           param->valuesF + (j * param->column_count),
+                           param->column_count << 2);
+                } while (++j < sym->register_count);
+            } // else if
+            else
+                memcpy(regf + start, param->valuesF, param->column_count << 2);
+        } // if
+        else if (sym->register_set == MOJOSHADER_SYMREGSET_FLOAT4)
+        {
+            // Sometimes int/bool parameters get thrown into float registers...
+            j = 0;
+            do
+            {
+                c = 0;
+                do
+                {
+                    regf[start + (j << 2) + c] = (float) param->valuesI[(j * param->column_count) + c];
+                } while (++c < param->column_count);
+            } while (++j < sym->register_count);
+        } // else if
+        else if (sym->register_set == MOJOSHADER_SYMREGSET_INT4)
+        {
+            if (sym->register_count > 1)
+            {
+                j = 0;
+                do
+                {
+                    memcpy(regi + start + (j << 2),
+                           param->valuesI + (j * param->column_count),
+                           param->column_count << 2);
+                } while (++j < sym->register_count);
+            } // if
+            else
+                memcpy(regi + start, param->valuesI, param->column_count << 2);
+        } // else if
+        else if (sym->register_set == MOJOSHADER_SYMREGSET_BOOL)
+        {
+            j = 0;
+            do
+            {
+                // regb is not a vec4, enjoy this bitshift! -flibit
+                regb[(start >> 2) + j] = param->valuesI[j];
+            } while (++j < sym->register_count);
+        } // else if
+    } // for
+} // copy_parameter_data
+
+
+void MOJOSHADER_glEffectCommitChanges(MOJOSHADER_glEffect *glEffect)
+{
+    MOJOSHADER_effectShader *rawVert = glEffect->current_vert_raw;
+    MOJOSHADER_effectShader *rawFrag = glEffect->current_frag_raw;
+
+    /* Used for shader selection from preshaders */
+    int i;
+    MOJOSHADER_effectValue *param;
+    float selector;
+    int shader_object;
+    int selector_ran = 0;
+
+    /* For effect passes with arrays of shaders, we have to run a preshader
+     * that determines which shader to use, based on a parameter's value.
+     * -flibit
+     */
+    // !!! FIXME: We're just running the preshaders every time. Blech. -flibit
+    #define SELECT_SHADER_FROM_PRESHADER(raw, gls) \
+        if (raw != NULL && raw->is_preshader) \
+        { \
+            i = 0; \
+            do \
+            { \
+                param = &glEffect->effect->params[raw->preshader_params[i]].value; \
+                memcpy(raw->preshader->registers + raw->preshader->symbols[i].register_index, \
+                       param->values, \
+                       param->value_count << 2); \
+            } while (++i < raw->preshader->symbol_count); \
+            MOJOSHADER_runPreshader(raw->preshader, &selector); \
+            shader_object = glEffect->effect->params[raw->params[0]].value.valuesI[(int) selector]; \
+            raw = &glEffect->effect->objects[shader_object].shader; \
+            i = 0; \
+            do \
+            { \
+                if (shader_object == glEffect->shader_indices[i]) \
+                { \
+                    gls = &glEffect->shaders[i]; \
+                    break; \
+                } \
+            } while (++i < glEffect->num_shaders); \
+            selector_ran = 1; \
+        }
+    SELECT_SHADER_FROM_PRESHADER(rawVert, glEffect->current_vert)
+    SELECT_SHADER_FROM_PRESHADER(rawFrag, glEffect->current_frag)
+    #undef SELECT_SHADER_FROM_PRESHADER
+    if (selector_ran)
+    {
+        MOJOSHADER_glBindShaders(glEffect->current_vert,
+                                 glEffect->current_frag);
+        if (glEffect->current_vert_raw != NULL)
+        {
+            glEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers;
+            glEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count;
+        } // if
+        if (glEffect->current_frag_raw != NULL)
+        {
+            glEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers;
+            glEffect->effect->state_changes->sampler_state_change_count = rawFrag->sampler_count;
+        } // if
+    } // if
+
+    /* This is where parameters are copied into the constant buffers.
+     * If you're looking for where things slow down immensely, look at
+     * the copy_parameter_data() and MOJOSHADER_runPreshader() functions.
+     * -flibit
+     */
+    // !!! FIXME: We're just copying everything every time. Blech. -flibit
+    // !!! FIXME: We're just running the preshaders every time. Blech. -flibit
+    // !!! FIXME: Will the preshader ever want int/bool registers? -flibit
+    #define COPY_PARAMETER_DATA(raw, stage) \
+        if (raw != NULL) \
+        { \
+            if (ctx->bound_program->stage##_float4_loc != -1) \
+                memset(ctx->stage##_reg_file_f, '\0', ctx->bound_program->stage##_uniforms_float4_count << 4); \
+            if (ctx->bound_program->stage##_int4_loc != -1) \
+                memset(ctx->stage##_reg_file_i, '\0', ctx->bound_program->stage##_uniforms_int4_count << 4); \
+            if (ctx->bound_program->stage##_bool_loc != -1) \
+                memset(ctx->stage##_reg_file_b, '\0', ctx->bound_program->stage##_uniforms_bool_count << 2); \
+            copy_parameter_data(glEffect->effect->params, raw->params, \
+                                raw->shader->symbols, \
+                                raw->shader->symbol_count, \
+                                ctx->stage##_reg_file_f, \
+                                ctx->stage##_reg_file_i, \
+                                ctx->stage##_reg_file_b); \
+            if (raw->shader->preshader) \
+            { \
+                copy_parameter_data(glEffect->effect->params, raw->preshader_params, \
+                                    raw->shader->preshader->symbols, \
+                                    raw->shader->preshader->symbol_count, \
+                                    raw->shader->preshader->registers, \
+                                    NULL, \
+                                    NULL); \
+                MOJOSHADER_runPreshader(raw->shader->preshader, ctx->stage##_reg_file_f); \
+            } \
+        }
+    COPY_PARAMETER_DATA(rawVert, vs)
+    COPY_PARAMETER_DATA(rawFrag, ps)
+    #undef COPY_PARAMETER_DATA
+
+    ctx->generation++;
+} // MOJOSHADER_glEffectCommitChanges
+
+
+void MOJOSHADER_glEffectEndPass(MOJOSHADER_glEffect *glEffect)
+{
+    assert(glEffect->effect->current_pass != -1);
+    glEffect->effect->current_pass = -1;
+} // MOJOSHADER_glEffectEndPass
+
+
+void MOJOSHADER_glEffectEnd(MOJOSHADER_glEffect *glEffect)
+{
+    if (glEffect->effect->restore_shader_state)
+    {
+        glEffect->effect->restore_shader_state = 0;
+        MOJOSHADER_glBindProgram(glEffect->prev_program);
+    } // if
+
+    glEffect->effect->state_changes = NULL;
+} // MOJOSHADER_glEffectEnd
+
+
+#endif // MOJOSHADER_EFFECT_SUPPORT
+
 // end of mojoshader_opengl.c ...
 
--- a/utils/testparse.c	Tue Oct 13 12:08:55 2015 -0400
+++ b/utils/testparse.c	Fri Jan 01 02:12:19 2016 -0500
@@ -177,6 +177,15 @@
             if (operand->type == MOJOSHADER_PRESHADEROPERAND_TEMP)
                 regch = 'r';
 
+            if (operand->array_register_count > 0)
+            {
+                for (i = operand->array_register_count - 1; i >= 0; i--)
+                    printf("c%d[", operand->array_registers[i]);
+                printf("%c%d.%c", regch, operand->index / 4, mask[idx]);
+                for (i = 0; i < operand->array_register_count; i++)
+                    printf("]");
+                break;
+            } // if
             printf("%c%d", regch, operand->index / 4);
             if (isscalar)
                 printf(".%c", mask[idx]);
@@ -403,10 +412,149 @@
 } // print_shader
 
 
+#ifdef MOJOSHADER_EFFECT_SUPPORT
+
+
+static void print_value(const MOJOSHADER_effectValue *value,
+                        const unsigned int indent)
+{
+    int i;
+
+    INDENT();
+    printf("VALUE: %s -> %s\n", value->name, value->semantic);
+
+    static const char *classes[] =
+    {
+        "SCALAR",
+        "VECTOR",
+        "ROW-MAJOR MATRIX",
+        "COLUMN-MAJOR MATRIX",
+        "OBJECT",
+        "STRUCT"
+    };
+    static const char *types[] =
+    {
+        "VOID",
+        "BOOL",
+        "INT",
+        "FLOAT",
+        "STRING",
+        "TEXTURE",
+        "TEXTURE1D",
+        "TEXTURE2D",
+        "TEXTURE3D",
+        "TEXTURECUBE",
+        "SAMPLER",
+        "SAMPLER1D",
+        "SAMPLER2D",
+        "SAMPLER3D",
+        "SAMPLERCUBE",
+        "PIXELSHADER",
+        "VERTEXSHADER",
+        "UNSUPPORTED"
+    };
+    do_indent(indent + 1);
+    printf("CLASS: %s\n", classes[value->value_class]);
+    do_indent(indent + 1);
+    printf("TYPE: %s\n", types[value->value_type]);
+
+    do_indent(indent + 1);
+    printf("ROWS/COLUMNS/ELEMENTS: %d, %d, %d\n",
+           value->row_count, value->column_count, value->element_count);
+    do_indent(indent + 1);
+    printf("TOTAL VALUES: %d\n", value->value_count);
+
+    if (value->value_type == MOJOSHADER_SYMTYPE_SAMPLER
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D
+     || value->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+    {
+        do_indent(indent + 1);
+        printf("SAMPLER VALUES:\n");
+        for (i = 0; i < value->value_count; i++)
+        {
+            MOJOSHADER_effectSamplerState *state = &value->valuesSS[i];
+
+            static const char *samplerstatetypes[] =
+            {
+                "UNKNOWN0",
+                "UNKNOWN1",
+                "UNKNOWN2",
+                "UNKNOWN3",
+                "TEXTURE",
+                "ADDRESSU",
+                "ADDRESSV",
+                "ADDRESSW",
+                "BORDERCOLOR",
+                "MAGFILTER",
+                "MINFILTER",
+                "MIPFILTER",
+                "MIPMAPLODBIAS",
+                "MAXMIPLEVEL",
+                "MAXANISOTROPY",
+                "SRGBTEXTURE",
+                "ELEMENTINDEX",
+                "DMAPOFFSET",
+            };
+            do_indent(indent + 2);
+            printf("TYPE: %s -> ", samplerstatetypes[state->type]);
+
+            /* Assuming only one value per state! */
+            if (state->type == MOJOSHADER_SAMP_MIPMAPLODBIAS)
+            {
+                /* float types */
+                printf("%.2f\n", *state->value.valuesF);
+            } // if
+            else
+            {
+                /* int/enum types */
+                printf("%d\n", *state->value.valuesI);
+            } // else
+        } // for
+    } // if
+    else
+    {
+        do_indent(indent + 1);
+        printf("%s VALUES:\n", types[value->value_type]);
+        for (i = 0; i < value->value_count; i++)
+        {
+            do_indent(indent + 2);
+            static const char *prints[] =
+            {
+                "%X\n",
+                "%d\n",
+                "%d\n",
+                "%.2f\n",
+                "%d\n",
+                "%d\n",
+                "%d\n",
+                "%d\n",
+                "%d\n",
+                "%d\n",
+                "SAMPLER?!\n",
+                "SAMPLER?!\n",
+                "SAMPLER?!\n",
+                "SAMPLER?!\n",
+                "SAMPLER?!\n",
+                "%d\n",
+                "%d\n",
+                "%X\n"
+            };
+            if (value->value_type == MOJOSHADER_SYMTYPE_FLOAT)
+                printf(prints[value->value_type], value->valuesF[i]);
+            else
+                printf(prints[value->value_type], value->valuesI[i]);
+        } // for
+    } // else
+} // print_value
+
+
 static void print_effect(const char *fname, const MOJOSHADER_effect *effect,
                          const unsigned int indent)
 {
-    INDENT(); printf("PROFILE: %s\n", effect->profile);
+    INDENT();
+    printf("PROFILE: %s\n", effect->profile);
     printf("\n");
     if (effect->error_count > 0)
     {
@@ -424,63 +572,107 @@
     {
         int i, j, k;
         const MOJOSHADER_effectTechnique *technique = effect->techniques;
-        const MOJOSHADER_effectTexture *texture = effect->textures;
-        const MOJOSHADER_effectShader *shader = effect->shaders;
+        const MOJOSHADER_effectObject *object = effect->objects;
         const MOJOSHADER_effectParam *param = effect->params;
 
         for (i = 0; i < effect->param_count; i++, param++)
         {
             INDENT();
-            printf("PARAM #%d '%s' -> '%s'\n", i, param->name, param->semantic);
+            printf("PARAM #%d\n", i);
+            print_value(&param->value, indent + 1);
+
+            if (param->annotation_count > 0)
+            {
+                do_indent(indent + 1);
+                printf("ANNOTATIONS:\n");
+            } // if
+            for (j = 0; j < param->annotation_count; j++)
+            {
+                print_value(&param->annotations[j], indent + 2);
+            } // for
         } // for
-
         printf("\n");
 
         for (i = 0; i < effect->technique_count; i++, technique++)
         {
             const MOJOSHADER_effectPass *pass = technique->passes;
-            INDENT(); printf("TECHNIQUE #%d ('%s'):\n", i, technique->name);
+            INDENT();
+            printf("TECHNIQUE #%d ('%s'):\n", i, technique->name);
             for (j = 0; j < technique->pass_count; j++, pass++)
             {
                 const MOJOSHADER_effectState *state = pass->states;
-                INDENT(); printf("    PASS #%d ('%s'):\n", j, pass->name);
+                do_indent(indent + 1);
+                printf("PASS #%d ('%s'):\n", j, pass->name);
                 for (k = 0; k < pass->state_count; k++, state++)
                 {
-                    INDENT(); printf("        STATE 0x%X\n", state->type);
+                    do_indent(indent + 2);
+                    printf("STATE %d:\n", state->type);
+                    print_value(&state->value, indent + 3);
                 } // for
             } // for
-            printf("\n");
         } // for
+        printf("\n");
 
-        for (i = 0; i < effect->texture_count; i++, texture++)
+        /* Start at index 1, 0 is always empty (thanks Microsoft!) */
+        object++;
+        for (i = 1; i < effect->object_count; i++, object++)
         {
             INDENT();
-            printf("TEXTURE #%d ('%s'): %u\n", i,
-                    texture->name, texture->param);
-        } // for
-
-        printf("\n");
-
-        for (i = 0; i < effect->shader_count; i++, shader++)
-        {
-            INDENT();
-            printf("SHADER #%d: technique %u, pass %u\n", i,
-                    shader->technique, shader->pass);
-            print_shader(fname, shader->shader, indent + 1);
+            if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
+             || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+            {
+                if (object->shader.is_preshader)
+                {
+                    printf("OBJECT #%d: PRESHADER, technique %u, pass %u, param %s\n", i,
+                           object->shader.technique, object->shader.pass,
+                           effect->params[object->shader.params[0]].value.name);
+                    print_preshader(object->shader.preshader, indent + 1);
+                } // if
+                else
+                {
+                    printf("OBJECT #%d: SHADER, technique %u, pass %u\n", i,
+                           object->shader.technique, object->shader.pass);
+                    print_shader(fname, object->shader.shader, indent + 1);
+                } // else
+            } // if
+            else if (object->type == MOJOSHADER_SYMTYPE_STRING)
+                printf("OBJECT #%d: STRING, '%s'\n", i,
+                       object->string.string);
+            else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+                printf("OBJECT #%d: MAPPING, '%s'\n", i,
+                       object->mapping.name);
+            else if (object->type == MOJOSHADER_SYMTYPE_TEXTURE
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE)
+                printf("OBJECT #%d: TEXTURE\n", i);
+            else
+                printf("UNKNOWN OBJECT: #%d\n", i);
         } // for
     } // else
 } // print_effect
 
 
+#endif // MOJOSHADER_EFFECT_SUPPORT
+
+
 static int do_parse(const char *fname, const unsigned char *buf,
                     const int len, const char *prof)
 {
     int retval = 0;
 
     // magic for an effects file (!!! FIXME: I _think_).
-    if ( (buf[0] == 0x01) && (buf[1] == 0x09) &&
-         (buf[2] == 0xFF) && (buf[3] == 0xFE) )
+    if ( ((buf[0] == 0x01) && (buf[1] == 0x09) &&
+          (buf[2] == 0xFF) && (buf[3] == 0xFE)) ||
+         ((buf[0] == 0xCF) && (buf[1] == 0x0B) &&
+          (buf[2] == 0xF0) && (buf[3] == 0xBC)) )
     {
+#ifdef MOJOSHADER_EFFECT_SUPPORT
         const MOJOSHADER_effect *effect;
         effect = MOJOSHADER_parseEffect(prof, buf, len, NULL, 0,
                                         NULL, 0, Malloc, Free, 0);
@@ -488,6 +680,9 @@
         printf("EFFECT: %s\n", fname);
         print_effect(fname, effect, 1);
         MOJOSHADER_freeEffect(effect);
+#else
+        printf("Is an effect, but effect support is disabled!\n");
+#endif
     } // if
 
     else  // do it as a regular compiled shader.