Merge remote-tracking branch 'gzdoom/master' into big_beautiful_merge
This commit is contained in:
commit
3fdd22ef91
1433 changed files with 484787 additions and 9566 deletions
|
|
@ -289,8 +289,8 @@ endif()
|
|||
|
||||
# Check for functions that may or may not exist.
|
||||
|
||||
require_stricmp()
|
||||
require_strnicmp()
|
||||
gz_require_stricmp()
|
||||
gz_require_strnicmp()
|
||||
|
||||
if( NOT MSVC )
|
||||
add_definitions( -D__forceinline=inline )
|
||||
|
|
@ -328,32 +328,8 @@ endif()
|
|||
list( APPEND PROJECT_LIBRARIES "zwidget" )
|
||||
list( APPEND PROJECT_LIBRARIES "webp" )
|
||||
|
||||
# ZMUSIC
|
||||
|
||||
if( MSVC )
|
||||
find_package( ZMusic )
|
||||
else()
|
||||
find_package( ZMusic REQUIRED )
|
||||
endif()
|
||||
|
||||
message("Building for target architecture: ${TARGET_ARCHITECTURE}")
|
||||
|
||||
if( MSVC AND NOT ZMUSIC_FOUND )
|
||||
# Use prebuilt library
|
||||
set( ZMUSIC_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../bin/windows/zmusic" )
|
||||
set( ZMUSIC_INCLUDE_DIR ${ZMUSIC_ROOT_PATH}/include )
|
||||
set( ZMUSIC_LIBRARIES zmusic )
|
||||
if( X64 )
|
||||
link_directories( ${ZMUSIC_ROOT_PATH}/64bit )
|
||||
elseif( ARM64 )
|
||||
link_directories( ${ZMUSIC_ROOT_PATH}/arm64 )
|
||||
else()
|
||||
link_directories( ${ZMUSIC_ROOT_PATH}/32bit )
|
||||
endif()
|
||||
set( ZMUSIC_FOUND TRUE )
|
||||
endif()
|
||||
|
||||
|
||||
# VPX
|
||||
|
||||
if( MSVC AND NOT VPX_FOUND )
|
||||
|
|
@ -392,6 +368,13 @@ if( ${HAVE_VM_JIT} )
|
|||
set( PROJECT_LIBRARIES ${PROJECT_LIBRARIES} "${ASMJIT_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
if ( CPPDAP_FOUND )
|
||||
include_directories( SYSTEM "${CPPDAP_INCLUDE_DIR}" )
|
||||
set( PROJECT_LIBRARIES ${PROJECT_LIBRARIES} "${CPPDAP_LIBRARIES}")
|
||||
else()
|
||||
message( SEND_ERROR "Could not find cppdap" )
|
||||
endif()
|
||||
|
||||
# Start defining source files for ZDoom
|
||||
set( PLAT_WIN32_SOURCES
|
||||
win32/i_steam.cpp
|
||||
|
|
@ -602,6 +585,9 @@ file( GLOB HEADER_FILES
|
|||
common/rendering/vulkan/textures/*.h
|
||||
common/scripting/core/*h
|
||||
common/scripting/vm/*h
|
||||
common/scripting/dap/*.h
|
||||
common/scripting/dap/Nodes/*.h
|
||||
common/scripting/dap/Protocol/*.h
|
||||
common/scripting/jit/*h
|
||||
common/scripting/interface/*.h
|
||||
common/scripting/backend/*.h
|
||||
|
|
@ -961,6 +947,7 @@ set (PCH_SOURCES
|
|||
launcher/launcherbuttonbar.cpp
|
||||
launcher/playgamepage.cpp
|
||||
launcher/settingspage.cpp
|
||||
launcher/networkpage.cpp
|
||||
|
||||
common/audio/sound/i_sound.cpp
|
||||
common/audio/sound/oalsound.cpp
|
||||
|
|
@ -1058,7 +1045,7 @@ set (PCH_SOURCES
|
|||
common/cutscenes/movieplayer.cpp
|
||||
common/cutscenes/screenjob.cpp
|
||||
common/utility/engineerrors.cpp
|
||||
common/utility/i_module.cpp
|
||||
|
||||
common/utility/gitinfo.cpp
|
||||
common/utility/m_alloc.cpp
|
||||
common/utility/utf8.cpp
|
||||
|
|
@ -1089,6 +1076,7 @@ set (PCH_SOURCES
|
|||
common/engine/palettecontainer.cpp
|
||||
common/engine/stringtable.cpp
|
||||
common/engine/i_net.cpp
|
||||
common/engine/i_protocol.cpp
|
||||
common/engine/i_interface.cpp
|
||||
common/engine/renderstyle.cpp
|
||||
common/engine/v_colortables.cpp
|
||||
|
|
@ -1112,6 +1100,7 @@ set (PCH_SOURCES
|
|||
common/rendering/v_video.cpp
|
||||
common/rendering/r_thread.cpp
|
||||
common/rendering/r_videoscale.cpp
|
||||
common/rendering/stb_include.cpp
|
||||
common/rendering/hwrenderer/hw_draw2d.cpp
|
||||
common/rendering/hwrenderer/data/hw_clock.cpp
|
||||
common/rendering/hwrenderer/data/hw_skydome.cpp
|
||||
|
|
@ -1147,7 +1136,30 @@ set (PCH_SOURCES
|
|||
common/scripting/frontend/zcc_parser.cpp
|
||||
common/scripting/backend/vmbuilder.cpp
|
||||
common/scripting/backend/codegen.cpp
|
||||
|
||||
common/scripting/dap/BreakpointManager.cpp
|
||||
common/scripting/dap/DebugExecutionManager.cpp
|
||||
common/scripting/dap/DebugServer.cpp
|
||||
common/scripting/dap/IdHandleBase.cpp
|
||||
common/scripting/dap/IdMap.cpp
|
||||
common/scripting/dap/IdProvider.cpp
|
||||
common/scripting/dap/Nodes/ArrayStateNode.cpp
|
||||
common/scripting/dap/Nodes/CVarScopeStateNode.cpp
|
||||
common/scripting/dap/Nodes/LocalScopeStateNode.cpp
|
||||
common/scripting/dap/Nodes/GlobalScopeStateNode.cpp
|
||||
common/scripting/dap/Nodes/ObjectStateNode.cpp
|
||||
common/scripting/dap/Nodes/StackFrameStateNode.cpp
|
||||
common/scripting/dap/Nodes/StackStateNode.cpp
|
||||
common/scripting/dap/Nodes/StateNodeBase.cpp
|
||||
common/scripting/dap/Nodes/StructStateNode.cpp
|
||||
common/scripting/dap/Nodes/ValueStateNode.cpp
|
||||
common/scripting/dap/PexCache.cpp
|
||||
common/scripting/dap/Protocol/struct_extensions.cpp
|
||||
common/scripting/dap/RuntimeEvents.cpp
|
||||
common/scripting/dap/RuntimeState.cpp
|
||||
common/scripting/dap/ZScriptDebugger.cpp
|
||||
common/scripting/dap/Nodes/RegistersScopeStateNode.cpp
|
||||
common/scripting/dap/Nodes/StatePointerNode.cpp
|
||||
common/scripting/dap/Nodes/DummyNode.cpp
|
||||
utility/nodebuilder/nodebuild.cpp
|
||||
utility/nodebuilder/nodebuild_classify_nosse2.cpp
|
||||
utility/nodebuilder/nodebuild_events.cpp
|
||||
|
|
@ -1156,6 +1168,10 @@ set (PCH_SOURCES
|
|||
utility/nodebuilder/nodebuild_utility.cpp
|
||||
)
|
||||
|
||||
if( ZMUSIC_SYSTEM_INSTALL OR NOT DEFINED BUILD_SHARED_LIBS OR BUILD_SHARED_LIBS)
|
||||
set ( PCH_SOURCES ${PCH_SOURCES} common/utility/i_module.cpp )
|
||||
endif()
|
||||
|
||||
if( ${HAVE_VM_JIT} )
|
||||
set( PCH_SOURCES ${PCH_SOURCES} ${VM_JIT_SOURCES} )
|
||||
else()
|
||||
|
|
@ -1319,6 +1335,7 @@ include_directories(
|
|||
../libraries/ZVulkan/include
|
||||
../libraries/ZWidget/include
|
||||
../libraries/webp/include
|
||||
../libraries/range_map/include
|
||||
${SYSTEM_SOURCES_DIR}
|
||||
)
|
||||
|
||||
|
|
@ -1430,6 +1447,13 @@ add_custom_command(TARGET zdoom POST_BUILD
|
|||
${CMAKE_SOURCE_DIR}/fm_banks/gs-by-papiezak-and-sneakernets.wopn $<TARGET_FILE_DIR:zdoom>/fm_banks/gs-by-papiezak-and-sneakernets.wopn
|
||||
)
|
||||
|
||||
if ( NOT APPLE AND NOT ZMUSIC_SYSTEM_INSTALL)
|
||||
# bring zmusic in
|
||||
add_custom_command(TARGET zdoom POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:zmusic> $<TARGET_FILE_DIR:zdoom>
|
||||
DEPENDS zmusic )
|
||||
endif()
|
||||
|
||||
if( WIN32 )
|
||||
set( INSTALL_SOUNDFONT_PATH . CACHE STRING "Directory where soundfonts and WOPL/WOPN banks will be placed during install." )
|
||||
else()
|
||||
|
|
@ -1439,6 +1463,26 @@ install(DIRECTORY "${PROJECT_BINARY_DIR}/soundfonts" "${PROJECT_BINARY_DIR}/fm_b
|
|||
DESTINATION ${INSTALL_SOUNDFONT_PATH}
|
||||
COMPONENT "Soundfont resources")
|
||||
|
||||
# Install Linux desktop launcher, icon and metadata
|
||||
if( UNIX AND NOT APPLE )
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/posix/freedesktop/org.zdoom.GZDoom.desktop
|
||||
DESTINATION share/applications
|
||||
PERMISSIONS WORLD_EXECUTE WORLD_READ GROUP_READ GROUP_EXECUTE OWNER_READ OWNER_WRITE OWNER_EXECUTE)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/posix/freedesktop/org.zdoom.GZDoom.metainfo.xml
|
||||
DESTINATION share/metainfo
|
||||
PERMISSIONS WORLD_READ GROUP_READ GROUP_EXECUTE OWNER_READ OWNER_WRITE)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/posix/freedesktop/org.zdoom.GZDoom.svg
|
||||
DESTINATION share/icons/hicolor/scalable/apps
|
||||
PERMISSIONS WORLD_READ GROUP_READ GROUP_EXECUTE OWNER_READ OWNER_WRITE)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/posix/freedesktop/org.zdoom.GZDoom-mime.xml
|
||||
DESTINATION share/mime/packages
|
||||
PERMISSIONS WORLD_READ GROUP_READ GROUP_EXECUTE OWNER_READ OWNER_WRITE)
|
||||
# note:
|
||||
# A post-install script would most likely need to run the following to register the desktop and mime files with the system
|
||||
# update-mime-database /usr/local/share/mime
|
||||
# update-desktop-database
|
||||
endif()
|
||||
|
||||
if( DEM_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||
# Need to enable intrinsics for these files.
|
||||
set_property( SOURCE
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ CCMD(togglemap)
|
|||
{
|
||||
if (gameaction == ga_nothing)
|
||||
{
|
||||
gameaction = ga_togglemap;
|
||||
AM_ToggleMap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1313,7 +1313,7 @@ void DAutomap::changeWindowLoc ()
|
|||
|
||||
void DAutomap::startDisplay()
|
||||
{
|
||||
int pnum;
|
||||
unsigned int pnum;
|
||||
|
||||
f_oldloc.x = FLT_MAX;
|
||||
amclock = 0;
|
||||
|
|
@ -2815,7 +2815,7 @@ void DAutomap::drawPlayers ()
|
|||
|
||||
mpoint_t pt;
|
||||
DAngle angle;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
if (!multiplayer)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include "v_video.h"
|
||||
|
||||
void G_SetMap(const char* mapname, int mode);
|
||||
void D_SingleTick();
|
||||
|
||||
LightmapCmdletGroup::LightmapCmdletGroup()
|
||||
{
|
||||
|
|
@ -37,7 +36,9 @@ void LightmapBuildCmdlet::OnCommand(FArgs args)
|
|||
G_SetMap(mapname.GetChars(), 0);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
#ifdef NEEDS_BIG_BEAUTIFUL_MERGE_PORTING
|
||||
D_SingleTick();
|
||||
#endif
|
||||
if (gameaction == ga_nothing)
|
||||
break;
|
||||
}
|
||||
|
|
@ -125,7 +126,9 @@ void LightmapDeleteCmdlet::OnCommand(FArgs args)
|
|||
G_SetMap(mapname.GetChars(), 0);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
#ifdef NEEDS_BIG_BEAUTIFUL_MERGE_PORTING
|
||||
D_SingleTick();
|
||||
#endif
|
||||
if (gameaction == ga_nothing)
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1840,6 +1840,51 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawLineFrame)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetTextureWidth)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(textureId);
|
||||
PARAM_BOOL(animated);
|
||||
|
||||
FGameTexture *tex = textureId >= 1 ? TexMan.GetGameTexture(FSetTextureID(textureId), animated) : nullptr;
|
||||
|
||||
ACTION_RETURN_FLOAT(tex ? tex->GetDisplayWidth() : -1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetTextureHeight)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(textureId);
|
||||
PARAM_BOOL(animated);
|
||||
|
||||
FGameTexture *tex = textureId >= 1 ? TexMan.GetGameTexture(FSetTextureID(textureId), animated) : nullptr;
|
||||
|
||||
ACTION_RETURN_FLOAT(tex ? tex->GetDisplayHeight() : -1);
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetTextureLeftOffset)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(textureId);
|
||||
PARAM_BOOL(animated);
|
||||
|
||||
FGameTexture *tex = textureId >= 1 ? TexMan.GetGameTexture(FSetTextureID(textureId), animated) : nullptr;
|
||||
|
||||
ACTION_RETURN_FLOAT(tex ? tex->GetDisplayLeftOffset() : -1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetTextureTopOffset)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(textureId);
|
||||
PARAM_BOOL(animated);
|
||||
|
||||
FGameTexture *tex = textureId >= 1 ? TexMan.GetGameTexture(FSetTextureID(textureId), animated) : nullptr;
|
||||
|
||||
ACTION_RETURN_FLOAT(tex ? tex->GetDisplayTopOffset() : -1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FCanvas, DrawLineFrame)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(FCanvas);
|
||||
|
|
|
|||
|
|
@ -647,5 +647,6 @@ void PerformWipe(FTexture* startimg, FTexture* endimg, int wipe_type, bool stops
|
|||
delete wiper;
|
||||
I_FreezeTime(false);
|
||||
GSnd->SetSfxPaused(false, 1);
|
||||
ClearPrevTime(); // Make sure this isn't tracked as a part of the delta time.
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ EXTERN_CVAR(Float, snd_musicvolume)
|
|||
EXTERN_CVAR(Int, snd_mididevice)
|
||||
EXTERN_CVAR(Float, mod_dumb_mastervolume)
|
||||
EXTERN_CVAR(Float, fluid_gain)
|
||||
EXTERN_CVAR(Float, opl_gain)
|
||||
EXTERN_CVAR(Float, adl_gain)
|
||||
EXTERN_CVAR(Float, opn_gain)
|
||||
|
||||
|
||||
CVAR(Bool, mus_calcgain, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // changing this will only take effect for the next song.
|
||||
|
|
@ -173,10 +176,16 @@ static TArray<SoundStream*> customStreams;
|
|||
|
||||
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, MusicCustomStreamType sampletype, StreamCallback cb, void *userdata)
|
||||
{
|
||||
int flags = 0;
|
||||
if (numchannels < 2) flags |= SoundStream::Mono;
|
||||
if (sampletype == MusicSamplesFloat) flags |= SoundStream::Float;
|
||||
auto stream = GSnd->CreateStream(cb, int(size), flags, samplerate, userdata);
|
||||
ChannelConfig chans;
|
||||
if(numchannels == 1)
|
||||
chans = ChannelConfig_Mono;
|
||||
else if(numchannels == 2)
|
||||
chans = ChannelConfig_Stereo;
|
||||
else
|
||||
return nullptr;
|
||||
const SampleType stype{(sampletype == MusicSamplesFloat) ? SampleType_Float32 : SampleType_Int16};
|
||||
|
||||
auto stream = GSnd->CreateStream(cb, int(size), stype, chans, samplerate, userdata);
|
||||
if (stream)
|
||||
{
|
||||
stream->Play(true, 1);
|
||||
|
|
@ -248,17 +257,14 @@ static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
|||
void S_CreateStream()
|
||||
{
|
||||
if (!mus_playing.handle) return;
|
||||
SoundStreamInfo fmt;
|
||||
ZMusic_GetStreamInfo(mus_playing.handle, &fmt);
|
||||
// always create a floating point streaming buffer so we can apply replay gain without risk of integer overflows.
|
||||
mus_playing.isfloat = fmt.mNumChannels > 0;
|
||||
SoundStreamInfoEx fmt;
|
||||
ZMusic_GetStreamInfoEx(mus_playing.handle, &fmt);
|
||||
mus_playing.isfloat = fmt.mSampleType == SampleType_Float32;
|
||||
if (!mus_playing.isfloat) fmt.mBufferSize *= 2;
|
||||
if (fmt.mBufferSize > 0) // if buffer size is 0 the library will play the song itself (e.g. Windows system synth.)
|
||||
{
|
||||
int flags = SoundStream::Float;
|
||||
if (abs(fmt.mNumChannels) < 2) flags |= SoundStream::Mono;
|
||||
|
||||
musicStream.reset(GSnd->CreateStream(FillStream, fmt.mBufferSize, flags, fmt.mSampleRate, nullptr));
|
||||
// always create a floating point streaming buffer so we can apply replay gain without risk of integer overflows.
|
||||
musicStream.reset(GSnd->CreateStream(FillStream, fmt.mBufferSize, SampleType_Float32, fmt.mChannelConfig, fmt.mSampleRate, nullptr));
|
||||
if (musicStream) musicStream->Play(true, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -444,6 +450,7 @@ EXTERN_CVAR(String, wildmidi_config)
|
|||
EXTERN_CVAR(String, adl_custom_bank)
|
||||
EXTERN_CVAR(Int, adl_bank)
|
||||
EXTERN_CVAR(Bool, adl_use_custom_bank)
|
||||
EXTERN_CVAR(Bool, adl_use_genmidi)
|
||||
EXTERN_CVAR(String, opn_custom_bank)
|
||||
EXTERN_CVAR(Bool, opn_use_custom_bank)
|
||||
EXTERN_CVAR(Int, opl_core)
|
||||
|
|
@ -599,17 +606,14 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const
|
|||
return; // unable to open
|
||||
}
|
||||
|
||||
SoundStreamInfo fmt;
|
||||
ZMusic_GetStreamInfo(handle, &fmt);
|
||||
SoundStreamInfoEx fmt;
|
||||
ZMusic_GetStreamInfoEx(handle, &fmt);
|
||||
if (fmt.mBufferSize == 0)
|
||||
{
|
||||
ZMusic_Close(handle);
|
||||
return; // external player.
|
||||
}
|
||||
|
||||
int flags = SoundStream::Float;
|
||||
if (abs(fmt.mNumChannels) < 2) flags |= SoundStream::Mono;
|
||||
|
||||
TArray<uint8_t> readbuffer(fmt.mBufferSize, true);
|
||||
TArray<float> lbuffer;
|
||||
TArray<float> rbuffer;
|
||||
|
|
@ -617,7 +621,7 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const
|
|||
{
|
||||
unsigned index;
|
||||
// 4 cases, all with different preparation needs.
|
||||
if (fmt.mNumChannels == -2) // 16 bit stereo
|
||||
if (fmt.mSampleType == SampleType_Int16 && fmt.mChannelConfig == ChannelConfig_Stereo) // 16 bit stereo
|
||||
{
|
||||
int16_t* sbuf = (int16_t*)readbuffer.Data();
|
||||
int numsamples = fmt.mBufferSize / 4;
|
||||
|
|
@ -630,7 +634,7 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const
|
|||
rbuffer[index + i] = sbuf[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
else if (fmt.mNumChannels == -1) // 16 bit mono
|
||||
else if (fmt.mSampleType == SampleType_Int16 && fmt.mChannelConfig == ChannelConfig_Mono) // 16 bit mono
|
||||
{
|
||||
int16_t* sbuf = (int16_t*)readbuffer.Data();
|
||||
int numsamples = fmt.mBufferSize / 2;
|
||||
|
|
@ -641,7 +645,7 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const
|
|||
lbuffer[index + i] = sbuf[i];
|
||||
}
|
||||
}
|
||||
else if (fmt.mNumChannels == 1) // float mono
|
||||
else if (fmt.mSampleType == SampleType_Float32 && fmt.mChannelConfig == ChannelConfig_Mono) // float mono
|
||||
{
|
||||
float* sbuf = (float*)readbuffer.Data();
|
||||
int numsamples = fmt.mBufferSize / 4;
|
||||
|
|
@ -651,7 +655,7 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const
|
|||
lbuffer[index + i] = sbuf[i] * 32768.f;
|
||||
}
|
||||
}
|
||||
else if (fmt.mNumChannels == 2) // float stereo
|
||||
else if (fmt.mSampleType == SampleType_Float32 && fmt.mChannelConfig == ChannelConfig_Stereo) // float stereo
|
||||
{
|
||||
float* sbuf = (float*)readbuffer.Data();
|
||||
int numsamples = fmt.mBufferSize / 8;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,11 @@ CUSTOM_CVAR(Bool, adl_use_custom_bank, false, CVAR_ARCHIVE | CVAR_VIRTUAL)
|
|||
FORWARD_BOOL_CVAR(adl_use_custom_bank);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, adl_use_genmidi, false, CVAR_ARCHIVE | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(adl_use_genmidi);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_VIRTUAL | CVAR_SYSTEM_ONLY)
|
||||
{
|
||||
FORWARD_STRING_CVAR(adl_custom_bank);
|
||||
|
|
@ -104,6 +109,30 @@ CUSTOM_CVAR(Int, adl_volume_model, 0 /*ADLMIDI_VolumeModel_AUTO*/, CVAR_ARCHIVE
|
|||
{
|
||||
FORWARD_CVAR(adl_volume_model);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, adl_chan_alloc, 0 /*ADLMIDI_ChanAlloc_AUTO*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(adl_chan_alloc);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, adl_auto_arpeggio, false, CVAR_ARCHIVE | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(adl_auto_arpeggio);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, adl_gain, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
if (!mus_usereplaygain)
|
||||
{
|
||||
FORWARD_CVAR(adl_gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replay gain will disable the user setting for consistency.
|
||||
float newval;
|
||||
ChangeMusicSetting(zmusic_adl_gain, mus_playing.handle, 1.0f, & newval);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
@ -233,6 +262,20 @@ CUSTOM_CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIR
|
|||
FORWARD_BOOL_CVAR(opl_fullpan);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, opl_gain, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
if (!mus_usereplaygain)
|
||||
{
|
||||
FORWARD_CVAR(opl_gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replay gain will disable the user setting for consistency.
|
||||
float newval;
|
||||
ChangeMusicSetting(zmusic_opl_gain, mus_playing.handle, 1.0f, & newval);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ZMUSIC_LITE
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
@ -271,6 +314,34 @@ CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_VIRTUAL)
|
|||
FORWARD_STRING_CVAR(opn_custom_bank);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, opn_volume_model, 0 /*OPNMIDI_VolumeModel_AUTO*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(adl_volume_model);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, opn_chan_alloc, -1 /*OPNMIDI_ChanAlloc_AUTO*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(adl_chan_alloc);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, opn_auto_arpeggio, false, CVAR_ARCHIVE | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(adl_auto_arpeggio);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, opn_gain, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
if (!mus_usereplaygain)
|
||||
{
|
||||
FORWARD_CVAR(opn_gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replay gain will disable the user setting for consistency.
|
||||
float newval;
|
||||
ChangeMusicSetting(zmusic_opn_gain, mus_playing.handle, 1.0f, & newval);
|
||||
}
|
||||
}
|
||||
//==========================================================================
|
||||
//
|
||||
// GUS MIDI device
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public:
|
|||
}
|
||||
|
||||
// Streaming sounds.
|
||||
SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
||||
SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, SampleType stype, ChannelConfig chans, int samplerate, void *userdata)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -303,7 +303,8 @@ const char *GetSampleTypeName(SampleType type)
|
|||
{
|
||||
case SampleType_UInt8: return "Unsigned 8-bit";
|
||||
case SampleType_Int16: return "Signed 16-bit";
|
||||
default: break;
|
||||
case SampleType_Float32: return "32-bit float";
|
||||
default: break;
|
||||
}
|
||||
return "(invalid sample type)";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,17 +65,6 @@ class SoundStream
|
|||
public:
|
||||
virtual ~SoundStream() = default;
|
||||
|
||||
enum
|
||||
{ // For CreateStream
|
||||
Mono = 1,
|
||||
Bits8 = 2,
|
||||
Bits32 = 4,
|
||||
Float = 8,
|
||||
|
||||
// For OpenStream
|
||||
Loop = 16
|
||||
};
|
||||
|
||||
struct Position {
|
||||
uint64_t samplesplayed;
|
||||
std::chrono::nanoseconds latency;
|
||||
|
|
@ -114,7 +103,7 @@ public:
|
|||
virtual float GetOutputRate() = 0;
|
||||
|
||||
// Streaming sounds.
|
||||
virtual SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) = 0;
|
||||
virtual SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, SampleType stype, ChannelConfig chans, int samplerate, void *userdata) = 0;
|
||||
|
||||
// Starts a sound.
|
||||
virtual FISoundChannel *StartSound (SoundHandle sfx, float vol, float pitch, int chanflags, FISoundChannel *reuse_chan, float startTime = 0.f) = 0;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,47 @@ EXTERN_CVAR (Int, snd_hrtf)
|
|||
#define GET_PTRID(x) ((uint32_t)(uintptr_t)(x))
|
||||
|
||||
|
||||
static constexpr uint8_t SampleTypeSize(SampleType stype)
|
||||
{
|
||||
switch(stype)
|
||||
{
|
||||
case SampleType_UInt8: return sizeof(uint8_t);
|
||||
case SampleType_Int16: return sizeof(int16_t);
|
||||
case SampleType_Float32: return sizeof(float);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr uint8_t ChannelCount(ChannelConfig chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case ChannelConfig_Mono: return 1;
|
||||
case ChannelConfig_Stereo: return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr ALenum GetFormat(SampleType stype, ChannelConfig chans)
|
||||
{
|
||||
if(stype == SampleType_UInt8)
|
||||
{
|
||||
if(chans == ChannelConfig_Mono) return AL_FORMAT_MONO8;
|
||||
if(chans == ChannelConfig_Stereo) return AL_FORMAT_STEREO8;
|
||||
}
|
||||
if(stype == SampleType_Int16)
|
||||
{
|
||||
if(chans == ChannelConfig_Mono) return AL_FORMAT_MONO16;
|
||||
if(chans == ChannelConfig_Stereo) return AL_FORMAT_STEREO16;
|
||||
}
|
||||
if(stype == SampleType_Float32)
|
||||
{
|
||||
if(chans == ChannelConfig_Mono) return AL_FORMAT_MONO_FLOAT32;
|
||||
if(chans == ChannelConfig_Stereo) return AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
static ALenum checkALError(const char *fn, unsigned int ln)
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
|
|
@ -461,7 +502,7 @@ public:
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
||||
bool Init(SoundStreamCallback callback, int buffbytes, SampleType stype, ChannelConfig chans, int samplerate, void *userdata)
|
||||
{
|
||||
if(!SetupSource())
|
||||
return false;
|
||||
|
|
@ -470,47 +511,15 @@ public:
|
|||
UserData = userdata;
|
||||
SampleRate = samplerate;
|
||||
|
||||
Format = AL_NONE;
|
||||
if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */
|
||||
{
|
||||
if((flags&Mono)) Format = AL_FORMAT_MONO8;
|
||||
else Format = AL_FORMAT_STEREO8;
|
||||
}
|
||||
else if((flags&Float))
|
||||
{
|
||||
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
{
|
||||
if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32;
|
||||
else Format = AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
}
|
||||
else if((flags&Bits32))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if((flags&Mono)) Format = AL_FORMAT_MONO16;
|
||||
else Format = AL_FORMAT_STEREO16;
|
||||
}
|
||||
|
||||
Format = GetFormat(stype, chans);
|
||||
if(Format == AL_NONE)
|
||||
{
|
||||
Printf("Unsupported format: 0x%x\n", flags);
|
||||
Printf("Unhandled audio format: %s, %s\n", GetChannelConfigName(chans),
|
||||
GetSampleTypeName(stype));
|
||||
return false;
|
||||
}
|
||||
|
||||
FrameSize = 1;
|
||||
if((flags&Bits8))
|
||||
FrameSize *= 1;
|
||||
else if((flags&(Bits32|Float)))
|
||||
FrameSize *= 4;
|
||||
else
|
||||
FrameSize *= 2;
|
||||
|
||||
if((flags&Mono))
|
||||
FrameSize *= 1;
|
||||
else
|
||||
FrameSize *= 2;
|
||||
FrameSize = SampleTypeSize(stype) * ChannelCount(chans);
|
||||
|
||||
buffbytes += FrameSize-1;
|
||||
buffbytes -= buffbytes%FrameSize;
|
||||
|
|
@ -526,16 +535,6 @@ public:
|
|||
|
||||
#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */
|
||||
|
||||
static size_t GetChannelCount(ChannelConfig chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case ChannelConfig_Mono: return 1;
|
||||
case ChannelConfig_Stereo: return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float GetRolloff(const FRolloffInfo *rolloff, float distance)
|
||||
{
|
||||
return soundEngine->GetRolloff(rolloff, distance);
|
||||
|
|
@ -1105,10 +1104,6 @@ SoundHandle OpenALSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, int length, int
|
|||
SoundHandle OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, int def_loop_start, int def_loop_end)
|
||||
{
|
||||
SoundHandle retval = { NULL };
|
||||
ALenum format = AL_NONE;
|
||||
ChannelConfig chans;
|
||||
SampleType type;
|
||||
int srate;
|
||||
uint32_t loop_start = 0, loop_end = ~0u;
|
||||
zmusic_bool startass = false, endass = false;
|
||||
|
||||
|
|
@ -1126,19 +1121,12 @@ SoundHandle OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, int def
|
|||
if (!decoder)
|
||||
return retval;
|
||||
|
||||
ChannelConfig chans;
|
||||
SampleType type;
|
||||
int srate;
|
||||
SoundDecoder_GetInfo(decoder, &srate, &chans, &type);
|
||||
int samplesize = 1;
|
||||
if (chans == ChannelConfig_Mono)
|
||||
{
|
||||
if (type == SampleType_UInt8) format = AL_FORMAT_MONO8, samplesize = 1;
|
||||
if (type == SampleType_Int16) format = AL_FORMAT_MONO16, samplesize = 2;
|
||||
}
|
||||
else if (chans == ChannelConfig_Stereo)
|
||||
{
|
||||
if (type == SampleType_UInt8) format = AL_FORMAT_STEREO8, samplesize = 2;
|
||||
if (type == SampleType_Int16) format = AL_FORMAT_STEREO16, samplesize = 4;
|
||||
}
|
||||
|
||||
ALenum format = GetFormat(type, chans);
|
||||
if (format == AL_NONE)
|
||||
{
|
||||
SoundDecoder_Close(decoder);
|
||||
|
|
@ -1146,6 +1134,7 @@ SoundHandle OpenALSoundRenderer::LoadSound(uint8_t *sfxdata, int length, int def
|
|||
GetSampleTypeName(type));
|
||||
return retval;
|
||||
}
|
||||
int samplesize = SampleTypeSize(type) * ChannelCount(chans);
|
||||
|
||||
TArray<uint8_t> data;
|
||||
unsigned total = 0;
|
||||
|
|
@ -1223,12 +1212,12 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx)
|
|||
}
|
||||
|
||||
|
||||
SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata)
|
||||
SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, SampleType stype, ChannelConfig chans, int samplerate, void *userdata)
|
||||
{
|
||||
if(StreamThread.get_id() == std::thread::id())
|
||||
StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this);
|
||||
OpenALSoundStream *stream = new OpenALSoundStream(this);
|
||||
if (!stream->Init(callback, buffbytes, flags, samplerate, userdata))
|
||||
if (!stream->Init(callback, buffbytes, stype, chans, samplerate, userdata))
|
||||
{
|
||||
delete stream;
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public:
|
|||
virtual float GetOutputRate();
|
||||
|
||||
// Streaming sounds.
|
||||
virtual SoundStream *CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata);
|
||||
SoundStream *CreateStream(SoundStreamCallback callback, int buffbytes, SampleType stype, ChannelConfig chans, int samplerate, void *userdata) override;
|
||||
|
||||
// Starts a sound.
|
||||
FISoundChannel *StartSound(SoundHandle sfx, float vol, float pitch, int chanflags, FISoundChannel *reuse_chan, float startTime) override;
|
||||
|
|
|
|||
|
|
@ -147,7 +147,9 @@ const char *KeyNames[NUM_KEYS] =
|
|||
"DPadUp","DPadDown","DPadLeft","DPadRight", // Gamepad buttons
|
||||
"Pad_Start","Pad_Back","LThumb","RThumb",
|
||||
"LShoulder","RShoulder","LTrigger","RTrigger",
|
||||
"Pad_A", "Pad_B", "Pad_X", "Pad_Y"
|
||||
"Pad_A", "Pad_B", "Pad_X", "Pad_Y",
|
||||
"Paddle_1", "Paddle_2", "Paddle_3", "Paddle_4",
|
||||
"Guide", "Pad_Misc", "Pad_Touchpad"
|
||||
};
|
||||
|
||||
FKeyBindings Bindings;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#include "c_commandbuffer.h"
|
||||
#include "vm.h"
|
||||
#include "common/widgets/errorwindow.h"
|
||||
#include "common/scripting/dap/GameEventEmit.h"
|
||||
#include "st_start.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
|
@ -494,7 +495,7 @@ int PrintString (int iprintlevel, const char *outline)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
if (printlevel != PRINT_LOG || Logfile != nullptr)
|
||||
if (printlevel != PRINT_LOG || !(iprintlevel & PRINT_NODAPEVENT) || Logfile != nullptr)
|
||||
{
|
||||
// Convert everything coming through here to UTF-8 so that all console text is in a consistent format
|
||||
int count;
|
||||
|
|
@ -518,6 +519,10 @@ int PrintString (int iprintlevel, const char *outline)
|
|||
{
|
||||
WriteLineToLog(Logfile, outline);
|
||||
}
|
||||
if (!(iprintlevel & PRINT_NODAPEVENT))
|
||||
{
|
||||
DebugServer::RuntimeEvents::EmitLogEvent(iprintlevel, outline);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return 0; // Don't waste time on calculating this if nothing at all was printed...
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "c_cvars.h"
|
||||
#include "i_protocol.h"
|
||||
#include "engineerrors.h"
|
||||
#include "printf.h"
|
||||
#include "palutil.h"
|
||||
|
|
@ -1281,12 +1282,10 @@ void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32_t filter)
|
|||
}
|
||||
}
|
||||
|
||||
void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact)
|
||||
void C_WriteCVars (TArrayView<uint8_t>& demo_p, uint32_t filter, bool compact)
|
||||
{
|
||||
FString dump = C_GetMassCVarString(filter, compact);
|
||||
size_t dumplen = dump.Len() + 1; // include terminating \0
|
||||
memcpy(*demo_p, dump.GetChars(), dumplen);
|
||||
*demo_p += dumplen;
|
||||
WriteFString(dump, demo_p);
|
||||
}
|
||||
|
||||
FString C_GetMassCVarString (uint32_t filter, bool compact)
|
||||
|
|
@ -1322,9 +1321,9 @@ FString C_GetMassCVarString (uint32_t filter, bool compact)
|
|||
return dump;
|
||||
}
|
||||
|
||||
void C_ReadCVars (uint8_t **demo_p)
|
||||
void C_ReadCVars (TArrayView<uint8_t>& demo_p)
|
||||
{
|
||||
char *ptr = *((char **)demo_p);
|
||||
char *ptr = (char *)demo_p.Data();
|
||||
char *breakpt;
|
||||
|
||||
if (*ptr++ != '\\')
|
||||
|
|
@ -1387,7 +1386,7 @@ void C_ReadCVars (uint8_t **demo_p)
|
|||
}
|
||||
}
|
||||
}
|
||||
*demo_p += strlen (*((char **)demo_p)) + 1;
|
||||
AdvanceStream(demo_p, strlen((char*)demo_p.Data()) + 1);
|
||||
}
|
||||
|
||||
struct FCVarBackup
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ private:
|
|||
// These need to go away!
|
||||
friend FString C_GetMassCVarString (uint32_t filter, bool compact);
|
||||
friend void C_SerializeCVars(FSerializer& arc, const char* label, uint32_t filter);
|
||||
friend void C_ReadCVars (uint8_t **demo_p);
|
||||
friend void C_ReadCVars (TArrayView<uint8_t>& demo_p);
|
||||
friend void C_BackupCVars (void);
|
||||
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
|
||||
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
|
||||
|
|
@ -289,12 +289,12 @@ private:
|
|||
// the cvar names are omitted to save space.
|
||||
FString C_GetMassCVarString (uint32_t filter, bool compact=false);
|
||||
|
||||
// Writes all cvars that could effect demo sync to *demo_p. These are
|
||||
// Writes all cvars that could effect demo sync to demo_p. These are
|
||||
// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set.
|
||||
void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact=false);
|
||||
void C_WriteCVars (TArrayView<uint8_t>& demo_p, uint32_t filter, bool compact=false);
|
||||
|
||||
// Read all cvars from *demo_p and set them appropriately.
|
||||
void C_ReadCVars (uint8_t **demo_p);
|
||||
// Read all cvars from demo_p and set them appropriately.
|
||||
void C_ReadCVars (TArrayView<uint8_t>& demo_p);
|
||||
|
||||
void C_InstallHandlers(ConsoleCallbacks* cb);
|
||||
|
||||
|
|
|
|||
|
|
@ -190,6 +190,22 @@ static const char *KeyConfCommands[] =
|
|||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
bool C_IsValidInt(const char* arg, int& value, int base)
|
||||
{
|
||||
char* end_read;
|
||||
value = std::strtol(arg, &end_read, base);
|
||||
ptrdiff_t chars_read = end_read - arg;
|
||||
return chars_read == strlen(arg);
|
||||
}
|
||||
|
||||
bool C_IsValidFloat(const char* arg, double& value)
|
||||
{
|
||||
char* end_read;
|
||||
value = std::strtod(arg, &end_read);
|
||||
ptrdiff_t chars_read = end_read - arg;
|
||||
return chars_read == strlen(arg);
|
||||
}
|
||||
|
||||
void C_DoCommand (const char *cmd, int keynum)
|
||||
{
|
||||
FConsoleCommand *com;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ void C_ClearDelayedCommands();
|
|||
|
||||
// Process a single console command. Does not handle wait.
|
||||
void C_DoCommand (const char *cmd, int keynum=0);
|
||||
bool C_IsValidInt(const char* arg, int& value, int base = 10);
|
||||
bool C_IsValidFloat(const char* arg, double& value);
|
||||
|
||||
FExecList *C_ParseExecFile(const char *file, FExecList *source);
|
||||
void C_SearchForPullins(FExecList *exec, const char *file, class FCommandLine &args);
|
||||
|
|
|
|||
|
|
@ -134,8 +134,15 @@ enum EKeyCodes
|
|||
KEY_PAD_B = 0x1C1,
|
||||
KEY_PAD_X = 0x1C2,
|
||||
KEY_PAD_Y = 0x1C3,
|
||||
KEY_PAD_PADDLE1 = 0x1C4,
|
||||
KEY_PAD_PADDLE2 = 0x1C5,
|
||||
KEY_PAD_PADDLE3 = 0x1C6,
|
||||
KEY_PAD_PADDLE4 = 0x1C7,
|
||||
KEY_PAD_GUIDE = 0x1C8,
|
||||
KEY_PAD_MISC1 = 0x1C9,
|
||||
KEY_PAD_TOUCHPAD = 0x1CA,
|
||||
|
||||
NUM_KEYS = 0x1C4,
|
||||
NUM_KEYS = 0x1CB,
|
||||
|
||||
NUM_JOYAXISBUTTONS = 8,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -123,12 +123,12 @@ void CallCreateFunction(const char* qname, DObject* runner)
|
|||
//
|
||||
//=============================================================================
|
||||
|
||||
DObject* CreateRunner(bool clearbefore)
|
||||
DObject* CreateRunner(bool clearbefore, int skipType)
|
||||
{
|
||||
auto obj = cutscene.runnerclass->CreateNew();
|
||||
auto func = LookupFunction("ScreenJobRunner.Init", false);
|
||||
VMValue val[3] = { obj, clearbefore, false };
|
||||
VMCall(func, val, 3, nullptr, 0);
|
||||
VMValue val[4] = { obj, clearbefore, false, skipType };
|
||||
VMCall(func, val, 4, nullptr, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ enum
|
|||
SJ_BLOCKUI = 1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ST_VOTE,
|
||||
ST_MUST_BE_SKIPPABLE,
|
||||
ST_UNSKIPPABLE,
|
||||
};
|
||||
|
||||
struct CutsceneDef
|
||||
{
|
||||
FString video;
|
||||
|
|
@ -47,7 +54,7 @@ bool CanWipe();
|
|||
|
||||
VMFunction* LookupFunction(const char* qname, bool validate = true);
|
||||
void CallCreateFunction(const char* qname, DObject* runner);
|
||||
DObject* CreateRunner(bool clearbefore = true);
|
||||
DObject* CreateRunner(bool clearbefore = true, int skipType = ST_VOTE);
|
||||
void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps);
|
||||
|
||||
struct CutsceneState
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include "c_cvars.h"
|
||||
#include "gstrings.h"
|
||||
#include "version.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_random.h"
|
||||
|
||||
static_assert(sizeof(void*) == 8,
|
||||
"Only LP64/LLP64 builds are officially supported. "
|
||||
|
|
@ -30,11 +32,31 @@ bool pauseext;
|
|||
|
||||
FStartupInfo GameStartupInfo;
|
||||
|
||||
CVAR(Bool, queryiwad, QUERYIWADDEFAULT, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
CVAR(String, defaultiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
CVAR(Bool, vid_fps, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, queryiwad, QUERYIWADDEFAULT, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, saveargs, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, savenetfile, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, savenetargs, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultargs, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultnetiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultnetargs, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetplayers, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnethostport, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetticdup, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetmode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetgamemode, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, defaultnetaltdm, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultnetaddress, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetjoinport, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetpage, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnethostteam, 255, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, defaultnetjointeam, 255, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, defaultnetextratic, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(String, defaultnetsavefile, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
EXTERN_CVAR(Bool, ui_generic)
|
||||
EXTERN_CVAR(Bool, vid_fullscreen)
|
||||
|
||||
CUSTOM_CVAR(String, language, "auto", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
|
|
@ -43,3 +65,126 @@ CUSTOM_CVAR(String, language, "auto", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOB
|
|||
if (sysCallbacks.LanguageChanged) sysCallbacks.LanguageChanged(self);
|
||||
}
|
||||
|
||||
// Some of this info has to be passed and managed from the front end since it's game-engine specific.
|
||||
FStartupSelectionInfo::FStartupSelectionInfo(const TArray<WadStuff>& wads, FArgs& args, int startFlags) : Wads(&wads), Args(&args), DefaultStartFlags(startFlags)
|
||||
{
|
||||
DefaultQueryIWAD = queryiwad;
|
||||
DefaultLanguage = language;
|
||||
DefaultFullscreen = vid_fullscreen;
|
||||
|
||||
if (defaultiwad[0] != '\0')
|
||||
{
|
||||
for (int i = 0; i < wads.SSize(); ++i)
|
||||
{
|
||||
if (!wads[i].Name.CompareNoCase(defaultiwad))
|
||||
{
|
||||
DefaultIWAD = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DefaultArgs = defaultargs;
|
||||
bSaveArgs = saveargs;
|
||||
|
||||
if (defaultnetiwad[0] != '\0')
|
||||
{
|
||||
for (int i = 0; i < wads.SSize(); ++i)
|
||||
{
|
||||
if (!wads[i].Name.CompareNoCase(defaultnetiwad))
|
||||
{
|
||||
DefaultNetIWAD = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DefaultNetArgs = defaultnetargs;
|
||||
DefaultNetPage = defaultnetpage;
|
||||
DefaultNetSaveFile = defaultnetsavefile;
|
||||
bSaveNetFile = savenetfile;
|
||||
bSaveNetArgs = savenetargs;
|
||||
|
||||
DefaultNetPlayers = defaultnetplayers;
|
||||
DefaultNetHostPort = defaultnethostport;
|
||||
DefaultNetTicDup = defaultnetticdup;
|
||||
DefaultNetMode = defaultnetmode;
|
||||
DefaultNetGameMode = defaultnetgamemode;
|
||||
DefaultNetAltDM = defaultnetaltdm;
|
||||
DefaultNetHostTeam = defaultnethostteam;
|
||||
DefaultNetExtraTic = defaultnetextratic;
|
||||
|
||||
DefaultNetAddress = defaultnetaddress;
|
||||
DefaultNetJoinPort = defaultnetjoinport;
|
||||
DefaultNetJoinTeam = defaultnetjointeam;
|
||||
}
|
||||
|
||||
// Return whatever IWAD the user selected.
|
||||
int FStartupSelectionInfo::SaveInfo()
|
||||
{
|
||||
DefaultLanguage.StripLeftRight();
|
||||
|
||||
DefaultArgs.StripLeftRight();
|
||||
|
||||
DefaultNetArgs.StripLeftRight();
|
||||
AdditionalNetArgs.StripLeftRight();
|
||||
DefaultNetAddress.StripLeftRight();
|
||||
DefaultNetSaveFile.StripLeftRight();
|
||||
|
||||
queryiwad = DefaultQueryIWAD;
|
||||
language = DefaultLanguage.GetChars();
|
||||
vid_fullscreen = DefaultFullscreen;
|
||||
|
||||
if (bNetStart)
|
||||
{
|
||||
savenetfile = bSaveNetFile;
|
||||
savenetargs = bSaveNetArgs;
|
||||
|
||||
defaultnetiwad = (*Wads)[DefaultNetIWAD].Name.GetChars();
|
||||
defaultnetpage = DefaultNetPage;
|
||||
defaultnetsavefile = savenetfile ? DefaultNetSaveFile.GetChars() : "";
|
||||
defaultnetargs = savenetargs ? DefaultNetArgs.GetChars() : "";
|
||||
|
||||
if (bHosting)
|
||||
{
|
||||
defaultnetplayers = DefaultNetPlayers;
|
||||
defaultnethostport = DefaultNetHostPort;
|
||||
defaultnetticdup = DefaultNetTicDup;
|
||||
defaultnetmode = DefaultNetMode;
|
||||
defaultnetgamemode = DefaultNetGameMode;
|
||||
defaultnetaltdm = DefaultNetAltDM;
|
||||
defaultnethostteam = DefaultNetHostTeam;
|
||||
defaultnetextratic = DefaultNetExtraTic;
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultnetaddress = DefaultNetAddress.GetChars();
|
||||
defaultnetjoinport = DefaultNetJoinPort;
|
||||
defaultnetjointeam = DefaultNetJoinTeam;
|
||||
}
|
||||
|
||||
if (!DefaultNetArgs.IsEmpty())
|
||||
Args->AppendArgsString(DefaultNetArgs);
|
||||
if (!AdditionalNetArgs.IsEmpty())
|
||||
Args->AppendArgsString(AdditionalNetArgs);
|
||||
|
||||
return DefaultNetIWAD;
|
||||
}
|
||||
|
||||
defaultiwad = (*Wads)[DefaultIWAD].Name.GetChars();
|
||||
saveargs = bSaveArgs;
|
||||
defaultargs = saveargs ? DefaultArgs.GetChars() : "";
|
||||
|
||||
if (!DefaultArgs.IsEmpty())
|
||||
Args->AppendArgsString(DefaultArgs);
|
||||
|
||||
return DefaultIWAD;
|
||||
}
|
||||
|
||||
FString GameUUID;
|
||||
static FRandom pr_uuid("GameUUID");
|
||||
|
||||
FString GenerateUUID()
|
||||
{
|
||||
FString uuid;
|
||||
uuid.AppendFormat("%08X-%04X-4%03X-9%03X-%08X%04X", pr_uuid.GenRand32(), pr_uuid(UINT16_MAX), pr_uuid(4095), pr_uuid(4095), pr_uuid.GenRand32(), pr_uuid(UINT16_MAX));
|
||||
return uuid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,12 @@ class FGameTexture;
|
|||
class FTextureID;
|
||||
enum EUpscaleFlags : int;
|
||||
class FConfigFile;
|
||||
class FArgs;
|
||||
struct FTranslationID;
|
||||
|
||||
extern FString GameUUID;
|
||||
FString GenerateUUID();
|
||||
|
||||
struct SystemCallbacks
|
||||
{
|
||||
bool (*G_Responder)(event_t* ev); // this MUST be set, otherwise nothing will work
|
||||
|
|
@ -58,6 +62,50 @@ struct WadStuff
|
|||
FString Name;
|
||||
};
|
||||
|
||||
struct FStartupSelectionInfo
|
||||
{
|
||||
const TArray<WadStuff>* Wads = nullptr;
|
||||
FArgs* Args = nullptr;
|
||||
|
||||
// Local game info
|
||||
int DefaultIWAD = 0;
|
||||
FString DefaultArgs = {};
|
||||
bool bSaveArgs = true;
|
||||
|
||||
// Settings
|
||||
int DefaultStartFlags = 0;
|
||||
bool DefaultQueryIWAD = true;
|
||||
FString DefaultLanguage = "auto";
|
||||
bool DefaultFullscreen = true;
|
||||
|
||||
// Net game info
|
||||
int DefaultNetIWAD = 0;
|
||||
bool bNetStart = false;
|
||||
bool bHosting = false;
|
||||
bool bSaveNetFile = false;
|
||||
bool bSaveNetArgs = true;
|
||||
int DefaultNetPage = 0;
|
||||
FString DefaultNetArgs = {};
|
||||
FString AdditionalNetArgs = {}; // These ones shouldn't be saved.
|
||||
FString DefaultNetSaveFile = {};
|
||||
int DefaultNetHostTeam = 255;
|
||||
int DefaultNetPlayers = 8;
|
||||
int DefaultNetHostPort = 0;
|
||||
int DefaultNetTicDup = 0;
|
||||
bool DefaultNetExtraTic = false;
|
||||
int DefaultNetMode = -1;
|
||||
int DefaultNetGameMode = -1;
|
||||
bool DefaultNetAltDM = false;
|
||||
|
||||
FString DefaultNetAddress = {};
|
||||
int DefaultNetJoinPort = 0;
|
||||
int DefaultNetJoinTeam = 255;
|
||||
|
||||
FStartupSelectionInfo() = delete;
|
||||
FStartupSelectionInfo(const TArray<WadStuff>& wads, FArgs& args, int startFlags);
|
||||
int SaveInfo();
|
||||
};
|
||||
|
||||
|
||||
extern FString endoomName;
|
||||
extern bool batchrun;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,91 +2,77 @@
|
|||
#define __I_NET_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tarray.h"
|
||||
|
||||
// Called by D_DoomMain.
|
||||
int I_InitNetwork (void);
|
||||
void I_NetCmd (void);
|
||||
void I_NetMessage(const char*, ...);
|
||||
void I_NetError(const char* error);
|
||||
void I_NetProgress(int val);
|
||||
void I_NetInit(const char* msg, int num);
|
||||
bool I_NetLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
void I_NetDone();
|
||||
inline constexpr size_t MAXPLAYERS = 64u;
|
||||
|
||||
enum ENetConstants
|
||||
{
|
||||
MAXNETNODES = 8, // max computers in a game
|
||||
DOOMCOM_ID = 0x12345678,
|
||||
BACKUPTICS = 36, // number of tics to remember
|
||||
MAXTICDUP = 5,
|
||||
LOCALCMDTICS =(BACKUPTICS*MAXTICDUP),
|
||||
BACKUPTICS = 35 * 5, // Remember up to 5 seconds of data.
|
||||
MAXTICDUP = 3,
|
||||
MAXSENDTICS = 35 * 1, // Only send up to 1 second of data at a time.
|
||||
STABILITYTICS = 17,
|
||||
LOCALCMDTICS = (BACKUPTICS * MAXTICDUP),
|
||||
MAX_MSGLEN = 14000,
|
||||
|
||||
CMD_SEND = 1,
|
||||
CMD_GET = 2,
|
||||
};
|
||||
|
||||
// [RH]
|
||||
// New generic packet structure:
|
||||
//
|
||||
// Header:
|
||||
// One byte with following flags.
|
||||
// One byte with starttic
|
||||
// One byte with master's maketic (master -> slave only!)
|
||||
// If NCMD_RETRANSMIT set, one byte with retransmitfrom
|
||||
// If NCMD_XTICS set, one byte with number of tics (minus 3, so theoretically up to 258 tics in one packet)
|
||||
// If NCMD_QUITTERS, one byte with number of players followed by one byte with each player's consolenum
|
||||
// If NCMD_MULTI, one byte with number of players followed by one byte with each player's consolenum
|
||||
// - The first player's consolenum is not included in this list, because it always matches the sender
|
||||
//
|
||||
// For each tic:
|
||||
// Two bytes with consistancy check, followed by tic data
|
||||
//
|
||||
// Setup packets are different, and are described just before D_ArbitrateNetStart().
|
||||
|
||||
enum ENCMD
|
||||
enum ENetCommand
|
||||
{
|
||||
NCMD_EXIT = 0x80,
|
||||
NCMD_RETRANSMIT = 0x40,
|
||||
NCMD_SETUP = 0x20,
|
||||
NCMD_MULTI = 0x10, // multiple players in this packet
|
||||
NCMD_QUITTERS = 0x08, // one or more players just quit (packet server only)
|
||||
NCMD_COMPRESSED = 0x04, // remainder of packet is compressed
|
||||
|
||||
NCMD_XTICS = 0x03, // packet contains >2 tics
|
||||
NCMD_2TICS = 0x02, // packet contains 2 tics
|
||||
NCMD_1TICS = 0x01, // packet contains 1 tic
|
||||
NCMD_0TICS = 0x00, // packet contains 0 tics
|
||||
CMD_NONE,
|
||||
CMD_SEND,
|
||||
CMD_GET,
|
||||
};
|
||||
|
||||
//
|
||||
// Network packet data.
|
||||
//
|
||||
struct doomcom_t
|
||||
enum ENetFlags
|
||||
{
|
||||
uint32_t id; // should be DOOMCOM_ID
|
||||
int16_t intnum; // DOOM executes an int to execute commands
|
||||
NCMD_EXIT = 0x80, // Client has left the game
|
||||
NCMD_RETRANSMIT = 0x40, //
|
||||
NCMD_SETUP = 0x20, // Guest is letting the host know who it is
|
||||
NCMD_LEVELREADY = 0x10, // After loading a level, guests send this over to the host who then sends it back after all are received
|
||||
NCMD_QUITTERS = 0x08, // Client is getting info about one or more players quitting (packet server only)
|
||||
NCMD_COMPRESSED = 0x04, // Remainder of packet is compressed
|
||||
NCMD_LATENCYACK = 0x02, // A latency packet was just read, so let the sender know.
|
||||
NCMD_LATENCY = 0x01, // Latency packet, used for measuring RTT.
|
||||
};
|
||||
|
||||
// communication between DOOM and the driver
|
||||
int16_t command; // CMD_SEND or CMD_GET
|
||||
int16_t remotenode; // dest for send, set by get (-1 = no packet).
|
||||
int16_t datalength; // bytes in data to be sent
|
||||
enum ENetMode
|
||||
{
|
||||
NET_PeerToPeer,
|
||||
NET_PacketServer
|
||||
};
|
||||
|
||||
// info common to all nodes
|
||||
int16_t numnodes; // console is always node 0.
|
||||
int16_t ticdup; // 1 = no duplication, 2-5 = dup for slow nets
|
||||
struct FClientStack : public TArray<int>
|
||||
{
|
||||
inline bool InGame(int i) const { return Find(i) < Size(); }
|
||||
|
||||
// info specific to this node
|
||||
int16_t consoleplayer;
|
||||
int16_t numplayers;
|
||||
void operator+=(const int i)
|
||||
{
|
||||
if (!InGame(i))
|
||||
SortedInsert(i);
|
||||
}
|
||||
|
||||
// packet data to be sent
|
||||
uint8_t data[MAX_MSGLEN];
|
||||
void operator-=(const int i)
|
||||
{
|
||||
Delete(Find(i));
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
extern doomcom_t doomcom;
|
||||
extern bool netgame, multiplayer;
|
||||
extern int consoleplayer;
|
||||
extern int Net_Arbitrator;
|
||||
extern FClientStack NetworkClients;
|
||||
extern ENetMode NetMode;
|
||||
extern uint8_t NetBuffer[MAX_MSGLEN];
|
||||
extern size_t NetBufferLength;
|
||||
extern uint8_t TicDup;
|
||||
extern int RemoteClient;
|
||||
extern int MaxClients;
|
||||
|
||||
bool I_InitNetwork();
|
||||
void I_ClearClient(size_t client);
|
||||
void I_NetCmd(ENetCommand cmd);
|
||||
void I_NetDone();
|
||||
void HandleIncomingConnection();
|
||||
void CloseNetwork();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
399
src/common/engine/i_protocol.cpp
Normal file
399
src/common/engine/i_protocol.cpp
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
** i_protocol.cpp
|
||||
** Basic network packet creation routines and simple IFF parsing
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "i_protocol.h"
|
||||
#include "engineerrors.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
// Unchecked stream functions.
|
||||
// Use the checked versions instead unless you're checking the stream size yourself!
|
||||
|
||||
char* UncheckedReadString(uint8_t** stream)
|
||||
{
|
||||
char* string = *((char**)stream);
|
||||
|
||||
*stream += strlen(string) + 1;
|
||||
return copystring(string);
|
||||
}
|
||||
|
||||
const char* UncheckedReadStringConst(uint8_t** stream)
|
||||
{
|
||||
const char* string = *((const char**)stream);
|
||||
*stream += strlen(string) + 1;
|
||||
return string;
|
||||
}
|
||||
|
||||
uint8_t UncheckedReadInt8(uint8_t** stream)
|
||||
{
|
||||
uint8_t v = **stream;
|
||||
*stream += 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
int16_t UncheckedReadInt16(uint8_t** stream)
|
||||
{
|
||||
int16_t v = (((*stream)[0]) << 8) | (((*stream)[1]));
|
||||
*stream += 2;
|
||||
return v;
|
||||
}
|
||||
|
||||
int32_t UncheckedReadInt32(uint8_t** stream)
|
||||
{
|
||||
int32_t v = (((*stream)[0]) << 24) | (((*stream)[1]) << 16) | (((*stream)[2]) << 8) | (((*stream)[3]));
|
||||
*stream += 4;
|
||||
return v;
|
||||
}
|
||||
|
||||
int64_t UncheckedReadInt64(uint8_t** stream)
|
||||
{
|
||||
int64_t v = (int64_t((*stream)[0]) << 56) | (int64_t((*stream)[1]) << 48) | (int64_t((*stream)[2]) << 40) | (int64_t((*stream)[3]) << 32)
|
||||
| (int64_t((*stream)[4]) << 24) | (int64_t((*stream)[5]) << 16) | (int64_t((*stream)[6]) << 8) | (int64_t((*stream)[7]));
|
||||
*stream += 8;
|
||||
return v;
|
||||
}
|
||||
|
||||
float UncheckedReadFloat(uint8_t** stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t i;
|
||||
float f;
|
||||
} fakeint;
|
||||
fakeint.i = UncheckedReadInt32(stream);
|
||||
return fakeint.f;
|
||||
}
|
||||
|
||||
double UncheckedReadDouble(uint8_t** stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int64_t i;
|
||||
double f;
|
||||
} fakeint;
|
||||
fakeint.i = UncheckedReadInt64(stream);
|
||||
return fakeint.f;
|
||||
}
|
||||
|
||||
void UncheckedWriteString(const char* string, uint8_t** stream)
|
||||
{
|
||||
char* p = *((char**)stream);
|
||||
|
||||
while (*string) {
|
||||
*p++ = *string++;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
*stream = (uint8_t*)p;
|
||||
}
|
||||
|
||||
void UncheckedWriteInt8(uint8_t v, uint8_t** stream)
|
||||
{
|
||||
**stream = v;
|
||||
*stream += 1;
|
||||
}
|
||||
|
||||
void UncheckedWriteInt16(int16_t v, uint8_t** stream)
|
||||
{
|
||||
(*stream)[0] = v >> 8;
|
||||
(*stream)[1] = v & 255;
|
||||
*stream += 2;
|
||||
}
|
||||
|
||||
void UncheckedWriteInt32(int32_t v, uint8_t** stream)
|
||||
{
|
||||
(*stream)[0] = v >> 24;
|
||||
(*stream)[1] = (v >> 16) & 255;
|
||||
(*stream)[2] = (v >> 8) & 255;
|
||||
(*stream)[3] = v & 255;
|
||||
*stream += 4;
|
||||
}
|
||||
|
||||
void UncheckedWriteInt64(int64_t v, uint8_t** stream)
|
||||
{
|
||||
(*stream)[0] = v >> 56;
|
||||
(*stream)[1] = (v >> 48) & 255;
|
||||
(*stream)[2] = (v >> 40) & 255;
|
||||
(*stream)[3] = (v >> 32) & 255;
|
||||
(*stream)[4] = (v >> 24) & 255;
|
||||
(*stream)[5] = (v >> 16) & 255;
|
||||
(*stream)[6] = (v >> 8) & 255;
|
||||
(*stream)[7] = v & 255;
|
||||
*stream += 8;
|
||||
}
|
||||
|
||||
void UncheckedWriteFloat(float v, uint8_t** stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t i;
|
||||
float f;
|
||||
} fakeint;
|
||||
fakeint.f = v;
|
||||
UncheckedWriteInt32(fakeint.i, stream);
|
||||
}
|
||||
|
||||
void UncheckedWriteDouble(double v, uint8_t** stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int64_t i;
|
||||
double f;
|
||||
} fakeint;
|
||||
fakeint.f = v;
|
||||
UncheckedWriteInt64(fakeint.i, stream);
|
||||
}
|
||||
|
||||
void AdvanceStream(TArrayView<uint8_t>& stream, size_t bytes)
|
||||
{
|
||||
assert(bytes <= stream.Size());
|
||||
stream = TArrayView(stream.Data() + bytes, stream.Size() - bytes);
|
||||
}
|
||||
|
||||
// Checked stream functions
|
||||
|
||||
char* ReadString(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
char* string = (char*)stream.Data();
|
||||
size_t len = strnlen(string, stream.Size());
|
||||
if (len == stream.Size())
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
AdvanceStream(stream, len + 1);
|
||||
return copystring(string);
|
||||
}
|
||||
|
||||
const char* ReadStringConst(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
const char* string = (const char*)stream.Data();
|
||||
size_t len = strnlen(string, stream.Size());
|
||||
if (len == stream.Size())
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
AdvanceStream(stream, len + 1);
|
||||
return string;
|
||||
}
|
||||
|
||||
void ReadBytes(TArrayView<uint8_t>& dst, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (dst.Size() > stream.Size())
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
if (dst.Size())
|
||||
{
|
||||
memcpy(dst.Data(), stream.Data(), dst.Size());
|
||||
AdvanceStream(stream, dst.Size());
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadInt8(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 1)
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
uint8_t v = stream[0];
|
||||
AdvanceStream(stream, 1);
|
||||
return v;
|
||||
}
|
||||
|
||||
int16_t ReadInt16(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 2)
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
int16_t v = ((stream[0]) << 8) | stream[1];
|
||||
AdvanceStream(stream, 2);
|
||||
return v;
|
||||
}
|
||||
|
||||
int32_t ReadInt32(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 4)
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
int32_t v = (stream[0] << 24) | (stream[1] << 16) | (stream[2] << 8) | stream[3];
|
||||
AdvanceStream(stream, 4);
|
||||
return v;
|
||||
}
|
||||
|
||||
int64_t ReadInt64(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 8)
|
||||
{
|
||||
I_Error("Attempted to read past end of stream");
|
||||
}
|
||||
int64_t v = (int64_t(stream[0]) << 56) | (int64_t(stream[1]) << 48) | (int64_t(stream[2]) << 40) | (int64_t(stream[3]) << 32)
|
||||
| (int64_t(stream[4]) << 24) | (int64_t(stream[5]) << 16) | (int64_t(stream[6]) << 8) | int64_t(stream[7]);
|
||||
AdvanceStream(stream, 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
float ReadFloat(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t i;
|
||||
float f;
|
||||
} fakeint;
|
||||
fakeint.i = ReadInt32(stream);
|
||||
return fakeint.f;
|
||||
}
|
||||
|
||||
double ReadDouble(TArrayView<uint8_t>& stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int64_t i;
|
||||
double f;
|
||||
} fakeint;
|
||||
fakeint.i = ReadInt64(stream);
|
||||
return fakeint.f;
|
||||
}
|
||||
|
||||
void WriteString(const char* string, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
char* p = (char*)stream.Data();
|
||||
unsigned int remaining = stream.Size();
|
||||
|
||||
while (*string) {
|
||||
if (remaining-- == 0)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
*p++ = *string++;
|
||||
}
|
||||
|
||||
if (remaining == 0)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
*p++ = 0;
|
||||
AdvanceStream(stream, p - (char*)stream.Data());
|
||||
}
|
||||
|
||||
void WriteBytes(const TArrayView<uint8_t>& source, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (source.Size() > stream.Size())
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
if (source.Size())
|
||||
{
|
||||
memcpy(stream.Data(), source.Data(), source.Size());
|
||||
AdvanceStream(stream, source.Size());
|
||||
}
|
||||
}
|
||||
|
||||
void WriteFString(FString& string, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
WriteBytes(string.GetTArrayView(), stream);
|
||||
}
|
||||
|
||||
void WriteInt8(uint8_t v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 1)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
stream[0] = v;
|
||||
AdvanceStream(stream, 1);
|
||||
}
|
||||
|
||||
void WriteInt16(int16_t v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 2)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
stream[0] = v >> 8;
|
||||
stream[1] = v & 255;
|
||||
AdvanceStream(stream, 2);
|
||||
}
|
||||
|
||||
void WriteInt32(int32_t v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 4)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
stream[0] = v >> 24;
|
||||
stream[1] = (v >> 16) & 255;
|
||||
stream[2] = (v >> 8) & 255;
|
||||
stream[3] = v & 255;
|
||||
AdvanceStream(stream, 4);
|
||||
}
|
||||
|
||||
void WriteInt64(int64_t v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
if (stream.Size() < 8)
|
||||
{
|
||||
I_Error("Attempted to write past end of stream");
|
||||
}
|
||||
stream[0] = v >> 56;
|
||||
stream[1] = (v >> 48) & 255;
|
||||
stream[2] = (v >> 40) & 255;
|
||||
stream[3] = (v >> 32) & 255;
|
||||
stream[4] = (v >> 24) & 255;
|
||||
stream[5] = (v >> 16) & 255;
|
||||
stream[6] = (v >> 8) & 255;
|
||||
stream[7] = v & 255;
|
||||
AdvanceStream(stream, 8);
|
||||
}
|
||||
|
||||
void WriteFloat(float v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t i;
|
||||
float f;
|
||||
} fakeint;
|
||||
fakeint.f = v;
|
||||
WriteInt32(fakeint.i, stream);
|
||||
}
|
||||
|
||||
void WriteDouble(double v, TArrayView<uint8_t>& stream)
|
||||
{
|
||||
union
|
||||
{
|
||||
int64_t i;
|
||||
double f;
|
||||
} fakeint;
|
||||
fakeint.f = v;
|
||||
WriteInt64(fakeint.i, stream);
|
||||
}
|
||||
77
src/common/engine/i_protocol.h
Normal file
77
src/common/engine/i_protocol.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
** i_protocol.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __I_PROTOCOL_H__
|
||||
#define __I_PROTOCOL_H__
|
||||
|
||||
#include "tarray.h"
|
||||
#include "zstring.h"
|
||||
|
||||
uint8_t UncheckedReadInt8(uint8_t** stream);
|
||||
int16_t UncheckedReadInt16(uint8_t** stream);
|
||||
int32_t UncheckedReadInt32(uint8_t** stream);
|
||||
int64_t UncheckedReadInt64(uint8_t** stream);
|
||||
float UncheckedReadFloat(uint8_t** stream);
|
||||
double UncheckedReadDouble(uint8_t** stream);
|
||||
char* UncheckedReadString(uint8_t** stream);
|
||||
const char* UncheckedReadStringConst(uint8_t** stream);
|
||||
void UncheckedWriteInt8(uint8_t val, uint8_t** stream);
|
||||
void UncheckedWriteInt16(int16_t val, uint8_t** stream);
|
||||
void UncheckedWriteInt32(int32_t val, uint8_t** stream);
|
||||
void UncheckedWriteInt64(int64_t val, uint8_t** stream);
|
||||
void UncheckedWriteFloat(float val, uint8_t** stream);
|
||||
void UncheckedWriteDouble(double val, uint8_t** stream);
|
||||
void UncheckedWriteString(const char* string, uint8_t** stream);
|
||||
|
||||
void AdvanceStream(TArrayView<uint8_t>& stream, size_t bytes);
|
||||
|
||||
uint8_t ReadInt8(TArrayView<uint8_t>& stream);
|
||||
int16_t ReadInt16(TArrayView<uint8_t>& stream);
|
||||
int32_t ReadInt32(TArrayView<uint8_t>& stream);
|
||||
int64_t ReadInt64(TArrayView<uint8_t>& stream);
|
||||
float ReadFloat(TArrayView<uint8_t>& stream);
|
||||
double ReadDouble(TArrayView<uint8_t>& stream);
|
||||
char* ReadString(TArrayView<uint8_t>& stream);
|
||||
const char* ReadStringConst(TArrayView<uint8_t>& stream);
|
||||
void ReadBytes(TArrayView<uint8_t>& dst, TArrayView<uint8_t>& stream);
|
||||
void WriteInt8(uint8_t val, TArrayView<uint8_t>& stream);
|
||||
void WriteInt16(int16_t val, TArrayView<uint8_t>& stream);
|
||||
void WriteInt32(int32_t val, TArrayView<uint8_t>& stream);
|
||||
void WriteInt64(int64_t val, TArrayView<uint8_t>& stream);
|
||||
void WriteFloat(float val, TArrayView<uint8_t>& stream);
|
||||
void WriteDouble(double val, TArrayView<uint8_t>& stream);
|
||||
void WriteString(const char* string, TArrayView<uint8_t>& stream);
|
||||
void WriteBytes(const TArrayView<uint8_t>& source, TArrayView<uint8_t>& stream);
|
||||
void WriteFString(FString& string, TArrayView<uint8_t>& stream);
|
||||
|
||||
#endif //__I_PROTOCOL_H__
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include <math.h>
|
||||
#include "c_dispatch.h"
|
||||
#include "vectors.h"
|
||||
#include "m_joy.h"
|
||||
#include "configfile.h"
|
||||
|
|
@ -57,9 +58,26 @@ EXTERN_CVAR(Bool, joy_ps2raw)
|
|||
EXTERN_CVAR(Bool, joy_dinput)
|
||||
EXTERN_CVAR(Bool, joy_xinput)
|
||||
|
||||
extern const float JOYDEADZONE_DEFAULT = 0.1; // reduced from 0.25
|
||||
|
||||
extern const float JOYSENSITIVITY_DEFAULT = 1.0;
|
||||
|
||||
extern const float JOYTHRESH_DEFAULT = 0.05;
|
||||
extern const float JOYTHRESH_TRIGGER = 0.05;
|
||||
extern const float JOYTHRESH_STICK_X = 0.65;
|
||||
extern const float JOYTHRESH_STICK_Y = 0.35;
|
||||
|
||||
extern const CubicBezier JOYCURVE[NUM_JOYCURVE] = {
|
||||
{{0.3, 0.0, 0.7, 0.4}}, // DEFAULT -> QUADRATIC
|
||||
|
||||
{{0.0, 0.0, 1.0, 1.0}}, // LINEAR
|
||||
{{0.3, 0.0, 0.7, 0.4}}, // QUADRATIC
|
||||
{{0.5, 0.0, 0.7, 0.2}}, // CUBIC
|
||||
};
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
CUSTOM_CVARD(Bool, use_joystick, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the joystick if it is present")
|
||||
CUSTOM_CVARD(Bool, use_joystick, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL, "enables input from the joystick if it is present")
|
||||
{
|
||||
#ifdef _WIN32
|
||||
joy_ps2raw->Callback();
|
||||
|
|
@ -163,6 +181,48 @@ bool M_LoadJoystickConfig(IJoystickConfig *joy)
|
|||
joy->SetAxisScale(i, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "threshold");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisDigitalThreshold(i, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisResponseCurve(i, (EJoyCurve)clamp(atoi(value), (int)JOYCURVE_CUSTOM, (int)NUM_JOYCURVE-1));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-x1");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisResponseCurvePoint(i, 0, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-y1");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisResponseCurvePoint(i, 1, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-x2");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisResponseCurvePoint(i, 2, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-y2");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
{
|
||||
joy->SetAxisResponseCurvePoint(i, 3, (float)atof(value));
|
||||
}
|
||||
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "map");
|
||||
value = GameConfig->GetValueForKey(key);
|
||||
if (value)
|
||||
|
|
@ -227,6 +287,33 @@ void M_SaveJoystickConfig(IJoystickConfig *joy)
|
|||
mysnprintf(value, countof(value), "%g", joy->GetAxisScale(i));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
}
|
||||
if (!joy->IsAxisDigitalThresholdDefault(i))
|
||||
{
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "threshold");
|
||||
mysnprintf(value, countof(value), "%g", joy->GetAxisDigitalThreshold(i));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
}
|
||||
if (!joy->IsAxisResponseCurveDefault(i))
|
||||
{
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve");
|
||||
mysnprintf(value, countof(value), "%d", joy->GetAxisResponseCurve(i));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
}
|
||||
if (joy->GetAxisResponseCurve(i) == JOYCURVE_CUSTOM)
|
||||
{
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-x1");
|
||||
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 0));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-y1");
|
||||
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 1));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-x2");
|
||||
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 2));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "curve-y2");
|
||||
mysnprintf(value, countof(value), "%g", joy->GetAxisResponseCurvePoint(i, 3));
|
||||
GameConfig->SetValueForKey(key, value);
|
||||
}
|
||||
if (!joy->IsAxisMapDefault(i))
|
||||
{
|
||||
mysnprintf(key + axislen, countof(key) - axislen, "map");
|
||||
|
|
@ -243,6 +330,174 @@ void M_SaveJoystickConfig(IJoystickConfig *joy)
|
|||
}
|
||||
}
|
||||
|
||||
CCMD (gamepad)
|
||||
{
|
||||
int COMMAND = 1, IDENTIFIER = 2, VALUE = 3;
|
||||
int argc = argv.argc()-1;
|
||||
|
||||
TArray<IJoystickConfig *> sticks;
|
||||
I_GetJoysticks(sticks);
|
||||
|
||||
auto usage = []()
|
||||
{
|
||||
Printf(
|
||||
"usage:"
|
||||
"\n\tgamepad list"
|
||||
"\n\tgamepad reset pad"
|
||||
"\n\tgamepad enabled pad [0|1]"
|
||||
"\n\tgamepad background pad [0|1]"
|
||||
"\n\tgamepad sensitivity pad [float]"
|
||||
"\n\tgamepad deadzone pad.axis [float]"
|
||||
"\n\tgamepad scale pad.axis [float]"
|
||||
"\n\tgamepad threshold pad.axis [float]"
|
||||
"\n\tgamepad curve pad.axis [-1|0|1|2|3]"
|
||||
"\n\tgamepad curve-x1 pad.axis [float]"
|
||||
"\n\tgamepad curve-y1 pad.axis [float]"
|
||||
"\n\tgamepad curve-x2 pad.axis [float]"
|
||||
"\n\tgamepad curve-y2 pad.axis [float]"
|
||||
"\n\tgamepad map pad.axis [-1|0|1|2|3|4]"
|
||||
"\n"
|
||||
);
|
||||
};
|
||||
|
||||
if (argc < COMMAND)
|
||||
{
|
||||
return usage();
|
||||
};
|
||||
|
||||
FName command = argv[COMMAND];
|
||||
|
||||
if (argc < IDENTIFIER)
|
||||
{
|
||||
if (command == "list")
|
||||
{
|
||||
for (int i = 0; i < sticks.SSize(); i++)
|
||||
{
|
||||
Printf("%d: '%s'\n", i, sticks[i]->GetName().GetChars());
|
||||
for (int j = 0; j < sticks[i]->GetNumAxes(); j++)
|
||||
{
|
||||
Printf(" %d.%d: '%s'\n", i, j, sticks[i]->GetAxisName(j));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
return usage();
|
||||
}
|
||||
|
||||
const char * id = argv[IDENTIFIER];
|
||||
const char * hasAxis = strchr(id, '.');
|
||||
|
||||
int pad, axis;
|
||||
|
||||
try {
|
||||
pad = (int)std::stod(id);
|
||||
|
||||
if (pad < 0 || pad >= sticks.SSize())
|
||||
{
|
||||
return (void) Printf("Pad # out of range\n");
|
||||
}
|
||||
} catch (...) {
|
||||
return (void) Printf("Failed to parse pad #\n");
|
||||
}
|
||||
|
||||
if (hasAxis)
|
||||
{
|
||||
try {
|
||||
axis = (int)std::stod(hasAxis+1);
|
||||
|
||||
if (axis < 0 || axis >= sticks[pad]->GetNumAxes())
|
||||
{
|
||||
return (void) Printf("Axis # out of range\n");
|
||||
}
|
||||
} catch (...) {
|
||||
return (void) Printf("Failed to parse axis #\n");
|
||||
}
|
||||
}
|
||||
|
||||
float value = 0;
|
||||
bool set = argc >= VALUE;
|
||||
|
||||
if (set)
|
||||
{
|
||||
try {
|
||||
value = std::stod(argv[VALUE]);
|
||||
} catch (...) {
|
||||
return (void) Printf("Failed to parse args\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "reset")
|
||||
{
|
||||
if (set) return usage();
|
||||
sticks[pad]->SetDefaultConfig();
|
||||
sticks[pad]->SetEnabled(true);
|
||||
sticks[pad]->SetEnabledInBackground(sticks[pad]->AllowsEnabledInBackground());
|
||||
sticks[pad]->SetSensitivity(1);
|
||||
return;
|
||||
}
|
||||
if (command == "enabled")
|
||||
{
|
||||
if (set) sticks[pad]->SetEnabled((int)value);
|
||||
return (void) Printf("%d\n", sticks[pad]->GetEnabled());
|
||||
}
|
||||
if (command == "background")
|
||||
{
|
||||
if (set) sticks[pad]->SetEnabledInBackground((int)value);
|
||||
return (void) Printf("%d\n", sticks[pad]->GetEnabledInBackground());
|
||||
}
|
||||
if (command == "sensitivity")
|
||||
{
|
||||
if (set) sticks[pad]->SetSensitivity(value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetSensitivity());
|
||||
}
|
||||
if (command == "deadzone")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisDeadZone(axis, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisDeadZone(axis));
|
||||
}
|
||||
if (command == "scale")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisScale(axis, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisScale(axis));
|
||||
}
|
||||
if (command == "threshold")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisDigitalThreshold(axis, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisDigitalThreshold(axis));
|
||||
}
|
||||
if (command == "curve")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisResponseCurve(axis, (EJoyCurve)value);
|
||||
return (void) Printf("%d\n", sticks[pad]->GetAxisResponseCurve(axis));
|
||||
}
|
||||
if (command == "curve-x1")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 0, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 0));
|
||||
}
|
||||
if (command == "curve-y1")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 1, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 1));
|
||||
}
|
||||
if (command == "curve-x2")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 2, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 2));
|
||||
}
|
||||
if (command == "curve-y2")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisResponseCurvePoint(axis, 3, value);
|
||||
return (void) Printf("%g\n", sticks[pad]->GetAxisResponseCurvePoint(axis, 3));
|
||||
}
|
||||
if (command == "map")
|
||||
{
|
||||
if (set) sticks[pad]->SetAxisMap(axis, (EJoyAxis)value);
|
||||
return (void) Printf("%d\n", sticks[pad]->GetAxisMap(axis));
|
||||
}
|
||||
|
||||
return usage();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
|
@ -278,6 +533,48 @@ double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons)
|
|||
return axisval;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Joy_ApplyResponseCurveBezier
|
||||
//
|
||||
// Applies cubic bezier easing function
|
||||
// Curve is defined by control points [(0,0) (x1,y1) (x2,y2) (1,1)]
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function/cubic-bezier
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
double Joy_ApplyResponseCurveBezier(const CubicBezier &curve, double input)
|
||||
{
|
||||
// clamp + trivial cases
|
||||
if (input == 0) return 0;
|
||||
double sign = (input >= 0)? 1.0: -1.0;
|
||||
input = abs(input);
|
||||
input = (input > 1.0)? 1.0: input;
|
||||
if (input == 1.0) return sign*input;
|
||||
|
||||
double t = input, T;
|
||||
float x1 = curve.x1, y1 = curve.y1, x2 = curve.x2, y2 = curve.y2;
|
||||
|
||||
const int max_iter = 4;
|
||||
for (auto i = 0; i < max_iter; i++)
|
||||
{
|
||||
T = 1-t;
|
||||
|
||||
double x = 3*T*T*t*x1 + 3*T*t*t*x2 + t*t*t;
|
||||
double dx = 3*T*T*x1 + 6*T*t*(x2-x1) + 3*t*t*(1-x2);
|
||||
|
||||
// no div by 0
|
||||
if (abs(dx) < 0.00001) break;
|
||||
|
||||
t = clamp(t - (x-input)/dx, 0.0, 1.0);
|
||||
}
|
||||
|
||||
T = 1-t;
|
||||
t = 3*T*T*t*y1 + 3*T*t*t*y2 + t*t*t;
|
||||
|
||||
return sign*t;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Joy_XYAxesToButtons
|
||||
|
|
@ -315,6 +612,22 @@ int Joy_XYAxesToButtons(double x, double y)
|
|||
return JoyAngleButtons[int(rad) & 7];
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Joy_GenerateButtonEvent
|
||||
//
|
||||
// Send either a button up or button down event for supplied key code
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void Joy_GenerateButtonEvent(bool down, EKeyCodes which)
|
||||
{
|
||||
event_t event = { 0,0,0,0,0,0,0 };
|
||||
event.type = down ? EV_KeyDown : EV_KeyUp;
|
||||
event.data1 = which;
|
||||
D_PostEvent(&event);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Joy_GenerateButtonEvents
|
||||
|
|
@ -330,15 +643,12 @@ void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, in
|
|||
int changed = oldbuttons ^ newbuttons;
|
||||
if (changed != 0)
|
||||
{
|
||||
event_t ev = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
int mask = 1;
|
||||
for (int j = 0; j < numbuttons; mask <<= 1, ++j)
|
||||
{
|
||||
if (changed & mask)
|
||||
{
|
||||
ev.data1 = base + j;
|
||||
ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp;
|
||||
D_PostEvent(&ev);
|
||||
Joy_GenerateButtonEvent(newbuttons & mask, static_cast<EKeyCodes>(base + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -349,15 +659,12 @@ void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, co
|
|||
int changed = oldbuttons ^ newbuttons;
|
||||
if (changed != 0)
|
||||
{
|
||||
event_t ev = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
int mask = 1;
|
||||
for (int j = 0; j < numbuttons; mask <<= 1, ++j)
|
||||
{
|
||||
if (changed & mask)
|
||||
{
|
||||
ev.data1 = keys[j];
|
||||
ev.type = (newbuttons & mask) ? EV_KeyDown : EV_KeyUp;
|
||||
D_PostEvent(&ev);
|
||||
Joy_GenerateButtonEvent(newbuttons & mask, static_cast<EKeyCodes>(keys[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,30 @@
|
|||
#define M_JOY_H
|
||||
|
||||
#include "basics.h"
|
||||
#include "keydef.h"
|
||||
#include "tarray.h"
|
||||
#include "c_cvars.h"
|
||||
|
||||
union CubicBezier {
|
||||
struct {
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
};
|
||||
float pts[4];
|
||||
};
|
||||
|
||||
enum EJoyCurve {
|
||||
JOYCURVE_CUSTOM = -1,
|
||||
JOYCURVE_DEFAULT,
|
||||
JOYCURVE_LINEAR,
|
||||
JOYCURVE_QUADRATIC,
|
||||
JOYCURVE_CUBIC,
|
||||
|
||||
NUM_JOYCURVE
|
||||
};
|
||||
|
||||
enum EJoyAxis
|
||||
{
|
||||
JOYAXIS_None = -1,
|
||||
|
|
@ -17,6 +38,18 @@ enum EJoyAxis
|
|||
NUM_JOYAXIS,
|
||||
};
|
||||
|
||||
extern const float JOYDEADZONE_DEFAULT;
|
||||
|
||||
extern const float JOYSENSITIVITY_DEFAULT;
|
||||
|
||||
extern const float JOYTHRESH_DEFAULT;
|
||||
|
||||
extern const float JOYTHRESH_TRIGGER;
|
||||
extern const float JOYTHRESH_STICK_X;
|
||||
extern const float JOYTHRESH_STICK_Y;
|
||||
|
||||
extern const CubicBezier JOYCURVE[NUM_JOYCURVE];
|
||||
|
||||
// Generic configuration interface for a controller.
|
||||
struct IJoystickConfig
|
||||
{
|
||||
|
|
@ -31,10 +64,16 @@ struct IJoystickConfig
|
|||
virtual EJoyAxis GetAxisMap(int axis) = 0;
|
||||
virtual const char *GetAxisName(int axis) = 0;
|
||||
virtual float GetAxisScale(int axis) = 0;
|
||||
virtual float GetAxisDigitalThreshold(int axis) = 0;
|
||||
virtual EJoyCurve GetAxisResponseCurve(int axis) = 0;
|
||||
virtual float GetAxisResponseCurvePoint(int axis, int point) = 0;
|
||||
|
||||
virtual void SetAxisDeadZone(int axis, float zone) = 0;
|
||||
virtual void SetAxisMap(int axis, EJoyAxis gameaxis) = 0;
|
||||
virtual void SetAxisScale(int axis, float scale) = 0;
|
||||
virtual void SetAxisDigitalThreshold(int axis, float threshold) = 0;
|
||||
virtual void SetAxisResponseCurve(int axis, EJoyCurve preset) = 0;
|
||||
virtual void SetAxisResponseCurvePoint(int axis, int point, float value) = 0;
|
||||
|
||||
virtual bool GetEnabled() = 0;
|
||||
virtual void SetEnabled(bool enabled) = 0;
|
||||
|
|
@ -48,6 +87,8 @@ struct IJoystickConfig
|
|||
virtual bool IsAxisDeadZoneDefault(int axis) = 0;
|
||||
virtual bool IsAxisMapDefault(int axis) = 0;
|
||||
virtual bool IsAxisScaleDefault(int axis) = 0;
|
||||
virtual bool IsAxisDigitalThresholdDefault(int axis) = 0;
|
||||
virtual bool IsAxisResponseCurveDefault(int axis) = 0;
|
||||
|
||||
virtual void SetDefaultConfig() = 0;
|
||||
virtual FString GetIdentifier() = 0;
|
||||
|
|
@ -58,10 +99,12 @@ EXTERN_CVAR(Bool, use_joystick);
|
|||
bool M_LoadJoystickConfig(IJoystickConfig *joy);
|
||||
void M_SaveJoystickConfig(IJoystickConfig *joy);
|
||||
|
||||
void Joy_GenerateButtonEvent(bool down, EKeyCodes which);
|
||||
void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, int base);
|
||||
void Joy_GenerateButtonEvents(int oldbuttons, int newbuttons, int numbuttons, const int *keys);
|
||||
int Joy_XYAxesToButtons(double x, double y);
|
||||
double Joy_RemoveDeadZone(double axisval, double deadzone, uint8_t *buttons);
|
||||
double Joy_ApplyResponseCurveBezier(const CubicBezier &curve, double input);
|
||||
|
||||
// These ought to be provided by a system-specific i_input.cpp.
|
||||
void I_GetAxes(float axes[NUM_JOYAXIS]);
|
||||
|
|
|
|||
|
|
@ -172,6 +172,8 @@ xx(Sum)
|
|||
xx(Unit)
|
||||
xx(Angle)
|
||||
xx(PlusZ)
|
||||
xx(Conjugate)
|
||||
xx(Inverse)
|
||||
xx(ToVector)
|
||||
xx(Size)
|
||||
xx(Push)
|
||||
|
|
@ -247,17 +249,17 @@ xx(snd_resampler)
|
|||
xx(AlwaysRun)
|
||||
|
||||
// menu names
|
||||
xx(Mainmenu)
|
||||
xx(Episodemenu)
|
||||
xx(Skillmenu)
|
||||
xx(MainMenu)
|
||||
xx(EpisodeMenu)
|
||||
xx(SkillMenu)
|
||||
xx(Startgame)
|
||||
xx(StartgameConfirm)
|
||||
xx(StartgameConfirmed)
|
||||
xx(Loadgamemenu)
|
||||
xx(Savegamemenu)
|
||||
xx(Optionsmenu)
|
||||
xx(Quitmenu)
|
||||
xx(Savemenu)
|
||||
xx(LoadgameMenu)
|
||||
xx(SavegameMenu)
|
||||
xx(OptionsMenu)
|
||||
xx(QuitMenu)
|
||||
xx(SaveMenu)
|
||||
xx(EndGameMenu)
|
||||
xx(HelpMenu)
|
||||
xx(SoundMenu)
|
||||
|
|
@ -274,6 +276,8 @@ xx(OptionMenuItemColorPicker)
|
|||
xx(OptionMenuItemStaticText)
|
||||
xx(OptionMenuItemStaticTextSwitchable)
|
||||
|
||||
xx(Handler)
|
||||
|
||||
xx(Color)
|
||||
|
||||
xx(Mididevices)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ enum
|
|||
PRINT_NONOTIFY = 1024, // Flag - do not add to notify buffer
|
||||
PRINT_NOLOG = 2048, // Flag - do not print to log file
|
||||
PRINT_NOTIFY = 4096, // Flag - add to game-native notify display - messages without this only go to the generic notification buffer.
|
||||
PRINT_NODAPEVENT = 8192, // Flag - do not emit the message as DAP debugger output.
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ std2:
|
|||
/* Other keywords from UnrealScript */
|
||||
'abstract' { RET(TK_Abstract); }
|
||||
'foreach' { RET(ParseVersion >= MakeVersion(4, 10, 0)? TK_ForEach : TK_Identifier); }
|
||||
'unsafe' { RET(ParseVersion >= MakeVersion(4, 14, 2)? TK_Unsafe : TK_Identifier); }
|
||||
'unsafe' { RET(ParseVersion >= MakeVersion(4, 15, 1)? TK_Unsafe : TK_Identifier); }
|
||||
'true' { RET(TK_True); }
|
||||
'false' { RET(TK_False); }
|
||||
'none' { RET(TK_None); }
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
#if !defined(__sun) || !defined(__sun__)
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval)
|
||||
{
|
||||
int32_t vv = value;
|
||||
|
|
@ -1060,6 +1061,7 @@ FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* def
|
|||
value = (int8_t)vv;
|
||||
return arc;
|
||||
}
|
||||
#endif
|
||||
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value, int8_t *defval)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -230,7 +230,9 @@ public:
|
|||
FString mLumpName;
|
||||
};
|
||||
|
||||
#if !defined(__sun) || !defined(__sun__)
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval);
|
||||
#endif
|
||||
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_t *defval);
|
||||
|
|
|
|||
|
|
@ -51,15 +51,22 @@ public:
|
|||
virtual ~FStartupScreen() = default;
|
||||
|
||||
virtual void Progress() {}
|
||||
|
||||
virtual void NetInit(const char *message, int num_players) {}
|
||||
virtual void NetProgress(int count) {}
|
||||
virtual void NetDone() {}
|
||||
virtual void NetClose() {}
|
||||
virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) { return false; }
|
||||
virtual void AppendStatusLine(const char* status) {}
|
||||
virtual void LoadingStatus(const char* message, int colors) {}
|
||||
|
||||
virtual void NetInit(const char* message, bool host) {}
|
||||
virtual void NetMessage(const char* message) {}
|
||||
virtual void NetConnect(int client, const char* name, unsigned flags, int status) {}
|
||||
virtual void NetUpdate(int client, int status) {}
|
||||
virtual void NetDisconnect(int client) {}
|
||||
virtual void NetProgress(int cur, int limit) {}
|
||||
virtual void NetDone() {}
|
||||
virtual void NetClose() {}
|
||||
virtual bool ShouldStartNet() { return false; }
|
||||
virtual int GetNetKickClient() { return -1; }
|
||||
virtual int GetNetBanClient() { return -1; }
|
||||
virtual bool NetLoop(bool (*loopCallback)(void *), void *data) { return false; }
|
||||
|
||||
protected:
|
||||
int MaxPos, CurPos, NotchPos;
|
||||
};
|
||||
|
|
@ -70,15 +77,20 @@ public:
|
|||
FBasicStartupScreen(int max_progress);
|
||||
~FBasicStartupScreen();
|
||||
|
||||
void Progress();
|
||||
void NetInit(const char* message, int num_players);
|
||||
void NetProgress(int count);
|
||||
void NetMessage(const char* format, ...); // cover for printf
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
bool NetLoop(bool (*timer_callback)(void*), void* userdata);
|
||||
protected:
|
||||
int NetMaxPos, NetCurPos;
|
||||
void Progress() override;
|
||||
|
||||
void NetInit(const char* message, bool host) override;
|
||||
void NetMessage(const char* message) override;
|
||||
void NetConnect(int client, const char* name, unsigned flags, int status) override;
|
||||
void NetUpdate(int client, int status) override;
|
||||
void NetDisconnect(int client) override;
|
||||
void NetProgress(int cur, int limit) override;
|
||||
void NetDone() override;
|
||||
void NetClose() override;
|
||||
bool ShouldStartNet() override;
|
||||
int GetNetKickClient() override;
|
||||
int GetNetBanClient() override;
|
||||
bool NetLoop(bool (*loopCallback)(void*), void* data) override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ public:
|
|||
static uint32_t LumpNameHash (const char *name); // [RH] Create hash key from an 8-char name
|
||||
|
||||
ptrdiff_t FileLength (int lump) const;
|
||||
uint32_t FileHash (int lump) const;
|
||||
int GetFileFlags (int lump); // Return the flags for this lump
|
||||
const char* GetFileShortName(int lump) const;
|
||||
const char *GetFileFullName (int lump, bool returnshort = true) const; // [RH] Returns the lump's full name
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ public:
|
|||
return (entry < NumLumps) ? Entries[entry].Position : 0;
|
||||
}
|
||||
|
||||
size_t GetEntryHash(uint32_t entry)
|
||||
{
|
||||
return (entry < NumLumps) ? Entries[entry].CRC32 : 0;
|
||||
}
|
||||
|
||||
// default is the safest reader type.
|
||||
virtual FileReader GetEntryReader(uint32_t entry, int readertype = READER_NEW, int flags = READERFLAG_SEEKABLE);
|
||||
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ FileData F7ZFile::Read(uint32_t entry)
|
|||
FileReader F7ZFile::GetEntryReader(uint32_t entry, int, int)
|
||||
{
|
||||
FileReader fr;
|
||||
if (entry < 0 || entry >= NumLumps) return fr;
|
||||
if (entry >= NumLumps) return fr;
|
||||
auto buffer = Read(entry);
|
||||
if (buffer.size() > 0)
|
||||
fr.OpenMemoryArray(buffer);
|
||||
|
|
|
|||
|
|
@ -759,6 +759,24 @@ ptrdiff_t FileSystem::FileLength (int lump) const
|
|||
return (int)lump_p.resfile->Length(lump_p.resindex);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FileHash
|
||||
//
|
||||
// Returns the file hash.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
uint32_t FileSystem::FileHash (int lump) const
|
||||
{
|
||||
if ((size_t)lump >= NumEntries)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
const auto &lump_p = FileInfo[lump];
|
||||
return lump_p.resfile->GetEntryHash(lump_p.resindex);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
|
@ -1264,13 +1282,16 @@ void FileSystem::ReadFile (int lump, void *dest)
|
|||
{
|
||||
std::unique_lock lock(Mutex);
|
||||
auto lumpr = OpenFileReader (lump);
|
||||
auto size = lumpr.GetLength ();
|
||||
auto numread = lumpr.Read (dest, size);
|
||||
|
||||
if (numread != size)
|
||||
if (lumpr.mReader != nullptr)
|
||||
{
|
||||
throw FileSystemException("W_ReadFile: only read %td of %td on '%s'\n",
|
||||
numread, size, FileInfo[lump].LongName);
|
||||
auto size = lumpr.GetLength ();
|
||||
auto numread = lumpr.Read (dest, size);
|
||||
|
||||
if (numread != size)
|
||||
{
|
||||
throw FileSystemException("W_ReadFile: only read %td of %td on '%s'\n",
|
||||
numread, size, FileInfo[lump].LongName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,56 @@ DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDeadZone)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisDigitalThreshold)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
ACTION_RETURN_FLOAT(self->GetAxisDigitalThreshold(axis));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDigitalThreshold)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
PARAM_FLOAT(dt);
|
||||
self->SetAxisDigitalThreshold(axis, (float)dt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisResponseCurve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
ACTION_RETURN_INT(self->GetAxisResponseCurve(axis));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisResponseCurve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
PARAM_INT(curve);
|
||||
self->SetAxisResponseCurve(axis, (EJoyCurve)curve);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisResponseCurvePoint)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
PARAM_INT(point);
|
||||
ACTION_RETURN_FLOAT(self->GetAxisResponseCurvePoint(axis, point));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisResponseCurvePoint)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
PARAM_INT(axis);
|
||||
PARAM_INT(point);
|
||||
PARAM_FLOAT(value);
|
||||
self->SetAxisResponseCurvePoint(axis, point, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisMap)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
|
||||
|
|
|
|||
|
|
@ -670,6 +670,9 @@ bool M_Responder (event_t *ev)
|
|||
case GK_BACKSPACE: mkey = MKEY_Clear; break;
|
||||
case GK_PGUP: mkey = MKEY_PageUp; break;
|
||||
case GK_PGDN: mkey = MKEY_PageDown; break;
|
||||
case GK_HOME: mkey = MKEY_Home; break;
|
||||
case GK_END: mkey = MKEY_End; break;
|
||||
|
||||
default:
|
||||
if (!keyup)
|
||||
{
|
||||
|
|
@ -682,7 +685,7 @@ bool M_Responder (event_t *ev)
|
|||
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
|
||||
{
|
||||
// eat blocked controller events without dispatching them.
|
||||
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers) return true;
|
||||
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers && ev->type == EV_KeyDown) return true;
|
||||
|
||||
keyup = ev->type == EV_KeyUp;
|
||||
|
||||
|
|
@ -776,7 +779,7 @@ bool M_Responder (event_t *ev)
|
|||
if (ev->data1 == KEY_ESCAPE)
|
||||
{
|
||||
M_StartControlPanel(true);
|
||||
M_SetMenu(NAME_Mainmenu, -1);
|
||||
M_SetMenu(NAME_MainMenu, -1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -785,7 +788,7 @@ bool M_Responder (event_t *ev)
|
|||
ConsoleState != c_down && gamestate != GS_LEVEL && m_use_mouse)
|
||||
{
|
||||
M_StartControlPanel(true);
|
||||
M_SetMenu(NAME_Mainmenu, -1);
|
||||
M_SetMenu(NAME_MainMenu, -1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ enum EMenuKey
|
|||
MKEY_Right,
|
||||
MKEY_PageUp,
|
||||
MKEY_PageDown,
|
||||
MKEY_Home,
|
||||
MKEY_End,
|
||||
//----------------- Keys past here do not repeat.
|
||||
MKEY_Enter,
|
||||
MKEY_Back, // Back to previous menu
|
||||
|
|
|
|||
|
|
@ -511,20 +511,33 @@ static void DoParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc, bool &s
|
|||
}
|
||||
else if (args[i]->isIntCompatible())
|
||||
{
|
||||
char *endp;
|
||||
int v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
int v;
|
||||
|
||||
if (sc.Compare("true"))
|
||||
{
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
v = 1;
|
||||
}
|
||||
else if (sc.Compare("false"))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *endp;
|
||||
v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args[i] == TypeBool) v = !!v;
|
||||
params.Push(v);
|
||||
if (args[i] == TypeBool) v = !!v;
|
||||
params.Push(v);
|
||||
}
|
||||
else if (args[i]->isFloat())
|
||||
{
|
||||
|
|
@ -1114,19 +1127,32 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc, int i
|
|||
}
|
||||
else if (args[i]->isIntCompatible())
|
||||
{
|
||||
char *endp;
|
||||
int v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
int v;
|
||||
|
||||
if (sc.Compare("true"))
|
||||
{
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
v = 1;
|
||||
}
|
||||
else if (sc.Compare("false"))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *endp;
|
||||
v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
}
|
||||
// Color ranges need to be marked for option menu items to support an older feature where a boolean number could be passed instead.
|
||||
v |= 0x12340000;
|
||||
}
|
||||
// Color ranges need to be marked for option menu items to support an older feature where a boolean number could be passed instead.
|
||||
v |= 0x12340000;
|
||||
}
|
||||
if (args[i] == TypeBool) v = !!v;
|
||||
params.Push(v);
|
||||
|
|
@ -1403,16 +1429,29 @@ static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc)
|
|||
}
|
||||
else if (args[i]->isIntCompatible())
|
||||
{
|
||||
char* endp;
|
||||
int v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
int v;
|
||||
|
||||
if (sc.Compare("true"))
|
||||
{
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
v = 1;
|
||||
}
|
||||
else if (sc.Compare("false"))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* endp;
|
||||
v = (int)strtoll(sc.String, &endp, 0);
|
||||
if (*endp != 0)
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
// special check for font color ranges.
|
||||
v = V_FindFontColor(sc.String);
|
||||
if (v == CR_UNTRANSLATED && !sc.Compare("untranslated"))
|
||||
{
|
||||
// todo: check other data types that may get used.
|
||||
sc.ScriptError("Integer expected, got %s", sc.String);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args[i] == TypeBool) v = !!v;
|
||||
|
|
|
|||
|
|
@ -47,9 +47,10 @@ CVAR(Bool, m_quickexit, false, CVAR_ARCHIVE)
|
|||
typedef void(*hfunc)();
|
||||
DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTERTYPE(Handler, hfunc);
|
||||
Handler();
|
||||
PARAM_SELF_PROLOGUE(DMenu);
|
||||
auto handler = reinterpret_cast<hfunc>(self->PointerVar<void>(NAME_Handler));
|
||||
if (handler != nullptr)
|
||||
handler();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +69,8 @@ DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode,
|
|||
|
||||
IFVIRTUALPTRNAME(p, NAME_MessageBoxMenu, Init)
|
||||
{
|
||||
VMValue params[] = { p, parent, &namestr, messagemode, playsound, action.GetIndex(), reinterpret_cast<void*>(handler) };
|
||||
p->PointerVar<void>(NAME_Handler) = reinterpret_cast<void*>(handler);
|
||||
VMValue params[] = { p, parent, &namestr, messagemode, playsound, action.GetIndex() };
|
||||
VMCall(func, params, countof(params), nullptr, 0);
|
||||
return (DMenu*)p;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,15 @@
|
|||
**
|
||||
*/
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "v_video.h"
|
||||
#include "menu.h"
|
||||
#include "vm.h"
|
||||
|
||||
// by request of Nash
|
||||
CVARD(Bool, silence_menu_scroll, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE, "Silences cursor movement when using mouse wheel");
|
||||
CVARD(Bool, silence_menu_hover, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE, "Silences cursor movement when implicitly selecting with mouse");
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
|
|
@ -65,4 +70,4 @@ DEFINE_ACTION_FUNCTION_NATIVE(_OptionMenuItemOption, SetCVarDescription, SetCVar
|
|||
PARAM_STRING(label);
|
||||
SetCVarDescription(cv, &label);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,11 +48,14 @@
|
|||
#include "savegamemanager.h"
|
||||
#include "m_argv.h"
|
||||
#include "i_specialpaths.h"
|
||||
#include "i_interface.h"
|
||||
|
||||
CVAR(String, save_dir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_SYSTEM_ONLY);
|
||||
FString SavegameFolder;
|
||||
CVAR(Int, save_sort_order, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
extern bool netgame;
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Save data maintenance
|
||||
|
|
@ -111,6 +114,11 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot)
|
|||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
|
||||
PARAM_INT(sel);
|
||||
if (!DMenu::InMenu)
|
||||
{
|
||||
Printf("Saves can only be deleted from within a menu\n");
|
||||
ACTION_RETURN_INT(-1);
|
||||
}
|
||||
ACTION_RETURN_INT(self->RemoveSaveSlot(sel));
|
||||
}
|
||||
|
||||
|
|
@ -184,6 +192,7 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit
|
|||
{
|
||||
node->SaveTitle = title;
|
||||
node->CreationTime = myasctime();
|
||||
node->UUID = GameUUID;
|
||||
node->bOldVersion = false;
|
||||
node->bMissingWads = false;
|
||||
|
||||
|
|
@ -203,6 +212,7 @@ void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &tit
|
|||
auto node = new FSaveGameNode;
|
||||
node->SaveTitle = title;
|
||||
node->CreationTime = myasctime();
|
||||
node->UUID = GameUUID;
|
||||
node->Filename = file;
|
||||
node->bOldVersion = false;
|
||||
node->bMissingWads = false;
|
||||
|
|
@ -264,12 +274,47 @@ void FSavegameManagerBase::DoSave(int Selected, const char *savegamestring)
|
|||
FString filename;
|
||||
int i;
|
||||
|
||||
for (i = 0;; ++i)
|
||||
if (netgame)
|
||||
{
|
||||
filename = BuildSaveName("save", i);
|
||||
if (!FileExists(filename))
|
||||
// For netgames it's usually a bad idea to use the default savexx names, so instead
|
||||
// sanitize the description to use as a name.
|
||||
filename = savegamestring;
|
||||
FixPathSeperator(filename);
|
||||
bool failed = false;
|
||||
if (filename[0] == '/')
|
||||
{
|
||||
break;
|
||||
Printf("saving to an absolute path is not allowed\n");
|
||||
failed = true;
|
||||
}
|
||||
else if (filename.IndexOf("..") >= 0)
|
||||
{
|
||||
Printf("'..' not allowed in file names\n");
|
||||
failed = true;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
// block all invalid characters for Windows file names
|
||||
else if (filename.IndexOfAny(":?*<>|") >= 0)
|
||||
{
|
||||
Printf("file name contains invalid characters\n");
|
||||
failed = true;
|
||||
}
|
||||
#endif
|
||||
if (failed)
|
||||
{
|
||||
M_ClearMenus();
|
||||
return;
|
||||
}
|
||||
filename = G_BuildSaveName(filename.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0;; ++i)
|
||||
{
|
||||
filename = BuildSaveName("save", i);
|
||||
if (!FileExists(filename))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PerformSaveGame(filename.GetChars(), savegamestring);
|
||||
|
|
@ -518,6 +563,27 @@ bool FSavegameManagerBase::RemoveNewSaveNode()
|
|||
return false;
|
||||
}
|
||||
|
||||
int FSavegameManagerBase::RemoveUUIDSaveSlots()
|
||||
{
|
||||
if (GameUUID.IsEmpty())
|
||||
return -1;
|
||||
|
||||
// Make sure there's any saves in the list first.
|
||||
if (!SaveGames.Size())
|
||||
ReadSaveStrings();
|
||||
|
||||
int index = -1;
|
||||
for (int i = SaveGames.Size() - 1; i >= 0; --i)
|
||||
{
|
||||
auto& save = SaveGames[i];
|
||||
if (save == &NewSaveNode || save->UUID.Compare(GameUUID))
|
||||
continue;
|
||||
|
||||
index = RemoveSaveSlot(i);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
|
||||
|
|
@ -539,9 +605,15 @@ DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData)
|
|||
ACTION_RETURN_INT(self->ExtractSaveData(sel));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveUUIDSaveSlots)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
|
||||
ACTION_RETURN_INT(self->RemoveUUIDSaveSlots());
|
||||
}
|
||||
|
||||
DEFINE_FIELD(FSaveGameNode, SaveTitle);
|
||||
DEFINE_FIELD(FSaveGameNode, Filename);
|
||||
DEFINE_FIELD(FSaveGameNode, UUID);
|
||||
DEFINE_FIELD(FSaveGameNode, bOldVersion);
|
||||
DEFINE_FIELD(FSaveGameNode, bMissingWads);
|
||||
DEFINE_FIELD(FSaveGameNode, bNoDelete);
|
||||
|
|
@ -561,7 +633,15 @@ FString G_GetSavegamesFolder()
|
|||
FString name;
|
||||
bool usefilter;
|
||||
|
||||
if (const char* const dir = Args->CheckValue("-savedir"))
|
||||
// Always use the netgame folder for multiplayer games to prevent any singleplayer saves
|
||||
// from being overridden by someone else. Also makes it easier for everyone to load from
|
||||
// it.
|
||||
if (netgame)
|
||||
{
|
||||
name = M_GetSavegamesPath();
|
||||
usefilter = true;
|
||||
}
|
||||
else if (const char* const dir = Args->CheckValue("-savedir"))
|
||||
{
|
||||
name = dir;
|
||||
usefilter = false; //-savedir specifies an absolute save directory path.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ struct FSaveGameNode
|
|||
FString SaveTitle;
|
||||
FString Filename;
|
||||
FString CreationTime;
|
||||
FString UUID;
|
||||
bool bOldVersion = false;
|
||||
bool bMissingWads = false;
|
||||
bool bNoDelete = false;
|
||||
|
|
@ -57,6 +58,7 @@ public:
|
|||
FSaveGameNode *GetSavegame(int i);
|
||||
void InsertNewSaveNode();
|
||||
bool RemoveNewSaveNode();
|
||||
int RemoveUUIDSaveSlots();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct FSpriteModelFrame
|
|||
float xrotate, yrotate, zrotate;
|
||||
float rotationCenterX, rotationCenterY, rotationCenterZ;
|
||||
float rotationSpeed;
|
||||
float viewModelFOV;
|
||||
private:
|
||||
unsigned int flags;
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -333,10 +333,22 @@ void DObject::Destroy ()
|
|||
GC::WriteBarrier(this);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DObject, Destroy)
|
||||
// This will be here until prediction can be reworked.
|
||||
// TODO: Fix prediction by serializing instead of using this terrible memcpy method.
|
||||
bool bPredictionGuard = false;
|
||||
|
||||
static void NativeDestroy(DObject* self)
|
||||
{
|
||||
if (bPredictionGuard && !(self->ObjectFlags & OF_ClientSide) && ((self->ObjectFlags & OF_Networked) || self->IsKindOf(NAME_Thinker)))
|
||||
DPrintf(DMSG_WARNING, TEXTCOLOR_RED "Destroyed non-client-side Object %s while predicting\n", self->GetClass()->TypeName.GetChars());
|
||||
if (!(self->ObjectFlags & OF_EuthanizeMe))
|
||||
self->Destroy();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, Destroy, NativeDestroy)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DObject);
|
||||
self->Destroy();
|
||||
NativeDestroy(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +429,67 @@ size_t DObject::PropagateMark()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void DObject::ClearNativePointerFields(const TArrayView<FName>& types)
|
||||
{
|
||||
auto cls = GetClass();
|
||||
if (cls->VMType == nullptr)
|
||||
return;
|
||||
|
||||
auto it = cls->VMType->Symbols.GetIterator();
|
||||
TMap<FName, PSymbol*>::Pair* sym = nullptr;
|
||||
while (it.NextPair(sym))
|
||||
{
|
||||
auto field = dyn_cast<PField>(sym->Value);
|
||||
if (field == nullptr)
|
||||
continue;
|
||||
|
||||
PType* base = field->Type;
|
||||
PType* t = base;
|
||||
if (base->isArray() && !base->isStaticArray())
|
||||
t = static_cast<PArray*>(base)->ElementType;
|
||||
else if (base->isDynArray())
|
||||
t = static_cast<PDynArray*>(base)->ElementType;
|
||||
else if (base->isMap())
|
||||
t = static_cast<PMap*>(base)->ValueType;
|
||||
|
||||
if (!t->isRealPointer())
|
||||
continue;
|
||||
|
||||
auto pType = static_cast<PPointer*>(t)->PointedType;
|
||||
if (!pType->isStruct() || !static_cast<PStruct*>(pType)->isNative || types.Find(static_cast<PStruct*>(pType)->TypeName) >= types.Size())
|
||||
continue;
|
||||
|
||||
if (base->isArray() && !base->isStaticArray())
|
||||
{
|
||||
auto arr = (void**)ScriptVar(sym->Key, nullptr);
|
||||
const size_t count = static_cast<PArray*>(base)->ElementCount;
|
||||
for (size_t i = 0u; i < count; ++i)
|
||||
arr[i] = nullptr;
|
||||
}
|
||||
else if (base->isDynArray())
|
||||
{
|
||||
static_cast<TArray<void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
}
|
||||
else if (base->isMap())
|
||||
{
|
||||
if (static_cast<PMap*>(base)->BackingClass == PMap::MAP_I32_PTR)
|
||||
static_cast<ZSMap<int, void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
else
|
||||
static_cast<ZSMap<FString, void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
PointerVar<void>(sym->Key) = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
template<typename M>
|
||||
static void MapPointerSubstitution(M *map, size_t &changed, DObject *old, DObject *notOld, const bool shouldSwap)
|
||||
{
|
||||
|
|
@ -573,6 +646,8 @@ void DObject::Serialize(FSerializer &arc)
|
|||
SerializeFlag("justspawned", OF_JustSpawned);
|
||||
SerializeFlag("spawned", OF_Spawned);
|
||||
SerializeFlag("networked", OF_Networked);
|
||||
SerializeFlag("clientside", OF_ClientSide);
|
||||
SerializeFlag("travelling", OF_Travelling);
|
||||
|
||||
ObjectFlags |= OF_SerialSuccess;
|
||||
|
||||
|
|
@ -668,7 +743,7 @@ void NetworkEntityManager::SetClientNetworkEntity(DObject* mo, const unsigned in
|
|||
|
||||
void NetworkEntityManager::AddNetworkEntity(DObject* const ent)
|
||||
{
|
||||
if (ent->IsNetworked())
|
||||
if (ent->IsNetworked() || ent->IsClientside())
|
||||
return;
|
||||
|
||||
// Slot 0 is reserved for the world.
|
||||
|
|
@ -758,6 +833,18 @@ DEFINE_ACTION_FUNCTION_NATIVE(DObject, GetNetworkID, GetNetworkID)
|
|||
ACTION_RETURN_INT(self->GetNetworkID());
|
||||
}
|
||||
|
||||
static int IsClientside(DObject* self)
|
||||
{
|
||||
return self->IsClientside();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DObject, IsClientside, IsClientside)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(DObject);
|
||||
|
||||
ACTION_RETURN_BOOL(self->IsClientside());
|
||||
}
|
||||
|
||||
static void EnableNetworking(DObject* const self, const bool enable)
|
||||
{
|
||||
self->EnableNetworking(enable);
|
||||
|
|
|
|||
|
|
@ -245,6 +245,9 @@ public:
|
|||
template<class T> T*& PointerVar(FName field);
|
||||
inline int* IntArray(FName field);
|
||||
|
||||
// Make sure native data is wiped correctly since it has no read barriers.
|
||||
void ClearNativePointerFields(const TArrayView<FName>& types);
|
||||
|
||||
// This is only needed for swapping out PlayerPawns and absolutely nothing else!
|
||||
virtual size_t PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail);
|
||||
|
||||
|
|
@ -359,12 +362,15 @@ private:
|
|||
public:
|
||||
inline bool IsNetworked() const { return (ObjectFlags & OF_Networked); }
|
||||
inline uint32_t GetNetworkID() const { return _networkID; }
|
||||
inline bool IsClientside() const { return (ObjectFlags & OF_ClientSide); }
|
||||
void SetNetworkID(const uint32_t id);
|
||||
void ClearNetworkID();
|
||||
void RemoveFromNetwork();
|
||||
virtual void EnableNetworking(const bool enable);
|
||||
};
|
||||
|
||||
extern bool bPredictionGuard;
|
||||
|
||||
// This is the only method aside from calling CreateNew that should be used for creating DObjects
|
||||
// to ensure that the Class pointer is always set.
|
||||
template<typename T, typename... Args>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ enum EObjectFlags
|
|||
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
|
||||
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
|
||||
OF_Networked = 1 << 14, // Object has a unique network identifier that makes it synchronizable between all clients.
|
||||
OF_ClientSide = 1 << 15, // Object is owned by a specific client rather than the server
|
||||
OF_Travelling = 1 << 16, // Object is currently moving from one level to another
|
||||
};
|
||||
|
||||
template<class T> class TObjPtr;
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ DObject *PClass::CreateNew()
|
|||
ConstructNative (mem);
|
||||
|
||||
if (Defaults != nullptr)
|
||||
((DObject *)mem)->ObjectFlags |= ((DObject *)Defaults)->ObjectFlags & OF_Transient;
|
||||
((DObject *)mem)->ObjectFlags |= ((DObject *)Defaults)->ObjectFlags & (OF_Transient | OF_ClientSide);
|
||||
|
||||
((DObject *)mem)->SetClass (const_cast<PClass *>(this));
|
||||
InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
|
||||
|
|
|
|||
|
|
@ -94,15 +94,23 @@ public:
|
|||
virtual EJoyAxis GetAxisMap(int axis);
|
||||
virtual const char* GetAxisName(int axis);
|
||||
virtual float GetAxisScale(int axis);
|
||||
float GetAxisDigitalThreshold(int axis);
|
||||
EJoyCurve GetAxisResponseCurve(int axis);
|
||||
float GetAxisResponseCurvePoint(int axis, int point);
|
||||
|
||||
virtual void SetAxisDeadZone(int axis, float deadZone);
|
||||
virtual void SetAxisMap(int axis, EJoyAxis gameAxis);
|
||||
virtual void SetAxisScale(int axis, float scale);
|
||||
void SetAxisDigitalThreshold(int axis, float threshold);
|
||||
void SetAxisResponseCurve(int axis, EJoyCurve preset);
|
||||
void SetAxisResponseCurvePoint(int axis, int point, float value);
|
||||
|
||||
virtual bool IsSensitivityDefault();
|
||||
virtual bool IsAxisDeadZoneDefault(int axis);
|
||||
virtual bool IsAxisMapDefault(int axis);
|
||||
virtual bool IsAxisScaleDefault(int axis);
|
||||
bool IsAxisDigitalThresholdDefault(int axis);
|
||||
bool IsAxisResponseCurveDefault(int axis);
|
||||
|
||||
virtual bool GetEnabled();
|
||||
virtual void SetEnabled(bool enabled);
|
||||
|
|
@ -146,6 +154,11 @@ private:
|
|||
float defaultDeadZone;
|
||||
float sensitivity;
|
||||
float defaultSensitivity;
|
||||
float digitalThreshold;
|
||||
float defaultDigitalThreshold;
|
||||
EJoyCurve responseCurvePreset;
|
||||
EJoyCurve defaultResponseCurvePreset;
|
||||
CubicBezier responseCurve;
|
||||
|
||||
EJoyAxis gameAxis;
|
||||
EJoyAxis defaultGameAxis;
|
||||
|
|
@ -177,10 +190,6 @@ private:
|
|||
|
||||
io_object_t m_notification;
|
||||
|
||||
|
||||
static const float DEFAULT_DEADZONE;
|
||||
static const float DEFAULT_SENSITIVITY;
|
||||
|
||||
void ProcessAxes();
|
||||
bool ProcessAxis (const IOHIDEventStruct& event);
|
||||
bool ProcessButton(const IOHIDEventStruct& event);
|
||||
|
|
@ -200,10 +209,6 @@ private:
|
|||
};
|
||||
|
||||
|
||||
const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f;
|
||||
const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f;
|
||||
|
||||
|
||||
IOHIDDeviceInterface** CreateDeviceInterface(const io_object_t device)
|
||||
{
|
||||
IOCFPlugInInterface** plugInInterface = NULL;
|
||||
|
|
@ -286,7 +291,7 @@ IOHIDQueueInterface** CreateDeviceQueue(IOHIDDeviceInterface** const interface)
|
|||
IOKitJoystick::IOKitJoystick(const io_object_t device)
|
||||
: m_interface(CreateDeviceInterface(device))
|
||||
, m_queue(CreateDeviceQueue(m_interface))
|
||||
, m_sensitivity(DEFAULT_SENSITIVITY)
|
||||
, m_sensitivity(JOYSENSITIVITY_DEFAULT)
|
||||
, m_enabled(true)
|
||||
, m_useAxesPolling(true)
|
||||
, m_notification(0)
|
||||
|
|
@ -386,6 +391,21 @@ float IOKitJoystick::GetAxisScale(int axis)
|
|||
return IS_AXIS_VALID ? m_axes[axis].sensitivity : 0.0f;
|
||||
}
|
||||
|
||||
float IOKitJoystick::GetAxisDigitalThreshold(int axis)
|
||||
{
|
||||
return IS_AXIS_VALID ? m_axes[axis].digitalThreshold : JOYTHRESH_DEFAULT;
|
||||
}
|
||||
|
||||
EJoyCurve IOKitJoystick::GetAxisResponseCurve(int axis)
|
||||
{
|
||||
return IS_AXIS_VALID ? m_axes[axis].responseCurvePreset : JOYCURVE_DEFAULT;
|
||||
}
|
||||
|
||||
float IOKitJoystick::GetAxisResponseCurvePoint(int axis, int point)
|
||||
{
|
||||
return ( IS_AXIS_VALID && unsigned(point) < 4 )? m_axes[axis].responseCurve.pts[point] : 0;
|
||||
}
|
||||
|
||||
void IOKitJoystick::SetAxisDeadZone(int axis, float deadZone)
|
||||
{
|
||||
if (IS_AXIS_VALID)
|
||||
|
|
@ -412,10 +432,37 @@ void IOKitJoystick::SetAxisScale(int axis, float scale)
|
|||
}
|
||||
}
|
||||
|
||||
void IOKitJoystick::SetAxisDigitalThreshold(int axis, float threshold)
|
||||
{
|
||||
if (IS_AXIS_VALID)
|
||||
{
|
||||
m_axes[axis].digitalThreshold = threshold;
|
||||
}
|
||||
}
|
||||
|
||||
void IOKitJoystick::SetAxisResponseCurve(int axis, EJoyCurve preset)
|
||||
{
|
||||
if (IS_AXIS_VALID)
|
||||
{
|
||||
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
|
||||
m_axes[axis].responseCurvePreset = preset;
|
||||
if (preset == JOYCURVE_CUSTOM) return;
|
||||
m_axes[axis].responseCurve = JOYCURVE[preset];
|
||||
}
|
||||
}
|
||||
|
||||
void IOKitJoystick::SetAxisResponseCurvePoint(int axis, int point, float value)
|
||||
{
|
||||
if (IS_AXIS_VALID && unsigned(point) < 4)
|
||||
{
|
||||
m_axes[axis].responseCurvePreset = JOYCURVE_CUSTOM;
|
||||
m_axes[axis].responseCurve.pts[point] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool IOKitJoystick::IsSensitivityDefault()
|
||||
{
|
||||
return DEFAULT_SENSITIVITY == m_sensitivity;
|
||||
return JOYSENSITIVITY_DEFAULT == m_sensitivity;
|
||||
}
|
||||
|
||||
bool IOKitJoystick::IsAxisDeadZoneDefault(int axis)
|
||||
|
|
@ -439,7 +486,19 @@ bool IOKitJoystick::IsAxisScaleDefault(int axis)
|
|||
: true;
|
||||
}
|
||||
|
||||
bool IOKitJoystick::IsAxisDigitalThresholdDefault(int axis)
|
||||
{
|
||||
return IS_AXIS_VALID
|
||||
? (m_axes[axis].digitalThreshold == m_axes[axis].defaultDigitalThreshold)
|
||||
: true;
|
||||
}
|
||||
|
||||
bool IOKitJoystick::IsAxisResponseCurveDefault(int axis)
|
||||
{
|
||||
return IS_AXIS_VALID
|
||||
? m_axes[axis].responseCurvePreset == m_axes[axis].defaultResponseCurvePreset
|
||||
: true;
|
||||
}
|
||||
|
||||
bool IOKitJoystick::GetEnabled()
|
||||
{
|
||||
|
|
@ -454,15 +513,18 @@ void IOKitJoystick::SetEnabled(bool enabled)
|
|||
|
||||
void IOKitJoystick::SetDefaultConfig()
|
||||
{
|
||||
m_sensitivity = DEFAULT_SENSITIVITY;
|
||||
m_sensitivity = JOYSENSITIVITY_DEFAULT;
|
||||
|
||||
const size_t axisCount = m_axes.Size();
|
||||
|
||||
for (size_t i = 0; i < axisCount; ++i)
|
||||
{
|
||||
m_axes[i].deadZone = DEFAULT_DEADZONE;
|
||||
m_axes[i].sensitivity = DEFAULT_SENSITIVITY;
|
||||
m_axes[i].deadZone = JOYDEADZONE_DEFAULT;
|
||||
m_axes[i].sensitivity = JOYSENSITIVITY_DEFAULT;
|
||||
m_axes[i].gameAxis = JOYAXIS_None;
|
||||
m_axes[i].digitalThreshold = JOYTHRESH_DEFAULT;
|
||||
m_axes[i].responseCurvePreset = JOYCURVE_DEFAULT;
|
||||
m_axes[i].responseCurve = JOYCURVE[JOYCURVE_DEFAULT];
|
||||
}
|
||||
|
||||
// Two axes? Horizontal is yaw and vertical is forward.
|
||||
|
|
@ -470,7 +532,10 @@ void IOKitJoystick::SetDefaultConfig()
|
|||
if (2 == axisCount)
|
||||
{
|
||||
m_axes[0].gameAxis = JOYAXIS_Yaw;
|
||||
m_axes[0].digitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
m_axes[1].gameAxis = JOYAXIS_Forward;
|
||||
m_axes[1].digitalThreshold = JOYTHRESH_STICK_Y;
|
||||
}
|
||||
|
||||
// Three axes? First two are movement, third is yaw.
|
||||
|
|
@ -478,8 +543,13 @@ void IOKitJoystick::SetDefaultConfig()
|
|||
else if (axisCount >= 3)
|
||||
{
|
||||
m_axes[0].gameAxis = JOYAXIS_Side;
|
||||
m_axes[0].digitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
m_axes[1].gameAxis = JOYAXIS_Forward;
|
||||
m_axes[1].digitalThreshold = JOYTHRESH_STICK_Y;
|
||||
|
||||
m_axes[2].gameAxis = JOYAXIS_Yaw;
|
||||
m_axes[2].digitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
// Four axes? First two are movement, last two are looking around.
|
||||
|
||||
|
|
@ -487,12 +557,14 @@ void IOKitJoystick::SetDefaultConfig()
|
|||
{
|
||||
m_axes[3].gameAxis = JOYAXIS_Pitch;
|
||||
// ??? m_axes[3].sensitivity = 0.75f;
|
||||
m_axes[3].digitalThreshold = JOYTHRESH_STICK_Y;
|
||||
|
||||
// Five axes? Use the fifth one for moving up and down.
|
||||
|
||||
if (axisCount >= 5)
|
||||
{
|
||||
m_axes[4].gameAxis = JOYAXIS_Up;
|
||||
m_axes[4].digitalThreshold = JOYTHRESH_STICK_Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -504,9 +576,11 @@ void IOKitJoystick::SetDefaultConfig()
|
|||
|
||||
for (size_t i = 0; i < axisCount; ++i)
|
||||
{
|
||||
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
|
||||
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
|
||||
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
|
||||
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
|
||||
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
|
||||
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
|
||||
m_axes[i].defaultDigitalThreshold = m_axes[i].digitalThreshold;
|
||||
m_axes[i].defaultResponseCurvePreset = m_axes[i].responseCurvePreset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -602,8 +676,9 @@ void IOKitJoystick::ProcessAxes()
|
|||
const double scaledValue = scaledMin +
|
||||
(event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue);
|
||||
const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL);
|
||||
const double smoothedValue = Joy_ApplyResponseCurveBezier(axis.responseCurve, filteredValue);
|
||||
|
||||
axis.value = static_cast<float>(filteredValue * m_sensitivity * axis.sensitivity);
|
||||
axis.value = static_cast<float>(smoothedValue * m_sensitivity * axis.sensitivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -635,8 +710,9 @@ bool IOKitJoystick::ProcessAxis(const IOHIDEventStruct& event)
|
|||
const double scaledValue = scaledMin +
|
||||
(event.value - axis.minValue) * (scaledMax - scaledMin) / (axis.maxValue - axis.minValue);
|
||||
const double filteredValue = Joy_RemoveDeadZone(scaledValue, axis.deadZone, NULL);
|
||||
const double smoothedValue = Joy_ApplyResponseCurveBezier(axis.responseCurve, filteredValue);
|
||||
|
||||
axis.value = static_cast<float>(filteredValue * m_sensitivity * axis.sensitivity);
|
||||
axis.value = static_cast<float>(smoothedValue * m_sensitivity * axis.sensitivity);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
#include "st_console.h"
|
||||
#include "v_text.h"
|
||||
|
||||
CVAR(String, queryiwad_key, "", CVAR_GLOBALCONFIG | CVAR_ARCHIVE); // Currently unimplemented.
|
||||
|
||||
EXTERN_CVAR(Bool, longsavemessages)
|
||||
double PerfToSec, PerfToMillisec;
|
||||
|
||||
|
|
@ -127,22 +129,33 @@ void I_CloseMainWindow()
|
|||
I_SetMainWindowVisible(false);
|
||||
}
|
||||
|
||||
bool HoldingQueryKey(const char* key)
|
||||
{
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
||||
int I_PickIWad(WadStuff* const wads, const int numwads, const bool showwin, const int defaultiwad, int&, FString&)
|
||||
bool I_PickIWad(bool showwin, FStartupSelectionInfo& info)
|
||||
{
|
||||
if (!showwin)
|
||||
{
|
||||
return defaultiwad;
|
||||
return true;
|
||||
}
|
||||
|
||||
I_SetMainWindowVisible(false);
|
||||
|
||||
extern int I_PickIWad_Cocoa(WadStuff*, int, bool, int);
|
||||
const int result = I_PickIWad_Cocoa(wads, numwads, showwin, defaultiwad);
|
||||
const int result = I_PickIWad_Cocoa(&(*info.Wads)[0], (int)info.Wads->Size(), showwin, info.DefaultIWAD);
|
||||
|
||||
I_SetMainWindowVisible(true);
|
||||
|
||||
return result;
|
||||
if (result >= 0)
|
||||
{
|
||||
info.DefaultIWAD = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -63,10 +63,18 @@ public:
|
|||
|
||||
// FStartupScreen functionality
|
||||
void Progress(int current, int maximum);
|
||||
void NetInit(const char* message, int playerCount);
|
||||
void NetProgress(int count);
|
||||
|
||||
void NetInit(const char* const message, const bool host);
|
||||
void NetMessage(const char* const message);
|
||||
void NetConnect(const int client, const char* const name, const unsigned flags, const int status);
|
||||
void NetUpdate(const int client, const int status);
|
||||
void NetDisconnect(const int client);
|
||||
void NetProgress(const int cur, const int limit);
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
bool ShouldStartNet();
|
||||
int GetNetKickClient();
|
||||
int GetNetBanClient();
|
||||
|
||||
private:
|
||||
NSWindow* m_window;
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ void FConsoleWindow::Progress(const int current, const int maximum)
|
|||
}
|
||||
|
||||
|
||||
void FConsoleWindow::NetInit(const char* const message, const int playerCount)
|
||||
void FConsoleWindow::NetInit(const char* const message, const bool host)
|
||||
{
|
||||
if (nil == m_netView)
|
||||
{
|
||||
|
|
@ -442,19 +442,9 @@ void FConsoleWindow::NetInit(const char* const message, const int playerCount)
|
|||
// Connection progress
|
||||
m_netProgressBar = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(12.0f, 40.0f, 488.0f, 16.0f)];
|
||||
[m_netProgressBar setAutoresizingMask:NSViewWidthSizable];
|
||||
[m_netProgressBar setMaxValue:playerCount];
|
||||
|
||||
if (0 == playerCount)
|
||||
{
|
||||
// Joining game
|
||||
[m_netProgressBar setIndeterminate:YES];
|
||||
[m_netProgressBar startAnimation:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hosting game
|
||||
[m_netProgressBar setIndeterminate:NO];
|
||||
}
|
||||
[m_netProgressBar setMaxValue:0];
|
||||
[m_netProgressBar setIndeterminate:YES];
|
||||
[m_netProgressBar startAnimation:nil];
|
||||
|
||||
// Cancel network game button
|
||||
m_netAbortButton = [[NSButton alloc] initWithFrame:NSMakeRect(432.0f, 8.0f, 72.0f, 28.0f)];
|
||||
|
|
@ -486,22 +476,32 @@ void FConsoleWindow::NetInit(const char* const message, const int playerCount)
|
|||
[m_netMessageText setStringValue:[NSString stringWithUTF8String:message]];
|
||||
|
||||
m_netCurPos = 0;
|
||||
m_netMaxPos = playerCount;
|
||||
|
||||
NetProgress(1); // You always know about yourself
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetProgress(const int count)
|
||||
void FConsoleWindow::NetMessage(const char* const message)
|
||||
{
|
||||
if (0 == count)
|
||||
{
|
||||
++m_netCurPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_netCurPos = count;
|
||||
}
|
||||
[m_netMessageText setStringValue:[NSString stringWithUTF8String:message]];
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetConnect(const int client, const char* const name, const unsigned flags, const int status)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetUpdate(const int client, const int status)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetDisconnect(const int client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FConsoleWindow::NetProgress(const int cur, const int limit)
|
||||
{
|
||||
m_netCurPos = cur;
|
||||
m_netMaxPos = limit;
|
||||
if (nil == m_netView)
|
||||
{
|
||||
return;
|
||||
|
|
@ -536,3 +536,18 @@ void FConsoleWindow::NetClose()
|
|||
{
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
bool FConsoleWindow::ShouldStartNet()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int FConsoleWindow::GetNetKickClient()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FConsoleWindow::GetNetBanClient()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,14 +95,34 @@ void FBasicStartupScreen::Progress()
|
|||
}
|
||||
|
||||
|
||||
void FBasicStartupScreen::NetInit(const char* const message, const int playerCount)
|
||||
void FBasicStartupScreen::NetInit(const char* const message, const bool host)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetInit(message, playerCount);
|
||||
FConsoleWindow::GetInstance().NetInit(message, host);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetProgress(const int count)
|
||||
void FBasicStartupScreen::NetMessage(const char* const message)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetProgress(count);
|
||||
FConsoleWindow::GetInstance().NetMessage(message);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetConnect(const int client, const char* const name, const unsigned flags, const int status)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetConnect(client, name, flags, status);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetUpdate(const int client, const int status)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetUpdate(client, status);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetDisconnect(const int client)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetDisconnect(client);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetProgress(const int cur, const int limit)
|
||||
{
|
||||
FConsoleWindow::GetInstance().NetProgress(cur, limit);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetDone()
|
||||
|
|
@ -115,11 +135,26 @@ void FBasicStartupScreen::NetClose()
|
|||
FConsoleWindow::GetInstance().NetClose();
|
||||
}
|
||||
|
||||
bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData)
|
||||
bool FBasicStartupScreen::ShouldStartNet()
|
||||
{
|
||||
return FConsoleWindow::GetInstance().ShouldStartNet();
|
||||
}
|
||||
|
||||
int FBasicStartupScreen::GetNetKickClient()
|
||||
{
|
||||
return FConsoleWindow::GetInstance().GetNetKickClient();
|
||||
}
|
||||
|
||||
int FBasicStartupScreen::GetNetBanClient()
|
||||
{
|
||||
return FConsoleWindow::GetInstance().GetNetBanClient();
|
||||
}
|
||||
|
||||
bool FBasicStartupScreen::NetLoop(bool (*loopCallback)(void*), void* const data)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (timerCallback(userData))
|
||||
if (loopCallback(data))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
#include "tarray.h"
|
||||
#include "zstring.h"
|
||||
|
||||
struct ticcmd_t;
|
||||
struct WadStuff;
|
||||
struct FStartupSelectionInfo;
|
||||
|
||||
#ifndef SHARE_DIR
|
||||
#define SHARE_DIR "/usr/local/share/"
|
||||
|
|
@ -38,7 +38,8 @@ void I_PrintStr (const char *str);
|
|||
void I_SetIWADInfo ();
|
||||
|
||||
// Pick from multiple IWADs to use
|
||||
int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad, int&, FString &);
|
||||
bool HoldingQueryKey(const char* key);
|
||||
bool I_PickIWad (bool showwin, FStartupSelectionInfo& info);
|
||||
|
||||
// [RH] Checks the registry for Steam's install path, so we can scan its
|
||||
// directories for IWADs if the user purchased any through Steam.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
|
|
|
|||
|
|
@ -31,10 +31,14 @@
|
|||
**
|
||||
*/
|
||||
#include <SDL.h>
|
||||
#include <SDL_events.h>
|
||||
#include "i_input.h"
|
||||
#include "c_cvars.h"
|
||||
#include "dobject.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_joy.h"
|
||||
#include "v_video.h"
|
||||
|
||||
#include "d_eventbase.h"
|
||||
#include "d_gui.h"
|
||||
#include "c_buttons.h"
|
||||
#include "c_console.h"
|
||||
|
|
@ -46,88 +50,147 @@
|
|||
#include "engineerrors.h"
|
||||
#include "i_interface.h"
|
||||
|
||||
|
||||
static void I_CheckGUICapture ();
|
||||
static void I_CheckNativeMouse ();
|
||||
|
||||
bool GUICapture;
|
||||
static bool NativeMouse = true;
|
||||
|
||||
CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
|
||||
extern int WaitingForKey;
|
||||
|
||||
static const SDL_Keycode DIKToKeySym[256] =
|
||||
{
|
||||
0, SDLK_ESCAPE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6,
|
||||
SDLK_7, SDLK_8, SDLK_9, SDLK_0,SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
|
||||
SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i,
|
||||
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
|
||||
SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
|
||||
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH, SDLK_z, SDLK_x, SDLK_c, SDLK_v,
|
||||
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
|
||||
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
|
||||
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
|
||||
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
|
||||
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11,
|
||||
SDLK_F12, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, SDLK_F16,
|
||||
SDLK_F17, SDLK_F18, SDLK_F19, SDLK_F20, SDLK_F21, SDLK_F22, SDLK_F23, SDLK_F24,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, SDLK_KP_EQUALS, 0, 0,
|
||||
0, SDLK_AT, SDLK_COLON, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, SDLK_KP_ENTER, SDLK_RCTRL, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, SDLK_KP_COMMA, 0, SDLK_KP_DIVIDE, 0, SDLK_SYSREQ,
|
||||
SDLK_RALT, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, SDLK_PAUSE, 0, SDLK_HOME,
|
||||
SDLK_UP, SDLK_PAGEUP, 0, SDLK_LEFT, 0, SDLK_RIGHT, 0, SDLK_END,
|
||||
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE, 0, 0, 0, 0,
|
||||
0, 0, 0, SDLK_LGUI, SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
|
||||
0, 0, 0, 0, 0, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
|
||||
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER, SDLK_MAIL, SDLK_MEDIASELECT, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
SDLK_UNKNOWN, SDLK_ESCAPE, SDLK_1, SDLK_2,
|
||||
SDLK_3, SDLK_4, SDLK_5, SDLK_6,
|
||||
SDLK_7, SDLK_8, SDLK_9, SDLK_0,
|
||||
SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_TAB,
|
||||
SDLK_q, SDLK_w, SDLK_e, SDLK_r,
|
||||
SDLK_t, SDLK_y, SDLK_u, SDLK_i,
|
||||
SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET,
|
||||
SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s,
|
||||
SDLK_d, SDLK_f, SDLK_g, SDLK_h,
|
||||
SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON,
|
||||
SDLK_QUOTE, SDLK_BACKQUOTE, SDLK_LSHIFT, SDLK_BACKSLASH,
|
||||
SDLK_z, SDLK_x, SDLK_c, SDLK_v,
|
||||
SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA,
|
||||
SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_KP_MULTIPLY,
|
||||
SDLK_LALT, SDLK_SPACE, SDLK_CAPSLOCK, SDLK_F1,
|
||||
SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5,
|
||||
SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9,
|
||||
SDLK_F10, SDLK_NUMLOCKCLEAR, SDLK_SCROLLLOCK, SDLK_KP_7,
|
||||
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4,
|
||||
SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
|
||||
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_F11,
|
||||
SDLK_F12, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_F13, SDLK_F14, SDLK_F15, SDLK_F16,
|
||||
SDLK_F17, SDLK_F18, SDLK_F19, SDLK_F20,
|
||||
SDLK_F21, SDLK_F22, SDLK_F23, SDLK_F24,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_KP_EQUALS, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_AT, SDLK_COLON, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_KP_ENTER, SDLK_RCTRL, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_KP_COMMA,
|
||||
SDLK_UNKNOWN, SDLK_KP_DIVIDE, SDLK_UNKNOWN, SDLK_SYSREQ,
|
||||
SDLK_RALT, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_PAUSE, SDLK_UNKNOWN, SDLK_HOME,
|
||||
SDLK_UP, SDLK_PAGEUP, SDLK_UNKNOWN, SDLK_LEFT,
|
||||
SDLK_UNKNOWN, SDLK_RIGHT, SDLK_UNKNOWN, SDLK_END,
|
||||
SDLK_DOWN, SDLK_PAGEDOWN, SDLK_INSERT, SDLK_DELETE,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_LGUI,
|
||||
SDLK_RGUI, SDLK_MENU, SDLK_POWER, SDLK_SLEEP,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_AC_SEARCH, SDLK_AC_BOOKMARKS, SDLK_AC_REFRESH,
|
||||
SDLK_AC_STOP, SDLK_AC_FORWARD, SDLK_AC_BACK, SDLK_COMPUTER,
|
||||
SDLK_MAIL, SDLK_MEDIASELECT, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN,
|
||||
SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN, SDLK_UNKNOWN
|
||||
};
|
||||
|
||||
static const SDL_Scancode DIKToKeyScan[256] =
|
||||
{
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
|
||||
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0 ,SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
|
||||
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
|
||||
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
|
||||
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
|
||||
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
|
||||
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
|
||||
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
|
||||
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
|
||||
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
|
||||
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
|
||||
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16,
|
||||
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_F20, SDL_SCANCODE_F21, SDL_SCANCODE_F22, SDL_SCANCODE_F23, SDL_SCANCODE_F24,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
|
||||
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
|
||||
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
|
||||
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER, SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2,
|
||||
SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6,
|
||||
SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0,
|
||||
SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB,
|
||||
SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R,
|
||||
SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I,
|
||||
SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET,
|
||||
SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S,
|
||||
SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H,
|
||||
SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON,
|
||||
SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH,
|
||||
SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V,
|
||||
SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA,
|
||||
SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_KP_MULTIPLY,
|
||||
SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1,
|
||||
SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5,
|
||||
SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9,
|
||||
SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_KP_7,
|
||||
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4,
|
||||
SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
|
||||
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
|
||||
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16,
|
||||
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_F20,
|
||||
SDL_SCANCODE_F21, SDL_SCANCODE_F22, SDL_SCANCODE_F23, SDL_SCANCODE_F24,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_EQUALS, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RCTRL, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_COMMA,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_SYSREQ,
|
||||
SDL_SCANCODE_RALT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_HOME,
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LEFT,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_END,
|
||||
SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI,
|
||||
SDL_SCANCODE_RGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_POWER, SDL_SCANCODE_SLEEP,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_REFRESH,
|
||||
SDL_SCANCODE_AC_STOP, SDL_SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_BACK, SDL_SCANCODE_COMPUTER,
|
||||
SDL_SCANCODE_MAIL, SDL_SCANCODE_MEDIASELECT, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
|
||||
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN
|
||||
};
|
||||
|
||||
static TMap<SDL_Keycode, uint8_t> InitKeySymMap ()
|
||||
|
|
@ -464,10 +527,63 @@ void MessagePump (const SDL_Event &sev)
|
|||
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
if (SDL_GameControllerFromInstanceID(sev.jdevice.which))
|
||||
break; // let SDL_CONTROLLERBUTTON* handle this
|
||||
event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button;
|
||||
if(event.data1 != 0)
|
||||
D_PostEvent(&event);
|
||||
I_JoyConsumeEvent(sev.jdevice.which, &event);
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
event.type = sev.type == SDL_CONTROLLERBUTTONDOWN ? EV_KeyDown : EV_KeyUp;
|
||||
switch (sev.cbutton.button)
|
||||
{
|
||||
case SDL_CONTROLLER_BUTTON_A: event.data1 = KEY_PAD_A; break;
|
||||
case SDL_CONTROLLER_BUTTON_B: event.data1 = KEY_PAD_B; break;
|
||||
case SDL_CONTROLLER_BUTTON_X: event.data1 = KEY_PAD_X; break;
|
||||
case SDL_CONTROLLER_BUTTON_Y: event.data1 = KEY_PAD_Y; break;
|
||||
case SDL_CONTROLLER_BUTTON_BACK: event.data1 = KEY_PAD_BACK; break;
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE: event.data1 = KEY_PAD_GUIDE; break;
|
||||
case SDL_CONTROLLER_BUTTON_START: event.data1 = KEY_PAD_START; break;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK: event.data1 = KEY_PAD_LTHUMB; break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: event.data1 = KEY_PAD_RTHUMB; break;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: event.data1 = KEY_PAD_LSHOULDER; break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: event.data1 = KEY_PAD_RSHOULDER; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: event.data1 = KEY_PAD_DPAD_UP; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: event.data1 = KEY_PAD_DPAD_DOWN; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: event.data1 = KEY_PAD_DPAD_LEFT; break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: event.data1 = KEY_PAD_DPAD_RIGHT; break;
|
||||
case SDL_CONTROLLER_BUTTON_MISC1: event.data1 = KEY_PAD_MISC1; break;
|
||||
case SDL_CONTROLLER_BUTTON_PADDLE1: event.data1 = KEY_PAD_PADDLE1; break;
|
||||
case SDL_CONTROLLER_BUTTON_PADDLE2: event.data1 = KEY_PAD_PADDLE2; break;
|
||||
case SDL_CONTROLLER_BUTTON_PADDLE3: event.data1 = KEY_PAD_PADDLE3; break;
|
||||
case SDL_CONTROLLER_BUTTON_PADDLE4: event.data1 = KEY_PAD_PADDLE4; break;
|
||||
case SDL_CONTROLLER_BUTTON_TOUCHPAD: event.data1 = KEY_PAD_TOUCHPAD; break;
|
||||
default: event.data1 = 0;
|
||||
}
|
||||
if(event.data1 != 0)
|
||||
I_JoyConsumeEvent(sev.cbutton.which, &event);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
if (sev.type == SDL_JOYDEVICEADDED && SDL_IsGameController(sev.jdevice.which)) // DeviceIndex Here
|
||||
break; // skip double event
|
||||
I_UpdateDeviceList();
|
||||
event.type = EV_DeviceChange;
|
||||
D_PostEvent (&event);
|
||||
break;
|
||||
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
if (sev.type == SDL_JOYDEVICEREMOVED && SDL_GameControllerFromInstanceID(sev.jdevice.which))
|
||||
break; // skip double event
|
||||
I_UpdateDeviceList();
|
||||
event.type = EV_DeviceChange;
|
||||
D_PostEvent (&event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
src/common/platform/posix/sdl/i_input.h
Normal file
13
src/common/platform/posix/sdl/i_input.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef I_INPUT_H
|
||||
#define I_INPUT_H
|
||||
|
||||
#include "d_eventbase.h"
|
||||
|
||||
extern int WaitingForKey;
|
||||
|
||||
static void I_CheckGUICapture ();
|
||||
static void I_CheckNativeMouse ();
|
||||
|
||||
void I_JoyConsumeEvent(int instanceID, event_t * event);
|
||||
|
||||
#endif
|
||||
|
|
@ -31,47 +31,80 @@
|
|||
**
|
||||
*/
|
||||
#include <SDL.h>
|
||||
#include <SDL_gamecontroller.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "basics.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
#include "d_eventbase.h"
|
||||
#include "i_input.h"
|
||||
#include "m_joy.h"
|
||||
#include "keydef.h"
|
||||
|
||||
#define DEFAULT_DEADZONE 0.25f;
|
||||
|
||||
// Very small deadzone so that floating point magic doesn't happen
|
||||
#define MIN_DEADZONE 0.000001f
|
||||
|
||||
class SDLInputJoystick: public IJoystickConfig
|
||||
{
|
||||
public:
|
||||
SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f) , Enabled(true)
|
||||
SDLInputJoystick(int DeviceIndex) :
|
||||
DeviceIndex(DeviceIndex),
|
||||
InstanceID(SDL_JoystickGetDeviceInstanceID(DeviceIndex)),
|
||||
Multiplier(JOYSENSITIVITY_DEFAULT),
|
||||
Enabled(true),
|
||||
SettingsChanged(false)
|
||||
{
|
||||
Device = SDL_JoystickOpen(DeviceIndex);
|
||||
if(Device != NULL)
|
||||
if (SDL_IsGameController(DeviceIndex))
|
||||
{
|
||||
NumAxes = SDL_JoystickNumAxes(Device);
|
||||
NumHats = SDL_JoystickNumHats(Device);
|
||||
Mapping = SDL_GameControllerOpen(DeviceIndex);
|
||||
Device = NULL;
|
||||
|
||||
SetDefaultConfig();
|
||||
DefaultAxes = DefaultControllerAxes;
|
||||
DefaultAxesCount = sizeof(DefaultControllerAxes) / sizeof(DefaultAxisConfig);
|
||||
|
||||
if(Mapping != NULL)
|
||||
{
|
||||
NumAxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
NumHats = 0;
|
||||
|
||||
SetDefaultConfig();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Device = SDL_JoystickOpen(DeviceIndex);
|
||||
Mapping = NULL;
|
||||
|
||||
DefaultAxes = DefaultJoystickAxes;
|
||||
DefaultAxesCount = sizeof(DefaultJoystickAxes) / sizeof(DefaultAxisConfig);
|
||||
|
||||
if(Device != NULL)
|
||||
{
|
||||
NumAxes = SDL_JoystickNumAxes(Device);
|
||||
NumHats = SDL_JoystickNumHats(Device);
|
||||
|
||||
SetDefaultConfig();
|
||||
}
|
||||
}
|
||||
M_LoadJoystickConfig(this);
|
||||
}
|
||||
~SDLInputJoystick()
|
||||
{
|
||||
if(Device != NULL)
|
||||
if(IsValid() && SettingsChanged)
|
||||
M_SaveJoystickConfig(this);
|
||||
SDL_JoystickClose(Device);
|
||||
if (Mapping)
|
||||
SDL_GameControllerClose(Mapping);
|
||||
if (Device)
|
||||
SDL_JoystickClose(Device);
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return Device != NULL;
|
||||
return Device != NULL || Mapping != NULL;
|
||||
}
|
||||
|
||||
FString GetName()
|
||||
{
|
||||
return SDL_JoystickName(Device);
|
||||
return (Mapping)
|
||||
? SDL_GameControllerName(Mapping)
|
||||
: SDL_JoystickName(Device);
|
||||
}
|
||||
float GetSensitivity()
|
||||
{
|
||||
|
|
@ -79,6 +112,7 @@ public:
|
|||
}
|
||||
void SetSensitivity(float scale)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Multiplier = scale;
|
||||
}
|
||||
|
||||
|
|
@ -102,58 +136,145 @@ public:
|
|||
{
|
||||
return Axes[axis].Multiplier;
|
||||
}
|
||||
float GetAxisDigitalThreshold(int axis)
|
||||
{
|
||||
return Axes[axis].DigitalThreshold;
|
||||
}
|
||||
EJoyCurve GetAxisResponseCurve(int axis)
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset;
|
||||
}
|
||||
float GetAxisResponseCurvePoint(int axis, int point)
|
||||
{
|
||||
return unsigned(point) < 4
|
||||
? Axes[axis].ResponseCurve.pts[point]
|
||||
: 0;
|
||||
};
|
||||
|
||||
void SetAxisDeadZone(int axis, float zone)
|
||||
{
|
||||
Axes[axis].DeadZone = clamp(zone, MIN_DEADZONE, 1.f);
|
||||
SettingsChanged = true;
|
||||
Axes[axis].DeadZone = clamp(zone, 0.f, 1.f);
|
||||
}
|
||||
void SetAxisMap(int axis, EJoyAxis gameaxis)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Axes[axis].GameAxis = gameaxis;
|
||||
}
|
||||
void SetAxisScale(int axis, float scale)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Axes[axis].Multiplier = scale;
|
||||
}
|
||||
void SetAxisDigitalThreshold(int axis, float threshold)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Axes[axis].DigitalThreshold = threshold;
|
||||
}
|
||||
void SetAxisResponseCurve(int axis, EJoyCurve preset)
|
||||
{
|
||||
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
|
||||
SettingsChanged = true;
|
||||
Axes[axis].ResponseCurvePreset = preset;
|
||||
if (preset == JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurve = JOYCURVE[preset];
|
||||
}
|
||||
void SetAxisResponseCurvePoint(int axis, int point, float value)
|
||||
{
|
||||
if (unsigned(point) < 4)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
|
||||
Axes[axis].ResponseCurve.pts[point] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Used by the saver to not save properties that are at their defaults.
|
||||
bool IsSensitivityDefault()
|
||||
{
|
||||
return Multiplier == 1.0f;
|
||||
return Multiplier == JOYSENSITIVITY_DEFAULT;
|
||||
}
|
||||
bool IsAxisDeadZoneDefault(int axis)
|
||||
{
|
||||
return Axes[axis].DeadZone <= MIN_DEADZONE;
|
||||
if(axis >= DefaultAxesCount)
|
||||
return Axes[axis].DeadZone == JOYDEADZONE_DEFAULT;
|
||||
return Axes[axis].DeadZone == DefaultAxes[axis].DeadZone;
|
||||
}
|
||||
bool IsAxisMapDefault(int axis)
|
||||
{
|
||||
if(axis >= 5)
|
||||
if(axis >= DefaultAxesCount)
|
||||
return Axes[axis].GameAxis == JOYAXIS_None;
|
||||
return Axes[axis].GameAxis == DefaultAxes[axis];
|
||||
return Axes[axis].GameAxis == DefaultAxes[axis].GameAxis;
|
||||
}
|
||||
bool IsAxisScaleDefault(int axis)
|
||||
{
|
||||
return Axes[axis].Multiplier == 1.0f;
|
||||
if(axis >= DefaultAxesCount)
|
||||
return Axes[axis].Multiplier == JOYSENSITIVITY_DEFAULT;
|
||||
return Axes[axis].Multiplier == DefaultAxes[axis].Multiplier;
|
||||
}
|
||||
bool IsAxisDigitalThresholdDefault(int axis)
|
||||
{
|
||||
if(axis >= DefaultAxesCount)
|
||||
return Axes[axis].DigitalThreshold == JOYTHRESH_DEFAULT;
|
||||
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
|
||||
}
|
||||
bool IsAxisResponseCurveDefault(int axis)
|
||||
{
|
||||
if(axis >= DefaultAxesCount)
|
||||
return Axes[axis].ResponseCurvePreset == JOYCURVE_DEFAULT;
|
||||
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
|
||||
}
|
||||
|
||||
void SetDefaultConfig()
|
||||
{
|
||||
if (Axes.size() == 0)
|
||||
{
|
||||
for(int i = 0;i < GetNumAxes();i++)
|
||||
{
|
||||
Axes.Push({});
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0;i < GetNumAxes();i++)
|
||||
{
|
||||
AxisInfo info;
|
||||
if(i < NumAxes)
|
||||
info.Name.Format("Axis %d", i+1);
|
||||
if (Mapping) {
|
||||
switch(i) {
|
||||
case SDL_CONTROLLER_AXIS_LEFTX: Axes[i].Name = "Left Stick X"; break;
|
||||
case SDL_CONTROLLER_AXIS_LEFTY: Axes[i].Name = "Left Stick Y"; break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX: Axes[i].Name = "Right Stick X"; break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY: Axes[i].Name = "Right Stick Y"; break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: Axes[i].Name = "Left Trigger"; break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: Axes[i].Name = "Right Trigger"; break;
|
||||
default: Axes[i].Name.Format("Axis %d", i+1); break;
|
||||
}
|
||||
} else {
|
||||
if(i < NumAxes)
|
||||
Axes[i].Name.Format("Axis %d", i+1);
|
||||
else
|
||||
Axes[i].Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
|
||||
}
|
||||
|
||||
Axes[i].Value = 0.0;
|
||||
Axes[i].ButtonValue = 0;
|
||||
|
||||
if (i < DefaultAxesCount)
|
||||
{
|
||||
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
|
||||
Axes[i].DeadZone = DefaultAxes[i].DeadZone;
|
||||
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
|
||||
Axes[i].DigitalThreshold = DefaultAxes[i].DigitalThreshold;
|
||||
Axes[i].ResponseCurvePreset = DefaultAxes[i].ResponseCurvePreset;
|
||||
Axes[i].ResponseCurve = JOYCURVE[DefaultAxes[i].ResponseCurvePreset];
|
||||
}
|
||||
else
|
||||
info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y');
|
||||
info.DeadZone = DEFAULT_DEADZONE;
|
||||
info.Multiplier = 1.0f;
|
||||
info.Value = 0.0;
|
||||
info.ButtonValue = 0;
|
||||
if(i >= 5)
|
||||
info.GameAxis = JOYAXIS_None;
|
||||
else
|
||||
info.GameAxis = DefaultAxes[i];
|
||||
Axes.Push(info);
|
||||
{
|
||||
Axes[i].GameAxis = JOYAXIS_None;
|
||||
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
|
||||
Axes[i].Multiplier = JOYSENSITIVITY_DEFAULT;
|
||||
Axes[i].DigitalThreshold = JOYTHRESH_DEFAULT;
|
||||
Axes[i].ResponseCurvePreset = JOYCURVE_DEFAULT;
|
||||
Axes[i].ResponseCurve = JOYCURVE[JOYCURVE_DEFAULT];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,9 +282,10 @@ public:
|
|||
{
|
||||
return Enabled;
|
||||
}
|
||||
|
||||
|
||||
void SetEnabled(bool enabled)
|
||||
{
|
||||
SettingsChanged = true;
|
||||
Enabled = enabled;
|
||||
}
|
||||
|
||||
|
|
@ -188,63 +310,101 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void ProcessInput()
|
||||
{
|
||||
void ProcessInput() {
|
||||
uint8_t buttonstate;
|
||||
|
||||
for (int i = 0; i < NumAxes; ++i)
|
||||
if (Mapping)
|
||||
{
|
||||
buttonstate = 0;
|
||||
// GameController API available
|
||||
|
||||
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
|
||||
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
|
||||
auto lastTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].DigitalThreshold;
|
||||
auto lastTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].DigitalThreshold;
|
||||
|
||||
// Map button to axis
|
||||
// X and Y are handled differently so if we have 2 or more axes then we'll use that code instead.
|
||||
if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS))
|
||||
for (auto i = 0; i < SDL_CONTROLLER_AXIS_MAX && i < NumAxes; ++i)
|
||||
{
|
||||
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
|
||||
Axes[i].ButtonValue = buttonstate;
|
||||
}
|
||||
}
|
||||
buttonstate = 0;
|
||||
|
||||
if(NumAxes > 1)
|
||||
{
|
||||
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value);
|
||||
Axes[i].Value = SDL_GameControllerGetAxis(Mapping, static_cast<SDL_GameControllerAxis>(i))/32767.0;
|
||||
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
|
||||
Axes[i].Value = Joy_ApplyResponseCurveBezier(Axes[i].ResponseCurve, Axes[i].Value);
|
||||
}
|
||||
|
||||
auto currTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].DigitalThreshold;
|
||||
auto currTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].DigitalThreshold;
|
||||
|
||||
if (lastTriggerL != currTriggerL) Joy_GenerateButtonEvent(currTriggerL, KEY_PAD_LTRIGGER);
|
||||
if (lastTriggerR != currTriggerR) Joy_GenerateButtonEvent(currTriggerR, KEY_PAD_RTRIGGER);
|
||||
|
||||
// todo: right stick
|
||||
buttonstate = Joy_XYAxesToButtons(
|
||||
abs(Axes[0].Value) < Axes[0].DigitalThreshold ? 0 : Axes[0].Value,
|
||||
abs(Axes[1].Value) < Axes[1].DigitalThreshold ? 0 : Axes[1].Value
|
||||
);
|
||||
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
|
||||
Axes[0].ButtonValue = buttonstate;
|
||||
}
|
||||
|
||||
// Map POV hats to buttons and axes. Why axes? Well apparently I have
|
||||
// a gamepad where the left control stick is a POV hat (instead of the
|
||||
// d-pad like you would expect, no that's pressure sensitive). Also
|
||||
// KDE's joystick dialog maps them to axes as well.
|
||||
for (int i = 0; i < NumHats; ++i)
|
||||
else
|
||||
{
|
||||
AxisInfo &x = Axes[NumAxes + i*2];
|
||||
AxisInfo &y = Axes[NumAxes + i*2 + 1];
|
||||
// Joystick API fallback
|
||||
|
||||
buttonstate = SDL_JoystickGetHat(Device, i);
|
||||
|
||||
// If we're going to assume that we can pass SDL's value into
|
||||
// Joy_GenerateButtonEvents then we might as well assume the format here.
|
||||
if(buttonstate & 0x1) // Up
|
||||
y.Value = -1.0;
|
||||
else if(buttonstate & 0x4) // Down
|
||||
y.Value = 1.0;
|
||||
else
|
||||
y.Value = 0.0;
|
||||
if(buttonstate & 0x2) // Left
|
||||
x.Value = 1.0;
|
||||
else if(buttonstate & 0x8) // Right
|
||||
x.Value = -1.0;
|
||||
else
|
||||
x.Value = 0.0;
|
||||
|
||||
if(i < 4)
|
||||
for (int i = 0; i < NumAxes; ++i)
|
||||
{
|
||||
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
|
||||
x.ButtonValue = buttonstate;
|
||||
buttonstate = 0;
|
||||
|
||||
Axes[i].Value = SDL_JoystickGetAxis(Device, i)/32767.0;
|
||||
Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate);
|
||||
Axes[i].Value = Joy_ApplyResponseCurveBezier(Axes[i].ResponseCurve, Axes[i].Value);
|
||||
|
||||
// Map button to axis
|
||||
// X and Y are handled differently so if we have 2 or more axes then we'll use that code instead.
|
||||
if (NumAxes == 1 || (i >= 2 && i < NUM_JOYAXISBUTTONS))
|
||||
{
|
||||
Joy_GenerateButtonEvents(Axes[i].ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
|
||||
Axes[i].ButtonValue = buttonstate;
|
||||
}
|
||||
}
|
||||
|
||||
if(NumAxes > 1)
|
||||
{
|
||||
buttonstate = Joy_XYAxesToButtons(
|
||||
abs(Axes[0].Value) < Axes[0].DigitalThreshold ? 0 : Axes[0].Value,
|
||||
abs(Axes[1].Value) < Axes[1].DigitalThreshold ? 0 : Axes[1].Value
|
||||
);
|
||||
Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
|
||||
Axes[0].ButtonValue = buttonstate;
|
||||
}
|
||||
|
||||
// Map POV hats to buttons and axes. Why axes? Well apparently I have
|
||||
// a gamepad where the left control stick is a POV hat (instead of the
|
||||
// d-pad like you would expect, no that's pressure sensitive). Also
|
||||
// KDE's joystick dialog maps them to axes as well.
|
||||
for (int i = 0; i < NumHats; ++i)
|
||||
{
|
||||
AxisInfo &x = Axes[NumAxes + i*2];
|
||||
AxisInfo &y = Axes[NumAxes + i*2 + 1];
|
||||
|
||||
buttonstate = SDL_JoystickGetHat(Device, i);
|
||||
|
||||
// If we're going to assume that we can pass SDL's value into
|
||||
// Joy_GenerateButtonEvents then we might as well assume the format here.
|
||||
if(buttonstate & 0x1) // Up
|
||||
y.Value = -1.0;
|
||||
else if(buttonstate & 0x4) // Down
|
||||
y.Value = 1.0;
|
||||
else
|
||||
y.Value = 0.0;
|
||||
if(buttonstate & 0x2) // Left
|
||||
x.Value = 1.0;
|
||||
else if(buttonstate & 0x8) // Right
|
||||
x.Value = -1.0;
|
||||
else
|
||||
x.Value = 0.0;
|
||||
|
||||
if(i < 4)
|
||||
{
|
||||
Joy_GenerateButtonEvents(x.ButtonValue, buttonstate, 4, KEY_JOYPOV1_UP + i*4);
|
||||
x.ButtonValue = buttonstate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -255,33 +415,72 @@ protected:
|
|||
FString Name;
|
||||
float DeadZone;
|
||||
float Multiplier;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
CubicBezier ResponseCurve;
|
||||
EJoyAxis GameAxis;
|
||||
double Value;
|
||||
uint8_t ButtonValue;
|
||||
};
|
||||
static const EJoyAxis DefaultAxes[5];
|
||||
struct DefaultAxisConfig
|
||||
{
|
||||
float DeadZone;
|
||||
EJoyAxis GameAxis;
|
||||
float Multiplier;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
};
|
||||
static const DefaultAxisConfig DefaultJoystickAxes[5];
|
||||
static const DefaultAxisConfig DefaultControllerAxes[6];
|
||||
const DefaultAxisConfig * DefaultAxes;
|
||||
int DefaultAxesCount;
|
||||
|
||||
int DeviceIndex;
|
||||
int InstanceID;
|
||||
SDL_Joystick *Device;
|
||||
SDL_GameController *Mapping;
|
||||
|
||||
float Multiplier;
|
||||
bool Enabled;
|
||||
TArray<AxisInfo> Axes;
|
||||
int NumAxes;
|
||||
int NumHats;
|
||||
bool SettingsChanged;
|
||||
|
||||
friend class SDLInputJoystickManager;
|
||||
};
|
||||
|
||||
// [Nash 4 Feb 2024] seems like on Linux, the third axis is actually the Left Trigger, resulting in the player uncontrollably looking upwards.
|
||||
const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_None, JOYAXIS_Yaw, JOYAXIS_Pitch};
|
||||
const SDLInputJoystick::DefaultAxisConfig SDLInputJoystick::DefaultJoystickAxes[5] = {
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_DEFAULT, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT}
|
||||
};
|
||||
|
||||
// Defaults if we have access to the GameController API for this device
|
||||
const SDLInputJoystick::DefaultAxisConfig SDLInputJoystick::DefaultControllerAxes[6] = {
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT},
|
||||
{JOYDEADZONE_DEFAULT, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT},
|
||||
};
|
||||
|
||||
class SDLInputJoystickManager
|
||||
{
|
||||
public:
|
||||
SDLInputJoystickManager()
|
||||
{
|
||||
for(int i = 0;i < SDL_NumJoysticks();i++)
|
||||
UpdateDeviceList();
|
||||
}
|
||||
|
||||
void UpdateDeviceList()
|
||||
{
|
||||
Joysticks.DeleteAndClear();
|
||||
for(int i = 0; i < SDL_NumJoysticks(); i++)
|
||||
{
|
||||
SDLInputJoystick *device = new SDLInputJoystick(i);
|
||||
if(device->IsValid())
|
||||
|
|
@ -290,17 +489,13 @@ public:
|
|||
delete device;
|
||||
}
|
||||
}
|
||||
~SDLInputJoystickManager()
|
||||
{
|
||||
for(unsigned int i = 0;i < Joysticks.Size();i++)
|
||||
delete Joysticks[i];
|
||||
}
|
||||
|
||||
void AddAxes(float axes[NUM_JOYAXIS])
|
||||
{
|
||||
for(unsigned int i = 0;i < Joysticks.Size();i++)
|
||||
Joysticks[i]->AddAxes(axes);
|
||||
}
|
||||
|
||||
void GetDevices(TArray<IJoystickConfig *> &sticks)
|
||||
{
|
||||
for(unsigned int i = 0;i < Joysticks.Size();i++)
|
||||
|
|
@ -315,15 +510,29 @@ public:
|
|||
for(unsigned int i = 0;i < Joysticks.Size();++i)
|
||||
if(Joysticks[i]->Enabled) Joysticks[i]->ProcessInput();
|
||||
}
|
||||
|
||||
bool IsJoystickEnabled(int instanceID)
|
||||
{
|
||||
for(unsigned int i = 0; i < Joysticks.Size(); i++)
|
||||
{
|
||||
if (Joysticks[i]->InstanceID != instanceID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return Joysticks[i]->Enabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
TArray<SDLInputJoystick *> Joysticks;
|
||||
TDeletingArray<SDLInputJoystick *> Joysticks;
|
||||
};
|
||||
static SDLInputJoystickManager *JoystickManager;
|
||||
|
||||
void I_StartupJoysticks()
|
||||
{
|
||||
#ifndef NO_SDL_JOYSTICK
|
||||
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0)
|
||||
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) >= 0)
|
||||
JoystickManager = new SDLInputJoystickManager();
|
||||
#endif
|
||||
}
|
||||
|
|
@ -332,7 +541,7 @@ void I_ShutdownInput()
|
|||
if(JoystickManager)
|
||||
{
|
||||
delete JoystickManager;
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +571,18 @@ void I_ProcessJoysticks()
|
|||
JoystickManager->ProcessInput();
|
||||
}
|
||||
|
||||
void I_JoyConsumeEvent(int instanceID, event_t * event)
|
||||
{
|
||||
if (event->type == EV_KeyDown)
|
||||
{
|
||||
bool okay = use_joystick && JoystickManager && JoystickManager->IsJoystickEnabled(instanceID);
|
||||
if (!okay) return;
|
||||
}
|
||||
D_PostEvent(event);
|
||||
}
|
||||
|
||||
IJoystickConfig *I_UpdateDeviceList()
|
||||
{
|
||||
JoystickManager->UpdateDeviceList();
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ void I_DetectOS()
|
|||
break;
|
||||
}
|
||||
|
||||
utsname unameInfo;
|
||||
struct utsname unameInfo;
|
||||
|
||||
if (uname(&unameInfo) == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if defined(__sun) || defined(__sun__)
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "version.h"
|
||||
|
|
@ -75,6 +79,7 @@ int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad
|
|||
#endif
|
||||
|
||||
double PerfToSec, PerfToMillisec;
|
||||
CVAR(String, queryiwad_key, "shift", CVAR_GLOBALCONFIG | CVAR_ARCHIVE);
|
||||
CVAR(Bool, con_printansi, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE);
|
||||
CVAR(Bool, con_4bitansi, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE);
|
||||
EXTERN_CVAR(Bool, longsavemessages)
|
||||
|
|
@ -302,17 +307,55 @@ void I_PrintStr(const char *cp)
|
|||
if (StartWindow) RedrawProgressBar(ProgressBarCurPos,ProgressBarMaxPos);
|
||||
}
|
||||
|
||||
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad, int& autoloadflags, FString &extraArgs)
|
||||
bool HoldingQueryKey(const char* key)
|
||||
{
|
||||
int code = 0;
|
||||
|
||||
if (!stricmp(key, "shift"))
|
||||
code = SDL_SCANCODE_LSHIFT;
|
||||
else if (!stricmp(key, "control") || !stricmp(key, "ctrl"))
|
||||
code = SDL_SCANCODE_LCTRL;
|
||||
|
||||
if (!code) return false;
|
||||
|
||||
auto window = SDL_CreateWindow(
|
||||
"HoldingQueryKey",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
1, 1,
|
||||
SDL_WINDOW_HIDDEN
|
||||
);
|
||||
|
||||
if (!window) return false;
|
||||
|
||||
SDL_PumpEvents();
|
||||
|
||||
auto keys = SDL_GetKeyboardState(nullptr);
|
||||
bool state = (code == SDL_SCANCODE_LCTRL && (keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]))
|
||||
|| (code == SDL_SCANCODE_LSHIFT && (keys[SDL_SCANCODE_LSHIFT] || keys[SDL_SCANCODE_RSHIFT]));
|
||||
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool I_PickIWad (bool showwin, FStartupSelectionInfo& info)
|
||||
{
|
||||
if (!showwin)
|
||||
{
|
||||
return defaultiwad;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
return I_PickIWad_Cocoa (wads, numwads, showwin, defaultiwad);
|
||||
const int ret = I_PickIWad_Cocoa(&(*info.Wads)[0], (int)info.Wads->Size(), showwin, info.DefaultIWAD);
|
||||
if (ret >= 0)
|
||||
{
|
||||
info.DefaultIWAD = ret;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
|
||||
return LauncherWindow::ExecModal(info);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,12 +53,21 @@ class FTTYStartupScreen : public FStartupScreen
|
|||
FTTYStartupScreen(int max_progress);
|
||||
~FTTYStartupScreen();
|
||||
|
||||
void Progress();
|
||||
void NetInit(const char *message, int num_players);
|
||||
void NetProgress(int count);
|
||||
void NetDone();
|
||||
void NetClose();
|
||||
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
|
||||
void Progress() override;
|
||||
|
||||
void NetInit(const char* message, bool host) override;
|
||||
void NetMessage(const char* message) override;
|
||||
void NetConnect(int client, const char* name, unsigned flags, int status) override;
|
||||
void NetUpdate(int client, int status) override;
|
||||
void NetDisconnect(int client) override;
|
||||
void NetProgress(int cur, int limit) override;
|
||||
void NetDone() override;
|
||||
void NetClose() override;
|
||||
bool ShouldStartNet() override;
|
||||
int GetNetKickClient() override;
|
||||
int GetNetBanClient() override;
|
||||
bool NetLoop(bool (*loopCallback)(void*), void* data) override;
|
||||
|
||||
protected:
|
||||
bool DidNetInit;
|
||||
int NetMaxPos, NetCurPos;
|
||||
|
|
@ -146,7 +155,7 @@ void FTTYStartupScreen::Progress()
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetInit(const char *message, int numplayers)
|
||||
void FTTYStartupScreen::NetInit(const char* message, bool host)
|
||||
{
|
||||
if (!DidNetInit)
|
||||
{
|
||||
|
|
@ -162,20 +171,63 @@ void FTTYStartupScreen::NetInit(const char *message, int numplayers)
|
|||
tcsetattr (STDIN_FILENO, TCSANOW, &rawtermios);
|
||||
DidNetInit = true;
|
||||
}
|
||||
if (numplayers == 1)
|
||||
{
|
||||
// Status message without any real progress info.
|
||||
fprintf (stderr, "\n%s.", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "\n%s: ", message);
|
||||
}
|
||||
fprintf(stderr, "\n%s.", message);
|
||||
fflush (stderr);
|
||||
TheNetMessage = message;
|
||||
NetMaxPos = numplayers;
|
||||
NetCurPos = 0;
|
||||
NetProgress(1); // You always know about yourself
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetMessage(const char* message)
|
||||
{
|
||||
TheNetMessage = message;
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetConnect(int client, const char* name, unsigned flags, int status)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetUpdate(int client, int status)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetDisconnect(int client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetProgress
|
||||
//
|
||||
// Sets the network progress meter.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetProgress(int cur, int limit)
|
||||
{
|
||||
int i;
|
||||
|
||||
NetMaxPos = limit;
|
||||
NetCurPos = cur;
|
||||
if (NetMaxPos == 0)
|
||||
{
|
||||
// Spinny-type progress meter, because we're a guest waiting for the host.
|
||||
fprintf(stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]);
|
||||
fflush(stderr);
|
||||
}
|
||||
else if (NetMaxPos > 1)
|
||||
{
|
||||
// Dotty-type progress meter.
|
||||
fprintf(stderr, "\r%s: ", TheNetMessage);
|
||||
for (i = 0; i < NetCurPos; ++i)
|
||||
{
|
||||
fputc('.', stderr);
|
||||
}
|
||||
fprintf(stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
@ -198,51 +250,26 @@ void FTTYStartupScreen::NetDone()
|
|||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetProgress
|
||||
//
|
||||
// Sets the network progress meter. If count is 0, it gets bumped by 1.
|
||||
// Otherwise, it is set to count.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FTTYStartupScreen::NetProgress(int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
NetCurPos++;
|
||||
}
|
||||
else if (count > 0)
|
||||
{
|
||||
NetCurPos = count;
|
||||
}
|
||||
if (NetMaxPos == 0)
|
||||
{
|
||||
// Spinny-type progress meter, because we're a guest waiting for the host.
|
||||
fprintf (stderr, "\r%s: %c", TheNetMessage, SpinnyProgressChars[NetCurPos & 3]);
|
||||
fflush (stderr);
|
||||
}
|
||||
else if (NetMaxPos > 1)
|
||||
{
|
||||
// Dotty-type progress meter.
|
||||
fprintf (stderr, "\r%s: ", TheNetMessage);
|
||||
for (i = 0; i < NetCurPos; ++i)
|
||||
{
|
||||
fputc ('.', stderr);
|
||||
}
|
||||
fprintf (stderr, "%*c[%2d/%2d]", NetMaxPos + 1 - NetCurPos, ' ', NetCurPos, NetMaxPos);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void FTTYStartupScreen::NetClose()
|
||||
{
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
bool FTTYStartupScreen::ShouldStartNet()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int FTTYStartupScreen::GetNetKickClient()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FTTYStartupScreen::GetNetBanClient()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FTTYStartupScreen :: NetLoop
|
||||
|
|
@ -257,7 +284,7 @@ void FTTYStartupScreen::NetClose()
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
|
||||
bool FTTYStartupScreen::NetLoop(bool (*loopCallback)(void *), void *data)
|
||||
{
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
|
|
@ -285,7 +312,7 @@ bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
|
|||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
if (timer_callback (userdata))
|
||||
if (loopCallback (data))
|
||||
{
|
||||
fputc ('\n', stderr);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
#include "version.h" // for GAMENAME
|
||||
|
||||
extern bool netgame;
|
||||
|
||||
|
||||
FString GetUserFile (const char *file)
|
||||
{
|
||||
|
|
@ -191,7 +193,10 @@ FString M_GetScreenshotsPath()
|
|||
|
||||
FString M_GetSavegamesPath()
|
||||
{
|
||||
return NicePath("$HOME/" GAME_DIR "/savegames/");
|
||||
FString pName = "$HOME/" GAME_DIR "/savegames/";
|
||||
if (netgame)
|
||||
pName << "netgame/";
|
||||
return NicePath(pName.GetChars());
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ public:
|
|||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#define DEFAULT_DEADZONE 0.25f
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
|
|
@ -170,15 +169,23 @@ public:
|
|||
EJoyAxis GetAxisMap(int axis);
|
||||
const char *GetAxisName(int axis);
|
||||
float GetAxisScale(int axis);
|
||||
float GetAxisDigitalThreshold(int axis);
|
||||
EJoyCurve GetAxisResponseCurve(int axis);
|
||||
float GetAxisResponseCurvePoint(int axis, int point);
|
||||
|
||||
void SetAxisDeadZone(int axis, float deadzone);
|
||||
void SetAxisMap(int axis, EJoyAxis gameaxis);
|
||||
void SetAxisScale(int axis, float scale);
|
||||
void SetAxisDigitalThreshold(int axis, float threshold);
|
||||
void SetAxisResponseCurve(int axis, EJoyCurve preset);
|
||||
void SetAxisResponseCurvePoint(int axis, int point, float value);
|
||||
|
||||
bool IsSensitivityDefault();
|
||||
bool IsAxisDeadZoneDefault(int axis);
|
||||
bool IsAxisMapDefault(int axis);
|
||||
bool IsAxisScaleDefault(int axis);
|
||||
bool IsAxisDigitalThresholdDefault(int axis);
|
||||
bool IsAxisResponseCurveDefault(int axis);
|
||||
|
||||
bool GetEnabled();
|
||||
void SetEnabled(bool enabled);
|
||||
|
|
@ -201,6 +208,9 @@ protected:
|
|||
float Value;
|
||||
float DeadZone, DefaultDeadZone;
|
||||
float Multiplier, DefaultMultiplier;
|
||||
float DigitalThreshold, DefaultDigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset, DefaultResponseCurvePreset;
|
||||
CubicBezier ResponseCurve;
|
||||
EJoyAxis GameAxis, DefaultGameAxis;
|
||||
uint8_t ButtonValue;
|
||||
};
|
||||
|
|
@ -454,16 +464,21 @@ void FDInputJoystick::ProcessInput()
|
|||
axisval = (value - info->Min) * 2.0 / (info->Max - info->Min) - 1.0;
|
||||
// Cancel out dead zone
|
||||
axisval = Joy_RemoveDeadZone(axisval, info->DeadZone, &buttonstate);
|
||||
axisval = Joy_ApplyResponseCurveBezier(info->ResponseCurve, axisval);
|
||||
info->Value = float(axisval);
|
||||
if (i < NUM_JOYAXISBUTTONS && (i > 2 || Axes.Size() == 1))
|
||||
{
|
||||
if (abs(axisval) < info->DigitalThreshold) buttonstate = 0;
|
||||
Joy_GenerateButtonEvents(info->ButtonValue, buttonstate, 2, KEY_JOYAXIS1PLUS + i*2);
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
// Since we sorted the axes, we know that the first two are definitely X and Y.
|
||||
// They are probably a single stick, so use angular position to determine buttons.
|
||||
buttonstate = Joy_XYAxesToButtons(Axes[0].Value, axisval);
|
||||
buttonstate = Joy_XYAxesToButtons(
|
||||
(abs(Axes[0].Value) < Axes[0].DigitalThreshold) ? 0 : Axes[0].Value,
|
||||
(abs(axisval) < info->DigitalThreshold) ? 0 : axisval
|
||||
);
|
||||
Joy_GenerateButtonEvents(info->ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS);
|
||||
}
|
||||
info->ButtonValue = buttonstate;
|
||||
|
|
@ -761,38 +776,54 @@ void FDInputJoystick::SetDefaultConfig()
|
|||
{
|
||||
unsigned i;
|
||||
|
||||
Multiplier = 1;
|
||||
Multiplier = JOYSENSITIVITY_DEFAULT;
|
||||
for (i = 0; i < Axes.Size(); ++i)
|
||||
{
|
||||
Axes[i].DeadZone = DEFAULT_DEADZONE;
|
||||
Axes[i].Multiplier = 1;
|
||||
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
|
||||
Axes[i].Multiplier = JOYSENSITIVITY_DEFAULT;
|
||||
Axes[i].GameAxis = JOYAXIS_None;
|
||||
Axes[i].DigitalThreshold = JOYTHRESH_DEFAULT;
|
||||
Axes[i].ResponseCurvePreset = JOYCURVE_DEFAULT;
|
||||
Axes[i].ResponseCurve = JOYCURVE[JOYCURVE_DEFAULT];
|
||||
}
|
||||
// Triggers on a 360 controller have a much smaller deadzone.
|
||||
if (Axes.Size() == 5 && Axes[4].Guid == GUID_ZAxis)
|
||||
{
|
||||
Axes[4].DeadZone = 30 / 256.f;
|
||||
Axes[4].DigitalThreshold = JOYTHRESH_TRIGGER;
|
||||
}
|
||||
// Two axes? Horizontal is yaw and vertical is forward.
|
||||
if (Axes.Size() == 2)
|
||||
{
|
||||
Axes[0].GameAxis = JOYAXIS_Yaw;
|
||||
Axes[0].DigitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
Axes[1].GameAxis = JOYAXIS_Forward;
|
||||
Axes[1].DigitalThreshold = JOYTHRESH_STICK_Y;
|
||||
}
|
||||
// Three axes? First two are movement, third is yaw.
|
||||
else if (Axes.Size() >= 3)
|
||||
{
|
||||
Axes[0].GameAxis = JOYAXIS_Side;
|
||||
Axes[0].DigitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
Axes[1].GameAxis = JOYAXIS_Forward;
|
||||
Axes[1].DigitalThreshold = JOYTHRESH_STICK_Y;
|
||||
|
||||
Axes[2].GameAxis = JOYAXIS_Yaw;
|
||||
Axes[2].DigitalThreshold = JOYTHRESH_STICK_X;
|
||||
|
||||
// Four axes? First two are movement, last two are looking around.
|
||||
if (Axes.Size() >= 4)
|
||||
{
|
||||
Axes[3].GameAxis = JOYAXIS_Pitch; Axes[3].Multiplier = 0.75f;
|
||||
Axes[3].GameAxis = JOYAXIS_Pitch;
|
||||
// Axes[3].Multiplier = 0.75f;
|
||||
Axes[3].DigitalThreshold = JOYTHRESH_STICK_Y;
|
||||
// Five axes? Use the fifth one for moving up and down.
|
||||
if (Axes.Size() >= 5)
|
||||
{
|
||||
Axes[4].GameAxis = JOYAXIS_Up;
|
||||
Axes[4].DigitalThreshold = JOYTHRESH_STICK_Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -805,6 +836,8 @@ void FDInputJoystick::SetDefaultConfig()
|
|||
Axes[i].DefaultDeadZone = Axes[i].DeadZone;
|
||||
Axes[i].DefaultMultiplier = Axes[i].Multiplier;
|
||||
Axes[i].DefaultGameAxis = Axes[i].GameAxis;
|
||||
Axes[i].DefaultDigitalThreshold = Axes[i].DigitalThreshold;
|
||||
Axes[i].DefaultResponseCurvePreset = Axes[i].ResponseCurvePreset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -849,7 +882,7 @@ void FDInputJoystick::SetSensitivity(float scale)
|
|||
|
||||
bool FDInputJoystick::IsSensitivityDefault()
|
||||
{
|
||||
return Multiplier == 1;
|
||||
return Multiplier == JOYSENSITIVITY_DEFAULT;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
@ -923,6 +956,51 @@ float FDInputJoystick::GetAxisScale(int axis)
|
|||
return Axes[axis].Multiplier;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: GetAxisDigitalThreshold
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
float FDInputJoystick::GetAxisDigitalThreshold(int axis)
|
||||
{
|
||||
if (unsigned(axis) >= Axes.Size())
|
||||
{
|
||||
return JOYTHRESH_DEFAULT;
|
||||
}
|
||||
return Axes[axis].DigitalThreshold;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: GetAxisResponseCurve
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
EJoyCurve FDInputJoystick::GetAxisResponseCurve(int axis)
|
||||
{
|
||||
if (unsigned(axis) >= Axes.Size())
|
||||
{
|
||||
return JOYCURVE_DEFAULT;
|
||||
}
|
||||
return Axes[axis].ResponseCurvePreset;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: GetAxisResponseCurvePoint
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
float FDInputJoystick::GetAxisResponseCurvePoint(int axis, int point)
|
||||
{
|
||||
if (unsigned(axis) >= Axes.Size() || unsigned(point) >= 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Axes[axis].ResponseCurve.pts[point];
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: SetAxisDeadZone
|
||||
|
|
@ -965,6 +1043,52 @@ void FDInputJoystick::SetAxisScale(int axis, float scale)
|
|||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: SetAxisDigitalThreshold
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDInputJoystick::SetAxisDigitalThreshold(int axis, float threshold)
|
||||
{
|
||||
if (unsigned(axis) < Axes.Size())
|
||||
{
|
||||
Axes[axis].DigitalThreshold = threshold;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: SetAxisResponseCurve
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDInputJoystick::SetAxisResponseCurve(int axis, EJoyCurve preset)
|
||||
{
|
||||
if (unsigned(axis) < Axes.Size())
|
||||
{
|
||||
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurvePreset = preset;
|
||||
if (preset == JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurve = JOYCURVE[preset];
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: SetAxisResponseCurvePoint
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDInputJoystick::SetAxisResponseCurvePoint(int axis, int point, float value)
|
||||
{
|
||||
if (unsigned(axis) < Axes.Size() && unsigned(point) < 4)
|
||||
{
|
||||
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
|
||||
Axes[axis].ResponseCurve.pts[point] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: IsAxisDeadZoneDefault
|
||||
|
|
@ -995,6 +1119,36 @@ bool FDInputJoystick::IsAxisScaleDefault(int axis)
|
|||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: IsAxisDigitalThresholdDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FDInputJoystick::IsAxisDigitalThresholdDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < Axes.Size())
|
||||
{
|
||||
return Axes[axis].DigitalThreshold == Axes[axis].DefaultDigitalThreshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: IsAxisResponseCurveDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FDInputJoystick::IsAxisResponseCurveDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < Axes.Size())
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset == Axes[axis].DefaultResponseCurvePreset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDInputJoystick :: GetEnabled
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ int BlockMouseMove;
|
|||
|
||||
static bool EventHandlerResultForNativeMouse;
|
||||
|
||||
// This is only used by the debugger to disable input while execution is paused.
|
||||
bool win32EnableInput = true;
|
||||
|
||||
EXTERN_CVAR(Bool, i_pauseinbackground);
|
||||
|
||||
|
|
@ -332,59 +334,61 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||
{
|
||||
LRESULT result;
|
||||
|
||||
if (message == WM_INPUT)
|
||||
{
|
||||
UINT size;
|
||||
|
||||
if (!GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) &&
|
||||
size != 0)
|
||||
if (win32EnableInput){
|
||||
if (message == WM_INPUT)
|
||||
{
|
||||
TArray<uint8_t> array(size, true);
|
||||
uint8_t *buffer = array.data();
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size)
|
||||
UINT size;
|
||||
|
||||
if (!GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) &&
|
||||
size != 0)
|
||||
{
|
||||
int code = GET_RAWINPUT_CODE_WPARAM(wParam);
|
||||
if (Keyboard == NULL || !Keyboard->ProcessRawInput((RAWINPUT *)buffer, code))
|
||||
TArray<uint8_t> array(size, true);
|
||||
uint8_t *buffer = array.data();
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size)
|
||||
{
|
||||
if (Mouse == NULL || !Mouse->ProcessRawInput((RAWINPUT *)buffer, code))
|
||||
int code = GET_RAWINPUT_CODE_WPARAM(wParam);
|
||||
if (Keyboard == NULL || !Keyboard->ProcessRawInput((RAWINPUT *)buffer, code))
|
||||
{
|
||||
if (JoyDevices[INPUT_RawPS2] != NULL)
|
||||
if (Mouse == NULL || !Mouse->ProcessRawInput((RAWINPUT *)buffer, code))
|
||||
{
|
||||
JoyDevices[INPUT_RawPS2]->ProcessRawInput((RAWINPUT *)buffer, code);
|
||||
if (JoyDevices[INPUT_RawPS2] != NULL)
|
||||
{
|
||||
JoyDevices[INPUT_RawPS2]->ProcessRawInput((RAWINPUT *)buffer, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
if (CallHook(Keyboard, hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (CallHook(Mouse, hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
||||
{
|
||||
if (CallHook(JoyDevices[i], hWnd, message, wParam, lParam, &result))
|
||||
if (CallHook(Keyboard, hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (GUICapture && GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (message == WM_LBUTTONDOWN && sysCallbacks.WantLeftButton() && sysCallbacks.WantLeftButton())
|
||||
{
|
||||
if (GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
||||
if (CallHook(Mouse, hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
for (int i = 0; i < NUM_JOYDEVICES; ++i)
|
||||
{
|
||||
if (CallHook(JoyDevices[i], hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (GUICapture && GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (message == WM_LBUTTONDOWN && sysCallbacks.WantLeftButton() && sysCallbacks.WantLeftButton())
|
||||
{
|
||||
if (GUIWndProcHook(hWnd, message, wParam, lParam, &result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -577,7 +581,7 @@ void I_ShutdownInput ()
|
|||
}
|
||||
}
|
||||
|
||||
void I_GetEvent ()
|
||||
void I_GetWindowEvent()
|
||||
{
|
||||
MSG mess;
|
||||
|
||||
|
|
@ -596,6 +600,12 @@ void I_GetEvent ()
|
|||
}
|
||||
DispatchMessage (&mess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I_GetEvent ()
|
||||
{
|
||||
I_GetWindowEvent();
|
||||
|
||||
if (Keyboard != NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
bool I_InitInput (void *hwnd);
|
||||
void I_ShutdownInput ();
|
||||
|
||||
void I_GetWindowEvent();
|
||||
|
||||
void I_GetEvent();
|
||||
|
||||
enum
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
|
||||
if (GetConsoleMode(StdOut, &mode))
|
||||
{
|
||||
if (SetConsoleMode(StdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT))
|
||||
if (SetConsoleMode(StdOut, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT))
|
||||
FancyStdOut = IsWindows10OrGreater(); // Windows 8.1 and lower do not understand ANSI formatting.
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +263,7 @@ int DoMain (HINSTANCE hInstance)
|
|||
DWORD mode;
|
||||
if (GetConsoleMode(StdOut, &mode))
|
||||
{
|
||||
if (SetConsoleMode(StdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
|
||||
if (SetConsoleMode(StdOut, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
|
||||
FancyStdOut = IsWindows10OrGreater(); // Windows 8.1 and lower do not understand ANSI formatting.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
#define DEFAULT_DEADZONE 0.25f
|
||||
#define STATUS_SWITCH_TIME 3
|
||||
|
||||
#define VID_PLAY_COM 0x0b43
|
||||
|
|
@ -104,15 +103,23 @@ public:
|
|||
EJoyAxis GetAxisMap(int axis);
|
||||
const char *GetAxisName(int axis);
|
||||
float GetAxisScale(int axis);
|
||||
float GetAxisDigitalThreshold(int axis);
|
||||
EJoyCurve GetAxisResponseCurve(int axis);
|
||||
float GetAxisResponseCurvePoint(int axis, int point);
|
||||
|
||||
void SetAxisDeadZone(int axis, float deadzone);
|
||||
void SetAxisMap(int axis, EJoyAxis gameaxis);
|
||||
void SetAxisScale(int axis, float scale);
|
||||
void SetAxisDigitalThreshold(int axis, float threshold);
|
||||
void SetAxisResponseCurve(int axis, EJoyCurve preset);
|
||||
void SetAxisResponseCurvePoint(int axis, int point, float value);
|
||||
|
||||
bool IsSensitivityDefault();
|
||||
bool IsAxisDeadZoneDefault(int axis);
|
||||
bool IsAxisMapDefault(int axis);
|
||||
bool IsAxisScaleDefault(int axis);
|
||||
bool IsAxisDigitalThresholdDefault(int axis);
|
||||
bool IsAxisResponseCurveDefault(int axis);
|
||||
|
||||
bool GetEnabled();
|
||||
void SetEnabled(bool enabled);
|
||||
|
|
@ -130,6 +137,9 @@ protected:
|
|||
float Value;
|
||||
float DeadZone;
|
||||
float Multiplier;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
CubicBezier ResponseCurve;
|
||||
EJoyAxis GameAxis;
|
||||
uint8_t ButtonValue;
|
||||
};
|
||||
|
|
@ -137,6 +147,8 @@ protected:
|
|||
{
|
||||
EJoyAxis GameAxis;
|
||||
float Multiplier;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
};
|
||||
enum
|
||||
{
|
||||
|
|
@ -357,11 +369,11 @@ static const char *AxisNames[] =
|
|||
|
||||
FRawPS2Controller::DefaultAxisConfig FRawPS2Controller::DefaultAxes[NUM_AXES] =
|
||||
{
|
||||
// Game axis, multiplier
|
||||
{ JOYAXIS_Side, 1 }, // ThumbLX
|
||||
{ JOYAXIS_Forward, 1 }, // ThumbLY
|
||||
{ JOYAXIS_Yaw, 1 }, // ThumbRX
|
||||
{ JOYAXIS_Pitch, 0.75 }, // ThumbRY
|
||||
// Game axis, multiplier, digital threshold, response curve A, response curve B
|
||||
{ JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbLX
|
||||
{ JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbLY
|
||||
{ JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbRX
|
||||
{ JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbRY
|
||||
};
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
|
@ -552,11 +564,16 @@ void FRawPS2Controller::ProcessThumbstick(int value1, AxisInfo *axis1, int value
|
|||
axisval2 = value2 * (2.0 / 255) - 1.0;
|
||||
axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL);
|
||||
axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL);
|
||||
axisval1 = Joy_ApplyResponseCurveBezier(axis1->ResponseCurve, axisval1);
|
||||
axisval2 = Joy_ApplyResponseCurveBezier(axis2->ResponseCurve, axisval2);
|
||||
axis1->Value = float(axisval1);
|
||||
axis2->Value = float(axisval2);
|
||||
|
||||
// We store all four buttons in the first axis and ignore the second.
|
||||
buttonstate = Joy_XYAxesToButtons(axisval1, axisval2);
|
||||
buttonstate = Joy_XYAxesToButtons(
|
||||
(abs(axisval1) < axis1->DigitalThreshold) ? 0 : axisval1,
|
||||
(abs(axisval2) < axis2->DigitalThreshold) ? 0 : axisval2
|
||||
);
|
||||
Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base);
|
||||
axis1->ButtonValue = buttonstate;
|
||||
}
|
||||
|
|
@ -644,10 +661,10 @@ void FRawPS2Controller::AddAxes(float axes[NUM_JOYAXIS])
|
|||
|
||||
void FRawPS2Controller::SetDefaultConfig()
|
||||
{
|
||||
Multiplier = 1;
|
||||
Multiplier = JOYSENSITIVITY_DEFAULT;
|
||||
for (int i = 0; i < NUM_AXES; ++i)
|
||||
{
|
||||
Axes[i].DeadZone = DEFAULT_DEADZONE;
|
||||
Axes[i].DeadZone = JOYDEADZONE_DEFAULT;
|
||||
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
|
||||
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
|
||||
}
|
||||
|
|
@ -712,7 +729,7 @@ void FRawPS2Controller::SetSensitivity(float scale)
|
|||
|
||||
bool FRawPS2Controller::IsSensitivityDefault()
|
||||
{
|
||||
return Multiplier == 1;
|
||||
return Multiplier == JOYSENSITIVITY_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -786,6 +803,51 @@ float FRawPS2Controller::GetAxisScale(int axis)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: GetAxisDigitalThreshold
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
float FRawPS2Controller::GetAxisDigitalThreshold(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].DigitalThreshold;
|
||||
}
|
||||
return JOYTHRESH_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: GetAxisResponseCurve
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
EJoyCurve FRawPS2Controller::GetAxisResponseCurve(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset;
|
||||
}
|
||||
return JOYCURVE_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: GetAxisResponseCurvePoint
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
float FRawPS2Controller::GetAxisResponseCurvePoint(int axis, int point)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
|
||||
{
|
||||
return Axes[axis].ResponseCurve.pts[point];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: SetAxisDeadZone
|
||||
|
|
@ -828,6 +890,52 @@ void FRawPS2Controller::SetAxisScale(int axis, float scale)
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: SetAxisDigitalThreshold
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FRawPS2Controller::SetAxisDigitalThreshold(int axis, float threshold)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
Axes[axis].DigitalThreshold = threshold;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: SetAxisResponseCurve
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FRawPS2Controller::SetAxisResponseCurve(int axis, EJoyCurve preset)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurvePreset = preset;
|
||||
if (preset == JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurve = JOYCURVE[preset];
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: SetAxisResponseCurvePoint
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FRawPS2Controller::SetAxisResponseCurvePoint(int axis, int point, float value)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
|
||||
{
|
||||
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
|
||||
Axes[axis].ResponseCurve.pts[point] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: IsAxisDeadZoneDefault
|
||||
|
|
@ -838,7 +946,7 @@ bool FRawPS2Controller::IsAxisDeadZoneDefault(int axis)
|
|||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].DeadZone == DEFAULT_DEADZONE;
|
||||
return Axes[axis].DeadZone == JOYDEADZONE_DEFAULT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -858,6 +966,36 @@ bool FRawPS2Controller::IsAxisScaleDefault(int axis)
|
|||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: IsAxisDigitalThresholdDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FRawPS2Controller::IsAxisDigitalThresholdDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: IsAxisResponseCurveDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FRawPS2Controller::IsAxisResponseCurveDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FRawPS2Controller :: GetEnabled
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@
|
|||
|
||||
static int isportable = -1;
|
||||
|
||||
extern bool netgame;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// IsProgramDirectoryWritable
|
||||
|
|
@ -378,6 +380,8 @@ FString M_GetSavegamesPath()
|
|||
path = GetKnownFolder(-1, FOLDERID_SavedGames, true);
|
||||
path << "/" GAMENAME "/";
|
||||
}
|
||||
if (netgame)
|
||||
path << "NetGame/";
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -365,6 +365,17 @@ static void SetQueryIWad(HWND dialog)
|
|||
queryiwad = query;
|
||||
}
|
||||
|
||||
bool HoldingQueryKey(const char* key)
|
||||
{
|
||||
int vkey = 0;
|
||||
if (!stricmp(key, "shift"))
|
||||
vkey = VK_SHIFT;
|
||||
else if (!stricmp(key, "control") || !stricmp(key, "ctrl"))
|
||||
vkey = VK_CONTROL;
|
||||
|
||||
return vkey && GetAsyncKeyState(vkey);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// I_PickIWad
|
||||
|
|
@ -373,26 +384,13 @@ static void SetQueryIWad(HWND dialog)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad, int& autoloadflags, FString &extraArgs)
|
||||
bool I_PickIWad(bool showwin, FStartupSelectionInfo& info)
|
||||
{
|
||||
int vkey;
|
||||
if (stricmp(queryiwad_key, "shift") == 0)
|
||||
if (showwin)
|
||||
{
|
||||
vkey = VK_SHIFT;
|
||||
return LauncherWindow::ExecModal(info);
|
||||
}
|
||||
else if (stricmp(queryiwad_key, "control") == 0 || stricmp (queryiwad_key, "ctrl") == 0)
|
||||
{
|
||||
vkey = VK_CONTROL;
|
||||
}
|
||||
else
|
||||
{
|
||||
vkey = 0;
|
||||
}
|
||||
if (showwin || (vkey != 0 && GetAsyncKeyState(vkey)))
|
||||
{
|
||||
return LauncherWindow::ExecModal(wads, numwads, defaultiwad, &autoloadflags, &extraArgs);
|
||||
}
|
||||
return defaultiwad;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
#include "zstring.h"
|
||||
#include "utf8.h"
|
||||
|
||||
struct ticcmd_t;
|
||||
struct WadStuff;
|
||||
struct FStartupSelectionInfo;
|
||||
|
||||
// [RH] Detects the OS the game is running under.
|
||||
void I_DetectOS (void);
|
||||
|
|
@ -38,7 +38,8 @@ void I_PrintStr (const char *cp);
|
|||
void I_SetIWADInfo ();
|
||||
|
||||
// Pick from multiple IWADs to use
|
||||
int I_PickIWad(WadStuff* wads, int numwads, bool queryiwad, int defaultiwad, int& autoloadflags, FString &extraArgs);
|
||||
bool HoldingQueryKey(const char* key);
|
||||
bool I_PickIWad(bool showwin, FStartupSelectionInfo& info);
|
||||
|
||||
// The ini could not be saved at exit
|
||||
bool I_WriteIniFailed (const char* filename);
|
||||
|
|
|
|||
|
|
@ -94,15 +94,23 @@ public:
|
|||
EJoyAxis GetAxisMap(int axis);
|
||||
const char *GetAxisName(int axis);
|
||||
float GetAxisScale(int axis);
|
||||
float GetAxisDigitalThreshold(int axis);
|
||||
EJoyCurve GetAxisResponseCurve(int axis);
|
||||
float GetAxisResponseCurvePoint(int axis, int point);
|
||||
|
||||
void SetAxisDeadZone(int axis, float deadzone);
|
||||
void SetAxisMap(int axis, EJoyAxis gameaxis);
|
||||
void SetAxisScale(int axis, float scale);
|
||||
void SetAxisDigitalThreshold(int axis, float threshold);
|
||||
void SetAxisResponseCurve(int axis, EJoyCurve preset);
|
||||
void SetAxisResponseCurvePoint(int axis, int point, float value);
|
||||
|
||||
bool IsSensitivityDefault();
|
||||
bool IsAxisDeadZoneDefault(int axis);
|
||||
bool IsAxisMapDefault(int axis);
|
||||
bool IsAxisScaleDefault(int axis);
|
||||
bool IsAxisDigitalThresholdDefault(int axis);
|
||||
bool IsAxisResponseCurveDefault(int axis);
|
||||
|
||||
bool GetEnabled();
|
||||
void SetEnabled(bool enabled);
|
||||
|
|
@ -122,12 +130,17 @@ protected:
|
|||
float Multiplier;
|
||||
EJoyAxis GameAxis;
|
||||
uint8_t ButtonValue;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
CubicBezier ResponseCurve;
|
||||
};
|
||||
struct DefaultAxisConfig
|
||||
{
|
||||
float DeadZone;
|
||||
EJoyAxis GameAxis;
|
||||
float Multiplier;
|
||||
float DigitalThreshold;
|
||||
EJoyCurve ResponseCurvePreset;
|
||||
};
|
||||
enum
|
||||
{
|
||||
|
|
@ -211,13 +224,13 @@ static const char *AxisNames[] =
|
|||
|
||||
FXInputController::DefaultAxisConfig FXInputController::DefaultAxes[NUM_AXES] =
|
||||
{
|
||||
// Dead zone, game axis, multiplier
|
||||
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Side, 1 }, // ThumbLX
|
||||
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Forward, 1 }, // ThumbLY
|
||||
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Yaw, 1 }, // ThumbRX
|
||||
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Pitch, 0.75 }, // ThumbRY
|
||||
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, 0 }, // LeftTrigger
|
||||
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, 0 } // RightTrigger
|
||||
// Dead zone, game axis, multiplier, digitalthreshold, curveA, curveB
|
||||
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Side, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbLX
|
||||
{ XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32768.f, JOYAXIS_Forward, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbLY
|
||||
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Yaw, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_X, JOYCURVE_DEFAULT }, // ThumbRX
|
||||
{ XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32768.f, JOYAXIS_Pitch, JOYSENSITIVITY_DEFAULT, JOYTHRESH_STICK_Y, JOYCURVE_DEFAULT }, // ThumbRY
|
||||
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT }, // LeftTrigger
|
||||
{ XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 256.f, JOYAXIS_None, JOYSENSITIVITY_DEFAULT, JOYTHRESH_TRIGGER, JOYCURVE_DEFAULT } // RightTrigger
|
||||
};
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
|
@ -327,11 +340,16 @@ void FXInputController::ProcessThumbstick(int value1, AxisInfo *axis1,
|
|||
axisval2 = (value2 - SHRT_MIN) * 2.0 / 65536 - 1.0;
|
||||
axisval1 = Joy_RemoveDeadZone(axisval1, axis1->DeadZone, NULL);
|
||||
axisval2 = Joy_RemoveDeadZone(axisval2, axis2->DeadZone, NULL);
|
||||
axisval1 = Joy_ApplyResponseCurveBezier(axis1->ResponseCurve, axisval1);
|
||||
axisval2 = Joy_ApplyResponseCurveBezier(axis2->ResponseCurve, axisval2);
|
||||
axis1->Value = float(axisval1);
|
||||
axis2->Value = float(axisval2);
|
||||
|
||||
// We store all four buttons in the first axis and ignore the second.
|
||||
buttonstate = Joy_XYAxesToButtons(axisval1, axisval2);
|
||||
buttonstate = Joy_XYAxesToButtons(
|
||||
abs(axisval1) < axis1->DigitalThreshold ? 0: axisval1,
|
||||
abs(axisval2) < axis2->DigitalThreshold ? 0: axisval2
|
||||
);
|
||||
Joy_GenerateButtonEvents(axis1->ButtonValue, buttonstate, 4, base);
|
||||
axis1->ButtonValue = buttonstate;
|
||||
}
|
||||
|
|
@ -351,6 +369,11 @@ void FXInputController::ProcessTrigger(int value, AxisInfo *axis, int base)
|
|||
double axisval;
|
||||
|
||||
axisval = Joy_RemoveDeadZone(value / 256.0, axis->DeadZone, &buttonstate);
|
||||
axisval = Joy_ApplyResponseCurveBezier(axis->ResponseCurve, axisval);
|
||||
|
||||
// TODO: probably just put this into Joy_RemoveDeadZone
|
||||
if (abs(axisval) < axis->DigitalThreshold) buttonstate = 0;
|
||||
|
||||
Joy_GenerateButtonEvents(axis->ButtonValue, buttonstate, 1, base);
|
||||
axis->ButtonValue = buttonstate;
|
||||
axis->Value = float(axisval);
|
||||
|
|
@ -431,12 +454,15 @@ void FXInputController::AddAxes(float axes[NUM_JOYAXIS])
|
|||
|
||||
void FXInputController::SetDefaultConfig()
|
||||
{
|
||||
Multiplier = 1;
|
||||
Multiplier = JOYSENSITIVITY_DEFAULT;
|
||||
for (int i = 0; i < NUM_AXES; ++i)
|
||||
{
|
||||
Axes[i].DeadZone = DefaultAxes[i].DeadZone;
|
||||
Axes[i].GameAxis = DefaultAxes[i].GameAxis;
|
||||
Axes[i].Multiplier = DefaultAxes[i].Multiplier;
|
||||
Axes[i].DigitalThreshold = DefaultAxes[i].DigitalThreshold;
|
||||
Axes[i].ResponseCurve = JOYCURVE[DefaultAxes[i].ResponseCurvePreset];
|
||||
Axes[i].ResponseCurvePreset = DefaultAxes[i].ResponseCurvePreset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -494,7 +520,7 @@ void FXInputController::SetSensitivity(float scale)
|
|||
|
||||
bool FXInputController::IsSensitivityDefault()
|
||||
{
|
||||
return Multiplier == 1;
|
||||
return Multiplier == JOYSENSITIVITY_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -565,6 +591,51 @@ float FXInputController::GetAxisScale(int axis)
|
|||
{
|
||||
return Axes[axis].Multiplier;
|
||||
}
|
||||
return JOYSENSITIVITY_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: GetAxisDigitalThreshold
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
float FXInputController::GetAxisDigitalThreshold(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].DigitalThreshold;
|
||||
}
|
||||
return JOYTHRESH_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: GetAxisResponseCurve
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
EJoyCurve FXInputController::GetAxisResponseCurve(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset;
|
||||
}
|
||||
return JOYCURVE_DEFAULT;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: GetAxisResponseCurvePoint
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
float FXInputController::GetAxisResponseCurvePoint(int axis, int point)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
|
||||
{
|
||||
return Axes[axis].ResponseCurve.pts[point];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -610,6 +681,52 @@ void FXInputController::SetAxisScale(int axis, float scale)
|
|||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: SetAxisDigitalThreshold
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FXInputController::SetAxisDigitalThreshold(int axis, float threshold)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
Axes[axis].DigitalThreshold = threshold;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: SetAxisResponseCurve
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FXInputController::SetAxisResponseCurve(int axis, EJoyCurve preset)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
if (preset >= NUM_JOYCURVE || preset < JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurvePreset = preset;
|
||||
if (preset == JOYCURVE_CUSTOM) return;
|
||||
Axes[axis].ResponseCurve = JOYCURVE[preset];
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FXInputController :: SetAxisResponseCurvePoint
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FXInputController::SetAxisResponseCurvePoint(int axis, int point, float value)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES && unsigned(point) < 4)
|
||||
{
|
||||
Axes[axis].ResponseCurvePreset = JOYCURVE_CUSTOM;
|
||||
Axes[axis].ResponseCurve.pts[point] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FXInputController :: IsAxisDeadZoneDefault
|
||||
|
|
@ -640,6 +757,36 @@ bool FXInputController::IsAxisScaleDefault(int axis)
|
|||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FXInputController :: IsAxisDigitalThresholdDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FXInputController::IsAxisDigitalThresholdDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].DigitalThreshold == DefaultAxes[axis].DigitalThreshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FXInputController :: IsAxisResponseCurveDefault
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FXInputController::IsAxisResponseCurveDefault(int axis)
|
||||
{
|
||||
if (unsigned(axis) < NUM_AXES)
|
||||
{
|
||||
return Axes[axis].ResponseCurvePreset == DefaultAxes[axis].ResponseCurvePreset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FXInputController :: GetEnabled
|
||||
|
|
|
|||
|
|
@ -92,8 +92,6 @@ FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
|
|||
FBasicStartupScreen::FBasicStartupScreen(int max_progress)
|
||||
: FStartupScreen(max_progress)
|
||||
{
|
||||
NetMaxPos = 0;
|
||||
NetCurPos = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -137,14 +135,43 @@ void FBasicStartupScreen::Progress()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void FBasicStartupScreen::NetInit(const char *message, int numplayers)
|
||||
void FBasicStartupScreen::NetInit(const char *message, bool host)
|
||||
{
|
||||
NetMaxPos = numplayers;
|
||||
NetStartWindow::ShowNetStartPane(message, numplayers);
|
||||
NetStartWindow::NetInit(message, host);
|
||||
}
|
||||
|
||||
NetMaxPos = numplayers;
|
||||
NetCurPos = 0;
|
||||
NetProgress(1); // You always know about yourself
|
||||
void FBasicStartupScreen::NetMessage(const char* message)
|
||||
{
|
||||
NetStartWindow::NetMessage(message);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetConnect(int client, const char* name, unsigned flags, int status)
|
||||
{
|
||||
NetStartWindow::NetConnect(client, name, flags, status);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetUpdate(int client, int status)
|
||||
{
|
||||
NetStartWindow::NetUpdate(client, status);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetDisconnect(int client)
|
||||
{
|
||||
NetStartWindow::NetDisconnect(client);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FBasicStartupScreen :: NetProgress
|
||||
//
|
||||
// Sets the network progress meter. If count is 0, it gets bumped by 1.
|
||||
// Otherwise, it is set to count.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FBasicStartupScreen::NetProgress(int cur, int limit)
|
||||
{
|
||||
NetStartWindow::NetProgress(cur, limit);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -157,30 +184,27 @@ void FBasicStartupScreen::NetInit(const char *message, int numplayers)
|
|||
|
||||
void FBasicStartupScreen::NetDone()
|
||||
{
|
||||
NetStartWindow::HideNetStartPane();
|
||||
NetStartWindow::NetDone();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FBasicStartupScreen :: NetProgress
|
||||
//
|
||||
// Sets the network progress meter. If count is 0, it gets bumped by 1.
|
||||
// Otherwise, it is set to count.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FBasicStartupScreen::NetProgress(int count)
|
||||
void FBasicStartupScreen::NetClose()
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
NetCurPos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
NetCurPos = count;
|
||||
}
|
||||
NetStartWindow::NetClose();
|
||||
}
|
||||
|
||||
NetStartWindow::SetNetStartProgress(count);
|
||||
bool FBasicStartupScreen::ShouldStartNet()
|
||||
{
|
||||
return NetStartWindow::ShouldStartNet();
|
||||
}
|
||||
|
||||
int FBasicStartupScreen::GetNetKickClient()
|
||||
{
|
||||
return NetStartWindow::GetNetKickClient();
|
||||
}
|
||||
|
||||
int FBasicStartupScreen::GetNetBanClient()
|
||||
{
|
||||
return NetStartWindow::GetNetBanClient();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -197,12 +221,7 @@ void FBasicStartupScreen::NetProgress(int count)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
|
||||
bool FBasicStartupScreen::NetLoop(bool (*loopCallback)(void*), void* data)
|
||||
{
|
||||
return NetStartWindow::RunMessageLoop(timer_callback, userdata);
|
||||
}
|
||||
|
||||
void FBasicStartupScreen::NetClose()
|
||||
{
|
||||
NetStartWindow::CloseNetStartPane();
|
||||
return NetStartWindow::NetLoop(loopCallback, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "textures.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "v_video.h"
|
||||
#include "printf.h"
|
||||
|
||||
|
||||
static bool IsGlslWhitespace(char c)
|
||||
|
|
@ -81,8 +82,8 @@ FString RemoveLegacyUserUniforms(FString code)
|
|||
{
|
||||
// User shaders must declare their uniforms via the GLDEFS file.
|
||||
|
||||
code.Substitute("uniform sampler2D tex;", " ");
|
||||
code.Substitute("uniform float timer;", " ");
|
||||
bool found = code.Substitute("uniform sampler2D tex;", " ");
|
||||
found = code.Substitute("uniform float timer;", " ") || found;
|
||||
|
||||
// The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace.
|
||||
|
||||
|
|
@ -120,6 +121,7 @@ FString RemoveLegacyUserUniforms(FString code)
|
|||
chars[i] = ' ';
|
||||
}
|
||||
startIndex = statementEndIndex;
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -127,6 +129,13 @@ FString RemoveLegacyUserUniforms(FString code)
|
|||
}
|
||||
}
|
||||
|
||||
if(found)
|
||||
{
|
||||
DPrintf(DMSG_WARNING, TEXTCOLOR_ORANGE "timer and tex uniforms should not be explicitly declared.\n");
|
||||
}
|
||||
|
||||
bool foundtexture2d = false;
|
||||
|
||||
// Also remove all occurences of the token 'texture2d'. Some shaders may still use this deprecated function to access a sampler.
|
||||
// Modern GLSL only allows use of 'texture'.
|
||||
while (true)
|
||||
|
|
@ -135,6 +144,8 @@ FString RemoveLegacyUserUniforms(FString code)
|
|||
if (matchIndex == -1)
|
||||
break;
|
||||
|
||||
foundtexture2d = true;
|
||||
|
||||
// Check if this is a real token.
|
||||
bool isKeywordStart = matchIndex == 0 || !isalnum(chars[matchIndex - 1] & 255);
|
||||
bool isKeywordEnd = matchIndex + 9 == len || !isalnum(chars[matchIndex + 9] & 255);
|
||||
|
|
@ -145,6 +156,11 @@ FString RemoveLegacyUserUniforms(FString code)
|
|||
startIndex = matchIndex + 9;
|
||||
}
|
||||
|
||||
if(foundtexture2d)
|
||||
{
|
||||
DPrintf(DMSG_WARNING, TEXTCOLOR_ORANGE "texture2d is deprecated, use texture instead.\n");
|
||||
}
|
||||
|
||||
code.UnlockBuffer();
|
||||
|
||||
return code;
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ void FSkyVertexBuffer::DoRenderDome(FRenderState& state, FGameTexture* tex, int
|
|||
RenderRow(state, DT_TriangleFan, rc, primStart);
|
||||
state.EnableTexture(true);
|
||||
}
|
||||
state.SetObjectColor(0xffffffff);
|
||||
state.SetObjectColor(color);
|
||||
for (int i = 1; i <= mRows; i++)
|
||||
{
|
||||
RenderRow(state, DT_TriangleStrip, i, primStart, i == 1);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ struct HWViewpointUniforms
|
|||
|
||||
FVector3 mCameraNormal;
|
||||
|
||||
float mThickFogDistance = -1.f;
|
||||
float mThickFogMultiplier = 30.f;
|
||||
|
||||
void CalcDependencies()
|
||||
{
|
||||
mNormalViewMatrix.computeNormalMatrix(mViewMatrix);
|
||||
|
|
|
|||
194
src/common/rendering/stb_include.cpp
Normal file
194
src/common/rendering/stb_include.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// stb_include.h - v0.02gz - parse and process #include directives - public domain
|
||||
//
|
||||
// To build this, in one source file that includes this file do
|
||||
// #define STB_INCLUDE_IMPLEMENTATION
|
||||
//
|
||||
// This program parses a string and replaces lines of the form
|
||||
// #include "foo"
|
||||
// with the contents of a file named "foo". It also embeds the
|
||||
// appropriate #line directives. Note that all include files must
|
||||
// reside in the location specified in the gzdoom filesystem.
|
||||
//
|
||||
// Credits:
|
||||
//
|
||||
// Written by Sean Barrett.
|
||||
//
|
||||
// Fixes:
|
||||
// Michal Klos
|
||||
//
|
||||
// GZDoom Conversion:
|
||||
// Jay
|
||||
|
||||
#include "stb_include.h"
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
static bool stb_include_load_file(FString filename, FString &out)
|
||||
{
|
||||
int f = fileSystem.FindFile(filename.GetChars());
|
||||
if(f < 0) return false;
|
||||
out = GetStringFromLump(f, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct include_info
|
||||
{
|
||||
int64_t offset;
|
||||
int64_t end;
|
||||
FString filename;
|
||||
int64_t next_line_after;
|
||||
};
|
||||
|
||||
static bool stb_include_isspace(int ch)
|
||||
{
|
||||
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
|
||||
}
|
||||
|
||||
static void skip_whitespace(const char * &s)
|
||||
{
|
||||
while (*s == ' ' || *s == '\t')
|
||||
{
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
static void skip_newline(const char * &s)
|
||||
{
|
||||
|
||||
while (*s != '\r' && *s != '\n' && *s != 0)
|
||||
{
|
||||
++s;
|
||||
}
|
||||
|
||||
if(s[0] == '\r' && s[1] == '\n')
|
||||
{
|
||||
s += 2;
|
||||
}
|
||||
else if (*s == '\r' || *s == '\n')
|
||||
{
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
static void find_end_quote(const char * &t)
|
||||
{
|
||||
while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0)
|
||||
{
|
||||
++t;
|
||||
}
|
||||
}
|
||||
|
||||
static void find_newline(const char * &s)
|
||||
{
|
||||
while (*s != '\r' && *s != '\n' && *s != 0)
|
||||
{
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
// find location of all #include and #inject
|
||||
static int64_t stb_include_find_includes(const char *text, TArray<include_info> &plist)
|
||||
{
|
||||
int64_t line_count = 1;
|
||||
int64_t inc_count = 0;
|
||||
const char *s = text;
|
||||
const char *start;
|
||||
|
||||
TArray<include_info> list;
|
||||
while (*s)
|
||||
{
|
||||
// parse is always at start of line when we reach here
|
||||
start = s;
|
||||
skip_whitespace(s);
|
||||
|
||||
if (*s == '#')
|
||||
{
|
||||
++s;
|
||||
skip_whitespace(s);
|
||||
|
||||
if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7]))
|
||||
{
|
||||
s += 7;
|
||||
skip_whitespace(s);
|
||||
|
||||
if (*s == '"')
|
||||
{
|
||||
const char * include_filename = ++s;
|
||||
|
||||
find_end_quote(include_filename);
|
||||
|
||||
if (*include_filename == '"')
|
||||
{ // ignore unterminated quotes
|
||||
FString filename;
|
||||
filename.AppendCStrPart(s, include_filename - s);
|
||||
s = include_filename;
|
||||
|
||||
find_newline(s);
|
||||
|
||||
// s points to the newline, so s-start is everything except the newline
|
||||
list.Push({start - text, s - text, filename, line_count + 1});
|
||||
inc_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_newline(s);
|
||||
|
||||
++line_count;
|
||||
}
|
||||
|
||||
plist = std::move(list);
|
||||
|
||||
return inc_count;
|
||||
}
|
||||
|
||||
FString stb_include_string(FString str, FString filename, TArray<FString> &filenames, FString &error)
|
||||
{
|
||||
error = "";
|
||||
TArray<include_info> inc_list;
|
||||
int64_t num = stb_include_find_includes(str.GetChars(), inc_list);
|
||||
FString text = "";
|
||||
size_t last = 0;
|
||||
|
||||
filenames.Push(filename);
|
||||
size_t curIndex = filenames.Size();
|
||||
|
||||
text.AppendFormat("\n#line 1 %zu // %s\n", curIndex, filename.GetChars());
|
||||
|
||||
for (int64_t i = 0; i < num; ++i)
|
||||
{
|
||||
text.AppendCStrPart(str.GetChars() + last, inc_list[i].offset - last);
|
||||
|
||||
FString inc = stb_include_file(inc_list[i].filename.GetChars(), filenames, error);
|
||||
if (!error.IsEmpty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
text += inc;
|
||||
|
||||
text.AppendFormat("\n#line %zu %zu // %s\n", inc_list[i].next_line_after, curIndex, filename.GetChars());
|
||||
// no newlines, because we kept the #include newlines, which will get appended next
|
||||
last = inc_list[i].end;
|
||||
}
|
||||
text.AppendCStrPart(str.GetChars() + last, str.Len() - last);
|
||||
return text;
|
||||
}
|
||||
|
||||
FString stb_include_file(FString filename, TArray<FString> &filenames, FString &error)
|
||||
{
|
||||
FString text;
|
||||
if (!stb_include_load_file(filename, text))
|
||||
{
|
||||
error += "Error: couldn't load '";
|
||||
error += filename;
|
||||
error += "'";
|
||||
return "";
|
||||
}
|
||||
return stb_include_string(text, filename, filenames, error);
|
||||
}
|
||||
33
src/common/rendering/stb_include.h
Normal file
33
src/common/rendering/stb_include.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// stb_include.h - v0.02gz - parse and process #include directives - public domain
|
||||
//
|
||||
// To build this, in one source file that includes this file do
|
||||
// #define STB_INCLUDE_IMPLEMENTATION
|
||||
//
|
||||
// This program parses a string and replaces lines of the form
|
||||
// #include "foo"
|
||||
// with the contents of a file named "foo". It also embeds the
|
||||
// appropriate #line directives. Note that all include files must
|
||||
// reside in the location specified in the gzdoom filesystem.
|
||||
//
|
||||
// Credits:
|
||||
//
|
||||
// Written by Sean Barrett.
|
||||
//
|
||||
// Fixes:
|
||||
// Michal Klos
|
||||
//
|
||||
// GZDoom Conversion:
|
||||
// Jay
|
||||
|
||||
#ifndef STB_INCLUDE_STB_INCLUDE_H
|
||||
#define STB_INCLUDE_STB_INCLUDE_H
|
||||
|
||||
#include "zstring.h"
|
||||
|
||||
// Do include-processing on the string 'str'.
|
||||
FString stb_include_string(FString str, FString filename, TArray<FString> &filenames, FString &error);
|
||||
|
||||
// Load the file 'filename' and do include-processing on the string therein.
|
||||
FString stb_include_file(FString filename, TArray<FString> &filenames, FString &error);
|
||||
|
||||
#endif
|
||||
|
|
@ -3326,11 +3326,11 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
|
|||
|
||||
if (compileEnvironment.CheckForCustomAddition)
|
||||
{
|
||||
auto result = compileEnvironment.CheckForCustomAddition(this, ctx);
|
||||
if (result)
|
||||
auto expr = compileEnvironment.CheckForCustomAddition(this, ctx);
|
||||
if (expr)
|
||||
{
|
||||
ABORT(right);
|
||||
goto goon;
|
||||
delete this;
|
||||
return expr->Resolve(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6701,7 +6701,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
|
|||
}
|
||||
FxExpression *self = new FxSelf(ScriptPosition);
|
||||
self = self->Resolve(ctx);
|
||||
newex = ResolveMember(ctx, ctx.Function->Variants[0].SelfClass, self, ctx.Function->Variants[0].SelfClass);
|
||||
newex = ResolveMember(ctx, ctx.Function->Variants[0].SelfClass, self, ctx.Function->Variants[0].SelfClass, ctx.Function->Variants[0].Flags & VARF_SafeConst);
|
||||
ABORT(newex);
|
||||
goto foundit;
|
||||
}
|
||||
|
|
@ -6863,7 +6863,7 @@ foundit:
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *classctx, FxExpression *&object, PContainerType *objtype)
|
||||
FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *classctx, FxExpression *&object, PContainerType *objtype, bool isConst)
|
||||
{
|
||||
PSymbol *sym;
|
||||
PSymbolTable *symtbl;
|
||||
|
|
@ -6956,7 +6956,7 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PContainerType *
|
|||
}
|
||||
}
|
||||
|
||||
auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition);
|
||||
auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition, isConst) : new FxStructMember(object, vsym, ScriptPosition, isConst);
|
||||
object = nullptr;
|
||||
return x->Resolve(ctx);
|
||||
}
|
||||
|
|
@ -7611,8 +7611,8 @@ FxMemberBase::FxMemberBase(EFxType type, PField *f, const FScriptPosition &p)
|
|||
}
|
||||
|
||||
|
||||
FxStructMember::FxStructMember(FxExpression *x, PField* mem, const FScriptPosition &pos)
|
||||
: FxMemberBase(EFX_StructMember, mem, pos)
|
||||
FxStructMember::FxStructMember(FxExpression *x, PField* mem, const FScriptPosition &pos, bool isConst)
|
||||
: FxMemberBase(EFX_StructMember, mem, pos), IsConst(isConst)
|
||||
{
|
||||
classx = x;
|
||||
}
|
||||
|
|
@ -7662,7 +7662,7 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable)
|
|||
bWritable = false;
|
||||
}
|
||||
|
||||
*writable = bWritable;
|
||||
*writable = bWritable && !IsConst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -7873,8 +7873,8 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
FxClassMember::FxClassMember(FxExpression *x, PField* mem, const FScriptPosition &pos)
|
||||
: FxStructMember(x, mem, pos)
|
||||
FxClassMember::FxClassMember(FxExpression *x, PField* mem, const FScriptPosition &pos, bool isConst)
|
||||
: FxStructMember(x, mem, pos, isConst)
|
||||
{
|
||||
ExprType = EFX_ClassMember;
|
||||
}
|
||||
|
|
@ -8072,7 +8072,6 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
{
|
||||
arraytype = static_cast<PArray*>(Array->ValueType);
|
||||
}
|
||||
ExpEmit arrayvar = Array->Emit(build);
|
||||
ExpEmit start;
|
||||
ExpEmit bound;
|
||||
bool nestedarray = false;
|
||||
|
|
@ -8080,31 +8079,99 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
if (SizeAddr != ~0u)
|
||||
{
|
||||
bool ismeta = Array->ExprType == EFX_ClassMember && static_cast<FxClassMember*>(Array)->membervar->Flags & VARF_Meta;
|
||||
|
||||
start = ExpEmit(build, REGT_POINTER);
|
||||
build->Emit(OP_LP, start.RegNum, arrayvar.RegNum, build->GetConstantInt(0));
|
||||
|
||||
|
||||
auto f = Create<PField>(NAME_None, TypeUInt32, ismeta? VARF_Meta : 0, SizeAddr);
|
||||
auto arraymemberbase = static_cast<FxMemberBase *>(Array);
|
||||
|
||||
auto origmembervar = arraymemberbase->membervar;
|
||||
auto origaddrreq = arraymemberbase->AddressRequested;
|
||||
auto origvaluetype = Array->ValueType;
|
||||
if (Array->ExprType == EFX_StructMember || Array->ExprType == EFX_ClassMember)
|
||||
{
|
||||
struct DummyVar : public FxExpression
|
||||
{
|
||||
ExpEmit dummy;
|
||||
DummyVar(ExpEmit e) : FxExpression(EFX_Expression,{}), dummy(e){}
|
||||
ExpEmit Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
return dummy;
|
||||
};
|
||||
};
|
||||
|
||||
arraymemberbase->membervar = f;
|
||||
arraymemberbase->AddressRequested = false;
|
||||
Array->ValueType = TypeUInt32;
|
||||
//fix expression bug
|
||||
FxStructMember * orig = static_cast<FxStructMember *>(Array);
|
||||
FxExpression * prev = orig->classx;
|
||||
ExpEmit objvar = prev->Emit(build);
|
||||
bool wasFixed = objvar.Fixed;
|
||||
|
||||
bound = Array->Emit(build);
|
||||
objvar.Fixed = true;
|
||||
|
||||
arraymemberbase->membervar = origmembervar;
|
||||
arraymemberbase->AddressRequested = origaddrreq;
|
||||
Array->ValueType = origvaluetype;
|
||||
orig->classx = new DummyVar(objvar);
|
||||
orig->classx->ValueType = prev->ValueType;
|
||||
ExpEmit arrayvar = Array->Emit(build);
|
||||
start = ExpEmit(build, REGT_POINTER);
|
||||
delete orig->classx;
|
||||
orig->classx = prev;
|
||||
|
||||
arrayvar.Free(build);
|
||||
build->Emit(OP_LP, start.RegNum, arrayvar.RegNum, build->GetConstantInt(0));
|
||||
|
||||
if(ismeta)
|
||||
{
|
||||
// [Jay0]
|
||||
// ugh do it the old way for meta since i don't want to duplicate that code here, but this time it only double-emits the member access, not the function call, so no bad side-effects, still not "right" though
|
||||
// TODO handle meta better
|
||||
|
||||
auto origmembervar = arraymemberbase->membervar;
|
||||
auto origaddrreq = arraymemberbase->AddressRequested;
|
||||
auto origvaluetype = Array->ValueType;
|
||||
|
||||
arraymemberbase->membervar = f;
|
||||
arraymemberbase->AddressRequested = false;
|
||||
Array->ValueType = TypeUInt32;
|
||||
|
||||
bound = Array->Emit(build);
|
||||
|
||||
arraymemberbase->membervar = origmembervar;
|
||||
arraymemberbase->AddressRequested = origaddrreq;
|
||||
Array->ValueType = origvaluetype;
|
||||
}
|
||||
else
|
||||
{
|
||||
bound = ExpEmit(build, REGT_INT);
|
||||
build->Emit(OP_LW, bound.RegNum, objvar.RegNum, build->GetConstantInt((int)SizeAddr));
|
||||
}
|
||||
|
||||
objvar.Fixed = wasFixed;
|
||||
objvar.Free(build);
|
||||
arrayvar.Free(build);
|
||||
}
|
||||
else
|
||||
{
|
||||
// [Jay0]
|
||||
// now only runs for global variables and stack variables, so the double-emit is """fine"""
|
||||
// TODO replace this entirely with something better still
|
||||
|
||||
ExpEmit arrayvar = Array->Emit(build);
|
||||
|
||||
start = ExpEmit(build, REGT_POINTER);
|
||||
build->Emit(OP_LP, start.RegNum, arrayvar.RegNum, build->GetConstantInt(0));
|
||||
|
||||
auto origmembervar = arraymemberbase->membervar;
|
||||
auto origaddrreq = arraymemberbase->AddressRequested;
|
||||
auto origvaluetype = Array->ValueType;
|
||||
|
||||
arraymemberbase->membervar = f;
|
||||
arraymemberbase->AddressRequested = false;
|
||||
Array->ValueType = TypeUInt32;
|
||||
|
||||
bound = Array->Emit(build);
|
||||
|
||||
arraymemberbase->membervar = origmembervar;
|
||||
arraymemberbase->AddressRequested = origaddrreq;
|
||||
Array->ValueType = origvaluetype;
|
||||
arrayvar.Free(build);
|
||||
}
|
||||
}
|
||||
else if ((Array->ExprType == EFX_ArrayElement || Array->ExprType == EFX_OutVarDereference) && Array->isStaticArray())
|
||||
{
|
||||
ExpEmit arrayvar = Array->Emit(build);
|
||||
bound = ExpEmit(build, REGT_INT);
|
||||
build->Emit(OP_LW, bound.RegNum, arrayvar.RegNum, build->GetConstantInt(myoffsetof(FArray, Count)));
|
||||
|
||||
|
|
@ -8114,7 +8181,10 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
|
|||
|
||||
nestedarray = true;
|
||||
}
|
||||
else start = arrayvar;
|
||||
else
|
||||
{
|
||||
start = Array->Emit(build);
|
||||
}
|
||||
|
||||
if (index->isConstant())
|
||||
{
|
||||
|
|
@ -9063,7 +9133,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
|||
else if (Self->IsQuaternion())
|
||||
{
|
||||
// Reuse vector built-ins for quaternion
|
||||
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit)
|
||||
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit || MethodName == NAME_Conjugate || MethodName == NAME_Inverse)
|
||||
{
|
||||
if (ArgList.Size() > 0)
|
||||
{
|
||||
|
|
@ -10361,6 +10431,9 @@ FxExpression *FxVectorBuiltin::Resolve(FCompileContext &ctx)
|
|||
ValueType = TypeFloat64;
|
||||
break;
|
||||
|
||||
case NAME_Conjugate:
|
||||
case NAME_Inverse:
|
||||
assert(Self->IsQuaternion());
|
||||
case NAME_Unit:
|
||||
ValueType = Self->ValueType;
|
||||
break;
|
||||
|
|
@ -10413,6 +10486,18 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build)
|
|||
build->Emit(vecSize == 2 ? OP_DIVVF2_RR : vecSize == 3 ? OP_DIVVF3_RR : OP_DIVVF4_RR, to.RegNum, op.RegNum, len.RegNum);
|
||||
len.Free(build);
|
||||
}
|
||||
else if (Function == NAME_Conjugate)
|
||||
{
|
||||
build->Emit(OP_CONJQ, to.RegNum, op.RegNum);
|
||||
}
|
||||
else if (Function == NAME_Inverse)
|
||||
{
|
||||
ExpEmit len(build, REGT_FLOAT);
|
||||
build->Emit(OP_DOTV4_RR, len.RegNum, op.RegNum, op.RegNum);
|
||||
build->Emit(OP_CONJQ, to.RegNum, op.RegNum);
|
||||
build->Emit(OP_DIVVF4_RR, to.RegNum, to.RegNum, len.RegNum);
|
||||
len.Free(build);
|
||||
}
|
||||
else if (Function == NAME_Angle)
|
||||
{
|
||||
build->Emit(OP_ATAN2, to.RegNum, op.RegNum + 1, op.RegNum);
|
||||
|
|
@ -10878,12 +10963,17 @@ FxExpression *FxCompoundStatement::Resolve(FCompileContext &ctx)
|
|||
|
||||
ExpEmit FxCompoundStatement::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
auto start = build->GetAddress();
|
||||
auto e = FxSequence::Emit(build);
|
||||
TArray<VMLocalVariable> locals;
|
||||
// Release all local variables in this block.
|
||||
for (auto l : LocalVars)
|
||||
{
|
||||
locals.Push({l->Name, l->ValueType, l->VarFlags, l->RegCount, l->RegNum, l->ScriptPosition.ScriptLine, l->StackOffset});
|
||||
l->Release(build);
|
||||
}
|
||||
auto end = build->GetAddress();
|
||||
build->AddBlock(locals, start, end);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -316,6 +316,8 @@ enum EFxType
|
|||
EFX_LocalArrayDeclaration,
|
||||
EFX_OutVarDereference,
|
||||
EFX_ToVector,
|
||||
EFX_FStateOffset,
|
||||
|
||||
EFX_COUNT
|
||||
};
|
||||
|
||||
|
|
@ -396,7 +398,7 @@ public:
|
|||
|
||||
FxIdentifier(FName i, const FScriptPosition &p);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
FxExpression *ResolveMember(FCompileContext&, PContainerType*, FxExpression*&, PContainerType*);
|
||||
FxExpression *ResolveMember(FCompileContext&, PContainerType*, FxExpression*&, PContainerType*, bool isConst = false);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1475,8 +1477,9 @@ class FxStructMember : public FxMemberBase
|
|||
{
|
||||
public:
|
||||
FxExpression *classx;
|
||||
bool IsConst;
|
||||
|
||||
FxStructMember(FxExpression*, PField*, const FScriptPosition&);
|
||||
FxStructMember(FxExpression*, PField*, const FScriptPosition&, bool isConst = false);
|
||||
~FxStructMember();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
bool RequestAddress(FCompileContext &ctx, bool *writable);
|
||||
|
|
@ -1494,7 +1497,7 @@ class FxClassMember : public FxStructMember
|
|||
{
|
||||
public:
|
||||
|
||||
FxClassMember(FxExpression*, PField*, const FScriptPosition&);
|
||||
FxClassMember(FxExpression*, PField*, const FScriptPosition&, bool isConst = false);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
|
@ -2387,7 +2390,7 @@ public:
|
|||
struct CompileEnvironment
|
||||
{
|
||||
FxExpression* (*SpecialTypeCast)(FxTypeCast* func, FCompileContext& ctx);
|
||||
bool (*CheckForCustomAddition)(FxAddSub* func, FCompileContext& ctx);
|
||||
FxExpression* (*CheckForCustomAddition)(FxAddSub* func, FCompileContext& ctx);
|
||||
FxExpression* (*CheckSpecialIdentifier)(FxIdentifier* func, FCompileContext& ctx);
|
||||
FxExpression* (*CheckSpecialGlobalIdentifier)(FxIdentifier* func, FCompileContext& ctx);
|
||||
FxExpression* (*ResolveSpecialIdentifier)(FxIdentifier* func, FxExpression*& object, PContainerType* objtype, FCompileContext& ctx);
|
||||
|
|
|
|||
|
|
@ -148,6 +148,14 @@ void VMFunctionBuilder::MakeFunction(VMScriptFunction *func)
|
|||
func->MaxParam = MaxParam;
|
||||
func->StackSize = VMFrame::FrameSize(func->NumRegD, func->NumRegF, func->NumRegS, func->NumRegA, func->MaxParam, func->ExtraSpace);
|
||||
|
||||
int i = 0;
|
||||
for (auto &block : Blocks)
|
||||
{
|
||||
|
||||
std::pair new_key = { func->Code + block.first.first, func->Code + block.first.second };
|
||||
func->LocalVariableBlocks.Push({{new_key.first, new_key.second}, block.second});
|
||||
i++;
|
||||
}
|
||||
// Technically, there's no reason why we can't end the function with
|
||||
// entries on the parameter stack, but it means the caller probably
|
||||
// did something wrong.
|
||||
|
|
@ -201,6 +209,11 @@ void VMFunctionBuilder::FillStringConstants(FString *konst)
|
|||
}
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::AddBlock(const TArray<VMLocalVariable> &block, size_t start, size_t end)
|
||||
{
|
||||
Blocks.Push( {{ start, end }, block});
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantInt
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
||||
class FxCompoundStatement;
|
||||
class VMFunctionBuilder;
|
||||
class FxExpression;
|
||||
class FxLocalVariableDeclaration;
|
||||
|
|
@ -52,6 +54,8 @@ public:
|
|||
friend class VMFunctionBuilder;
|
||||
};
|
||||
|
||||
using BlockMap = TArray<std::pair<std::pair<size_t, size_t>, TArray<VMLocalVariable>>>;
|
||||
|
||||
VMFunctionBuilder(int numimplicits);
|
||||
~VMFunctionBuilder();
|
||||
|
||||
|
|
@ -97,6 +101,8 @@ public:
|
|||
void FillAddressConstants(FVoidObj *konst);
|
||||
void FillStringConstants(FString *strings);
|
||||
|
||||
void AddBlock(const TArray<VMLocalVariable> &vars, size_t start, size_t end);
|
||||
|
||||
// PARAM increases ActiveParam; CALL decreases it.
|
||||
void ParamChange(int delta);
|
||||
|
||||
|
|
@ -128,7 +134,7 @@ private:
|
|||
int ActiveParam;
|
||||
|
||||
TArray<VMOP> Code;
|
||||
|
||||
BlockMap Blocks;
|
||||
};
|
||||
|
||||
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen);
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ VersionInfo PField::GetVersion()
|
|||
{
|
||||
VersionInfo Highest = { 0,0,0 };
|
||||
if (!(Flags & VARF_Deprecated)) Highest = mVersion;
|
||||
if (Type->mVersion > Highest) Highest = Type->mVersion;
|
||||
if (Type->mVersion > Highest && !Type->TypeDeprecated) Highest = Type->mVersion;
|
||||
return Highest;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1493,6 +1493,7 @@ PPointer::PPointer(PType *pointsat, bool isconst)
|
|||
{
|
||||
mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst ? "readonly " : "");
|
||||
mVersion = pointsat->mVersion;
|
||||
TypeDeprecated = pointsat->TypeDeprecated;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1674,7 +1675,11 @@ PClassPointer::PClassPointer(PClass *restrict)
|
|||
loadOp = OP_LP;
|
||||
storeOp = OP_SP;
|
||||
Flags |= TYPE_ClassPointer;
|
||||
if (restrict) mVersion = restrict->VMType->mVersion;
|
||||
if (restrict)
|
||||
{
|
||||
mVersion = restrict->VMType->mVersion;
|
||||
TypeDeprecated = restrict->VMType->TypeDeprecated;
|
||||
}
|
||||
else mVersion = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ enum
|
|||
VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only)
|
||||
VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data.
|
||||
VARF_Abstract = (1<<24), // [Player701] Function does not have a body and must be overridden in subclasses
|
||||
VARF_SafeConst = (1<<25), // [Jay] properly-working const function/unsafe clearscope field
|
||||
};
|
||||
|
||||
// Basic information shared by all types ------------------------------------
|
||||
|
|
@ -110,6 +111,8 @@ public:
|
|||
bool SizeKnown = true;
|
||||
|
||||
bool VMInternalStruct = false;
|
||||
bool TypeDeprecated = false; // mVersion is deprecation version, not minimum version
|
||||
FString mDeprecationMessage;
|
||||
|
||||
PType * LocalType = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ void VMDumpConstants(FILE *out, const VMScriptFunction *func)
|
|||
}
|
||||
}
|
||||
|
||||
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func)
|
||||
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func, uint64_t starting_offset)
|
||||
{
|
||||
VMFunction *callfunc = nullptr;
|
||||
const char *name;
|
||||
|
|
@ -322,13 +322,13 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
|
|||
name = cmpname;
|
||||
}
|
||||
}
|
||||
printf_wrapper(out, "%08x: %02x%02x%02x%02x %-8s", i << 2, code[i].op, code[i].a, code[i].b, code[i].c, name);
|
||||
printf_wrapper(out, "%08llx: %02x%02x%02x%02x %-8s", starting_offset + (i << 2), code[i].op, code[i].a, code[i].b, code[i].c, name);
|
||||
col = 0;
|
||||
switch (code[i].op)
|
||||
{
|
||||
case OP_JMP:
|
||||
//case OP_TRY:
|
||||
col = printf_wrapper(out, "%08x", (i + 1 + code[i].i24) << 2);
|
||||
col = printf_wrapper(out, "%08llx", starting_offset + ((i + 1 + code[i].i24) << 2));
|
||||
break;
|
||||
|
||||
case OP_PARAMI:
|
||||
|
|
@ -510,7 +510,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
|
|||
}
|
||||
else
|
||||
{
|
||||
col += printf_wrapper(out, " => %08x", (i + 2 + code[i+1].i24) << 2);
|
||||
col += printf_wrapper(out, " => %08llx", starting_offset + ((i + 2 + code[i+1].i24) << 2));
|
||||
}
|
||||
}
|
||||
if (col > 30)
|
||||
|
|
|
|||
492
src/common/scripting/dap/BreakpointManager.cpp
Normal file
492
src/common/scripting/dap/BreakpointManager.cpp
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
#include "BreakpointManager.h"
|
||||
#include <cstdint>
|
||||
#include <regex>
|
||||
#include "Utilities.h"
|
||||
#include "RuntimeEvents.h"
|
||||
#include "GameInterfaces.h"
|
||||
#include "DebugExecutionManager.h"
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
|
||||
int64_t BreakpointManager::GetBreakpointID()
|
||||
{
|
||||
++m_CurrentID;
|
||||
int64_t id = m_CurrentID;
|
||||
if (id < 0)
|
||||
{
|
||||
m_CurrentID = 1;
|
||||
id = m_CurrentID;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
int BreakpointManager::AddInvalidBreakpoint(
|
||||
std::vector<dap::Breakpoint> &breakpoints, int line, void *address, const std::string &reason, const dap::optional<dap::Source> &source = {})
|
||||
{
|
||||
auto breakpointId = GetBreakpointID();
|
||||
dap::Breakpoint &breakpoint = breakpoints.emplace_back();
|
||||
breakpoint.id = breakpointId;
|
||||
breakpoint.message = reason;
|
||||
breakpoint.source = source;
|
||||
breakpoint.verified = false;
|
||||
breakpoint.reason = "failed";
|
||||
if (line)
|
||||
{
|
||||
breakpoint.line = line;
|
||||
}
|
||||
if (address)
|
||||
{
|
||||
breakpoint.instructionReference = AddrToString(nullptr, address);
|
||||
}
|
||||
return breakpointId;
|
||||
}
|
||||
|
||||
bool BreakpointManager::AddBreakpointInfo(
|
||||
const std::shared_ptr<Binary> &binary,
|
||||
VMScriptFunction *function,
|
||||
int line,
|
||||
void *p_instrRef,
|
||||
int offset,
|
||||
BreakpointInfo::Type type,
|
||||
std::vector<dap::Breakpoint> &r_bpoint,
|
||||
const std::string &funcText)
|
||||
{
|
||||
// Only call this with positional breakpoints (line, script function, instruction)
|
||||
assert(p_instrRef != nullptr);
|
||||
int64_t breakpointId = GetBreakpointID();
|
||||
int sourceRef = -1;
|
||||
if (binary)
|
||||
{
|
||||
sourceRef = binary->GetScriptRef();
|
||||
}
|
||||
auto instrRef = (void *)(static_cast<char *>(p_instrRef) + offset);
|
||||
bool found = false;
|
||||
if (m_breakpoints.find(instrRef) != m_breakpoints.end())
|
||||
{
|
||||
for (auto &binfo : m_breakpoints[instrRef])
|
||||
{
|
||||
if (binfo.type == type)
|
||||
{
|
||||
if (sourceRef == -1 || binfo.ref == sourceRef)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_breakpoints[instrRef] = {};
|
||||
}
|
||||
auto &binfo = m_breakpoints[instrRef].emplace_back();
|
||||
binfo.type = type;
|
||||
binfo.ref = sourceRef;
|
||||
binfo.funcBreakpointText = funcText;
|
||||
binfo.bpoint.id = breakpointId;
|
||||
binfo.bpoint.line = line;
|
||||
binfo.bpoint.instructionReference = AddrToString(function, p_instrRef);
|
||||
if (offset)
|
||||
{
|
||||
binfo.bpoint.offset = offset;
|
||||
}
|
||||
if (binary) binfo.bpoint.source = binary->GetDapSource();
|
||||
binfo.bpoint.verified = false;
|
||||
// Only send back one breakpoint per line for line breakpoints, or the DAP client will get confused
|
||||
if (type == BreakpointInfo::Type::Line)
|
||||
{
|
||||
for (auto &kv : m_breakpoints)
|
||||
{
|
||||
for (auto &existing : kv.second)
|
||||
{
|
||||
// not the one we just added and the same type
|
||||
if (binfo.bpoint.id != existing.bpoint.id && existing.type == type)
|
||||
{
|
||||
if ((sourceRef == -1 || existing.ref == sourceRef) && (existing.bpoint.line.value(0) == line))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
binfo.bpoint.verified = true;
|
||||
r_bpoint.push_back(binfo.bpoint);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BreakpointManager::GetBpointsForResponse(BreakpointInfo::Type type, std::vector<dap::Breakpoint> &responseBpoints)
|
||||
{
|
||||
for (const auto &bPoints : m_breakpoints)
|
||||
{
|
||||
if (bPoints.second.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (bPoints.second[0].type != type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto &bp : bPoints.second)
|
||||
{
|
||||
responseBpoints.push_back(bp.bpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dap::ResponseOrError<dap::SetBreakpointsResponse> BreakpointManager::SetBreakpoints(const dap::Source &source, const dap::SetBreakpointsRequest &request)
|
||||
{
|
||||
RETURN_COND_DAP_ERROR(!request.breakpoints.has_value(), "SetBreakpoints: No breakpoints provided!");
|
||||
auto &srcBreakpoints = request.breakpoints.value();
|
||||
dap::SetBreakpointsResponse response;
|
||||
std::set<int> breakpointLines;
|
||||
auto scriptPath = source.name.value("");
|
||||
auto sourceRef = GetSourceReference(source);
|
||||
|
||||
std::map<int, BreakpointInfo> foundBreakpoints;
|
||||
auto addInvalidBreakpoint = [&](int line, const std::string &reason, bool shouldLog = true)
|
||||
{
|
||||
if (shouldLog)
|
||||
{
|
||||
LogError("SetBreakpoints: %s", reason.c_str());
|
||||
}
|
||||
AddInvalidBreakpoint(response.breakpoints, line, nullptr, reason, source);
|
||||
};
|
||||
ClearBreakpointsForScript(sourceRef, BreakpointInfo::Type::Line);
|
||||
|
||||
auto binary = m_pexCache->GetScript(source);
|
||||
if (!binary)
|
||||
{
|
||||
// check if the archive name is loaded
|
||||
auto archive_name = source.origin.value("");
|
||||
int containerNum = fileSystem.CheckIfResourceFileLoaded(archive_name.c_str());
|
||||
std::string error_message = containerNum == -1 ? StringFormat("%s: Archive %s not loaded", scriptPath.c_str(), archive_name.c_str())
|
||||
: StringFormat("%s: Could not find script in loaded sources!", scriptPath.c_str());
|
||||
for (const auto &srcBreakpoint : srcBreakpoints)
|
||||
{
|
||||
addInvalidBreakpoint(srcBreakpoint.line, error_message);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
if (!binary->HasFunctions())
|
||||
{
|
||||
for (const auto &srcBreakpoint : srcBreakpoints)
|
||||
{
|
||||
addInvalidBreakpoint(srcBreakpoint.line, StringFormat("Script %s is present but not loaded", scriptPath.c_str()));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
else if (!binary->HasFunctionLines())
|
||||
{
|
||||
for (const auto &srcBreakpoint : srcBreakpoints)
|
||||
{
|
||||
addInvalidBreakpoint(srcBreakpoint.line, StringFormat("No debug info found for script %s", scriptPath.c_str()));
|
||||
}
|
||||
}
|
||||
int srcRef = binary->GetScriptRef();
|
||||
for (const auto &srcBreakpoint : srcBreakpoints)
|
||||
{
|
||||
int breakpointsSet = 0;
|
||||
int line = static_cast<int>(srcBreakpoint.line);
|
||||
int instructionNum = -1;
|
||||
int64_t breakpointId = -1;
|
||||
auto found = binary->FindFunctionRangesByLine(line);
|
||||
if (found.size() == 0)
|
||||
{
|
||||
addInvalidBreakpoint(line, "Invalid instruction", false);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (!found.empty())
|
||||
{
|
||||
auto func = found.top()->mapped();
|
||||
if (func == nullptr || IsFunctionAbstract(func) || func->LineInfoCount == 0)
|
||||
{
|
||||
found.pop();
|
||||
continue;
|
||||
}
|
||||
for (unsigned int i = 0; i < func->LineInfoCount; i++)
|
||||
{
|
||||
if (func->LineInfo[i].LineNumber == line)
|
||||
{
|
||||
instructionNum = func->LineInfo[i].InstructionIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (instructionNum == -1)
|
||||
{
|
||||
found.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
void *instrRef = func->Code + instructionNum;
|
||||
auto actualBin = binary;
|
||||
|
||||
// Mixin; find the actual script
|
||||
if (srcRef != GetScriptReference(func->SourceFileName.GetChars()))
|
||||
{
|
||||
actualBin = m_pexCache->GetScript(func->SourceFileName.GetChars());
|
||||
if (!actualBin)
|
||||
{
|
||||
addInvalidBreakpoint(line, StringFormat("Could not find script %s in loaded sources!", func->SourceFileName.GetChars()));
|
||||
found.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (AddBreakpointInfo(actualBin, func, line, instrRef, 0, BreakpointInfo::Type::Line, response.breakpoints))
|
||||
{
|
||||
breakpointId = response.breakpoints.back().id.value(-1);
|
||||
}
|
||||
found.pop();
|
||||
breakpointsSet++;
|
||||
}
|
||||
if (breakpointId == -1)
|
||||
{
|
||||
addInvalidBreakpoint(line, StringFormat("No function found for line %d in script %s", line, scriptPath.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
dap::ResponseOrError<dap::SetFunctionBreakpointsResponse> BreakpointManager::SetFunctionBreakpoints(const dap::SetFunctionBreakpointsRequest &request)
|
||||
{
|
||||
auto &breakpoints = request.breakpoints;
|
||||
dap::SetFunctionBreakpointsResponse response;
|
||||
// each request clears the previous function breakpoints
|
||||
ClearBreakpointsType(BreakpointInfo::Type::Function);
|
||||
m_nativeFunctionBreakpoints.clear();
|
||||
int bpointCount = 0;
|
||||
|
||||
for (const auto &breakpoint : breakpoints)
|
||||
{
|
||||
auto fullFuncName = breakpoint.name;
|
||||
// function names are `class.function`
|
||||
auto func_name_parts = Split(fullFuncName, ".");
|
||||
|
||||
if (func_name_parts.size() != 2)
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, nullptr, StringFormat("Invalid function name %s", fullFuncName.c_str()));
|
||||
continue;
|
||||
}
|
||||
auto className = FName(func_name_parts[0]);
|
||||
auto functionName = FName(func_name_parts[1]);
|
||||
auto cls = PClass::FindClass(className);
|
||||
auto func = PClass::FindFunction(className, functionName);
|
||||
if (!func)
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, nullptr, StringFormat("Could not find function %s in loaded sources!", fullFuncName.c_str()));
|
||||
continue;
|
||||
}
|
||||
if (IsFunctionNative(func))
|
||||
{
|
||||
BreakpointInfo bpoint_info;
|
||||
bpoint_info.type = BreakpointInfo::Type::Function;
|
||||
bpoint_info.ref = -1;
|
||||
bpoint_info.funcBreakpointText = fullFuncName;
|
||||
bpoint_info.bpoint.id = GetBreakpointID();
|
||||
bpoint_info.bpoint.line = 1;
|
||||
bpoint_info.bpoint.verified = true;
|
||||
m_nativeFunctionBreakpoints[func->QualifiedName] = bpoint_info;
|
||||
response.breakpoints.push_back(bpoint_info.bpoint);
|
||||
continue;
|
||||
}
|
||||
// script function
|
||||
auto scriptFunction = dynamic_cast<VMScriptFunction *>(func);
|
||||
auto scriptName = scriptFunction->SourceFileName.GetChars();
|
||||
dap::Source source;
|
||||
auto binary = m_pexCache->GetScript(scriptName);
|
||||
if (!binary)
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, nullptr, StringFormat("Could not find script %s in loaded sources!", scriptName));
|
||||
continue;
|
||||
}
|
||||
if (scriptFunction->LineInfoCount == 0)
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, nullptr, StringFormat("Could not find line info for function %s!", fullFuncName.c_str()), source);
|
||||
continue;
|
||||
}
|
||||
auto lineNum = scriptFunction->LineInfo[0].LineNumber;
|
||||
auto instructionNum = scriptFunction->LineInfo[0].InstructionIndex;
|
||||
void *instrRef = scriptFunction->Code + instructionNum;
|
||||
AddBreakpointInfo(binary, scriptFunction, lineNum, instrRef, 0, BreakpointInfo::Type::Function, response.breakpoints, fullFuncName);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
void BreakpointManager::ClearBreakpoints(bool emitChanged)
|
||||
{
|
||||
if (emitChanged)
|
||||
{
|
||||
std::vector<int> refs;
|
||||
for (auto &kv : m_breakpoints)
|
||||
{
|
||||
for (auto bpointInfo : kv.second)
|
||||
{
|
||||
if (emitChanged && bpointInfo.bpoint.verified)
|
||||
{
|
||||
bpointInfo.bpoint.verified = false;
|
||||
RuntimeEvents::EmitBreakpointChangedEvent(bpointInfo.bpoint, "changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_breakpoints.clear();
|
||||
}
|
||||
|
||||
void BreakpointManager::ClearBreakpointsType(BreakpointInfo::Type type)
|
||||
{
|
||||
std::vector<void *> toRemove;
|
||||
for (auto &KV : m_breakpoints)
|
||||
{
|
||||
auto bpinfos = KV.second;
|
||||
for (int64_t i = bpinfos.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (bpinfos[i].type == type)
|
||||
{
|
||||
bpinfos.erase(bpinfos.begin() + i);
|
||||
}
|
||||
}
|
||||
if (bpinfos.empty())
|
||||
{
|
||||
toRemove.push_back(KV.first);
|
||||
}
|
||||
}
|
||||
for (auto &key : toRemove)
|
||||
{
|
||||
m_breakpoints.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakpointManager::ClearBreakpointsForScript(int ref, BreakpointInfo::Type type, bool emitChanged)
|
||||
{
|
||||
std::vector<void *> toRemove;
|
||||
for (auto &KV : m_breakpoints)
|
||||
{
|
||||
auto bpinfos = KV.second;
|
||||
for (int64_t i = bpinfos.size() - 1; i >= 0; i--)
|
||||
{
|
||||
auto &bpinfo = bpinfos[i];
|
||||
if (bpinfo.ref == ref && (type == BreakpointInfo::Type::NONE || type == bpinfo.type))
|
||||
{
|
||||
if (emitChanged && bpinfo.bpoint.verified)
|
||||
{
|
||||
bpinfo.bpoint.verified = false;
|
||||
RuntimeEvents::EmitBreakpointChangedEvent(bpinfo.bpoint, "changed");
|
||||
}
|
||||
bpinfos.erase(bpinfos.begin() + i);
|
||||
}
|
||||
if (bpinfos.empty())
|
||||
{
|
||||
toRemove.push_back(KV.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &key : toRemove)
|
||||
{
|
||||
m_breakpoints.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool BreakpointManager::GetExecutionIsAtValidBreakpoint(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc)
|
||||
{
|
||||
return m_breakpoints.find((void *)pc) != m_breakpoints.end() || (!m_nativeFunctionBreakpoints.empty() && IsAtNativeBreakpoint(stack));
|
||||
}
|
||||
|
||||
inline bool BreakpointManager::IsAtNativeBreakpoint(VMFrameStack *stack)
|
||||
{
|
||||
return PCIsAtNativeCall(stack->TopFrame())
|
||||
&& m_nativeFunctionBreakpoints.find(GetCalledFunction(stack->TopFrame())->QualifiedName) != m_nativeFunctionBreakpoints.end();
|
||||
}
|
||||
|
||||
void BreakpointManager::SetBPStoppedEventInfo(VMFrameStack *stack, dap::StoppedEvent &event)
|
||||
{
|
||||
std::vector<dap::integer> breakpoints;
|
||||
if (!stack->HasFrames())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto frame = stack->TopFrame();
|
||||
std::string description = "Paused on breakpoint";
|
||||
if (m_breakpoints.find((void *)frame->PC) != m_breakpoints.end())
|
||||
{
|
||||
for (auto &bpoint : m_breakpoints[(void *)frame->PC])
|
||||
{
|
||||
breakpoints.push_back(bpoint.bpoint.id.value(-1));
|
||||
}
|
||||
}
|
||||
if (IsAtNativeBreakpoint(stack))
|
||||
{
|
||||
auto func = GetCalledFunction(frame);
|
||||
auto &bpoint_info = m_nativeFunctionBreakpoints[func->QualifiedName];
|
||||
description = std::string("Paused on breakpoint at '") + bpoint_info.funcBreakpointText + "'";
|
||||
if (!CaseInsensitiveEquals(bpoint_info.funcBreakpointText, func->QualifiedName))
|
||||
{
|
||||
event.text = description + " (" + func->QualifiedName + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
event.text = description;
|
||||
}
|
||||
breakpoints.push_back(m_nativeFunctionBreakpoints[func->QualifiedName].bpoint.id.value(-1));
|
||||
}
|
||||
if (breakpoints.empty())
|
||||
{
|
||||
LogInternalError("No breakpoints found for stopped event");
|
||||
}
|
||||
if (!description.empty())
|
||||
{
|
||||
event.description = description;
|
||||
}
|
||||
event.reason = "breakpoint";
|
||||
event.hitBreakpointIds = breakpoints;
|
||||
}
|
||||
|
||||
dap::ResponseOrError<dap::SetInstructionBreakpointsResponse> BreakpointManager::SetInstructionBreakpoints(const dap::SetInstructionBreakpointsRequest &request)
|
||||
{
|
||||
auto breakpoints = request.breakpoints;
|
||||
|
||||
dap::SetInstructionBreakpointsResponse response;
|
||||
ClearBreakpointsType(BreakpointInfo::Type::Instruction);
|
||||
for (unsigned int i = 0; i < breakpoints.size(); i++)
|
||||
{
|
||||
auto &bp = breakpoints[i];
|
||||
void *srcAddress = (void *)(std::stoull(bp.instructionReference.substr(2), nullptr, 16));
|
||||
int64_t offset = bp.offset.value(0);
|
||||
void *address = offset + static_cast<char *>(srcAddress);
|
||||
|
||||
auto found = m_pexCache->GetFunctionsAtAddress(address);
|
||||
if (found.empty())
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, address, StringFormat("No function found for address %p", address));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto func = found.front();
|
||||
auto bpoint_info = BreakpointInfo {};
|
||||
|
||||
bpoint_info.type = BreakpointInfo::Type::Instruction;
|
||||
int ref;
|
||||
auto scriptFunc = dynamic_cast<VMScriptFunction *>(func);
|
||||
|
||||
if (IsFunctionNative(func) || !scriptFunc)
|
||||
{
|
||||
AddInvalidBreakpoint(response.breakpoints, 1, address, StringFormat("Instruction breakpoints are not supported for native functions"));
|
||||
continue;
|
||||
}
|
||||
auto line = scriptFunc->LineInfo[0].LineNumber;
|
||||
auto binary = m_pexCache->GetScript(scriptFunc->SourceFileName.GetChars());
|
||||
dap::Breakpoint bpoint;
|
||||
AddBreakpointInfo(binary, scriptFunc, line, srcAddress, (int)offset, BreakpointInfo::Type::Instruction, response.breakpoints);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
76
src/common/scripting/dap/BreakpointManager.h
Normal file
76
src/common/scripting/dap/BreakpointManager.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <dap/protocol.h>
|
||||
#include <dap/session.h>
|
||||
|
||||
#include "GameInterfaces.h"
|
||||
#include "IdProvider.h"
|
||||
|
||||
#include "PexCache.h"
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
class BreakpointManager
|
||||
{
|
||||
public:
|
||||
struct BreakpointInfo
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
NONE = -1,
|
||||
Line,
|
||||
Function,
|
||||
Instruction
|
||||
};
|
||||
Type type;
|
||||
int ref;
|
||||
std::string funcBreakpointText;
|
||||
const char *nativeFuncName;
|
||||
dap::Breakpoint bpoint;
|
||||
};
|
||||
|
||||
struct ScriptBreakpoints
|
||||
{
|
||||
int ref {-1};
|
||||
dap::Source source;
|
||||
std::time_t modificationTime {0};
|
||||
std::map<void *, BreakpointInfo> breakpoints;
|
||||
};
|
||||
|
||||
|
||||
explicit BreakpointManager(PexCache *pexCache) : m_pexCache(pexCache) { }
|
||||
int64_t GetBreakpointID();
|
||||
int AddInvalidBreakpoint(
|
||||
std::vector<dap::Breakpoint> &breakpoints, int line, void *address, const std::string &reason, const dap::optional<dap::Source> &source);
|
||||
bool AddBreakpointInfo(
|
||||
const std::shared_ptr<Binary> &binary,
|
||||
VMScriptFunction *function,
|
||||
int line,
|
||||
void *p_instrRef,
|
||||
int offset,
|
||||
BreakpointInfo::Type type,
|
||||
std::vector<dap::Breakpoint> &r_bpoint,
|
||||
const std::string &funcText = {});
|
||||
void GetBpointsForResponse(BreakpointInfo::Type type, std::vector<dap::Breakpoint> &responseBpoints);
|
||||
dap::ResponseOrError<dap::SetBreakpointsResponse> SetBreakpoints(const dap::Source &src, const dap::SetBreakpointsRequest &request);
|
||||
dap::ResponseOrError<dap::SetFunctionBreakpointsResponse> SetFunctionBreakpoints(const dap::SetFunctionBreakpointsRequest &request);
|
||||
dap::ResponseOrError<dap::SetInstructionBreakpointsResponse> SetInstructionBreakpoints(const dap::SetInstructionBreakpointsRequest &request);
|
||||
void ClearBreakpoints(bool emitChanged = false);
|
||||
void ClearBreakpointsForScript(int ref, BreakpointInfo::Type type, bool emitChanged = false);
|
||||
bool GetExecutionIsAtValidBreakpoint(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc);
|
||||
inline bool IsAtNativeBreakpoint(VMFrameStack *stack);
|
||||
void SetBPStoppedEventInfo(VMFrameStack *stack, dap::StoppedEvent &event);
|
||||
private:
|
||||
|
||||
PexCache *m_pexCache;
|
||||
std::map<void *, std::vector<BreakpointInfo>> m_breakpoints;
|
||||
// set of case-insensitive strings
|
||||
std::map<std::string_view, BreakpointInfo, ci_less> m_nativeFunctionBreakpoints;
|
||||
IdProvider m_idProvider;
|
||||
int64_t m_CurrentID = 1;
|
||||
size_t times_seen = 0;
|
||||
|
||||
void ClearBreakpointsType(BreakpointInfo::Type type);
|
||||
};
|
||||
}
|
||||
388
src/common/scripting/dap/DebugExecutionManager.cpp
Normal file
388
src/common/scripting/dap/DebugExecutionManager.cpp
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
#include "DebugExecutionManager.h"
|
||||
#include <thread>
|
||||
// #include "Window.h"
|
||||
#include "GameInterfaces.h"
|
||||
#ifdef _WIN32
|
||||
extern void I_GetWindowEvent();
|
||||
extern bool win32EnableInput;
|
||||
#endif
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
// using namespace RE::BSScript::Internal;
|
||||
static constexpr const char *pauseReasonStrings[] = {"none", "step", "breakpoint", "paused", "exception"};
|
||||
static constexpr size_t pauseReasonStringsSize = sizeof(pauseReasonStrings) / sizeof(pauseReasonStrings[0]);
|
||||
static_assert(pauseReasonStringsSize == static_cast<int>(DebugExecutionManager::pauseReason::exception) + 1, "pauseReasonStrings size mismatch");
|
||||
static constexpr const char *exceptionStrings[]
|
||||
= {"Other", "ReadNil", "WriteNil", "TooManyTries", "ArrayOutOfBounds", "DivisionByZero", "BadSelf", "StringFormat"};
|
||||
static constexpr size_t exceptionStringsSize = sizeof(exceptionStrings) / sizeof(exceptionStrings[0]);
|
||||
static_assert(exceptionStringsSize == X_FORMAT_ERROR + 1, "exceptionStrings size mismatch");
|
||||
|
||||
static constexpr const char *exceptionFilters[] = {"VM"};
|
||||
static constexpr const char *exceptionFilterDescriptions[] = {"VM exceptions"};
|
||||
static_assert(sizeof(exceptionFilters) / sizeof(exceptionFilters[0]) == (size_t)DebugExecutionManager::ExceptionFilter::kMAX, "exceptionFilters size mismatch");
|
||||
static_assert(
|
||||
sizeof(exceptionFilterDescriptions) / sizeof(exceptionFilterDescriptions[0]) == (size_t)DebugExecutionManager::ExceptionFilter::kMAX,
|
||||
"exceptionFilterDescriptions size mismatch");
|
||||
|
||||
|
||||
DebugExecutionManager::pauseReason DebugExecutionManager::CheckState(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case DebuggerState::kPaused:
|
||||
{
|
||||
return pauseReason::paused;
|
||||
}
|
||||
case DebuggerState::kRunning:
|
||||
{
|
||||
if (m_breakpointManager->GetExecutionIsAtValidBreakpoint(stack, ret, numret, pc))
|
||||
{
|
||||
return pauseReason::breakpoint;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DebuggerState::kStepping:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
if (m_breakpointManager->GetExecutionIsAtValidBreakpoint(stack, ret, numret, pc))
|
||||
{
|
||||
return pauseReason::breakpoint;
|
||||
}
|
||||
else if (!RuntimeState::GetStack(m_currentStepStackId))
|
||||
{
|
||||
return pauseReason::CONTINUING;
|
||||
}
|
||||
else if (m_currentStepStackFrame)
|
||||
{
|
||||
VMScriptFunction *func = nullptr;
|
||||
auto lastInst = m_lastInstruction;
|
||||
m_lastInstruction = pc;
|
||||
std::vector<VMFrame *> currentFrames;
|
||||
RuntimeState::GetStackFrames(stack, currentFrames);
|
||||
if (!currentFrames.empty())
|
||||
{
|
||||
ptrdiff_t stepFrameIndex = -1;
|
||||
const auto stepFrameIter = std::find(currentFrames.begin(), currentFrames.end(), m_currentStepStackFrame);
|
||||
if (stepFrameIter != currentFrames.end() && m_currentVMFunction && m_currentStepStackFrame->Func == m_currentVMFunction)
|
||||
{
|
||||
stepFrameIndex = std::distance(currentFrames.begin(), stepFrameIter);
|
||||
}
|
||||
// Only get the function if we're not stepping by instruction and the frame exists
|
||||
if (m_granularity != kInstruction && stepFrameIndex != -1)
|
||||
{
|
||||
func = !IsFunctionNative(m_currentStepStackFrame->Func) ? dynamic_cast<VMScriptFunction *>(m_currentStepStackFrame->Func) : nullptr;
|
||||
// if we're in the same frame, the last instruction was at the previous address, and the line is the same, we should continue
|
||||
if (func && stepFrameIndex == 0 && lastInst == pc - 1 && m_lastLine == func->PCToLine(pc))
|
||||
{
|
||||
// NONE will cause the function to continue execution without resetting the step state
|
||||
return pauseReason::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_currentStepType)
|
||||
{
|
||||
case StepType::STEP_IN:
|
||||
return pauseReason::step;
|
||||
break;
|
||||
case StepType::STEP_OUT:
|
||||
// If the stack exists, but the original frame is gone, we know we're in a previous frame now.
|
||||
if (stepFrameIndex == -1)
|
||||
{
|
||||
return pauseReason::step;
|
||||
}
|
||||
break;
|
||||
case StepType::STEP_OVER:
|
||||
if (stepFrameIndex <= 0)
|
||||
{
|
||||
return pauseReason::step;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_granularity != kInstruction && func)
|
||||
{
|
||||
m_lastLine = func->PCToLine(pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastLine = -1;
|
||||
}
|
||||
// we deliberately don't set shouldContinue here in an else here, as we want to continue until we hit the next step point
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more frames on stack, should continue
|
||||
if (!stack->HasFrames())
|
||||
{
|
||||
return pauseReason::CONTINUING;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return pauseReason::NONE;
|
||||
}
|
||||
|
||||
void DebugExecutionManager::ResetStepState(DebuggerState state, VMFrameStack *stack)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
// `stack` is thread_local, we're currently on that thread,
|
||||
// and the debugger will be running in a separate thread, so we need to set it here.
|
||||
m_state = state;
|
||||
|
||||
m_currentStepStackId = 0;
|
||||
m_currentStepStackFrame = nullptr;
|
||||
m_currentVMFunction = nullptr;
|
||||
if (state == DebuggerState::kPaused && stack)
|
||||
{
|
||||
RuntimeState::m_GlobalVMStack = stack;
|
||||
}
|
||||
else if (state == DebuggerState::kRunning)
|
||||
{
|
||||
m_lastLine = -1;
|
||||
m_lastInstruction = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugExecutionManager::WaitWhilePaused(pauseReason pauseReason, VMFrameStack *stack)
|
||||
{
|
||||
if (pauseReason != pauseReason::NONE)
|
||||
{
|
||||
if (pauseReason > pauseReason::NONE)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
win32EnableInput = false;
|
||||
#endif
|
||||
|
||||
while (m_state == DebuggerState::kPaused)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
I_GetWindowEvent();
|
||||
#endif
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
win32EnableInput = true;
|
||||
#endif
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
// reset the state
|
||||
m_runtimeState->Reset();
|
||||
if (m_state != DebuggerState::kRunning && stack)
|
||||
{
|
||||
RuntimeState::m_GlobalVMStack = stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugExecutionManager::HandleInstruction(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc)
|
||||
{
|
||||
if (m_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pauseReason pauseReason = CheckState(stack, ret, numret, pc);
|
||||
switch (pauseReason)
|
||||
{
|
||||
case pauseReason::NONE:
|
||||
break;
|
||||
case pauseReason::CONTINUING:
|
||||
{
|
||||
ResetStepState(DebuggerState::kRunning, stack);
|
||||
if (m_session)
|
||||
{
|
||||
dap::ContinuedEvent event;
|
||||
event.allThreadsContinued = true;
|
||||
event.threadId = 1;
|
||||
m_session->send(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // not NONE
|
||||
{
|
||||
// don't reset the last line or last instruction here
|
||||
ResetStepState(DebuggerState::kPaused, stack);
|
||||
if (m_session)
|
||||
{
|
||||
dap::StoppedEvent event;
|
||||
event.allThreadsStopped = true;
|
||||
event.reason = pauseReasonStrings[(int)pauseReason];
|
||||
if (pauseReason == pauseReason::breakpoint)
|
||||
{
|
||||
m_breakpointManager->SetBPStoppedEventInfo(stack, event);
|
||||
}
|
||||
event.threadId = 1;
|
||||
m_session->send(event);
|
||||
}
|
||||
// TODO: How to do this in GZDoom?
|
||||
// Window::ReleaseFocus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
WaitWhilePaused(pauseReason, stack);
|
||||
}
|
||||
|
||||
void DebugExecutionManager::HandleException(EVMAbortException reason, const std::string &message, const std::string &stackTrace)
|
||||
{
|
||||
if (m_exceptionFilters.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're in a VM exception being thrown, we're on the main thread
|
||||
ResetStepState(DebuggerState::kPaused, &GlobalVMStack);
|
||||
if (m_session)
|
||||
{
|
||||
auto event = dap::StoppedEvent();
|
||||
event.allThreadsStopped = true;
|
||||
if (!stackTrace.empty())
|
||||
{
|
||||
event.text = message + "\n" + stackTrace;
|
||||
}
|
||||
else
|
||||
{
|
||||
event.text = message;
|
||||
}
|
||||
event.reason = "exception";
|
||||
event.description = "Paused on exception: " + (reason < exceptionStringsSize ? std::string(exceptionStrings[(int)reason]) : "Unknown");
|
||||
event.threadId = 1;
|
||||
m_session->send(event);
|
||||
};
|
||||
WaitWhilePaused(pauseReason::exception, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void DebugExecutionManager::Open(std::shared_ptr<dap::Session> ses)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
m_closed = false;
|
||||
m_session = ses;
|
||||
}
|
||||
|
||||
void DebugExecutionManager::Close()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
m_state = DebuggerState::kRunning;
|
||||
m_closed = true;
|
||||
m_session = nullptr;
|
||||
}
|
||||
|
||||
bool DebugExecutionManager::Continue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
m_state = DebuggerState::kRunning;
|
||||
if (m_session){
|
||||
m_session->send(dap::ContinuedEvent());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebugExecutionManager::Pause()
|
||||
{
|
||||
if (m_state == DebuggerState::kPaused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
m_state = DebuggerState::kPaused;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebugExecutionManager::Step(uint32_t stackId, const StepType stepType, StepGranularity stepGranularity)
|
||||
{
|
||||
if (m_state != DebuggerState::kPaused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_instructionMutex);
|
||||
const auto stack = RuntimeState::GetStack(stackId);
|
||||
if (stack)
|
||||
{
|
||||
if (stack->HasFrames())
|
||||
{
|
||||
m_currentStepStackFrame = stack->TopFrame();
|
||||
if (m_currentStepStackFrame)
|
||||
{
|
||||
m_currentVMFunction = m_currentStepStackFrame->Func;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentStepStackId = stackId;
|
||||
m_currentStepType = stepType;
|
||||
m_state = DebuggerState::kStepping;
|
||||
m_granularity = stepGranularity;
|
||||
m_lastInstruction = nullptr;
|
||||
if (m_granularity != kInstruction)
|
||||
{
|
||||
VMScriptFunction *func = !IsFunctionNative(m_currentStepStackFrame->Func) ? dynamic_cast<VMScriptFunction *>(m_currentStepStackFrame->Func) : nullptr;
|
||||
if (func)
|
||||
{
|
||||
m_lastInstruction = m_currentStepStackFrame->PC;
|
||||
m_lastLine = func->PCToLine(m_currentStepStackFrame->PC);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dap::array<dap::Breakpoint> DebugExecutionManager::SetExceptionBreakpointFilters(const std::vector<std::string> &filterIds)
|
||||
{
|
||||
m_exceptionFilters.clear();
|
||||
dap::array<dap::Breakpoint> breakpoints;
|
||||
for (unsigned int i = 0; i < filterIds.size(); i++)
|
||||
{
|
||||
auto breakpoint = dap::Breakpoint();
|
||||
int64_t id = (int64_t)DebugExecutionManager::GetFilterID(filterIds[i]);
|
||||
breakpoint.id = id;
|
||||
breakpoint.verified = false;
|
||||
if (id != -1)
|
||||
{
|
||||
m_exceptionFilters.insert((ExceptionFilter)id);
|
||||
breakpoint.verified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
breakpoint.reason = "failed";
|
||||
breakpoint.message = "No such exception filter";
|
||||
}
|
||||
breakpoints.push_back(breakpoint);
|
||||
}
|
||||
return breakpoints;
|
||||
}
|
||||
|
||||
DebugExecutionManager::ExceptionFilter DebugExecutionManager::GetFilterID(const std::string &filter_string)
|
||||
{
|
||||
for (int i = 0; i < (int)ExceptionFilter::kMAX; i++)
|
||||
{
|
||||
if (filter_string == exceptionFilters[i])
|
||||
{
|
||||
return (ExceptionFilter)i;
|
||||
}
|
||||
}
|
||||
return (ExceptionFilter)-1;
|
||||
}
|
||||
|
||||
dap::array<dap::ExceptionBreakpointsFilter> DebugExecutionManager::GetAllExceptionFilters()
|
||||
{
|
||||
dap::array<dap::ExceptionBreakpointsFilter> filters;
|
||||
for (int i = 0; i < (int)ExceptionFilter::kMAX; i++)
|
||||
{
|
||||
dap::ExceptionBreakpointsFilter filter;
|
||||
filter.filter = exceptionFilters[i];
|
||||
filter.label = exceptionFilterDescriptions[i];
|
||||
filter.description = exceptionFilterDescriptions[i];
|
||||
filter.conditionDescription = exceptionFilterDescriptions[i];
|
||||
filter.def = true;
|
||||
filters.push_back(filter);
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
90
src/common/scripting/dap/DebugExecutionManager.h
Normal file
90
src/common/scripting/dap/DebugExecutionManager.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "GameInterfaces.h"
|
||||
|
||||
#include "BreakpointManager.h"
|
||||
#include "RuntimeState.h"
|
||||
#include <dap/session.h>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
enum StepType
|
||||
{
|
||||
STEP_IN = 0,
|
||||
STEP_OVER,
|
||||
STEP_OUT
|
||||
};
|
||||
|
||||
enum StepGranularity
|
||||
{
|
||||
kInstruction = 0,
|
||||
kLine,
|
||||
kStatement
|
||||
};
|
||||
|
||||
class DebugExecutionManager
|
||||
{
|
||||
|
||||
public:
|
||||
enum class DebuggerState
|
||||
{
|
||||
kRunning = 0,
|
||||
kPaused,
|
||||
kStepping
|
||||
};
|
||||
enum class pauseReason
|
||||
{
|
||||
CONTINUING = -1,
|
||||
NONE = 0,
|
||||
step,
|
||||
breakpoint,
|
||||
paused,
|
||||
exception
|
||||
};
|
||||
enum class ExceptionFilter
|
||||
{
|
||||
kScript,
|
||||
kMAX
|
||||
};
|
||||
private:
|
||||
std::mutex m_instructionMutex;
|
||||
bool m_closed;
|
||||
|
||||
std::shared_ptr<dap::Session> m_session;
|
||||
RuntimeState *m_runtimeState;
|
||||
BreakpointManager *m_breakpointManager;
|
||||
|
||||
std::atomic<DebuggerState> m_state = DebuggerState::kRunning;
|
||||
std::atomic<uint32_t> m_currentStepStackId = 0;
|
||||
StepType m_currentStepType = StepType::STEP_IN;
|
||||
StepGranularity m_granularity;
|
||||
int m_lastLine = -1;
|
||||
const VMOP *m_lastInstruction = nullptr;
|
||||
VMFrame *m_currentStepStackFrame;
|
||||
VMFunction *m_currentVMFunction;
|
||||
std::set<ExceptionFilter> m_exceptionFilters = {ExceptionFilter::kScript};
|
||||
public:
|
||||
explicit DebugExecutionManager(RuntimeState *runtimeState, BreakpointManager *breakpointManager) : m_closed(true), m_runtimeState(runtimeState), m_breakpointManager(breakpointManager), m_currentStepStackFrame(nullptr)
|
||||
{
|
||||
}
|
||||
static dap::array<dap::ExceptionBreakpointsFilter> GetAllExceptionFilters();
|
||||
|
||||
|
||||
void Close();
|
||||
void HandleInstruction(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc);
|
||||
void HandleException(EVMAbortException reason, const std::string &message, const std::string &stackTrace);
|
||||
void Open(std::shared_ptr<dap::Session> ses);
|
||||
bool Continue();
|
||||
bool Pause();
|
||||
bool Step(uint32_t stackId, StepType stepType, StepGranularity stepGranularity);
|
||||
dap::array<dap::Breakpoint> SetExceptionBreakpointFilters(const std::vector<std::string> &filterIds);
|
||||
static ExceptionFilter GetFilterID(const std::string &filter_string);
|
||||
bool IsPaused() const { return m_state == DebuggerState::kPaused; }
|
||||
private:
|
||||
inline pauseReason CheckState(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc);
|
||||
void ResetStepState(DebuggerState state, VMFrameStack *stack);
|
||||
void WaitWhilePaused(pauseReason pauseReason, VMFrameStack *stack);
|
||||
};
|
||||
}
|
||||
161
src/common/scripting/dap/DebugServer.cpp
Normal file
161
src/common/scripting/dap/DebugServer.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include "DebugServer.h"
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <dap/network.h>
|
||||
#include "ZScriptDebugger.h"
|
||||
|
||||
// Main entry point for the debug server
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
DebugServer::DebugServer()
|
||||
{
|
||||
terminate = false;
|
||||
stopped = false;
|
||||
quitting = false;
|
||||
debugger = std::unique_ptr<ZScriptDebugger>(new ZScriptDebugger());
|
||||
}
|
||||
|
||||
void DebugServer::RunRestartThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ResetThreadLock lock(mutex);
|
||||
cv.wait(lock, [&] { return terminate; });
|
||||
quitting = debugger->EndSession(closed);
|
||||
terminate = false;
|
||||
if (stopped || quitting)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (restart_server){
|
||||
m_server->stop();
|
||||
m_server = nullptr;
|
||||
m_server = dap::net::Server::create();
|
||||
if (!StartServer()) {
|
||||
RuntimeEvents::UnsubscribeFromDebuggerEnabled([](){return true;});
|
||||
stopped = true;
|
||||
break;
|
||||
}
|
||||
restart_server = false;
|
||||
}
|
||||
}
|
||||
if (quitting)
|
||||
{
|
||||
throw CExitEvent(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DebugServer::Listen(int p_port)
|
||||
{
|
||||
terminate = false;
|
||||
stopped = false;
|
||||
quitting = false;
|
||||
closed = false;
|
||||
if (!p_port)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
port = p_port;
|
||||
if (!m_server)
|
||||
{
|
||||
m_server = dap::net::Server::create();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
restart_thread = std::thread(std::bind(&DebugServer::RunRestartThread, this));
|
||||
return StartServer();
|
||||
}
|
||||
|
||||
void DebugServer::onClientConnected(const std::shared_ptr<dap::ReaderWriter> &connection)
|
||||
{
|
||||
if (!connection || !connection->isOpen())
|
||||
{
|
||||
LogInternalError("Client connected but connection is not open");
|
||||
return;
|
||||
}
|
||||
closed = false;
|
||||
std::shared_ptr<dap::Session> sess;
|
||||
sess = dap::Session::create();
|
||||
sess->bind(
|
||||
connection,
|
||||
[this]()
|
||||
{
|
||||
LogInternal("DAP connection closed.");
|
||||
// try_lock here because this can be called when the reset thread is ending the session
|
||||
ResetThreadLock lock(mutex, std::defer_lock);
|
||||
if (lock.try_lock() && !terminate && !debugger->IsEndingSession())
|
||||
{
|
||||
LogInternalError("DAP connection closed without terminating session.");
|
||||
closed = true;
|
||||
terminate = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
});
|
||||
// Registering a handle for when we send the DisconnectResponse;
|
||||
// After we send the disconnect response, the restart thread will stop the session and restart the server.
|
||||
sess->registerSentHandler(
|
||||
[this](const dap::ResponseOrError<dap::DisconnectResponse> &)
|
||||
{
|
||||
LogInternal("Debugger disconnecting...");
|
||||
ResetThreadLock lock(mutex);
|
||||
terminate = true;
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
LogInternal("DAP client connected.");
|
||||
debugger->StartSession(sess);
|
||||
};
|
||||
|
||||
void DebugServer::onError(const char *msg)
|
||||
{
|
||||
LogInternalError("Server error: %s\n", msg);
|
||||
if (restart_server){
|
||||
LogInternalError("Restart failed! Stopping server...");
|
||||
} else {
|
||||
LogInternalError("Restarting server...");
|
||||
{
|
||||
ResetThreadLock lock(mutex);
|
||||
terminate = true;
|
||||
closed = true;
|
||||
restart_server = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool DebugServer::StartServer()
|
||||
{
|
||||
if (!m_server->start(port, [this](auto conn){this->onClientConnected(conn);}, [this](auto msg){this->onError(msg);}))
|
||||
{
|
||||
LogInternalError("DAP debugging server failed to start on port %d, debugging will be unavailable!", port);
|
||||
return false;
|
||||
}
|
||||
RuntimeEvents::SubscribeToDebuggerEnabled([](){return true;});
|
||||
LogInternal("DAP debugging server started on port %d", port);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebugServer::Stop()
|
||||
{
|
||||
RuntimeEvents::UnsubscribeFromDebuggerEnabled([](){return true;});
|
||||
{
|
||||
ResetThreadLock lock(mutex);
|
||||
terminate = true;
|
||||
stopped = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
if (m_server) {
|
||||
m_server->stop();
|
||||
}
|
||||
if (restart_thread.joinable())
|
||||
{
|
||||
restart_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
DebugServer::~DebugServer() { Stop(); }
|
||||
}
|
||||
46
src/common/scripting/dap/DebugServer.h
Normal file
46
src/common/scripting/dap/DebugServer.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace dap
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
class Server;
|
||||
}
|
||||
class ReaderWriter;
|
||||
}
|
||||
namespace DebugServer
|
||||
{
|
||||
class ZScriptDebugger;
|
||||
class DebugServer
|
||||
{
|
||||
public:
|
||||
DebugServer();
|
||||
~DebugServer();
|
||||
|
||||
void RunRestartThread();
|
||||
bool Listen(int port);
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void onClientConnected(const std::shared_ptr<dap::ReaderWriter> &connection);
|
||||
void onError(const char * msg);
|
||||
bool StartServer();
|
||||
|
||||
using ResetThreadLock = std::unique_lock<std::mutex>;
|
||||
std::unique_ptr<ZScriptDebugger> debugger;
|
||||
std::unique_ptr<dap::net::Server> m_server;
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex; // guards 'terminate'
|
||||
int port;
|
||||
bool stopped = false;
|
||||
bool terminate = false;
|
||||
bool restart_server = false;
|
||||
bool closed = false;
|
||||
bool quitting = false; // On receiving a disconnect request with a terminateDebuggee flag
|
||||
std::thread restart_thread;
|
||||
};
|
||||
}
|
||||
15
src/common/scripting/dap/GameEventEmit.h
Normal file
15
src/common/scripting/dap/GameEventEmit.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
#include "vm.h"
|
||||
|
||||
class VMFrameStack;
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
namespace RuntimeEvents
|
||||
{
|
||||
void EmitInstructionExecutionEvent(VMFrameStack *stack, VMReturn *ret, int numret, const VMOP *pc);
|
||||
void EmitLogEvent(int level, const char *message);
|
||||
void EmitExceptionEvent(EVMAbortException reason, const std::string &message, const std::string &stackTrace);
|
||||
bool IsDebugServerRunning();
|
||||
}
|
||||
}
|
||||
1159
src/common/scripting/dap/GameInterfaces.h
Normal file
1159
src/common/scripting/dap/GameInterfaces.h
Normal file
File diff suppressed because it is too large
Load diff
1
src/common/scripting/dap/IdHandleBase.cpp
Normal file
1
src/common/scripting/dap/IdHandleBase.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "IdHandleBase.h"
|
||||
20
src/common/scripting/dap/IdHandleBase.h
Normal file
20
src/common/scripting/dap/IdHandleBase.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "IdMap.h"
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
template <class T> class IdHandleBase
|
||||
{
|
||||
IdMap<T> *m_idMap;
|
||||
public:
|
||||
explicit IdHandleBase(IdMap<T> *idMap) : m_idMap(idMap)
|
||||
{
|
||||
static_assert(std::is_base_of<IdHandleBase<T>, T>());
|
||||
m_idMap->Add(static_cast<T *>(this));
|
||||
}
|
||||
|
||||
virtual ~IdHandleBase() { m_idMap->Remove(static_cast<T *>(this)); }
|
||||
|
||||
uint32_t GetId() const { return m_idMap->GetId(static_cast<T *>(this)); }
|
||||
};
|
||||
}
|
||||
1
src/common/scripting/dap/IdMap.cpp
Normal file
1
src/common/scripting/dap/IdMap.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "IdMap.h"
|
||||
101
src/common/scripting/dap/IdMap.h
Normal file
101
src/common/scripting/dap/IdMap.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
#include "IdProvider.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace DebugServer
|
||||
{
|
||||
template <typename T> class IdMap
|
||||
{
|
||||
std::shared_ptr<IdProvider> m_idProvider;
|
||||
std::unordered_map<uint32_t, T> m_idsToElements;
|
||||
std::unordered_map<T, uint32_t> m_elementsToIds;
|
||||
|
||||
std::recursive_mutex m_elementsMutex;
|
||||
public:
|
||||
explicit IdMap(const std::shared_ptr<IdProvider> idProvider) : m_idProvider(idProvider) { }
|
||||
|
||||
~IdMap() { Clear(); }
|
||||
|
||||
bool Get(uint32_t id, T &value)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
auto pair = m_idsToElements.find(id);
|
||||
if (pair != m_idsToElements.end())
|
||||
{
|
||||
value = pair->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetId(T element, uint32_t &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
auto pair = m_elementsToIds.find(element);
|
||||
if (pair != m_elementsToIds.end())
|
||||
{
|
||||
id = pair->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AddOrGetExisting(T element, uint32_t &id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
if (GetId(element, id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
id = m_idProvider->GetNext();
|
||||
m_idsToElements.emplace(id, element);
|
||||
m_elementsToIds.emplace(element, id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Remove(uint32_t id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
auto pair = m_idsToElements.find(id);
|
||||
if (pair != m_idsToElements.end())
|
||||
{
|
||||
m_idsToElements.erase(id);
|
||||
m_elementsToIds.erase(pair->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Remove(T element)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
uint32_t id;
|
||||
if (GetId(element), id)
|
||||
{
|
||||
return Remove(id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_elementsMutex);
|
||||
|
||||
m_idsToElements.clear();
|
||||
m_elementsToIds.clear();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue