#include <math.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

#include "coduo_engine_structs.h"
#include "coduo_x87emu.h" /* defines EMULATE_X87; x87 shim when it is 1 */

enum {
    SV_GAME_SYSCALL_COM_ERROR_DROP = 1,
    SV_GAME_SYSCALL_SOUND_ALIAS_LOCALE = 2,
    SV_GAME_SYSCALL_WEAPON_MEMORY_STATE = 1,
    SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX = 0,
    SV_GAME_SYSCALL_XANIM_MILLISECONDS = 1000
};

extern cvar_t *host_cvar_sv_maxclients_pointer_slot;
extern coduo_host_svs_header_t svs;
extern char *sv_entityParsePoint;

extern void Com_Printf(const char *format, ...);
extern void Com_Error(int32_t code, const char *format, ...);
extern int32_t Sys_Milliseconds(void);
extern void Cvar_Register(vmCvar_t *vmCvar,
                          const char *name, const char *value,
                          uint32_t flags);
extern void Cvar_Update(vmCvar_t *vmCvar);
extern cvar_t *Cvar_Set(const char *name, const char *value);
extern int32_t Cvar_VariableIntegerValue(const char *name);
extern float Cvar_VariableValue(const char *name);
extern void Cvar_VariableStringBuffer(
    const char *name, char *buffer,
    int32_t bufferLength);
extern int32_t Cmd_Argc(void);
extern const char *Cmd_Argv(uint32_t arg);
extern void Cmd_ArgvBuffer(uint32_t arg, char *buffer,
                           int32_t bufferLength);
extern void *SV_GameHunkAlloc(size_t size);
extern void *SV_GameHunkAllocLow(size_t size);
extern void *SV_GameHunkAllocAlign(size_t size,
                                   int32_t alignment);
extern void *SV_GameHunkAllocLowAlign(size_t size,
                                      int32_t alignment);
extern void *SV_GameHunkAllocateTempMemory(size_t size);
extern void SV_GameHunkFreeTempMemory(void *ptr);
extern int32_t
FS_FOpenFileByMode(const char *path, int32_t *handleOut,
                   coduo_fs_mode_t mode);
extern int32_t FS_Read(void *buffer, int32_t length,
                       int32_t handle);
extern int32_t FS_Write(const void *buffer, int32_t length,
                        int32_t handle);
extern void FS_Rename(const char *from, const char *to);
extern void FS_FCloseFile(int32_t handle);
extern void Cbuf_ExecuteText(coduo_cbuf_exec_when_t execWhen,
                             const char *text);
extern void SV_LocateGameData(sharedEntity_t *gEnts,
                              int32_t numGEntities,
                              int32_t sizeofGEntity,
                              playerState_t *clients,
                              int32_t sizeofClient);
extern void SV_GameDropClient(int32_t clientNum,
                              const char *reason);
extern void SV_GameSendServerCommand(int32_t clientNum,
                                     qboolean reliable,
                                     const char *command);
extern void SV_SetConfigstring(int32_t index,
                               const char *value);
extern void SV_GetConfigstring(int32_t index, char *buffer,
                               int32_t bufferSize);
extern const char *SV_GetConfigstringConst(int32_t index);
extern qboolean NET_IsLocalAddress(netadr_t adr);
extern void SV_GetUserinfo(int32_t clientNum, char *buffer,
                           int32_t bufferSize);
extern void SV_SetUserinfo(int32_t clientNum, const char *info);
extern void SV_GetServerinfo(char *buffer,
                             int32_t bufferSize);
extern void SV_SetBrushModel(sharedEntity_t *gentity);
extern void SV_Trace(trace_t *trace,
                     const vec3_t start, const vec3_t mins, const vec3_t maxs,
                     const vec3_t end, int32_t passEntityNum,
                     int32_t contentMask, qboolean capsule,
                     qboolean useDObj, const uint8_t *dobjTracePartState,
                     qboolean locational);
extern void SV_SightTrace(int32_t *trace,
                          const vec3_t start, const vec3_t mins,
                          const vec3_t maxs, const vec3_t end,
                          int32_t passEntityNum,
                          int32_t passOwnerNum,
                          int32_t contentMask,
                          qboolean capsuleMode);
extern int32_t SV_SightTraceToEntity(
    const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
    int32_t entityNum, int32_t contentMask,
    qboolean capsule);
extern void CM_BoxTrace(trace_t *trace,
                        const vec3_t start, const vec3_t end,
                        const vec3_t mins, const vec3_t maxs,
                        int32_t model,
                        int32_t brushMask, qboolean capsule);
extern int32_t
CM_BoxSightTrace(int32_t result,
                 const vec3_t start, const vec3_t end, const vec3_t mins,
                 const vec3_t maxs, int32_t model,
                 int32_t brushMask, qboolean capsule);
extern int32_t SV_PointContents(
    const vec3_t point, int32_t passEntityNum,
    int32_t contentMask);
extern qboolean SV_inPVS(const vec3_t p1, const vec3_t p2);
extern qboolean SV_inPVSIgnorePortals(const vec3_t p1, const vec3_t p2);
extern int32_t
SV_inSnapshot(const vec3_t origin, int32_t entityNum);
extern void SV_AdjustAreaPortalState(sharedEntity_t *ent,
                                     qboolean open);
extern qboolean CM_AreasConnected(int32_t area1,
                                  int32_t area2);
extern void SV_LinkEntity(sharedEntity_t *gentity);
extern void SV_UnlinkEntity(sharedEntity_t *gentity);
extern int32_t
CM_AreaEntities(const vec3_t mins, const vec3_t maxs,
                int32_t *entityList,
                int32_t maxcount,
                int32_t contentMask);
extern qboolean SV_EntityContact(const vec3_t mins, const vec3_t maxs,
                                 const sharedEntity_t *gentity,
                                 qboolean capsule);
extern void SV_GetUsercmd(int32_t clientNum,
                          usercmd_t *usercmdOut);
extern char *Com_Parse(char **data_p);
extern void Q_strncpyz(char *dest, const char *src, int32_t destsize);
extern int32_t
FS_GetFileList(const char *path, const char *extension, char *listBuffer,
               int32_t bufferSize);
extern qboolean SV_MapExists(const char *mapName);
extern time_t Com_RealTime(qtime_t *qtime);
extern void Sys_SnapVector(vec3_t vec);
extern const char *Com_SoundAliasString(const char *name,
                                        int32_t locale);
extern coduo_sound_alias_t *Com_PickSoundAlias(const char *name,
                                               int32_t locale, ...);
extern int32_t Com_SoundAliasIndex(const coduo_sound_alias_t *alias,
                                   int32_t locale);
extern int32_t SurfaceTypeFromName(const char *name);
extern const char *SurfaceTypeToName(int32_t surfaceType);
extern int32_t SV_AddTestClient(void);
extern qboolean SV_GetArchivedClientInfo(int32_t clientNum,
                                         int32_t *archiveTime,
                                         playerState_t *playerStateOut,
                                         coduo_archived_client_info_meta_t
                                             *clientInfoOut);
extern void SV_SetSnapshotArchiveEnabled(qboolean enabled);
extern void *Z_Malloc(size_t size);
extern void Z_Free(void *ptr);
extern XAnimTree *Com_XAnimCreateTree(
    XAnim *tree);
extern XAnimTree *Com_XAnimCreateSmallTree(
    XAnim *tree);
extern void Com_XAnimFreeSmallTree(XAnimTree *tree);
extern qboolean XModelExists(const char *modelName);
extern XModel *SV_XModelGet(const char *modelName);
extern void DObjCreateForEntity(DObjModel *models,
                                uint16_t modelCount,
                                XAnimTree *tree,
                                int32_t entityNum,
                                uint16_t gameId);
extern DObj *DObjGetForEntity(int32_t entityNum);
extern void DObjFreeForEntity(int32_t entityNum, qboolean freeAll);
extern XAnim *
XAnimRuntimeTreeSourceTree(XAnimTree *runtimeTree);
extern void XAnimClearTreeGoalWeights(
    XAnimTree *runtimeTree, int32_t nodeIndex,
    float blendTime);
extern void XAnimClearGoalWeight(XAnimTree *runtimeTree,
                                 int32_t nodeIndex,
                                 float blendTime);
extern void XAnimClearTreeGoalWeightsStrict(
    XAnimTree *runtimeTree, int32_t nodeIndex,
    float blendTime);
extern void XAnimSetCompleteGoalWeightKnob(
    XAnimTree *runtimeTree, int32_t animIndex,
    int32_t weightBits, int32_t blendTimeBits, int32_t rateBits,
    uint16_t notifyName, uint16_t root, qboolean restart);
extern int32_t XAnimSetCompleteGoalWeightKnobAll(
    XAnimTree *runtimeTree, int32_t animIndex, int32_t knob,
    int32_t weightBits, int32_t blendTimeBits, int32_t rateBits,
    uint16_t notifyName, int32_t root, qboolean restart);
extern void XAnimSetAnimRate(XAnimTree *runtimeTree,
                             int32_t animIndex, int32_t rateBits);
extern void XAnimSetTime(XAnimTree *runtimeTree,
                         int32_t animIndex, float time);
extern void XAnimSetGoalWeightKnob(XAnimTree *runtimeTree,
                                   int32_t animIndex, int32_t weightBits,
                                   int32_t blendTimeBits, int32_t rateBits,
                                   uint16_t notifyName, uint16_t root,
                                   qboolean restart);
extern void XAnimClearTree(XAnimTree *runtimeTree);
extern XAnim *
Scr_GetAnims(uint32_t treeIndex);
extern qboolean XAnimHasTime(XAnim *tree, int32_t animIndex);
extern qboolean XAnimIsPrimitive(XAnim *tree, int32_t animIndex);
extern long double XAnimGetLength(XAnim *tree,
                                  int32_t animIndex);
#if EMULATE_X87
extern x87f coduo_engine_xanim_get_length_x87(
    XAnim *tree, int32_t animIndex);
#endif
extern void XAnimSetCompleteGoalWeight(XAnimTree *runtimeTree,
                                       int32_t animIndex, int32_t weightBits,
                                       int32_t blendTimeBits, int32_t rateBits,
                                       uint16_t notifyName, uint16_t root,
                                       qboolean restart);
extern int32_t XAnimSetGoalWeight(XAnimTree *runtimeTree,
                                  int32_t animIndex, int32_t weightBits,
                                  int32_t blendTimeBits, int32_t rateBits,
                                  uint16_t notifyName, uint16_t root,
                                  qboolean restart);
extern void XAnimCalcAbsDelta(XAnimTree *runtimeTree,
                              int32_t animIndex, float *rotationDelta,
                              vec3_t moveDelta);
extern void XAnimCalcDelta(XAnimTree *runtimeTree,
                           int32_t animIndex, float *rotationDelta,
                           vec3_t moveDelta, int32_t weightSelector);
extern void XAnimGetRelDelta(XAnim *tree, int32_t animIndex,
                             float *rotationDelta, vec3_t moveDelta,
                             float startTime,
                             float endTime);
extern void XAnimGetAbsDelta(XAnim *tree, int32_t animIndex,
                             float *rotationDelta, vec3_t moveDelta,
                             float time);
extern qboolean XAnimIsLooped(XAnim *tree, int32_t animIndex);
extern qboolean XAnimNotetrackExists(XAnim *tree,
                                     int32_t animIndex, uint16_t notetrack);
extern float XAnimGetTime(XAnimTree *runtimeTree,
                          int32_t animIndex);
extern float XAnimGetWeight(XAnimTree *runtimeTree,
                            int32_t animIndex);
extern void SV_DObjDumpInfo(int32_t entityNum);
extern qboolean SV_DObjCreateSkelForBone(int32_t entityNum,
                                         int32_t boneIndex);
extern qboolean SV_DObjCreateSkelForBones(int32_t entityNum,
                                          const uint32_t *partBits);
extern qboolean SV_DObjUpdateServerTime(int32_t entityNum,
                                        float serverTime,
                                        qboolean notify);
extern void SV_DObjInitServerTime(int32_t entityNum,
                                  float serverTime);
extern void SV_DObjGetHierarchyBits(int32_t entityNum,
                                    int32_t boneIndex,
                                    uint32_t *partBits);
extern void SV_DObjCalcAnim(int32_t entityNum,
                            const uint32_t *partBits);
extern void SV_DObjCalcSkel(int32_t entityNum,
                            const uint32_t *partBits);
extern void XAnimLoadAnimTree(XAnimTree *runtimeTree);
extern void XAnimSaveAnimTree(XAnimTree *runtimeTree);
extern void XAninCloneAnimTree(XAnimTree *sourceTree,
                              XAnimTree *destTree);
extern int32_t SV_DObjNumBones(int32_t entityNum);
extern int32_t SV_DObjGetBoneIndex(
    int32_t entityNum, const char *tagName);
extern DObjSkelMat *SV_DObjGetMatrixArray(int32_t entityNum);
extern void SV_DObjDisplayAnim(int32_t entityNum);
extern qboolean XAnimHasFinished(XAnimTree *runtimeTree,
                                 int32_t animIndex);
extern int32_t XAnimGetNumChildren(XAnim *tree,
                                   int32_t animIndex);
extern int32_t XAnimGetChildAt(XAnim *tree, int32_t animIndex,
                               int32_t childIndex);
extern int32_t XModelNumBones(const XModel *model);
extern const uint16_t *XModelBoneNames(const XModel *model);
extern DObjAnimMat *SV_DObjGetRotTransArray(int32_t entityNum);
extern qboolean SV_DObjSetRotTransIndex(int32_t entityNum,
                                         const uint32_t *partBits,
                                         int32_t boneIndex);
extern qboolean SV_DObjSetControlRotTransIndex(int32_t entityNum,
                                                const uint32_t *partBits,
                                                int32_t boneIndex);
extern void SV_DObjGetBounds(int32_t entityNum, vec3_t mins,
                             vec3_t maxs);
extern const char *XAnimGetAnimName(XAnim *tree,
                                    int32_t animIndex);
extern XAnimTree *SV_DObjGetTree(int32_t entityNum);
extern int32_t XAnimGetAnimTreeSize(XAnim *tree);
extern void SV_XModelDebugBoxes(int32_t entityNum);
extern void *GetWeaponInfoMemory(int32_t bytes,
                                 int32_t *priorState,
                                 int32_t callerState);
extern void FreeWeaponInfoMemory(int32_t callerState,
                                 qboolean keepMemory);
extern void SV_FreeClientScriptPers(void);
extern void SV_ResetEntityParsePoint(void);
extern void MatrixMultiply(float in1[3][3], float in2[3][3],
                           float out[3][3]);
extern void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right,
                         vec3_t up);
extern void PerpendicularVector(vec3_t dst, const vec3_t src);
extern int32_t FloatAsInt(float value);

static const char svGameSyscallStringFormat[] = "%s";
static const char svGameSyscallLocalizedErrorFormat[] = "\x15%s";
static const char svGameSyscallBadTrapError[] = "Bad game system trap: %i";

#define SV_ARG(index) (parms[(index)])
#define SV_PTR(type, index) ((type *)SV_ARG(index))
#define SV_CONST_PTR(type, index) ((const type *)SV_ARG(index))
#define SV_STRING(index) ((const char *)SV_ARG(index))
#define SV_INT(index) ((int32_t)SV_ARG(index))
#define SV_SIZE(index) ((size_t)SV_ARG(index))
#define SV_UINT16(index) ((uint16_t)SV_ARG(index))

/* NOT_FROM_ORIGINAL_SOURCE: VM syscall ABI packs float arguments as int bits. */
static float coduomp_game_syscall_int_as_float(int32_t value)
{
    float result;

    memcpy(&result, &value, sizeof(result));
    return result;
}

intptr_t
SV_GameSystemCalls(intptr_t *parms)
{
    XAnim *anims;
    coduo_syscall_id_t syscall = (coduo_syscall_id_t)SV_ARG(0);

    switch (syscall) {
    case CODUO_SYSCALL_PRINTF:
        Com_Printf(svGameSyscallStringFormat, SV_STRING(1));
        return 0;
    case CODUO_SYSCALL_ERROR:
        Com_Error(SV_GAME_SYSCALL_COM_ERROR_DROP, svGameSyscallLocalizedErrorFormat,
                  SV_STRING(1));
        return 0;
    case CODUO_SYSCALL_ERROR_LOCALIZED:
        Com_Error(SV_GAME_SYSCALL_COM_ERROR_DROP, svGameSyscallStringFormat,
                  SV_STRING(1));
        return 0;
    case CODUO_SYSCALL_MILLISECONDS:
        return Sys_Milliseconds();
    case CODUO_SYSCALL_CVAR_REGISTER:
        Cvar_Register(SV_PTR(vmCvar_t, 1), SV_STRING(2), SV_STRING(3),
                      (uint32_t)SV_ARG(4));
        return 0;
    case CODUO_SYSCALL_CVAR_UPDATE:
        Cvar_Update(SV_PTR(vmCvar_t, 1));
        return 0;
    case CODUO_SYSCALL_CVAR_SET:
        Cvar_Set(SV_STRING(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_CVAR_VARIABLE_INTEGER_VALUE:
        return Cvar_VariableIntegerValue(SV_STRING(1));
    case CODUO_SYSCALL_CVAR_VARIABLE_VALUE:
        *SV_PTR(float, 2) = Cvar_VariableValue(SV_STRING(1));
        return 0;
    case CODUO_SYSCALL_CVAR_VARIABLE_STRING_BUFFER:
        Cvar_VariableStringBuffer(SV_STRING(1), SV_PTR(char, 2), SV_INT(3));
        return 0;
    case CODUO_SYSCALL_ARGC:
        return Cmd_Argc();
    case CODUO_SYSCALL_ARGV:
        Cmd_ArgvBuffer((uint32_t)SV_ARG(1), SV_PTR(char, 2),
                       SV_INT(3));
        return 0;
    case CODUO_SYSCALL_HUNK_ALLOC:
        return (intptr_t)SV_GameHunkAlloc(SV_SIZE(1));
    case CODUO_SYSCALL_HUNK_ALLOC_LOW:
        return (intptr_t)SV_GameHunkAllocLow(SV_SIZE(1));
    case CODUO_SYSCALL_HUNK_ALLOC_ALIGN:
        return (intptr_t)SV_GameHunkAllocAlign(SV_SIZE(1), SV_INT(2));
    case CODUO_SYSCALL_HUNK_ALLOC_LOW_ALIGN:
        return (intptr_t)SV_GameHunkAllocLowAlign(SV_SIZE(1), SV_INT(2));
    case CODUO_SYSCALL_HUNK_ALLOC_TEMP:
        return (intptr_t)SV_GameHunkAllocateTempMemory(SV_SIZE(1));
    case CODUO_SYSCALL_HUNK_FREE_TEMP:
        SV_GameHunkFreeTempMemory(SV_PTR(void, 1));
        return 0;
    case CODUO_SYSCALL_FS_FOPEN_FILE:
        return FS_FOpenFileByMode(SV_STRING(1), SV_PTR(int32_t, 2),
                                  (coduo_fs_mode_t)SV_ARG(3));
    case CODUO_SYSCALL_FS_READ:
        FS_Read(SV_PTR(void, 1), SV_INT(2), SV_INT(3));
        return 0;
    case CODUO_SYSCALL_FS_WRITE:
        return FS_Write(SV_CONST_PTR(void, 1), SV_INT(2), SV_INT(3));
    case CODUO_SYSCALL_FS_RENAME:
        FS_Rename(SV_STRING(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_FS_FCLOSE_FILE:
        FS_FCloseFile(SV_INT(1));
        return 0;
    case CODUO_SYSCALL_SEND_CONSOLE_COMMAND:
        Cbuf_ExecuteText(SV_INT(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_LOCATE_GAME_DATA:
        SV_LocateGameData(SV_PTR(sharedEntity_t, 1), SV_INT(2),
                          SV_INT(3), SV_PTR(playerState_t, 4), SV_INT(5));
        return 0;
    case CODUO_SYSCALL_GET_GUID:
        if (SV_INT(1) < 0 ||
            host_cvar_sv_maxclients_pointer_slot->integer <= SV_INT(1)) {
            return 0;
        }
        return svs.clients[SV_INT(1)].guid;
    case CODUO_SYSCALL_DROP_CLIENT:
        SV_GameDropClient(SV_INT(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_SEND_SERVER_COMMAND:
        SV_GameSendServerCommand(SV_INT(1), (qboolean)SV_ARG(2),
                                 SV_STRING(3));
        return 0;
    case CODUO_SYSCALL_SET_CONFIGSTRING:
        SV_SetConfigstring(SV_INT(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_GET_CONFIGSTRING:
        SV_GetConfigstring(SV_INT(1), SV_PTR(char, 2), SV_INT(3));
        return 0;
    case CODUO_SYSCALL_GET_CONFIGSTRING_CONST:
        return (intptr_t)SV_GetConfigstringConst(SV_INT(1));
    case CODUO_SYSCALL_IS_LOCAL_CLIENT:
        return NET_IsLocalAddress(svs.clients[SV_INT(1)].netchan.remoteAddress);
    case CODUO_SYSCALL_GET_CLIENT_PING:
        return svs.clients[SV_INT(1)].ping;
    case CODUO_SYSCALL_GET_USERINFO:
        SV_GetUserinfo(SV_INT(1), SV_PTR(char, 2), SV_INT(3));
        return 0;
    case CODUO_SYSCALL_SET_USERINFO:
        SV_SetUserinfo(SV_INT(1), SV_STRING(2));
        return 0;
    case CODUO_SYSCALL_GET_SERVERINFO:
        SV_GetServerinfo(SV_PTR(char, 1), SV_INT(2));
        return 0;
    case CODUO_SYSCALL_SET_BRUSH_MODEL:
        SV_SetBrushModel(SV_PTR(sharedEntity_t, 1));
        return 0;
    case CODUO_SYSCALL_TRACE:
        SV_Trace(SV_PTR(trace_t, 1),
                 SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                 SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5), SV_INT(6),
                 SV_INT(7), qfalse, qfalse, NULL, qfalse);
        return 0;
    case CODUO_SYSCALL_TRACE_CAPSULE:
        SV_Trace(SV_PTR(trace_t, 1),
                 SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                 SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5), SV_INT(6),
                 SV_INT(7), qtrue, qfalse, NULL, qfalse);
        return 0;
    case CODUO_SYSCALL_SIGHT_TRACE:
        SV_SightTrace(SV_PTR(int32_t, 1),
                      SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                      SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5),
                      SV_INT(6), SV_INT(7), SV_INT(8), qfalse);
        return 0;
    case CODUO_SYSCALL_SIGHT_TRACE_CAPSULE:
        SV_SightTrace(SV_PTR(int32_t, 1),
                      SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                      SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5),
                      SV_INT(6), SV_INT(7), SV_INT(8), qtrue);
        return 0;
    case CODUO_SYSCALL_SIGHT_TRACE_TO_ENTITY:
        return SV_SightTraceToEntity(SV_CONST_PTR(float, 1),
                                     SV_CONST_PTR(float, 2),
                                     SV_CONST_PTR(float, 3),
                                     SV_CONST_PTR(float, 4), SV_INT(5),
                                     SV_INT(6), qtrue);
    case CODUO_SYSCALL_CM_BOX_TRACE:
        CM_BoxTrace(SV_PTR(trace_t, 1),
                    SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                    SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5), SV_INT(6),
                    SV_INT(7), qfalse);
        return 0;
    case CODUO_SYSCALL_CM_CAPSULE_TRACE:
        CM_BoxTrace(SV_PTR(trace_t, 1),
                    SV_CONST_PTR(float, 2), SV_CONST_PTR(float, 3),
                    SV_CONST_PTR(float, 4), SV_CONST_PTR(float, 5), SV_INT(6),
                    SV_INT(7), qtrue);
        return 0;
    case CODUO_SYSCALL_CM_BOX_SIGHT_TRACE:
        return CM_BoxSightTrace(0, SV_CONST_PTR(float, 1),
                                SV_CONST_PTR(float, 2),
                                SV_CONST_PTR(float, 3),
                                SV_CONST_PTR(float, 4), SV_INT(5), SV_INT(6),
                                qfalse);
    case CODUO_SYSCALL_CM_CAPSULE_SIGHT_TRACE:
        return CM_BoxSightTrace(0, SV_CONST_PTR(float, 1),
                                SV_CONST_PTR(float, 2),
                                SV_CONST_PTR(float, 3),
                                SV_CONST_PTR(float, 4), SV_INT(5), SV_INT(6),
                                qtrue);
    case CODUO_SYSCALL_LOCATIONAL_TRACE:
        SV_Trace(SV_PTR(trace_t, 1),
                 SV_CONST_PTR(float, 2), NULL, NULL, SV_CONST_PTR(float, 3),
                 SV_INT(4), SV_INT(5), qfalse, qtrue,
                 SV_CONST_PTR(uint8_t, 6), qtrue);
        return 0;
    case CODUO_SYSCALL_POINT_CONTENTS:
        return SV_PointContents(SV_CONST_PTR(float, 1), SV_INT(2), SV_INT(3));
    case CODUO_SYSCALL_IN_PVS:
        return SV_inPVS(SV_CONST_PTR(float, 1), SV_CONST_PTR(float, 2));
    case CODUO_SYSCALL_IN_PVS_IGNORE_PORTALS:
        return SV_inPVSIgnorePortals(SV_CONST_PTR(float, 1),
                                     SV_CONST_PTR(float, 2));
    case CODUO_SYSCALL_IN_SNAPSHOT:
        return SV_inSnapshot(SV_CONST_PTR(float, 1), SV_INT(2));
    case CODUO_SYSCALL_ADJUST_AREA_PORTAL_STATE:
        SV_AdjustAreaPortalState(SV_PTR(sharedEntity_t, 1),
                                 (qboolean)SV_ARG(2));
        return 0;
    case CODUO_SYSCALL_AREAS_CONNECTED:
        return CM_AreasConnected(SV_INT(1), SV_INT(2));
    case CODUO_SYSCALL_LINK_ENTITY:
        SV_LinkEntity(SV_PTR(sharedEntity_t, 1));
        return 0;
    case CODUO_SYSCALL_UNLINK_ENTITY:
        SV_UnlinkEntity(SV_PTR(sharedEntity_t, 1));
        return 0;
    case CODUO_SYSCALL_ENTITIES_IN_BOX:
        return CM_AreaEntities(SV_CONST_PTR(float, 1), SV_CONST_PTR(float, 2),
                               SV_PTR(int32_t, 3),
                               SV_INT(4), SV_INT(5));
    case CODUO_SYSCALL_ENTITY_CONTACT:
        return SV_EntityContact(SV_CONST_PTR(float, 1),
                                SV_CONST_PTR(float, 2),
                                SV_CONST_PTR(sharedEntity_t, 3),
                                qfalse);
    case CODUO_SYSCALL_GET_USERCMD:
        SV_GetUsercmd(SV_INT(1), SV_PTR(usercmd_t, 2));
        return 0;
    case CODUO_SYSCALL_GET_ENTITY_TOKEN: {
        const char *token = Com_Parse(&sv_entityParsePoint);
        Q_strncpyz(SV_PTR(char, 1), token, SV_INT(2));
        return (sv_entityParsePoint == NULL && token[0] == '\0') ? 0 : 1;
    }
    case CODUO_SYSCALL_FS_GET_FILE_LIST:
        return FS_GetFileList(SV_STRING(1), SV_STRING(2), SV_PTR(char, 3),
                              SV_INT(4));
    case CODUO_SYSCALL_MAP_EXISTS:
        return SV_MapExists(SV_STRING(1));
    case CODUO_SYSCALL_REAL_TIME:
        return (intptr_t)Com_RealTime(SV_PTR(qtime_t, 1));
    case CODUO_SYSCALL_SNAP_VECTOR:
        Sys_SnapVector(SV_PTR(float, 1));
        return 0;
    case CODUO_SYSCALL_ENTITY_CONTACT_CAPSULE:
        return SV_EntityContact(SV_CONST_PTR(float, 1),
                                SV_CONST_PTR(float, 2),
                                SV_CONST_PTR(sharedEntity_t, 3),
                                qtrue);
    case CODUO_SYSCALL_COM_SOUND_ALIAS_STRING:
        return (intptr_t)Com_SoundAliasString(
            SV_STRING(1), SV_GAME_SYSCALL_SOUND_ALIAS_LOCALE);
    case CODUO_SYSCALL_COM_PICK_SOUND_ALIAS:
        return (intptr_t)Com_PickSoundAlias(
            SV_STRING(1), SV_GAME_SYSCALL_SOUND_ALIAS_LOCALE, SV_PTR(void, 2));
    case CODUO_SYSCALL_COM_SOUND_ALIAS_INDEX:
        return Com_SoundAliasIndex(SV_CONST_PTR(coduo_sound_alias_t, 1),
                                   SV_GAME_SYSCALL_SOUND_ALIAS_LOCALE);
    case CODUO_SYSCALL_SURFACE_TYPE_FROM_NAME:
        return SurfaceTypeFromName(SV_STRING(1));
    case CODUO_SYSCALL_SURFACE_TYPE_TO_NAME:
        return (intptr_t)SurfaceTypeToName(SV_INT(1));
    case CODUO_SYSCALL_ADD_TEST_CLIENT:
        return SV_AddTestClient();
    case CODUO_SYSCALL_GET_ARCHIVED_CLIENT_INFO:
        return SV_GetArchivedClientInfo(
            SV_INT(1), SV_PTR(int32_t, 2),
            SV_PTR(playerState_t, 3),
            SV_PTR(coduo_archived_client_info_meta_t, 4));
    case CODUO_SYSCALL_ADD_DEBUG_STRING:
    case CODUO_SYSCALL_ADD_DEBUG_LINE:
        return 0;
    case CODUO_SYSCALL_SET_ARCHIVE:
        SV_SetSnapshotArchiveEnabled((qboolean)SV_ARG(1));
        return 0;
    case CODUO_SYSCALL_Z_MALLOC:
        return (intptr_t)Z_Malloc((size_t)SV_ARG(1));
    case CODUO_SYSCALL_Z_FREE:
        Z_Free(SV_PTR(void, 1));
        return 0;
    case CODUO_SYSCALL_XANIM_CREATE_TREE:
        return (intptr_t)Com_XAnimCreateTree(SV_PTR(XAnim, 1));
    case CODUO_SYSCALL_XANIM_CREATE_SMALL_TREE:
        return (intptr_t)Com_XAnimCreateSmallTree(
            SV_PTR(XAnim, 1));
    case CODUO_SYSCALL_XANIM_FREE_SMALL_TREE:
        Com_XAnimFreeSmallTree(SV_PTR(XAnimTree, 1));
        return 0;
    case CODUO_SYSCALL_XMODEL_EXISTS:
        return XModelExists(SV_STRING(1));
    case CODUO_SYSCALL_XMODEL_GET:
        return (intptr_t)SV_XModelGet(SV_STRING(1));
    case CODUO_SYSCALL_DOBJ_CREATE:
        DObjCreateForEntity(SV_PTR(DObjModel, 1), SV_UINT16(2),
                            SV_PTR(XAnimTree, 3), SV_INT(4),
                            SV_UINT16(5));
        return 0;
    case CODUO_SYSCALL_DOBJ_EXISTS:
        return DObjGetForEntity(SV_INT(1)) != NULL;
    case CODUO_SYSCALL_SAFE_DOBJ_FREE:
        DObjFreeForEntity(SV_INT(1), (qboolean)SV_ARG(2));
        return 0;
    case CODUO_SYSCALL_XANIM_GET_ANIMS:
        return (intptr_t)XAnimRuntimeTreeSourceTree(
            SV_PTR(XAnimTree, 1));
    case CODUO_SYSCALL_XANIM_CLEAR_TREE_GOAL_WEIGHTS:
        XAnimClearTreeGoalWeights(SV_PTR(XAnimTree, 1),
                                  SV_INT(2),
                                  coduomp_game_syscall_int_as_float(SV_INT(3)));
        return 0;
    case CODUO_SYSCALL_XANIM_CLEAR_GOAL_WEIGHT:
        XAnimClearGoalWeight(SV_PTR(XAnimTree, 1), SV_INT(2),
                             coduomp_game_syscall_int_as_float(SV_INT(3)));
        return 0;
    case CODUO_SYSCALL_XANIM_CLEAR_TREE_GOAL_WEIGHTS_STRICT:
        XAnimClearTreeGoalWeightsStrict(
            SV_PTR(XAnimTree, 1), SV_INT(2),
            coduomp_game_syscall_int_as_float(SV_INT(3)));
        return 0;
    case CODUO_SYSCALL_XANIM_SET_COMPLETE_GOAL_WEIGHT_KNOB:
        XAnimSetCompleteGoalWeightKnob(
            SV_PTR(XAnimTree, 1), SV_INT(2), SV_INT(3),
            SV_INT(4), SV_INT(5), SV_UINT16(6),
            SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX,
            (qboolean)SV_ARG(7));
        return 0;
    case CODUO_SYSCALL_XANIM_SET_COMPLETE_GOAL_WEIGHT_KNOB_ALL:
        return XAnimSetCompleteGoalWeightKnobAll(
            SV_PTR(XAnimTree, 1), SV_INT(2), SV_INT(3),
            SV_INT(4), SV_INT(5), SV_INT(6), SV_UINT16(7),
            SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX,
            (qboolean)SV_ARG(8));
    case CODUO_SYSCALL_XANIM_SET_ANIM_RATE:
        XAnimSetAnimRate(SV_PTR(XAnimTree, 1), SV_INT(2),
                         SV_INT(3));
        return 0;
    case CODUO_SYSCALL_XANIM_SET_TIME:
        XAnimSetTime(SV_PTR(XAnimTree, 1), SV_INT(2),
                     coduomp_game_syscall_int_as_float(SV_INT(3)));
        return 0;
    case CODUO_SYSCALL_XANIM_SET_GOAL_WEIGHT_KNOB:
        XAnimSetGoalWeightKnob(SV_PTR(XAnimTree, 1),
                               SV_INT(2), SV_INT(3), SV_INT(4), SV_INT(5),
                               SV_UINT16(6),
                               SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX,
                               (qboolean)SV_ARG(7));
        return 0;
    case CODUO_SYSCALL_XANIM_CLEAR_TREE:
        XAnimClearTree(SV_PTR(XAnimTree, 1));
        return 0;
    case CODUO_SYSCALL_XANIM_HAS_TIME:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimHasTime(anims, SV_INT(2));
    case CODUO_SYSCALL_XANIM_IS_PRIMITIVE:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimIsPrimitive(anims, SV_INT(2));
    case CODUO_SYSCALL_XANIM_GET_LENGTH:
        /*
         * VA 0x08090391..0x080903b6 keeps the XAnimGetLength x87
         * result live, multiplies by 1000.0f, then fistp stores with the FPU
         * control word temporarily switched to chop mode.
         */
#if EMULATE_X87
        return x87f_store_i32_trunc(x87f_mul(
            x87f_load_f32((float)SV_GAME_SYSCALL_XANIM_MILLISECONDS),
            coduo_engine_xanim_get_length_x87(
                SV_PTR(XAnim, 1), SV_INT(2))));
#else
        return (int32_t)((long double)SV_GAME_SYSCALL_XANIM_MILLISECONDS *
                         XAnimGetLength(
                             SV_PTR(XAnim, 1), SV_INT(2)));
#endif
    case CODUO_SYSCALL_XANIM_GET_LENGTH_SECONDS:
        anims = Scr_GetAnims(SV_INT(1));
#if EMULATE_X87
        return FloatAsInt(x87f_store_f32(
            coduo_engine_xanim_get_length_x87(anims, SV_INT(2))));
#else
        return FloatAsInt((float)XAnimGetLength(anims, SV_INT(2)));
#endif
    case CODUO_SYSCALL_XANIM_SET_COMPLETE_GOAL_WEIGHT:
        XAnimSetCompleteGoalWeight(SV_PTR(XAnimTree, 1),
                                   SV_INT(2), SV_INT(3), SV_INT(4),
                                   SV_INT(5), SV_UINT16(6),
                                   SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX,
                                   (qboolean)SV_ARG(7));
        return 0;
    case CODUO_SYSCALL_XANIM_SET_GOAL_WEIGHT:
        XAnimSetGoalWeight(SV_PTR(XAnimTree, 1), SV_INT(2),
                           SV_INT(3), SV_INT(4), SV_INT(5), SV_UINT16(6),
                           SV_GAME_SYSCALL_XANIM_ROOT_NODE_INDEX,
                           (qboolean)SV_ARG(7));
        return 0;
    case CODUO_SYSCALL_XANIM_CALC_ABS_DELTA:
        XAnimCalcAbsDelta(SV_PTR(XAnimTree, 1), SV_INT(2),
                          SV_PTR(float, 3), SV_PTR(float, 4));
        return 0;
    case CODUO_SYSCALL_XANIM_CALC_DELTA:
        XAnimCalcDelta(SV_PTR(XAnimTree, 1), SV_INT(2),
                       SV_PTR(float, 3), SV_PTR(float, 4), SV_INT(5));
        return 0;
    case CODUO_SYSCALL_XANIM_GET_REL_DELTA:
        anims = Scr_GetAnims(SV_INT(1));
        XAnimGetRelDelta(anims, SV_INT(2), SV_PTR(float, 3),
                         SV_PTR(float, 4),
                         coduomp_game_syscall_int_as_float(SV_INT(5)),
                         coduomp_game_syscall_int_as_float(SV_INT(6)));
        return 0;
    case CODUO_SYSCALL_XANIM_GET_ABS_DELTA:
        anims = Scr_GetAnims(SV_INT(1));
        XAnimGetAbsDelta(anims, SV_INT(2), SV_PTR(float, 3),
                         SV_PTR(float, 4),
                         coduomp_game_syscall_int_as_float(SV_INT(5)));
        return 0;
    case CODUO_SYSCALL_XANIM_IS_LOOPED:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimIsLooped(anims, SV_INT(2));
    case CODUO_SYSCALL_XANIM_NOTETRACK_EXISTS:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimNotetrackExists(anims, SV_INT(2), SV_UINT16(3));
    case CODUO_SYSCALL_XANIM_GET_TIME:
        return FloatAsInt(
            XAnimGetTime(SV_PTR(XAnimTree, 1), SV_INT(2)));
    case CODUO_SYSCALL_XANIM_GET_WEIGHT:
        return FloatAsInt(
            XAnimGetWeight(SV_PTR(XAnimTree, 1), SV_INT(2)));
    case CODUO_SYSCALL_DOBJ_DUMP_INFO:
        SV_DObjDumpInfo(SV_INT(1));
        return 0;
    case CODUO_SYSCALL_DOBJ_CREATE_SKEL_FOR_BONE:
        return SV_DObjCreateSkelForBone(SV_INT(1), SV_INT(2));
    case CODUO_SYSCALL_DOBJ_CREATE_SKEL_FOR_BONES:
        return SV_DObjCreateSkelForBones(SV_INT(1), SV_CONST_PTR(uint32_t, 2));
    case CODUO_SYSCALL_DOBJ_UPDATE_SERVER_TIME:
        return SV_DObjUpdateServerTime(SV_INT(1),
                                       coduomp_game_syscall_int_as_float(
                                           SV_INT(2)),
                                       (qboolean)SV_ARG(3));
    case CODUO_SYSCALL_DOBJ_INIT_SERVER_TIME:
        SV_DObjInitServerTime(
            SV_INT(1), coduomp_game_syscall_int_as_float(SV_INT(2)));
        return 0;
    case CODUO_SYSCALL_DOBJ_GET_HIERARCHY_BITS:
        SV_DObjGetHierarchyBits(SV_INT(1), SV_INT(2), SV_PTR(uint32_t, 3));
        return 0;
    case CODUO_SYSCALL_DOBJ_CALC_ANIM:
        SV_DObjCalcAnim(SV_INT(1), SV_CONST_PTR(uint32_t, 2));
        return 0;
    case CODUO_SYSCALL_DOBJ_CALC_SKEL:
        SV_DObjCalcSkel(SV_INT(1), SV_CONST_PTR(uint32_t, 2));
        return 0;
    case CODUO_SYSCALL_XANIM_LOAD_ANIM_TREE:
        XAnimLoadAnimTree(SV_PTR(XAnimTree, 1));
        return 0;
    case CODUO_SYSCALL_XANIM_SAVE_ANIM_TREE:
        XAnimSaveAnimTree(SV_PTR(XAnimTree, 1));
        return 0;
    case CODUO_SYSCALL_XANIM_CLONE_ANIM_TREE:
        XAninCloneAnimTree(SV_PTR(XAnimTree, 1),
                          SV_PTR(XAnimTree, 2));
        return 0;
    case CODUO_SYSCALL_DOBJ_NUM_BONES:
        return SV_DObjNumBones(SV_INT(1));
    case CODUO_SYSCALL_DOBJ_GET_BONE_INDEX:
        return SV_DObjGetBoneIndex(SV_INT(1), SV_STRING(2));
    case CODUO_SYSCALL_DOBJ_GET_MATRIX_ARRAY:
        return (intptr_t)SV_DObjGetMatrixArray(SV_INT(1));
    case CODUO_SYSCALL_DOBJ_DISPLAY_ANIM:
        SV_DObjDisplayAnim(SV_INT(1));
        return 0;
    case CODUO_SYSCALL_XANIM_HAS_FINISHED:
        return XAnimHasFinished(SV_PTR(XAnimTree, 1),
                                SV_INT(2));
    case CODUO_SYSCALL_XANIM_GET_NUM_CHILDREN:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimGetNumChildren(anims, SV_INT(2));
    case CODUO_SYSCALL_XANIM_GET_CHILD_AT:
        anims = Scr_GetAnims(SV_INT(1));
        return XAnimGetChildAt(anims, SV_INT(2), SV_INT(3));
    case CODUO_SYSCALL_XMODEL_NUM_BONES:
        return XModelNumBones((XModel *)SV_ARG(1));
    case CODUO_SYSCALL_XMODEL_GET_BONE_NAMES:
        return (intptr_t)XModelBoneNames((XModel *)SV_ARG(1));
    case CODUO_SYSCALL_DOBJ_GET_ROT_TRANS_ARRAY:
        return (intptr_t)SV_DObjGetRotTransArray(SV_INT(1));
    case CODUO_SYSCALL_DOBJ_SET_ROT_TRANS_INDEX:
        return SV_DObjSetRotTransIndex(SV_INT(1), SV_CONST_PTR(uint32_t, 2),
                                       SV_INT(3));
    case CODUO_SYSCALL_DOBJ_SET_CONTROL_ROT_TRANS_INDEX:
        return SV_DObjSetControlRotTransIndex(SV_INT(1),
                                              SV_CONST_PTR(uint32_t, 2),
                                              SV_INT(3));
    case CODUO_SYSCALL_DOBJ_GET_BOUNDS:
        SV_DObjGetBounds(SV_INT(1), SV_PTR(float, 2), SV_PTR(float, 3));
        return 0;
    case CODUO_SYSCALL_XANIM_GET_ANIM_NAME:
        anims = Scr_GetAnims(SV_INT(1));
        return (intptr_t)XAnimGetAnimName(anims, SV_INT(2));
    case CODUO_SYSCALL_DOBJ_GET_TREE:
        return (intptr_t)SV_DObjGetTree(SV_INT(1));
    case CODUO_SYSCALL_XANIM_GET_ANIM_TREE_SIZE:
        return XAnimGetAnimTreeSize(SV_PTR(XAnim, 1));
    case CODUO_SYSCALL_XMODEL_DEBUG_BOXES:
        SV_XModelDebugBoxes(SV_INT(1));
        return 0;
    case CODUO_SYSCALL_GET_WEAPON_INFO_MEMORY:
        return (intptr_t)GetWeaponInfoMemory(
            SV_INT(1), SV_PTR(int32_t, 2),
            SV_GAME_SYSCALL_WEAPON_MEMORY_STATE);
    case CODUO_SYSCALL_FREE_WEAPON_INFO_MEMORY:
        FreeWeaponInfoMemory(SV_GAME_SYSCALL_WEAPON_MEMORY_STATE,
                             (qboolean)SV_ARG(1));
        return 0;
    case CODUO_SYSCALL_FREE_CLIENT_SCRIPT_PERS:
        SV_FreeClientScriptPers();
        return 0;
    case CODUO_SYSCALL_RESET_ENTITY_PARSE_POINT:
        SV_ResetEntityParsePoint();
        return 0;
    case CODUO_SYSCALL_MEMSET:
        memset(SV_PTR(void, 1), SV_INT(2), (size_t)SV_ARG(3));
        return 0;
    case CODUO_SYSCALL_MEMCPY:
        memcpy(SV_PTR(void, 1), SV_CONST_PTR(void, 2), (size_t)SV_ARG(3));
        return 0;
    case CODUO_SYSCALL_STRNCPY:
        /* Stock passes this signed syscall slot as a raw 32-bit size_t. */
        return (intptr_t)strncpy(SV_PTR(char, 1), SV_STRING(2),
                                 (size_t)(uint32_t)SV_ARG(3));
    case CODUO_SYSCALL_SIN:
        return FloatAsInt(
            (float)sin((double)coduomp_game_syscall_int_as_float(SV_INT(1))));
    case CODUO_SYSCALL_COS:
        return FloatAsInt(
            (float)cos((double)coduomp_game_syscall_int_as_float(SV_INT(1))));
    case CODUO_SYSCALL_ATAN2:
        return FloatAsInt(
            (float)atan2(
                (double)coduomp_game_syscall_int_as_float(SV_INT(1)),
                (double)coduomp_game_syscall_int_as_float(SV_INT(2))));
    case CODUO_SYSCALL_SQRT:
        return FloatAsInt(
            (float)sqrt(
                (double)coduomp_game_syscall_int_as_float(SV_INT(1))));
    case CODUO_SYSCALL_MATRIX_MULTIPLY:
        MatrixMultiply(SV_PTR(vec3_t, 1), SV_PTR(vec3_t, 2),
                       SV_PTR(vec3_t, 3));
        return 0;
    case CODUO_SYSCALL_ANGLE_VECTORS:
        AngleVectors(SV_CONST_PTR(float, 1), SV_PTR(float, 2),
                     SV_PTR(float, 3), SV_PTR(float, 4));
        return 0;
    case CODUO_SYSCALL_PERPENDICULAR_VECTOR:
        PerpendicularVector(SV_PTR(float, 1), SV_CONST_PTR(float, 2));
        return 0;
    case CODUO_SYSCALL_FLOOR:
        return FloatAsInt(
            (float)floor(
                (double)coduomp_game_syscall_int_as_float(SV_INT(1))));
    case CODUO_SYSCALL_CEIL:
        return FloatAsInt(
            (float)ceil(
                (double)coduomp_game_syscall_int_as_float(SV_INT(1))));
    default:
        Com_Error(SV_GAME_SYSCALL_COM_ERROR_DROP, svGameSyscallBadTrapError,
                  syscall);
        return -1;
    }
}

#undef SV_UINT16
#undef SV_SIZE
#undef SV_INT
#undef SV_STRING
#undef SV_CONST_PTR
#undef SV_PTR
#undef SV_ARG
