CMake: Make the game build with Visual Studio (2019 16.8 or newer)

The easiest way to build this is to check out the dhewm3-libs project
(https://github.com/dhewm/dhewm3-libs/) to provide the dependencies
(SDL2, OpenAL, cURL) and set YQUAKE2LIBS accordingly, by passing
-DYQUAKE2LIBS=c:/path/to/dhewm3-libs/i686-w64-mingw32 to cmake.

I wouldn't really recommend building with MSVC - I just somehow made it
work and ignored all the warnings and I have no idea how portable the
resulting binaries are etc. For binaries you actually want to use, please
continue using MinGW-w64. Especially my workaround for VLAs (C99 variable
length arrays) is kinda fishy, particularly if those arrays are allocated
in a loop (that's inly done in ref_gl1.dll's code).

The only reason I did this is that I had to debug on Windows and, at least
for my specific bug, gdb didn't really work with binaries produced by
MingGW-w64 and MSVC's debugger works well with binaries produced by MSVC.

Currently requires VS 2019 16.8 or newer with C11 (/std:c11) because I
couldn't get YQ2_ALIGNAS_TYPE() to work with MSVC without _Alignas().
If we can get this to work, VS2015 or newer might suffice (but not older
versions, because their so called C standardlib didn't provide exotic
functions like snprintf()).

# Conflicts:
#	CMakeLists.txt
This commit is contained in:
Daniel Gibson 2022-03-12 16:55:55 +01:00
parent bbe639a7b0
commit ed918cf423
16 changed files with 117 additions and 34 deletions

View File

@ -91,7 +91,7 @@ endif()
# We need to pass some options to minizip / unzip. # We need to pass some options to minizip / unzip.
add_definitions(-DNOUNCRYPT) add_definitions(-DNOUNCRYPT)
if(NOT CMAKE_SYSTEM_NAME MATCHES "Linux" OR NOT CMAKE_SYSTEM_NAME MATCHES "Windows") if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux") AND NOT (CMAKE_SYSTEM_NAME MATCHES "Windows"))
add_definitions(-DIOAPI_NO_64) add_definitions(-DIOAPI_NO_64)
endif() endif()
@ -589,19 +589,18 @@ endif()
# Main Quake 2 executable # Main Quake 2 executable
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_executable(yquake2 ${Client-Source} ${Client-Header} ${Platform-Specific-Source} add_executable(yquake2 WIN32 ${Client-Source} ${Client-Header} ${Platform-Specific-Source}
${Backends-Generic-Source}) ${Backends-Generic-Source})
set_target_properties(yquake2 PROPERTIES set_target_properties(yquake2 PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
C_STANDARD 11
) )
target_link_libraries(yquake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags} target_link_libraries(yquake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags}
${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm) ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm)
# Wrapper for the Windows binary # Wrapper for the Windows binary
add_executable(quake2 ${Wrapper-Source}) add_executable(quake2 WIN32 ${Wrapper-Source})
set_target_properties(quake2 PROPERTIES set_target_properties(quake2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release)
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
)
else() else()
add_executable(quake2 ${Client-Source} ${Client-Header} ${Platform-Specific-Source} add_executable(quake2 ${Client-Source} ${Client-Header} ${Platform-Specific-Source}
${Backends-Generic-Source}) ${Backends-Generic-Source})
@ -618,6 +617,7 @@ add_executable(q2ded ${Server-Source} ${Server-Header} ${Platform-Specific-Sourc
set_target_properties(q2ded PROPERTIES set_target_properties(q2ded PROPERTIES
COMPILE_DEFINITIONS "DEDICATED_ONLY" COMPILE_DEFINITIONS "DEDICATED_ONLY"
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
C_STANDARD 11
) )
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
target_link_libraries(q2ded ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm) target_link_libraries(q2ded ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm)
@ -632,6 +632,7 @@ set_target_properties(game PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2
SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
C_STANDARD 11
) )
target_link_libraries(game ${yquake2LinkerFlags}) target_link_libraries(game ${yquake2LinkerFlags})
@ -642,6 +643,7 @@ set_target_properties(ref_gl1 PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
C_STANDARD 11
) )
target_link_libraries(ref_gl1 ${yquake2LinkerFlags} ${yquake2OpenGLLinkerFlags} ${yquake2SDLLinkerFlags}) target_link_libraries(ref_gl1 ${yquake2LinkerFlags} ${yquake2OpenGLLinkerFlags} ${yquake2SDLLinkerFlags})
@ -652,6 +654,7 @@ set_target_properties(ref_gl3 PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
C_STANDARD 11
) )
target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags})
@ -662,5 +665,6 @@ set_target_properties(ref_soft PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release
SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}
C_STANDARD 11
) )
target_link_libraries(ref_soft ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) target_link_libraries(ref_soft ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags})

View File

@ -427,9 +427,10 @@ R_DrawParticles2(int num_particles, const particle_t particles[],
float scale; float scale;
YQ2_ALIGNAS_TYPE(unsigned) byte color[4]; YQ2_ALIGNAS_TYPE(unsigned) byte color[4];
GLfloat vtx[3*num_particles*3]; YQ2_VLA(GLfloat, vtx, 3 * num_particles * 3);
GLfloat tex[2*num_particles*3]; YQ2_VLA(GLfloat, tex, 2 * num_particles * 3);
GLfloat clr[4*num_particles*3]; YQ2_VLA(GLfloat, clr, 4 * num_particles * 3);
unsigned int index_vtx = 0; unsigned int index_vtx = 0;
unsigned int index_tex = 0; unsigned int index_tex = 0;
unsigned int index_clr = 0; unsigned int index_clr = 0;
@ -511,6 +512,10 @@ R_DrawParticles2(int num_particles, const particle_t particles[],
glColor4f(1, 1, 1, 1); glColor4f(1, 1, 1, 1);
glDepthMask(1); /* back to normal Z buffering */ glDepthMask(1); /* back to normal Z buffering */
R_TexEnv(GL_REPLACE); R_TexEnv(GL_REPLACE);
YQ2_VLAFREE(vtx);
YQ2_VLAFREE(tex);
YQ2_VLAFREE(clr);
} }
void void
@ -525,8 +530,9 @@ R_DrawParticles(void)
YQ2_ALIGNAS_TYPE(unsigned) byte color[4]; YQ2_ALIGNAS_TYPE(unsigned) byte color[4];
const particle_t *p; const particle_t *p;
GLfloat vtx[3*r_newrefdef.num_particles]; YQ2_VLA(GLfloat, vtx, 3 * r_newrefdef.num_particles);
GLfloat clr[4*r_newrefdef.num_particles]; YQ2_VLA(GLfloat, clr, 4*r_newrefdef.num_particles);
unsigned int index_vtx = 0; unsigned int index_vtx = 0;
unsigned int index_clr = 0; unsigned int index_clr = 0;
@ -564,6 +570,9 @@ R_DrawParticles(void)
glColor4f( 1, 1, 1, 1 ); glColor4f( 1, 1, 1, 1 );
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
YQ2_VLAFREE(vtx);
YQ2_VLAFREE(clr);
} }
else else
{ {

View File

@ -176,9 +176,9 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp)
} }
total = count; total = count;
GLfloat vtx[3*total]; YQ2_VLA(GLfloat, vtx, 3*total); // FIXME: alloca in loop is bad!
GLfloat tex[2*total]; YQ2_VLA(GLfloat, tex, 2*total);
GLfloat clr[4 * total]; YQ2_VLA(GLfloat, clr, 4*total);
unsigned int index_vtx = 0; unsigned int index_vtx = 0;
unsigned int index_tex = 0; unsigned int index_tex = 0;
unsigned int index_clr = 0; unsigned int index_clr = 0;
@ -240,6 +240,10 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp)
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_COLOR_ARRAY);
YQ2_VLAFREE(vtx);
YQ2_VLAFREE(tex);
YQ2_VLAFREE(clr)
} }
if (currententity->flags & if (currententity->flags &
@ -294,7 +298,8 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
} }
total = count; total = count;
GLfloat vtx[3*total];
YQ2_VLA(GLfloat, vtx, 3*total); // FIXME: alloca in loop is bad!
unsigned int index_vtx = 0; unsigned int index_vtx = 0;
do do
@ -320,6 +325,7 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
glDrawArrays( type, 0, total ); glDrawArrays( type, 0, total );
glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY );
YQ2_VLAFREE(vtx);
} }
/* stencilbuffer shadows */ /* stencilbuffer shadows */

View File

@ -137,7 +137,7 @@ R_ScreenShot(void)
// so swap bottom rows with top rows // so swap bottom rows with top rows
{ {
size_t bytesPerRow = 3*w; size_t bytesPerRow = 3*w;
byte rowBuffer[bytesPerRow]; YQ2_VLA(byte, rowBuffer, bytesPerRow);
byte *curRowL = buffer; // first byte of first row byte *curRowL = buffer; // first byte of first row
byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row
while(curRowL < curRowH) while(curRowL < curRowH)
@ -149,6 +149,7 @@ R_ScreenShot(void)
curRowL += bytesPerRow; curRowL += bytesPerRow;
curRowH -= bytesPerRow; curRowH -= bytesPerRow;
} }
YQ2_VLAFREE(rowBuffer);
} }
ri.Vid_WriteScreenshot(w, h, 3, buffer); ri.Vid_WriteScreenshot(w, h, 3, buffer);

View File

@ -101,7 +101,7 @@ R_DrawGLFlowingPoly(msurface_t *fa)
scroll = -64.0; scroll = -64.0;
} }
GLfloat tex[2*p->numverts]; YQ2_VLA(GLfloat, tex, 2*p->numverts);
unsigned int index_tex = 0; unsigned int index_tex = 0;
v = p->verts [ 0 ]; v = p->verts [ 0 ];
@ -122,6 +122,8 @@ R_DrawGLFlowingPoly(msurface_t *fa)
glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY );
YQ2_VLAFREE(tex);
} }
static void static void
@ -210,7 +212,7 @@ R_DrawGLPolyChain(glpoly_t *p, float soffset, float toffset)
v = p->verts[0]; v = p->verts[0];
GLfloat tex[2*p->numverts]; YQ2_VLA(GLfloat, tex, 2*p->numverts); // FIXME: alloca in loop is bad!
unsigned int index_tex = 0; unsigned int index_tex = 0;
for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE )
@ -230,6 +232,8 @@ R_DrawGLPolyChain(glpoly_t *p, float soffset, float toffset)
glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY );
YQ2_VLAFREE(tex);
} }
} }
} }

View File

@ -301,7 +301,7 @@ R_EmitWaterPolys(msurface_t *fa)
{ {
p = bp; p = bp;
GLfloat tex[2*p->numverts]; YQ2_VLA(GLfloat, tex, 2*p->numverts); // FIXME: alloca in loop is bad!
unsigned int index_tex = 0; unsigned int index_tex = 0;
for ( i = 0, v = p->verts [ 0 ]; i < p->numverts; i++, v += VERTEXSIZE ) for ( i = 0, v = p->verts [ 0 ]; i < p->numverts; i++, v += VERTEXSIZE )
@ -328,6 +328,8 @@ R_EmitWaterPolys(msurface_t *fa)
glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY );
YQ2_VLAFREE(tex);
} }
} }

View File

@ -925,7 +925,7 @@ GL3_DrawParticles(void)
} part_vtx; } part_vtx;
assert(sizeof(part_vtx)==9*sizeof(float)); // remember to update GL3_SurfInit() if this changes! assert(sizeof(part_vtx)==9*sizeof(float)); // remember to update GL3_SurfInit() if this changes!
part_vtx buf[numParticles]; YQ2_VLA(part_vtx, buf, numParticles);
// TODO: viewOrg could be in UBO // TODO: viewOrg could be in UBO
vec3_t viewOrg; vec3_t viewOrg;
@ -962,6 +962,8 @@ GL3_DrawParticles(void)
glDisable(GL_BLEND); glDisable(GL_BLEND);
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glDisable(GL_PROGRAM_POINT_SIZE); glDisable(GL_PROGRAM_POINT_SIZE);
YQ2_VLAFREE(buf);
} }
} }

View File

@ -131,7 +131,7 @@ GL3_ScreenShot(void)
// so swap bottom rows with top rows // so swap bottom rows with top rows
{ {
size_t bytesPerRow = 3*w; size_t bytesPerRow = 3*w;
byte rowBuffer[bytesPerRow]; YQ2_VLA(byte, rowBuffer, bytesPerRow);
byte *curRowL = buffer; // first byte of first row byte *curRowL = buffer; // first byte of first row
byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row
while(curRowL < curRowH) while(curRowL < curRowH)
@ -143,6 +143,7 @@ GL3_ScreenShot(void)
curRowL += bytesPerRow; curRowL += bytesPerRow;
curRowH -= bytesPerRow; curRowH -= bytesPerRow;
} }
YQ2_VLAFREE(rowBuffer);
} }
ri.Vid_WriteScreenshot(w, h, 3, buffer); ri.Vid_WriteScreenshot(w, h, 3, buffer);

View File

@ -29,6 +29,23 @@
#include "../vid/header/ref.h" #include "../vid/header/ref.h"
#ifdef _MSC_VER
#include <malloc.h>
#define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \
TYPE * VARNAME = (TYPE *) _malloca(sizeof(TYPE) * NUMELEMS)
#define YQ2_VLAFREE(VARNAME) \
_freea(VARNAME); VARNAME=NULL;
#else // other compilers hopefully support C99 VLAs (gcc/mingw and clang do)
#define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \
TYPE VARNAME[NUMELEMS]
#define YQ2_VLAFREE(VARNAME)
#endif
/* /*
* skins will be outline flood filled and mip mapped * skins will be outline flood filled and mip mapped
* pics and sprites with alpha will be outline flood filled * pics and sprites with alpha will be outline flood filled
@ -58,7 +75,7 @@ typedef enum
#define MAX_LBM_HEIGHT 480 #define MAX_LBM_HEIGHT 480
extern void R_Printf(int level, const char* msg, ...) __attribute__ ((format (printf, 2, 3))); extern void R_Printf(int level, const char* msg, ...) PRINTF_ATTR(2, 3);
extern void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height); extern void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height);
extern void GetPCXInfo(char *filename, int *width, int *height); extern void GetPCXInfo(char *filename, int *width, int *height);

View File

@ -163,7 +163,7 @@ static qboolean
S_IsSilencedMuzzleFlash(const wavinfo_t* info, const void* raw_data, const char* name) S_IsSilencedMuzzleFlash(const wavinfo_t* info, const void* raw_data, const char* name)
{ {
/* Skip the prefix. */ /* Skip the prefix. */
static const size_t base_sound_string_length = strlen("sound/"); static const size_t base_sound_string_length = 6; //strlen("sound/");
const char* base_name = name + base_sound_string_length; const char* base_name = name + base_sound_string_length;
/* Match to well-known muzzle flash sound names. */ /* Match to well-known muzzle flash sound names. */

View File

@ -135,7 +135,7 @@ CreateSDLWindow(int flags, int w, int h)
Com_Printf("Likely SDL bug #4700, trying to work around it\n"); Com_Printf("Likely SDL bug #4700, trying to work around it\n");
/* Mkay, try to hack around that. */ /* Mkay, try to hack around that. */
SDL_DisplayMode wanted_mode = {}; SDL_DisplayMode wanted_mode = {0};
wanted_mode.w = w; wanted_mode.w = w;
wanted_mode.h = h; wanted_mode.h = h;

View File

@ -207,7 +207,7 @@ typedef struct
typedef struct typedef struct
{ {
YQ2_ATTR_NORETURN_FUNCPTR void (IMPORT *Sys_Error) (int err_level, char *str, ...) __attribute__ ((format (printf, 2, 3))); YQ2_ATTR_NORETURN_FUNCPTR void (IMPORT *Sys_Error) (int err_level, char *str, ...) PRINTF_ATTR(2, 3);
void (IMPORT *Cmd_AddCommand) (char *name, void(*cmd)(void)); void (IMPORT *Cmd_AddCommand) (char *name, void(*cmd)(void));
void (IMPORT *Cmd_RemoveCommand) (char *name); void (IMPORT *Cmd_RemoveCommand) (char *name);

View File

@ -25,7 +25,9 @@
* ======================================================================= * =======================================================================
*/ */
#ifndef _MSC_VER
#include <libgen.h> #include <libgen.h>
#endif
#include "header/common.h" #include "header/common.h"
#include "header/glob.h" #include "header/glob.h"
@ -380,7 +382,7 @@ FS_FOpenFile(const char *rawname, fileHandle_t *f, qboolean gamedir_only)
// Remove self references and empty dirs from the requested path. // Remove self references and empty dirs from the requested path.
// ZIPs and PAKs don't support them, but they may be hardcoded in // ZIPs and PAKs don't support them, but they may be hardcoded in
// some custom maps or models. // some custom maps or models.
char name[MAX_QPATH] = {}; char name[MAX_QPATH] = {0};
size_t namelen = strlen(rawname); size_t namelen = strlen(rawname);
for (int input = 0, output = 0; input < namelen; input++) for (int input = 0, output = 0; input < namelen; input++)
{ {
@ -1614,6 +1616,27 @@ FS_GetNextRawPath(const char* lastRawPath)
return NULL; return NULL;
} }
#ifdef _MSC_VER // looks like MSVC/the Windows CRT doesn't have basename()
// returns the last part of the given pathname, after last (back)slash
// NOTE: this is not a fully compliant basename() implementation, as it doesn't
// handle trailing (back)slashes at all - because we don't need that.
static char* basename( char* n )
{
char* r1 = strrchr( n, '\\' );
char* r2 = strrchr( n, '/' );
if (r1 != NULL)
{
if (r2 != NULL)
{
return (r2 > r1) ? (r2 + 1) : (r1 + 1);
}
return r1 + 1;
}
return (r2 != NULL) ? (r2 + 1) : n;
}
#endif // _MSC_VER
void void
FS_AddDirToSearchPath(char *dir, qboolean create) { FS_AddDirToSearchPath(char *dir, qboolean create) {
char *file; char *file;
@ -1971,7 +1994,7 @@ static void FS_AddDirToRawPath (const char *rawdir, qboolean create, qboolean re
} }
// Make sure that the dir doesn't end with a slash. // Make sure that the dir doesn't end with a slash.
for (size_t s = strlen(dir) - 1; s >= 0; s--) for (size_t s = strlen(dir) - 1; s > 0; s--)
{ {
if (dir[s] == '/') if (dir[s] == '/')
{ {

View File

@ -711,11 +711,11 @@ void FS_CreatePath(char *path);
void Com_BeginRedirect(int target, char *buffer, int buffersize, void (*flush)(int, char *)); void Com_BeginRedirect(int target, char *buffer, int buffersize, void (*flush)(int, char *));
void Com_EndRedirect(void); void Com_EndRedirect(void);
void Com_Printf(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void Com_Printf(char *fmt, ...) PRINTF_ATTR(1, 2);
void Com_DPrintf(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void Com_DPrintf(char *fmt, ...) PRINTF_ATTR(1, 2);
void Com_VPrintf(int print_level, const char *fmt, va_list argptr); /* print_level is PRINT_ALL or PRINT_DEVELOPER */ void Com_VPrintf(int print_level, const char *fmt, va_list argptr); /* print_level is PRINT_ALL or PRINT_DEVELOPER */
void Com_MDPrintf(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void Com_MDPrintf(char *fmt, ...) PRINTF_ATTR(1, 2);
YQ2_ATTR_NORETURN void Com_Error(int code, char *fmt, ...) __attribute__ ((format (printf, 2, 3))); YQ2_ATTR_NORETURN void Com_Error(int code, char *fmt, ...) PRINTF_ATTR(2, 3);
YQ2_ATTR_NORETURN void Com_Quit(void); YQ2_ATTR_NORETURN void Com_Quit(void);
/* Ugly work around for unsupported /* Ugly work around for unsupported

View File

@ -65,7 +65,11 @@ typedef unsigned char byte;
// must be used as prefix (YQ2_ATTR_NORETURN void bla();)! // must be used as prefix (YQ2_ATTR_NORETURN void bla();)!
#define YQ2_ATTR_NORETURN __attribute__ ((noreturn)) #define YQ2_ATTR_NORETURN __attribute__ ((noreturn))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#error "We only support MSVC in C11 mode (/std:c11) or higher, requires Visual Studio 2019 version 16.8 or higher"
// in that case, we should've used the #if __STDC_VERSION__ >= 201112L case above
#define YQ2_ALIGNAS_SIZE( SIZE ) __declspec(align(SIZE)) #define YQ2_ALIGNAS_SIZE( SIZE ) __declspec(align(SIZE))
// FIXME: for some reason, the following line doesn't work, which is why we require C11 support for MSVC
#define YQ2_ALIGNAS_TYPE( TYPE ) __declspec(align(__alignof(TYPE))) #define YQ2_ALIGNAS_TYPE( TYPE ) __declspec(align(__alignof(TYPE)))
// must be used as prefix (YQ2_ATTR_NORETURN void bla();)! // must be used as prefix (YQ2_ATTR_NORETURN void bla();)!
#define YQ2_ATTR_NORETURN __declspec(noreturn) #define YQ2_ATTR_NORETURN __declspec(noreturn)
@ -133,6 +137,12 @@ typedef unsigned char byte;
#define Q2_DLL_EXPORTED __attribute__((__visibility__("default"))) #define Q2_DLL_EXPORTED __attribute__((__visibility__("default")))
#endif #endif
#ifdef _MSC_VER
#define PRINTF_ATTR(FMT, VARGS)
#else // at least GCC/mingw and clang support this
#define PRINTF_ATTR(FMT, VARGS) __attribute__((format(printf, FMT , VARGS )));
#endif
/* per-level limits */ /* per-level limits */
#define MAX_CLIENTS 256 /* absolute limit */ #define MAX_CLIENTS 256 /* absolute limit */
#define MAX_EDICTS 1024 /* must change protocol to increase more */ #define MAX_EDICTS 1024 /* must change protocol to increase more */
@ -310,7 +320,7 @@ float BigFloat(float l);
float LittleFloat(float l); float LittleFloat(float l);
void Swap_Init(void); void Swap_Init(void);
char *va(char *format, ...) __attribute__ ((format (printf, 1, 2))); char *va(char *format, ...) PRINTF_ATTR(1, 2);
/* ============================================= */ /* ============================================= */

View File

@ -1034,7 +1034,11 @@ Com_PageInMemory(byte *buffer, int size)
int int
Q_stricmp(const char *s1, const char *s2) Q_stricmp(const char *s1, const char *s2)
{ {
#ifdef _MSC_VER
return stricmp(s1, s2);
#else
return strcasecmp(s1, s2); return strcasecmp(s1, s2);
#endif
} }
int int