From edb4686a489cd402df745d5ac60f730fd5325a6f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 31 Jul 2010 04:03:39 +0000 Subject: [PATCH] - Enable backpatching in the internal nodebuilder. SVN r2477 (polyobjects) --- src/nodebuild.cpp | 122 +++++++++++++++++++++++++----- src/nodebuild.h | 60 ++++++++++----- src/nodebuild_classify_nosse2.cpp | 39 +++++----- src/nodebuild_classify_sse2.cpp | 50 ++++++------ src/p_setup.cpp | 2 +- src/r_bsp.cpp | 3 +- zdoom.vcproj | 4 +- 7 files changed, 194 insertions(+), 86 deletions(-) diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index aa663e2e1e..e29a64ff72 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -63,7 +63,7 @@ const int AAPreference = 16; #endif FNodeBuilder::FNodeBuilder(FLevel &level) -: Level(level), GLNodes(false), EnableSSE2(true), SegsStuffed(0) +: Level(level), GLNodes(false), SegsStuffed(0) { VertexMap = NULL; @@ -71,8 +71,8 @@ FNodeBuilder::FNodeBuilder(FLevel &level) FNodeBuilder::FNodeBuilder (FLevel &level, TArray &polyspots, TArray &anchors, - bool makeGLNodes, bool enableSSE2) - : Level(level), GLNodes(makeGLNodes), EnableSSE2(enableSSE2), SegsStuffed(0) + bool makeGLNodes) + : Level(level), GLNodes(makeGLNodes), SegsStuffed(0) { VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY); FindUsedVertices (Level.Vertices, Level.NumVertices); @@ -90,10 +90,9 @@ FNodeBuilder::~FNodeBuilder() } } -void FNodeBuilder::BuildMini(bool makeGLNodes, bool enableSSE2) +void FNodeBuilder::BuildMini(bool makeGLNodes) { GLNodes = makeGLNodes; - EnableSSE2 = enableSSE2; GroupSegPlanesSimple(); BuildTree(); } @@ -540,7 +539,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) int realSegs[2] = { 0, 0 }; int specialSegs[2] = { 0, 0 }; DWORD i = set; - int sidev1, sidev2; + int sidev[2]; int side; bool splitter = false; unsigned int max, m2, p, q; @@ -559,7 +558,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) } else { - side = ClassifyLine (node, test, sidev1, sidev2); + side = ClassifyLine (node, &Vertices[test->v1], &Vertices[test->v2], sidev); } switch (side) { @@ -569,9 +568,9 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) // The "right" thing to do in this case is to only reject it if there is // another nosplit seg from the same sector at this vertex. Note that a line // that lies exactly on top of the splitter is okay. - if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0)) + if (test->loopnum && honorNoSplit && (sidev[0] == 0 || sidev[1] == 0)) { - if ((sidev1 | sidev2) != 0) + if ((sidev[0] | sidev[1]) != 0) { max = Touched.Size(); for (p = 0; p < max; ++p) @@ -783,18 +782,18 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou FPrivSeg *seg = &Segs[set]; int next = seg->next; - int sidev1, sidev2, side; + int sidev[2], side; if (HackSeg == set) { HackSeg = DWORD_MAX; side = 1; - sidev1 = sidev2 = 0; + sidev[0] = sidev[1] = 0; hack = true; } else { - side = ClassifyLine (node, seg, sidev1, sidev2); + side = ClassifyLine (node, &Vertices[seg->v1], &Vertices[seg->v2], sidev); hack = false; } @@ -837,7 +836,7 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou Printf("SelectVertexClose selected endpoint of seg %u\n", set); } - seg2 = SplitSeg (set, vertnum, sidev1); + seg2 = SplitSeg (set, vertnum, sidev[0]); Segs[seg2].next = outset0; outset0 = seg2; @@ -848,7 +847,7 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou if (Segs[set].partner != DWORD_MAX) { int partner1 = Segs[set].partner; - int partner2 = SplitSeg (partner1, vertnum, sidev2); + int partner2 = SplitSeg (partner1, vertnum, sidev[1]); // The newly created seg stays in the same set as the // back seg because it has not been considered for splitting // yet. If it had been, then the front seg would have already @@ -869,17 +868,17 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou } if (side >= 0 && GLNodes) { - if (sidev1 == 0) + if (sidev[0] == 0) { double dist1 = AddIntersection (node, seg->v1); - if (sidev2 == 0) + if (sidev[1] == 0) { double dist2 = AddIntersection (node, seg->v2); FSplitSharer share = { dist1, set, dist2 > dist1 }; SplitSharers.Push (share); } } - else if (sidev2 == 0) + else if (sidev[1] == 0) { AddIntersection (node, seg->v2); } @@ -1072,3 +1071,92 @@ void FNodeBuilder::PrintSet (int l, DWORD set) } Printf (PRINT_LOG, "*\n"); } + + + +#ifdef BACKPATCH +#ifdef _WIN32 +extern "C" { +__declspec(dllimport) int __stdcall VirtualProtect(void *, unsigned long, unsigned long, unsigned long *); +} +#define PAGE_EXECUTE_READWRITE 64 +#else +#include +#include +#endif + +#ifdef __GNUC__ +extern "C" int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +#else +static int *CallerOffset; +int ClassifyLineBackpatchC (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +#endif +{ + // Select the routine based on SSE2 availability and patch the caller so that + // they call that routine directly next time instead of going through here. + int *calleroffset; + int diff; + int (*func)(node_t &, const FSimpleVert *, const FSimpleVert *, int[2]); + +#ifdef __GNUC__ + calleroffset = (int *)__builtin_return_address(0); +#else + calleroffset = CallerOffset; +#endif +// printf ("Patching for SSE %d @ %p %d\n", SSELevel, calleroffset, *calleroffset); + + if (CPU.bSSE2) + { + func = ClassifyLineSSE2; + diff = (char *)ClassifyLineSSE2 - (char *)calleroffset; + } + else + { + func = ClassifyLine2; + diff = (char *)ClassifyLine2 - (char *)calleroffset; + } + + calleroffset--; + // Patch the caller. +#ifdef _WIN32 + unsigned long oldprotect; + if (VirtualProtect (calleroffset, 4, PAGE_EXECUTE_READWRITE, &oldprotect)) +#else + // must make this page-aligned for mprotect + long pagesize = sysconf(_SC_PAGESIZE); + char *callerpage = (char *)((intptr_t)calleroffset & ~(pagesize - 1)); + size_t protectlen = (intptr_t)calleroffset + sizeof(void*) - (intptr_t)callerpage; + int ptect; + if (!(ptect = mprotect(callerpage, protectlen, PROT_READ|PROT_WRITE|PROT_EXEC))) +#endif + { + *calleroffset = diff; +#ifdef _WIN32 + VirtualProtect (calleroffset, sizeof(void*), oldprotect, &oldprotect); +#else + mprotect(callerpage, protectlen, PROT_READ|PROT_EXEC); +#endif + } + + // And return by calling the real function. + return func (node, v1, v2, sidev); +} + +#ifndef __GNUC__ +// The ClassifyLineBackpatch() function here is a stub that uses inline assembly and nakedness +// to retrieve the return address of the stack before sending control to the real +// ClassifyLineBackpatchC() function. Since BACKPATCH shouldn't be defined on 64-bit builds, +// we're okay that VC++ can't do inline assembly on that target. + +extern "C" __declspec(noinline) __declspec(naked) int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +{ + // We store the return address in a global, so as not to need to mess with the parameter list. + __asm + { + mov eax, [esp] + mov CallerOffset, eax + jmp ClassifyLineBackpatchC + } +} +#endif +#endif diff --git a/src/nodebuild.h b/src/nodebuild.h index fa72e6d526..423aa09962 100644 --- a/src/nodebuild.h +++ b/src/nodebuild.h @@ -1,6 +1,7 @@ #include "doomdata.h" #include "tarray.h" #include "r_defs.h" +#include "x86.h" struct FPolySeg; struct FMiniBSP; @@ -43,6 +44,27 @@ private: FEvent *Predecessor (FEvent *event) const; }; +struct FSimpleVert +{ + fixed_t x, y; +}; + +extern "C" +{ + int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#ifndef DISABLE_SSE + int ClassifyLineSSE1 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); + int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#ifdef BACKPATCH +#ifdef __GNUC__ + int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) __attribute__((noinline)); +#else + int __declspec(noinline) ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#endif +#endif +#endif +} + class FNodeBuilder { struct FPrivSeg @@ -63,9 +85,8 @@ class FNodeBuilder bool planefront; FPrivSeg *hashnext; }; - struct FPrivVert + struct FPrivVert : FSimpleVert { - fixed_t x, y; DWORD segs; // segs that use this vertex as v1 DWORD segs2; // segs that use this vertex as v2 @@ -175,7 +196,7 @@ public: FNodeBuilder (FLevel &level); FNodeBuilder (FLevel &level, TArray &polyspots, TArray &anchors, - bool makeGLNodes, bool enableSSE2); + bool makeGLNodes); ~FNodeBuilder (); void Extract (node_t *&nodes, int &nodeCount, @@ -187,7 +208,7 @@ public: void Clear(); void AddPolySegs(FPolySeg *segs, int numsegs); void AddSegs(seg_t *segs, int numsegs); - void BuildMini(bool makeGLNodes, bool enableSSE2); + void BuildMini(bool makeGLNodes); void ExtractMini(FMiniBSP *bsp); static angle_t PointToAngle (fixed_t dx, fixed_t dy); @@ -220,7 +241,6 @@ private: DWORD HackMate; // Seg to use in front of hack seg FLevel &Level; bool GLNodes; // Add minisegs to make GL nodes? - bool EnableSSE2; // Progress meter stuff int SegsStuffed; @@ -251,9 +271,7 @@ private: // 1 = seg is in back // -1 = seg cuts the node - inline int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); - int ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); - int ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); + inline int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]); void FixSplitSharers (const node_t &node); double AddIntersection (const node_t &node, int vertex); @@ -313,21 +331,27 @@ inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int return s_num > 0.0 ? -1 : 1; } -inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]) { -#if !defined(_M_IX86) && !defined(_M_X64) && !defined(__i386__) && !defined(__amd64__) - return ClassifyLine2 (node, seg, sidev1, sidev2); -#elif defined(__SSE2__) +#ifdef DISABLE_SSE + return ClassifyLine2 (node, v1, v2, sidev); +#else +#if defined(__SSE2__) || defined(_M_IX64) // If compiling with SSE2 support everywhere, just use the SSE2 version. - return ClassifyLineSSE2 (node, seg, sidev1, sidev2); + return ClassifyLineSSE2 (node, v1, v2, sidev); #elif defined(_MSC_VER) && _MSC_VER < 1300 - // VC 6 does not support SSE2 optimizations. - return ClassifyLine2 (node, seg, sidev1, sidev2); + // VC 6 does not support SSE optimizations. + return ClassifyLine2 (node, v1, v2, sidev); #else // Select the routine based on our flag. - if (EnableSSE2) - return ClassifyLineSSE2 (node, seg, sidev1, sidev2); +#ifdef BACKPATCH + return ClassifyLineBackpatch (node, v1, v2, sidev); +#else + if (CPU.bSSE2) + return ClassifyLineSSE2 (node, v1, v2, sidev); else - return ClassifyLine2 (node, seg, sidev1, sidev2); + return ClassifyLine2 (node, v1, v2, sidev); +#endif +#endif #endif } diff --git a/src/nodebuild_classify_nosse2.cpp b/src/nodebuild_classify_nosse2.cpp index 2b7f6bec6b..3fd1da2726 100644 --- a/src/nodebuild_classify_nosse2.cpp +++ b/src/nodebuild_classify_nosse2.cpp @@ -3,11 +3,8 @@ #define FAR_ENOUGH 17179869184.f // 4<<32 -int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +extern "C" int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) { - const FPrivVert *v1 = &Vertices[seg->v1]; - const FPrivVert *v2 = &Vertices[seg->v2]; - double d_x1 = double(node.x); double d_y1 = double(node.y); double d_dx = double(node.dx); @@ -26,13 +23,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, { if (s_num2 <= -FAR_ENOUGH) { - sidev1 = sidev2 = 1; + sidev[0] = sidev[1] = 1; return 1; } if (s_num2 >= FAR_ENOUGH) { - sidev1 = 1; - sidev2 = -1; + sidev[0] = 1; + sidev[1] = -1; return -1; } nears = 1; @@ -41,13 +38,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, { if (s_num2 >= FAR_ENOUGH) { - sidev1 = sidev2 = -1; + sidev[0] = sidev[1] = -1; return 0; } if (s_num2 <= -FAR_ENOUGH) { - sidev1 = -1; - sidev2 = 1; + sidev[0] = -1; + sidev[1] = 1; return -1; } nears = 1; @@ -65,41 +62,41 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, double dist = s_num1 * s_num1 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev1 = 0; + sidev[0] = 0; } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } if (nears & 1) { double dist = s_num2 * s_num2 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev2 = 0; + sidev[1] = 0; } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } - if ((sidev1 | sidev2) == 0) + if ((sidev[0] | sidev[1]) == 0) { // seg is coplanar with the splitter, so use its orientation to determine // which child it ends up in. If it faces the same direction as the splitter, // it goes in front. Otherwise, it goes in back. @@ -127,11 +124,11 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, } } } - else if (sidev1 <= 0 && sidev2 <= 0) + else if (sidev[0] <= 0 && sidev[1] <= 0) { return 0; } - else if (sidev1 >= 0 && sidev2 >= 0) + else if (sidev[0] >= 0 && sidev[1] >= 0) { return 1; } diff --git a/src/nodebuild_classify_sse2.cpp b/src/nodebuild_classify_sse2.cpp index 05e4684a82..01c469093c 100644 --- a/src/nodebuild_classify_sse2.cpp +++ b/src/nodebuild_classify_sse2.cpp @@ -1,18 +1,16 @@ +#ifndef DISABLE_SSE + #include "doomtype.h" #include "nodebuild.h" #define FAR_ENOUGH 17179869184.f // 4<<32 -// This function is identical to the ClassifyLine2 version. So how does it use SSE2? -// Easy! By explicitly enabling SSE2 in the configuration properties for this one -// file, we can build it with SSE2 enabled without forcing SSE2 on the rest of the -// project. +// You may notice that this function is identical to ClassifyLine2. +// The reason it is SSE2 is because this file is explicitly compiled +// with SSE2 math enabled, but the other files are not. -int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +extern "C" int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) { - const FPrivVert *v1 = &Vertices[seg->v1]; - const FPrivVert *v2 = &Vertices[seg->v2]; - double d_x1 = double(node.x); double d_y1 = double(node.y); double d_dx = double(node.dx); @@ -31,13 +29,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side { if (s_num2 <= -FAR_ENOUGH) { - sidev1 = sidev2 = 1; + sidev[0] = sidev[1] = 1; return 1; } if (s_num2 >= FAR_ENOUGH) { - sidev1 = 1; - sidev2 = -1; + sidev[0] = 1; + sidev[1] = -1; return -1; } nears = 1; @@ -46,13 +44,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side { if (s_num2 >= FAR_ENOUGH) { - sidev1 = sidev2 = -1; + sidev[0] = sidev[1] = -1; return 0; } if (s_num2 <= -FAR_ENOUGH) { - sidev1 = -1; - sidev2 = 1; + sidev[0] = -1; + sidev[1] = 1; return -1; } nears = 1; @@ -70,41 +68,41 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side double dist = s_num1 * s_num1 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev1 = 0; + sidev[0] = 0; } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } if (nears & 1) { double dist = s_num2 * s_num2 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev2 = 0; + sidev[1] = 0; } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } - if ((sidev1 | sidev2) == 0) + if ((sidev[0] | sidev[1]) == 0) { // seg is coplanar with the splitter, so use its orientation to determine // which child it ends up in. If it faces the same direction as the splitter, // it goes in front. Otherwise, it goes in back. @@ -132,13 +130,15 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side } } } - else if (sidev1 <= 0 && sidev2 <= 0) + else if (sidev[0] <= 0 && sidev[1] <= 0) { return 0; } - else if (sidev1 >= 0 && sidev2 >= 0) + else if (sidev[0] >= 0 && sidev[1] >= 0) { return 1; } return -1; } + +#endif diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 287e50877f..430683d075 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3790,7 +3790,7 @@ void P_SetupLevel (char *lumpname, int position) }; leveldata.FindMapBounds (); UsingGLNodes |= genglnodes; - FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes, CPU.bSSE2); + FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes); delete[] vertexes; builder.Extract (nodes, numnodes, segs, numsegs, diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index f761ef4550..3bac644b4e 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -44,7 +44,6 @@ #include "a_sharedglobal.h" #include "g_level.h" #include "nodebuild.h" -#include "x86.h" // State. #include "doomstat.h" @@ -1050,7 +1049,7 @@ static void R_BuildPolyBSP(subsector_t *sub) { PolyNodeBuilder.AddPolySegs(&pn->segs[0], (int)pn->segs.Size()); } - PolyNodeBuilder.BuildMini(false, CPU.bSSE2); + PolyNodeBuilder.BuildMini(false); if (sub->BSP == NULL) { sub->BSP = new FMiniBSP; diff --git a/zdoom.vcproj b/zdoom.vcproj index d556c6ca85..913cff4a99 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -57,7 +57,7 @@ OmitFramePointers="true" WholeProgramOptimization="false" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";game-music-emu\gme;gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY" + PreprocessorDefinitions="NDEBUG,WIN32,_WIN32,_WINDOWS,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH" StringPooling="true" ExceptionHandling="1" RuntimeLibrary="0" @@ -287,7 +287,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;"jpeg-6b";game-music-emu\gme;gdtoa;bzip2;lzma\C" - PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY" + PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH;NO_VA_COPY,BACKPATCH" MinimalRebuild="true" RuntimeLibrary="1" EnableFunctionLevelLinking="true"