mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-08 19:49:32 -04:00
Merge branch 'main' of https://github.com/TwilitRealm/dusk into randomizer
This commit is contained in:
@@ -355,6 +355,10 @@ endif ()
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND GAME_LIBS Ws2_32)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
list(APPEND GAME_LIBS dbghelp)
|
||||
list(APPEND GAME_COMPILE_DEFS DUSK_CRASH_DBGHELP=1)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/no_backend.cpp)
|
||||
@@ -509,6 +513,10 @@ if (ANDROID)
|
||||
target_link_options(dusklight PRIVATE "-Wl,-u,SDL_main")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||
target_link_options(dusklight PRIVATE "-Wl,--build-id=sha1")
|
||||
endif ()
|
||||
|
||||
if (NOT APPLE)
|
||||
add_custom_command(TARGET dusklight POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
|
||||
+3
-1
@@ -326,7 +326,9 @@
|
||||
"BUILD_SHARED_LIBS": {
|
||||
"type": "BOOL",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"AURORA_SDL3_VERSION": "3.4.8",
|
||||
"AURORA_SDL3_REF": "refs/tags/release-3.4.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+1
-1
Submodule extern/aurora updated: 3b06e240a3...cb2c340d6c
@@ -1420,6 +1420,7 @@ set(DUSK_FILES
|
||||
src/d/actor/d_a_alink_dusk.cpp
|
||||
src/dusk/asserts.cpp
|
||||
src/dusk/config.cpp
|
||||
src/dusk/crash_handler.cpp
|
||||
src/dusk/crash_reporting.cpp
|
||||
src/dusk/data.cpp
|
||||
src/dusk/data.hpp
|
||||
|
||||
@@ -4570,6 +4570,7 @@ public:
|
||||
cXyz mIBChainInterpCurrHandRoot;
|
||||
bool mIBChainInterpPrevValid;
|
||||
bool mIBChainInterpCurrValid;
|
||||
bool mIsRollstab = false;
|
||||
#endif
|
||||
}; // Size: 0x385C
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
// Signals are visible to all achievement checks within the same tick, then cleared.
|
||||
void signal(const char* key);
|
||||
bool hasSignal(const char* key) const;
|
||||
int signalCount(const char* key) const;
|
||||
|
||||
std::vector<Achievement> getAchievements() const;
|
||||
|
||||
@@ -62,7 +63,7 @@ private:
|
||||
void processEntry(Entry& e);
|
||||
|
||||
std::vector<Entry> m_entries;
|
||||
std::unordered_set<std::string_view> m_signals;
|
||||
std::unordered_map<std::string_view, int> m_signals;
|
||||
bool m_loaded = false;
|
||||
bool m_dirty = false;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk::crash_handler {
|
||||
|
||||
void install();
|
||||
|
||||
} // namespace dusk::crash_handler
|
||||
@@ -12,6 +12,7 @@ namespace dusk {
|
||||
void InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel);
|
||||
void ShutdownFileLogging();
|
||||
const char* GetLogFilePath();
|
||||
int GetLogFileDescriptor();
|
||||
void SendToStubLog(AuroraLogLevel level, const char* module, const char* message);
|
||||
}
|
||||
|
||||
@@ -19,7 +20,11 @@ extern bool StubLogEnabled;
|
||||
|
||||
extern aurora::Module DuskLog;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define STUB_LOG() DuskLog.debug("{} is a stub", __FUNCTION__)
|
||||
#else
|
||||
#define STUB_LOG()
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
#define STUB_RET(...) \
|
||||
|
||||
@@ -202,6 +202,8 @@ struct UserSettings {
|
||||
ConfigVar<bool> invertCameraYAxis;
|
||||
ConfigVar<bool> invertFirstPersonXAxis;
|
||||
ConfigVar<bool> invertFirstPersonYAxis;
|
||||
ConfigVar<bool> invertAirSwimX;
|
||||
ConfigVar<bool> invertAirSwimY;
|
||||
ConfigVar<float> freeCameraSensitivity;
|
||||
ConfigVar<bool> debugFlyCam;
|
||||
ConfigVar<bool> debugFlyCamLockEvents;
|
||||
|
||||
@@ -76,6 +76,7 @@ void JPARegistAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
}
|
||||
|
||||
void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
ZoneScoped;
|
||||
JPABaseEmitter* emtr = work->mpEmtr;
|
||||
GXColor prm = ptcl->mPrmClr;
|
||||
prm.r = COLOR_MULTI(prm.r, emtr->mGlobalPrmClr.r);
|
||||
@@ -87,6 +88,7 @@ void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
}
|
||||
|
||||
void JPARegistPrmAlphaEnv(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
ZoneScoped;
|
||||
JPABaseEmitter* emtr = work->mpEmtr;
|
||||
GXColor prm = ptcl->mPrmClr;
|
||||
GXColor env = ptcl->mEnvClr;
|
||||
@@ -225,6 +227,7 @@ void JPAGenTexCrdMtxPrj(JPAEmitterWorkData* param_0) {
|
||||
}
|
||||
|
||||
void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) {
|
||||
ZoneScoped;
|
||||
JPABaseShape* shape = work->mpRes->getBsp();
|
||||
f32 dVar16 = work->mpEmtr->mTick;
|
||||
f32 dVar15 = 0.5f * (1.0f + shape->getTilingS());
|
||||
@@ -256,6 +259,7 @@ void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) {
|
||||
}
|
||||
|
||||
void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
ZoneScoped;
|
||||
JPABaseShape* shape = work->mpRes->getBsp();
|
||||
f32 dVar16 = param_1->mAge;
|
||||
f32 dVar15 = 0.5f * (1.0f + shape->getTilingS());
|
||||
@@ -286,14 +290,17 @@ void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1)
|
||||
}
|
||||
|
||||
void JPALoadTex(JPAEmitterWorkData* work) {
|
||||
ZoneScoped;
|
||||
work->mpResMgr->load(work->mpRes->getTexIdx(work->mpRes->getBsp()->getTexIdx()), GX_TEXMAP0);
|
||||
}
|
||||
|
||||
void JPALoadTexAnm(JPAEmitterWorkData* work) {
|
||||
ZoneScoped;
|
||||
work->mpResMgr->load(work->mpRes->getTexIdx(work->mpEmtr->mTexAnmIdx), GX_TEXMAP0);
|
||||
}
|
||||
|
||||
void JPALoadTexAnm(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
ZoneScoped;
|
||||
work->mpResMgr->load(work->mpRes->getTexIdx(ptcl->mTexAnmIdx), GX_TEXMAP0);
|
||||
}
|
||||
|
||||
@@ -446,6 +453,7 @@ void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
JGeometry::TVec3<f32> pos;
|
||||
#if TARGET_PC
|
||||
Mtx ptclPosMtx;
|
||||
@@ -475,6 +483,7 @@ void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
if (work->mpRes->getUsrIdx() == 0x89d7) {
|
||||
int a = 0;
|
||||
}
|
||||
@@ -518,6 +527,7 @@ void JPADrawYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
JGeometry::TVec3<f32> local_48;
|
||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
||||
Mtx local_38;
|
||||
@@ -542,6 +552,7 @@ void JPADrawRotYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
JGeometry::TVec3<f32> local_48;
|
||||
MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48);
|
||||
f32 sinRot = JMASSin(param_1->mRotateAngle);
|
||||
@@ -1268,6 +1279,8 @@ void JPADrawStripeX(JPAEmitterWorkData* param_0) {
|
||||
}
|
||||
|
||||
void JPADrawEmitterCallBackB(JPAEmitterWorkData* work) {
|
||||
ZoneScoped;
|
||||
|
||||
JPABaseEmitter* emtr = work->mpEmtr;
|
||||
if (emtr->mpEmtrCallBack == NULL) {
|
||||
return;
|
||||
@@ -1282,6 +1295,7 @@ void JPADrawParticleCallBack(JPAEmitterWorkData* work, JPABaseParticle* ptcl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
emtr->mpPtclCallBack->draw(emtr, ptcl);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <gx.h>
|
||||
|
||||
void JPALoadExTex(JPAEmitterWorkData* work) {
|
||||
ZoneScoped;
|
||||
|
||||
JPAExTexShape* ets = work->mpRes->getEts();
|
||||
|
||||
GXTexCoordID secTexCoordID = GX_TEXCOORD1;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
private static final String TAG = "SDL";
|
||||
private static final int SDL_MAJOR_VERSION = 3;
|
||||
private static final int SDL_MINOR_VERSION = 4;
|
||||
private static final int SDL_MICRO_VERSION = 4;
|
||||
private static final int SDL_MICRO_VERSION = 8;
|
||||
/*
|
||||
// Display InputType.SOURCE/CLASS of events and devices
|
||||
//
|
||||
@@ -570,7 +570,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
public static int getNaturalOrientation() {
|
||||
int result = SDL_ORIENTATION_UNKNOWN;
|
||||
|
||||
Activity activity = (Activity)getContext();
|
||||
Activity activity = getContext();
|
||||
if (activity != null) {
|
||||
Configuration config = activity.getResources().getConfiguration();
|
||||
Display display = activity.getWindowManager().getDefaultDisplay();
|
||||
@@ -590,7 +590,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
public static int getCurrentRotation() {
|
||||
int result = 0;
|
||||
|
||||
Activity activity = (Activity)getContext();
|
||||
Activity activity = getContext();
|
||||
if (activity != null) {
|
||||
Display display = activity.getWindowManager().getDefaultDisplay();
|
||||
switch (display.getRotation()) {
|
||||
@@ -1292,7 +1292,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
public static double getDiagonal()
|
||||
{
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
Activity activity = (Activity)getContext();
|
||||
Activity activity = getContext();
|
||||
if (activity == null) {
|
||||
return 0.0;
|
||||
}
|
||||
@@ -1940,7 +1940,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
||||
return;
|
||||
}
|
||||
|
||||
Activity activity = (Activity)getContext();
|
||||
Activity activity = getContext();
|
||||
if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
activity.requestPermissions(new String[]{permission}, requestCode);
|
||||
} else {
|
||||
|
||||
@@ -1165,6 +1165,10 @@ int daAlink_c::procCutFinishInit(int i_type) {
|
||||
const daAlink_cutParamTbl* cutParams = &cutParamTable[i_type];
|
||||
BOOL is_proc_frontRoll = mProcID == PROC_FRONT_ROLL;
|
||||
|
||||
#if TARGET_PC
|
||||
mIsRollstab = (is_proc_frontRoll != FALSE) && (i_type == CUT_FINISH_PARAM_STAB);
|
||||
#endif
|
||||
|
||||
commonProcInit(PROC_CUT_FINISH);
|
||||
setCutType(cutParams->m_cutType);
|
||||
field_0x3198 = cutParams->m_recoilAnmID;
|
||||
|
||||
@@ -216,7 +216,7 @@ void daAlink_c::setSpeedAndAngleSwim() {
|
||||
if (checkEventRun()) {
|
||||
var_r28 = mMoveAngle;
|
||||
} else {
|
||||
var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle));
|
||||
var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimX ? -1.0f : 1.0f)));
|
||||
}
|
||||
|
||||
cLib_addCalcAngleS(&shape_angle.y, var_r28, mpHIO->mSwim.m.mUnderwaterTurnRate, mpHIO->mSwim.m.mUnderwaterMaxTurn, mpHIO->mSwim.m.mUnderwaterMinTurn);
|
||||
@@ -835,7 +835,7 @@ void daAlink_c::setSwimMoveAnime() {
|
||||
} else {
|
||||
s16 var_r24;
|
||||
if (checkInputOnR() && !checkEventRun()) {
|
||||
var_r24 = 13653.0f * cM_scos(mStickAngle);
|
||||
var_r24 = 13653.0f * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimY ? -1.0f : 1.0f));
|
||||
} else {
|
||||
var_r24 = 0;
|
||||
}
|
||||
|
||||
@@ -2192,6 +2192,9 @@ static void damage_check(b_gnd_class* i_this) {
|
||||
i_this->mDamageInvulnerabilityTimer = 100;
|
||||
}
|
||||
}
|
||||
#if TARGET_PC
|
||||
dusk::AchievementSystem::get().signal("ganondorf_hit");
|
||||
#endif
|
||||
}
|
||||
|
||||
cXyz hitmark_size(1.0f, 1.0f, 1.0f);
|
||||
@@ -2218,6 +2221,7 @@ static void damage_check(b_gnd_class* i_this) {
|
||||
i_this->field_0xc7c = 0;
|
||||
dScnPly_c::setPauseTimer(7);
|
||||
a_this->health = 100;
|
||||
dusk::AchievementSystem::get().signal("ganondorf_knocked_down");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3530,6 +3530,15 @@ void daKago_c::action() {
|
||||
#endif
|
||||
mStickY = mDoCPd_c::getStickY(PAD_1);
|
||||
|
||||
#ifdef TARGET_PC
|
||||
if(dusk::getSettings().game.invertAirSwimX) {
|
||||
mStickX = -mStickX;
|
||||
}
|
||||
if(dusk::getSettings().game.invertAirSwimY) {
|
||||
mStickY = -mStickY;
|
||||
}
|
||||
#endif
|
||||
|
||||
u8 prevIsWaterfall = mIsWaterfall;
|
||||
mIsWaterfall = FALSE;
|
||||
fpcM_Search(s_waterfall, this);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/version.hpp"
|
||||
#include "dusk/randomizer/game/randomizer_context.hpp"
|
||||
#endif
|
||||
@@ -1140,8 +1141,14 @@ static int lure_standby(dmg_rod_class* i_this) {
|
||||
dComIfGp_setDoStatusForce(42, 0);
|
||||
}
|
||||
|
||||
i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1) * mDoCPd_c::getStickX3D(PAD_1);
|
||||
if (mDoCPd_c::getStickX3D(PAD_1) < 0.0f) {
|
||||
f32 stick_x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
stick_x = -stick_x;
|
||||
}
|
||||
#endif
|
||||
i_this->rod_stick_x = stick_x * stick_x;
|
||||
if (stick_x < 0.0f) {
|
||||
i_this->rod_stick_x *= -1.0f;
|
||||
}
|
||||
|
||||
@@ -3678,7 +3685,13 @@ static void uki_standby(dmg_rod_class* i_this) {
|
||||
cLib_addCalc2(&i_this->field_0x150c, substickX, 0.5f, 0.2f);
|
||||
|
||||
if (i_this->field_0x1508 > 0.3f && i_this->play_cam_mode < 5) {
|
||||
ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * mDoCPd_c::getStickX3D(PAD_1));
|
||||
f32 stick_x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
stick_x = -stick_x;
|
||||
}
|
||||
#endif
|
||||
ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * stick_x);
|
||||
}
|
||||
|
||||
cMtx_YrotS(*calc_mtx, i_this->field_0x1418);
|
||||
@@ -5055,8 +5068,15 @@ static void play_camera(dmg_rod_class* i_this) {
|
||||
static f32 old_stick_x = 0.0f;
|
||||
static f32 old_stick_sx = 0.0f;
|
||||
|
||||
f32 stick_x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
stick_x = -stick_x;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (
|
||||
(mDoCPd_c::getStickX3D(PAD_1) >= 0.8f && old_stick_x < 0.8f) || (mDoCPd_c::getStickX3D(PAD_1) <= -0.8f && old_stick_x > -0.8f)
|
||||
(stick_x >= 0.8f && old_stick_x < 0.8f) || (stick_x <= -0.8f && old_stick_x > -0.8f)
|
||||
#if VERSION != VERSION_SHIELD_DEBUG
|
||||
|| (mDoCPd_c::getSubStickX3D(PAD_1) >= 0.8f && old_stick_sx < 0.8f) || (mDoCPd_c::getSubStickX3D(PAD_1) <= -0.8f && old_stick_sx > -0.8f)
|
||||
#endif
|
||||
@@ -5072,7 +5092,7 @@ static void play_camera(dmg_rod_class* i_this) {
|
||||
}
|
||||
|
||||
if (i_this->play_cam_timer >= 15) {
|
||||
if (mDoCPd_c::getStickX3D(PAD_1) >= 0.5f
|
||||
if (stick_x >= 0.5f
|
||||
#if VERSION != VERSION_SHIELD_DEBUG
|
||||
|| mDoCPd_c::getSubStickX3D(PAD_1) >= 0.5f
|
||||
#endif
|
||||
@@ -5094,8 +5114,8 @@ static void play_camera(dmg_rod_class* i_this) {
|
||||
}
|
||||
}
|
||||
|
||||
old_stick_x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
old_stick_sx = mDoCPd_c::getSubStickX(PAD_1);
|
||||
old_stick_x = stick_x;
|
||||
old_stick_sx = mDoCPd_c::getSubStickX3D(PAD_1);
|
||||
|
||||
if (i_this->play_cam_timer == 1) {
|
||||
if (i_this->field_0xf81 == 0) {
|
||||
@@ -5800,7 +5820,14 @@ static int dmg_rod_Execute(dmg_rod_class* i_this) {
|
||||
|
||||
i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
i_this->rod_stick_y = mDoCPd_c::getStickY(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
i_this->rod_stick_x = -i_this->rod_stick_x;
|
||||
}
|
||||
i_this->rod_substick_x = mDoCPd_c::getSubStickX3D(PAD_1);
|
||||
#else
|
||||
i_this->rod_substick_x = mDoCPd_c::getSubStickX(PAD_1);
|
||||
#endif
|
||||
i_this->prev_rod_substick_y = i_this->rod_substick_y;
|
||||
i_this->rod_substick_y = mDoCPd_c::getSubStickY(PAD_1);
|
||||
|
||||
|
||||
@@ -943,6 +943,11 @@ static int ni_play(ni_class* i_this) {
|
||||
s16 var_r28 = 0x4000;
|
||||
|
||||
i_this->mPadMainStickX = mDoCPd_c::getStickX3D(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
i_this->mPadMainStickX = -i_this->mPadMainStickX;
|
||||
}
|
||||
#endif
|
||||
i_this->mPadMainStickY = mDoCPd_c::getStickY(PAD_1);
|
||||
i_this->mPadSubStickY = mDoCPd_c::getSubStickY(PAD_1);
|
||||
i_this->mPadSubStickX = mDoCPd_c::getSubStickX(PAD_1);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#if TARGET_PC
|
||||
#include "dusk/achievements.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#endif
|
||||
|
||||
static int plCutLRC[58] = {
|
||||
@@ -448,6 +449,10 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) {
|
||||
#if TARGET_PC
|
||||
if (fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) {
|
||||
dusk::AchievementSystem::get().signal("enemy_killed");
|
||||
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
|
||||
if (link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab) {
|
||||
dusk::AchievementSystem::get().signal("rollstab_kill");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1973,6 +1973,11 @@ void dMenu_Fmap2DBack_c::stageMapMove(STControl* i_stick, u8 param_1, bool param
|
||||
if (stick_value >= slow_bound && param_2 && field_0x1238 != 2) {
|
||||
bVar6 = true;
|
||||
s16 angle = i_stick->getAngleStick();
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
angle = -angle;
|
||||
}
|
||||
#endif
|
||||
f32 local_68 = mTexMaxX - mTexMinX;
|
||||
f32 spot_zoom = getSpotMapZoomRate();
|
||||
f32 region_zoom = getRegionMapZoomRate(mRegionCursor);
|
||||
|
||||
@@ -394,8 +394,21 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4
|
||||
icon_size_y *= _c7c;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
f32 rotation = mIconInfo[info_idx].rotation;
|
||||
if (dusk::getSettings().game.enableMirrorMode &&
|
||||
(mIconInfo[info_idx].icon_no == ICON_LINK_e ||
|
||||
mIconInfo[info_idx].icon_no == ICON_LINK_ENTER_e))
|
||||
{
|
||||
rotation = -rotation;
|
||||
}
|
||||
|
||||
mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z,
|
||||
rotation);
|
||||
#else
|
||||
mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z,
|
||||
mIconInfo[info_idx].rotation);
|
||||
#endif
|
||||
|
||||
if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) {
|
||||
mPictures[mIconInfo[info_idx].icon_no]->setAlpha((180.0f * _c80) / 255.0f);
|
||||
|
||||
+173
-8
@@ -11,6 +11,7 @@
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include "d/actor/d_a_ni.h"
|
||||
#include "d/actor/d_a_npc4.h"
|
||||
#include "d/actor/d_a_b_gnd.h"
|
||||
#include "d/actor/d_a_b_ob.h"
|
||||
#include "d/actor/d_a_player.h"
|
||||
#include "d/d_demo.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#include "f_op/f_op_actor_mng.h"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#include "dusk/logging.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
@@ -35,6 +37,9 @@ static void* s_cucco_play_search(void* i_actor, void*) {
|
||||
}
|
||||
|
||||
static void checkGoatHerding(Achievement& a, int32_t threshMs) {
|
||||
if (strcmp(dComIfGp_getStartStageName(), "F_SP00") != 0) {
|
||||
return;
|
||||
}
|
||||
if (dMeter2Info_getMaxCount() != 20 || dMeter2Info_getNowCount() != 20) {
|
||||
return;
|
||||
}
|
||||
@@ -65,6 +70,25 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"three_heart_clear",
|
||||
"Hero Mode",
|
||||
"Defeat Ganondorf with only 3 heart containers.",
|
||||
AchievementCategory::Challenge,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
|
||||
if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) {
|
||||
return;
|
||||
}
|
||||
if (dComIfGs_getMaxLife() < 20) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"completionist",
|
||||
@@ -201,7 +225,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
hasAncientDoc = true;
|
||||
}
|
||||
}
|
||||
if (!hasJewelRod || !hasAncientDoc) {
|
||||
if (!hasJewelRod || (!hasAncientDoc && !dComIfGs_isEventBit(dSv_event_flag_c::F_0302))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,7 +289,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
{
|
||||
"hylian_loach",
|
||||
"Legendary Catch",
|
||||
"Catch a Hylian Loach.",
|
||||
"Obtain the Hylian Loach in your fishing journal.",
|
||||
AchievementCategory::Collection,
|
||||
false, 0, 0, false
|
||||
},
|
||||
@@ -280,7 +304,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
{
|
||||
"all_fish",
|
||||
"Gone Fishin'",
|
||||
"Catch all 6 species of fish.",
|
||||
"Obtain all 6 species of fish in your fishing journal.",
|
||||
AchievementCategory::Collection,
|
||||
true, 6, 0, false
|
||||
},
|
||||
@@ -392,7 +416,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) {
|
||||
if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() < 20) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
@@ -506,6 +530,29 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"rollstab_triple",
|
||||
"Surgical Skewer",
|
||||
"Kill 3 enemies with a single rollstab.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
static int rollstabKills = 0;
|
||||
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
|
||||
const bool inRollstab = link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab;
|
||||
if (!inRollstab) {
|
||||
rollstabKills = 0;
|
||||
return;
|
||||
}
|
||||
rollstabKills += AchievementSystem::get().signalCount("rollstab_kill");
|
||||
if (rollstabKills >= 3) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Minigame
|
||||
{
|
||||
{
|
||||
@@ -600,6 +647,9 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (strcmp(dComIfGp_getStartStageName(), "F_SP114") != 0) {
|
||||
return;
|
||||
}
|
||||
const int32_t bestMs = dComIfGs_getRaceGameTime();
|
||||
if (dComIfGs_isEventBit(dSv_event_flag_c::F_0481) &&
|
||||
bestMs > 0 && bestMs <= 70000) {
|
||||
@@ -681,7 +731,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
{
|
||||
"long_jump_attack",
|
||||
"Long Jump Attack",
|
||||
"Travel more than 20 meters in a single jump attack before landing.",
|
||||
"Travel more than 15 meters in a single jump attack before landing.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
@@ -711,7 +761,7 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
inJump = false;
|
||||
const float dx = link->current.pos.x - startX;
|
||||
const float dz = link->current.pos.z - startZ;
|
||||
if (dx * dx + dz * dz >= 2000.0f * 2000.0f) {
|
||||
if (dx * dx + dz * dz >= 1500.0f * 1500.0f) {
|
||||
a.progress = 1;
|
||||
}
|
||||
} else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) {
|
||||
@@ -800,6 +850,66 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"ganondorf_3hit",
|
||||
"Autospin Annihilation",
|
||||
"Finish off Ganondorf in the final duel after only 3 attacks.",
|
||||
AchievementCategory::Misc,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
auto& sys = AchievementSystem::get();
|
||||
const auto* link = static_cast<const daAlink_c*>(daPy_getPlayerActorClass());
|
||||
|
||||
static int autospinCount = 0;
|
||||
static int pendingHits = 0;
|
||||
static bool invalidated = false;
|
||||
static bool wasInFight = false;
|
||||
|
||||
auto* gnd = static_cast<b_gnd_class*>(fopAcM_SearchByName(fpcNm_B_GND_e));
|
||||
const bool inFight = gnd != nullptr && !gnd->checkRide();
|
||||
|
||||
if (inFight && !wasInFight) {
|
||||
autospinCount = 0;
|
||||
pendingHits = 0;
|
||||
invalidated = false;
|
||||
}
|
||||
wasInFight = inFight;
|
||||
|
||||
if (!inFight) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool hitOccurred = sys.hasSignal("ganondorf_hit");
|
||||
const bool knockedDown = sys.hasSignal("ganondorf_knocked_down");
|
||||
|
||||
if (hitOccurred && knockedDown) {
|
||||
// Spin completing an autospin: pendingHits should be exactly 1 (the spin attack)
|
||||
if (pendingHits == 1) {
|
||||
autospinCount++;
|
||||
pendingHits = 0;
|
||||
} else {
|
||||
invalidated = true;
|
||||
}
|
||||
} else if (hitOccurred) {
|
||||
pendingHits++;
|
||||
if (pendingHits > 1) {
|
||||
invalidated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (link != nullptr && link->mProcID == daAlink_c::PROC_GANON_FINISH) {
|
||||
if (!invalidated && autospinCount == 3) {
|
||||
a.progress = 1;
|
||||
}
|
||||
autospinCount = 0;
|
||||
pendingHits = 0;
|
||||
invalidated = false;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
// Glitched
|
||||
{
|
||||
{
|
||||
@@ -1012,6 +1122,55 @@ std::vector<AchievementSystem::Entry> AchievementSystem::makeEntries() {
|
||||
a.progress = 1;
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"early_city",
|
||||
"Early City",
|
||||
"Obtain the Double Clawshots without obtaining the Dominion Rod.",
|
||||
AchievementCategory::Glitched,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (daPy_getPlayerActorClass() == nullptr) {
|
||||
return;
|
||||
}
|
||||
bool hasDoubleClawshot = false;
|
||||
bool hasDominionRod = false;
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
const auto item = dComIfGs_getItem(i, false);
|
||||
if (item == dItemNo_W_HOOKSHOT_e) {
|
||||
hasDoubleClawshot = true;
|
||||
}
|
||||
if (item == dItemNo_COPY_ROD_e || item == dItemNo_COPY_ROD_2_e) {
|
||||
hasDominionRod = true;
|
||||
}
|
||||
}
|
||||
if (hasDoubleClawshot && !hasDominionRod) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
},
|
||||
{
|
||||
{
|
||||
"early_kakariko",
|
||||
"Gorge Skip",
|
||||
"Collect the Kakariko warp portal without warping the gorge bridge.",
|
||||
AchievementCategory::Glitched,
|
||||
false, 0, 0, false
|
||||
},
|
||||
[](Achievement& a, json&) {
|
||||
if (dComIfGs_isEventBit(dSv_event_flag_c::M_018) || dComIfGp_getStageStagInfo() == nullptr) {
|
||||
return;
|
||||
}
|
||||
const bool savedPortal = g_dComIfG_gameInfo.info.getSavedata().getSave(dStage_SaveTbl_ELDIN).getBit().isSwitch(31);
|
||||
const bool livePortal = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == dStage_SaveTbl_ELDIN && dComIfGs_isSaveSwitch(31);
|
||||
if (savedPortal || livePortal) {
|
||||
a.progress = 1;
|
||||
}
|
||||
},
|
||||
{}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1088,11 +1247,17 @@ void AchievementSystem::clearAll() {
|
||||
}
|
||||
|
||||
void AchievementSystem::signal(const char* key) {
|
||||
m_signals.insert(key);
|
||||
m_signals[key]++;
|
||||
}
|
||||
|
||||
bool AchievementSystem::hasSignal(const char* key) const {
|
||||
return m_signals.count(key) > 0;
|
||||
const auto it = m_signals.find(key);
|
||||
return it != m_signals.end() && it->second > 0;
|
||||
}
|
||||
|
||||
int AchievementSystem::signalCount(const char* key) const {
|
||||
const auto it = m_signals.find(key);
|
||||
return it != m_signals.end() ? it->second : 0;
|
||||
}
|
||||
|
||||
void AchievementSystem::clearOne(const char* key) {
|
||||
|
||||
@@ -0,0 +1,754 @@
|
||||
#if !defined(_WIN32) && !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "dusk/crash_handler.h"
|
||||
|
||||
#include "dusk/logging.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <io.h>
|
||||
|
||||
#if defined(DUSK_CRASH_DBGHELP)
|
||||
#include <dbghelp.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
#else
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#ifndef NT_GNU_BUILD_ID
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef DUSK_ARCH
|
||||
#define DUSK_ARCH "unknown"
|
||||
#endif
|
||||
|
||||
namespace dusk::crash_handler {
|
||||
namespace {
|
||||
|
||||
constexpr int kStderrFd = 2;
|
||||
constexpr int kMaxFrames = 128;
|
||||
constexpr char kHexDigits[] = "0123456789abcdef";
|
||||
|
||||
struct CrashContext {
|
||||
uintptr_t moduleBase = 0;
|
||||
char modulePath[1024] = {};
|
||||
uint8_t buildId[64] = {};
|
||||
unsigned buildIdLen = 0;
|
||||
unsigned pdbAge = 0;
|
||||
};
|
||||
CrashContext g_ctx;
|
||||
|
||||
void rawWrite(int fd, const char* data, size_t len) {
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
_write(fd, data, static_cast<unsigned int>(len));
|
||||
#else
|
||||
while (len > 0) {
|
||||
const ssize_t written = ::write(fd, data, len);
|
||||
if (written <= 0) {
|
||||
return;
|
||||
}
|
||||
data += written;
|
||||
len -= static_cast<size_t>(written);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void writeStr(int fd, const char* s) {
|
||||
if (s != nullptr) {
|
||||
rawWrite(fd, s, std::strlen(s));
|
||||
}
|
||||
}
|
||||
|
||||
void writeHex(int fd, unsigned long long value) {
|
||||
char buf[2 + 16];
|
||||
size_t o = sizeof(buf);
|
||||
do {
|
||||
buf[--o] = kHexDigits[value & 0xF];
|
||||
value >>= 4;
|
||||
} while (value != 0);
|
||||
buf[--o] = 'x';
|
||||
buf[--o] = '0';
|
||||
rawWrite(fd, buf + o, sizeof(buf) - o);
|
||||
}
|
||||
|
||||
void writeDec(int fd, unsigned int value) {
|
||||
char buf[10];
|
||||
size_t o = sizeof(buf);
|
||||
do {
|
||||
buf[--o] = static_cast<char>('0' + value % 10);
|
||||
value /= 10;
|
||||
} while (value != 0);
|
||||
rawWrite(fd, buf + o, sizeof(buf) - o);
|
||||
}
|
||||
|
||||
void writeHexBytes(int fd, const uint8_t* data, unsigned len) {
|
||||
char buf[2];
|
||||
for (unsigned i = 0; i < len; ++i) {
|
||||
buf[0] = kHexDigits[data[i] >> 4];
|
||||
buf[1] = kHexDigits[data[i] & 0xF];
|
||||
rawWrite(fd, buf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const char* moduleName() {
|
||||
const char* name = g_ctx.modulePath;
|
||||
for (const char* p = g_ctx.modulePath; *p != '\0'; ++p) {
|
||||
if (*p == '/' || *p == '\\') {
|
||||
name = p + 1;
|
||||
}
|
||||
}
|
||||
return name[0] != '\0' ? name : "(unknown)";
|
||||
}
|
||||
|
||||
const char* symbolFor(uintptr_t pc, unsigned long long* disp) {
|
||||
#if defined(_WIN32) && defined(DUSK_CRASH_DBGHELP)
|
||||
alignas(SYMBOL_INFO) static char storage[sizeof(SYMBOL_INFO) + 512];
|
||||
auto* sym = reinterpret_cast<SYMBOL_INFO*>(storage);
|
||||
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym->MaxNameLen = 511;
|
||||
DWORD64 d = 0;
|
||||
if (SymFromAddr(GetCurrentProcess(), pc, &d, sym)) {
|
||||
*disp = d;
|
||||
return sym->Name;
|
||||
}
|
||||
return nullptr;
|
||||
#elif defined(_WIN32)
|
||||
(void)pc;
|
||||
(void)disp;
|
||||
return nullptr;
|
||||
#else
|
||||
Dl_info info;
|
||||
if (dladdr(reinterpret_cast<void*>(pc), &info) != 0 && info.dli_sname != nullptr) {
|
||||
const auto base = reinterpret_cast<uintptr_t>(info.dli_saddr);
|
||||
*disp = pc >= base ? pc - base : 0;
|
||||
return info.dli_sname;
|
||||
}
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void emitFrame(int fd, int index, uintptr_t pc) {
|
||||
writeStr(fd, "#");
|
||||
if (index < 10) {
|
||||
writeStr(fd, "0");
|
||||
}
|
||||
writeDec(fd, static_cast<unsigned int>(index));
|
||||
writeStr(fd, " abs=");
|
||||
writeHex(fd, pc);
|
||||
writeStr(fd, " rva=");
|
||||
writeHex(fd, pc >= g_ctx.moduleBase ? pc - g_ctx.moduleBase : 0ull);
|
||||
writeStr(fd, " ");
|
||||
writeStr(fd, moduleName());
|
||||
unsigned long long disp = 0;
|
||||
const char* sym = symbolFor(pc, &disp);
|
||||
if (sym != nullptr && sym[0] != '\0') {
|
||||
writeStr(fd, " ");
|
||||
writeStr(fd, sym);
|
||||
writeStr(fd, "+");
|
||||
writeHex(fd, disp);
|
||||
}
|
||||
writeStr(fd, "\n");
|
||||
}
|
||||
|
||||
void emitHeader(int fd, const char* reason, unsigned long long code, bool hasCode,
|
||||
uintptr_t faultAddr, uintptr_t crashPc, bool crashPcKnown) {
|
||||
writeStr(fd, "\n==================== DUSKLIGHT CRASHED ====================\n");
|
||||
writeStr(fd, "Build: " DUSK_WC_DESCRIBE " (" DUSK_WC_BRANCH ")\n");
|
||||
writeStr(fd, "Revision: " DUSK_WC_REVISION " Date: " DUSK_WC_DATE
|
||||
" Type: " DUSK_BUILD_TYPE "\n");
|
||||
writeStr(fd, "Platform: " DUSK_PLATFORM_NAME " / " DUSK_ARCH "\n");
|
||||
writeStr(fd, "Module: ");
|
||||
writeStr(fd, g_ctx.modulePath[0] != '\0' ? g_ctx.modulePath : "(unknown)");
|
||||
writeStr(fd, "\nModule base: ");
|
||||
writeHex(fd, g_ctx.moduleBase);
|
||||
writeStr(fd, "\nBuild-ID: ");
|
||||
if (g_ctx.buildIdLen != 0) {
|
||||
writeHexBytes(fd, g_ctx.buildId, g_ctx.buildIdLen);
|
||||
#if defined(_WIN32)
|
||||
if (g_ctx.pdbAge != 0) {
|
||||
writeStr(fd, " (Age=");
|
||||
writeDec(fd, g_ctx.pdbAge);
|
||||
writeStr(fd, ")");
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
writeStr(fd, "(unavailable)");
|
||||
}
|
||||
writeStr(fd, "\nReason: ");
|
||||
writeStr(fd, reason);
|
||||
if (hasCode) {
|
||||
writeStr(fd, " (");
|
||||
writeHex(fd, code);
|
||||
writeStr(fd, ")");
|
||||
}
|
||||
writeStr(fd, "\nFault addr: ");
|
||||
writeHex(fd, faultAddr);
|
||||
writeStr(fd, "\nCrash PC: ");
|
||||
if (crashPcKnown) {
|
||||
writeHex(fd, crashPc);
|
||||
writeStr(fd, " rva=");
|
||||
writeHex(fd, crashPc >= g_ctx.moduleBase ? crashPc - g_ctx.moduleBase : 0ull);
|
||||
} else {
|
||||
writeStr(fd, "(unavailable on this platform)");
|
||||
}
|
||||
writeStr(fd, "\n");
|
||||
writeStr(fd, "Backtrace:\n");
|
||||
}
|
||||
|
||||
void emitFooter(int fd) {
|
||||
writeStr(fd, "========================================================\n");
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
LONG g_inHandler = 0;
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = nullptr;
|
||||
|
||||
void captureBuildId() {
|
||||
const auto* base = reinterpret_cast<const uint8_t*>(g_ctx.moduleBase);
|
||||
if (base == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
return;
|
||||
}
|
||||
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
||||
return;
|
||||
}
|
||||
const IMAGE_DATA_DIRECTORY& dir =
|
||||
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
||||
if (dir.VirtualAddress == 0 || dir.Size == 0) {
|
||||
return;
|
||||
}
|
||||
const auto* dbg = reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>(base + dir.VirtualAddress);
|
||||
const unsigned count = dir.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (dbg[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
continue;
|
||||
}
|
||||
const auto* cv = base + dbg[i].AddressOfRawData;
|
||||
if (std::memcmp(cv, "RSDS", 4) != 0) {
|
||||
continue;
|
||||
}
|
||||
std::memcpy(g_ctx.buildId, cv + 4, sizeof(GUID));
|
||||
g_ctx.buildIdLen = sizeof(GUID);
|
||||
std::memcpy(&g_ctx.pdbAge, cv + 4 + sizeof(GUID), sizeof(g_ctx.pdbAge));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char* exceptionName(DWORD code) {
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return "EXCEPTION_ACCESS_VIOLATION";
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
return "EXCEPTION_STACK_OVERFLOW";
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||
default:
|
||||
return "EXCEPTION";
|
||||
}
|
||||
}
|
||||
|
||||
int captureBacktraceWin(CONTEXT ctx, uintptr_t* out, int cap) {
|
||||
int n = 0;
|
||||
while (n < cap) {
|
||||
#if defined(_M_X64)
|
||||
const DWORD64 ip = ctx.Rip;
|
||||
#elif defined(_M_ARM64)
|
||||
const DWORD64 ip = ctx.Pc;
|
||||
#else
|
||||
const DWORD64 ip = 0;
|
||||
#endif
|
||||
if (ip == 0) {
|
||||
break;
|
||||
}
|
||||
out[n++] = static_cast<uintptr_t>(ip);
|
||||
#if defined(_M_X64) || defined(_M_ARM64)
|
||||
DWORD64 imageBase = 0;
|
||||
PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(ip, &imageBase, nullptr);
|
||||
if (fn != nullptr) {
|
||||
PVOID handlerData = nullptr;
|
||||
DWORD64 establisherFrame = 0;
|
||||
RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, ip, fn, &ctx, &handlerData,
|
||||
&establisherFrame, nullptr);
|
||||
continue;
|
||||
}
|
||||
#if defined(_M_X64)
|
||||
if (ctx.Rsp == 0) {
|
||||
break;
|
||||
}
|
||||
ctx.Rip = *reinterpret_cast<const DWORD64*>(ctx.Rsp);
|
||||
ctx.Rsp += sizeof(DWORD64);
|
||||
#else
|
||||
if (ctx.Lr == 0 || ctx.Lr == ip) {
|
||||
break;
|
||||
}
|
||||
ctx.Pc = ctx.Lr;
|
||||
ctx.Lr = 0;
|
||||
#endif
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void emit(int fd, EXCEPTION_POINTERS* ep) {
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DWORD code = ep->ExceptionRecord->ExceptionCode;
|
||||
const uintptr_t pc = reinterpret_cast<uintptr_t>(ep->ExceptionRecord->ExceptionAddress);
|
||||
uintptr_t faultAddr = 0;
|
||||
if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) {
|
||||
faultAddr = static_cast<uintptr_t>(ep->ExceptionRecord->ExceptionInformation[1]);
|
||||
}
|
||||
|
||||
emitHeader(fd, exceptionName(code), code, true, faultAddr, pc, true);
|
||||
|
||||
uintptr_t frames[kMaxFrames];
|
||||
const int frameCount = captureBacktraceWin(*ep->ContextRecord, frames, kMaxFrames);
|
||||
for (int i = 0; i < frameCount; ++i) {
|
||||
emitFrame(fd, i, frames[i]);
|
||||
}
|
||||
|
||||
emitFooter(fd);
|
||||
}
|
||||
|
||||
LONG WINAPI windowsHandler(EXCEPTION_POINTERS* ep) {
|
||||
if (InterlockedCompareExchange(&g_inHandler, 1, 0) != 0) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
emit(kStderrFd, ep);
|
||||
const int logFd = dusk::GetLogFileDescriptor();
|
||||
if (logFd >= 0) {
|
||||
emit(logFd, ep);
|
||||
}
|
||||
if (g_prevFilter != nullptr) {
|
||||
return g_prevFilter(ep);
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
constexpr int kSignals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE};
|
||||
constexpr int kSignalCount = static_cast<int>(sizeof(kSignals) / sizeof(kSignals[0]));
|
||||
constexpr int kAltStackSize = 128 * 1024;
|
||||
|
||||
volatile std::sig_atomic_t g_inHandler = 0;
|
||||
char g_altStack[kAltStackSize];
|
||||
struct sigaction g_prev[kSignalCount];
|
||||
std::terminate_handler g_prevTerminate = nullptr;
|
||||
|
||||
void crashRegs(void* ucv, uintptr_t& pc, uintptr_t& lr, uintptr_t& fp) {
|
||||
pc = 0;
|
||||
lr = 0;
|
||||
fp = 0;
|
||||
if (ucv == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto* uc = static_cast<ucontext_t*>(ucv);
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__aarch64__) || defined(__arm64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext->__ss.__pc);
|
||||
lr = static_cast<uintptr_t>(uc->uc_mcontext->__ss.__lr);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext->__ss.__fp);
|
||||
#elif defined(__x86_64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext->__ss.__rip);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext->__ss.__rbp);
|
||||
#endif
|
||||
#elif defined(__ANDROID__)
|
||||
#if defined(__aarch64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.pc);
|
||||
lr = static_cast<uintptr_t>(uc->uc_mcontext.regs[30]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.regs[29]);
|
||||
#elif defined(__x86_64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_RIP]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_RBP]);
|
||||
#elif defined(__arm__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.arm_pc);
|
||||
lr = static_cast<uintptr_t>(uc->uc_mcontext.arm_lr);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.arm_fp);
|
||||
#elif defined(__i386__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_EIP]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_EBP]);
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#if defined(__x86_64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_RIP]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_RBP]);
|
||||
#elif defined(__aarch64__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.pc);
|
||||
lr = static_cast<uintptr_t>(uc->uc_mcontext.regs[30]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.regs[29]);
|
||||
#elif defined(__i386__)
|
||||
pc = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_EIP]);
|
||||
fp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_EBP]);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool pcNearFunctionEntry(uintptr_t pc) {
|
||||
constexpr uintptr_t kPrologueWindow = 20;
|
||||
Dl_info info;
|
||||
if (dladdr(reinterpret_cast<void*>(pc), &info) == 0 || info.dli_saddr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const auto start = reinterpret_cast<uintptr_t>(info.dli_saddr);
|
||||
return pc >= start && pc - start <= kPrologueWindow;
|
||||
}
|
||||
|
||||
int captureBacktraceFP(uintptr_t pc, uintptr_t lr, uintptr_t fp, uintptr_t* out, int cap) {
|
||||
int n = 0;
|
||||
if (pc != 0 && n < cap) {
|
||||
out[n++] = pc;
|
||||
}
|
||||
bool dedupeLr = false;
|
||||
if (lr != 0 && lr != pc && n < cap && pcNearFunctionEntry(pc)) {
|
||||
out[n++] = lr;
|
||||
dedupeLr = true;
|
||||
}
|
||||
uintptr_t cur = fp;
|
||||
uintptr_t prev = 0;
|
||||
constexpr uintptr_t kMaxFrameSpan = 16u << 20;
|
||||
while (n < cap) {
|
||||
if (cur == 0 || (cur & (sizeof(uintptr_t) - 1)) != 0 || cur <= prev) {
|
||||
break;
|
||||
}
|
||||
const auto* slot = reinterpret_cast<const uintptr_t*>(cur);
|
||||
const uintptr_t next = slot[0];
|
||||
const uintptr_t ret = slot[1];
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
const bool skip = dedupeLr && ret == lr;
|
||||
dedupeLr = false;
|
||||
if (!skip) {
|
||||
out[n++] = ret;
|
||||
}
|
||||
if (next != 0 && next > cur && next - cur > kMaxFrameSpan) {
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
struct UnwindState {
|
||||
uintptr_t* pcs;
|
||||
int count;
|
||||
int cap;
|
||||
int skip;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code unwindCb(struct _Unwind_Context* ctx, void* arg) {
|
||||
auto* s = static_cast<UnwindState*>(arg);
|
||||
const uintptr_t ip = static_cast<uintptr_t>(_Unwind_GetIP(ctx));
|
||||
if (ip == 0) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
if (s->skip > 0) {
|
||||
--s->skip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
if (s->count >= s->cap) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
s->pcs[s->count++] = ip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
int captureBacktrace(uintptr_t* pcs, int cap, int skip) {
|
||||
UnwindState s{pcs, 0, cap, skip};
|
||||
_Unwind_Backtrace(&unwindCb, &s);
|
||||
return s.count;
|
||||
}
|
||||
|
||||
void prewarmUnwinder() {
|
||||
uintptr_t warm[4];
|
||||
captureBacktrace(warm, 4, 0);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
void captureBuildId() {
|
||||
const auto* header = reinterpret_cast<const struct mach_header_64*>(g_ctx.moduleBase);
|
||||
if (header == nullptr || header->magic != MH_MAGIC_64) {
|
||||
return;
|
||||
}
|
||||
const auto* lc = reinterpret_cast<const struct load_command*>(
|
||||
reinterpret_cast<const char*>(header) + sizeof(struct mach_header_64));
|
||||
for (uint32_t i = 0; i < header->ncmds; ++i) {
|
||||
if (lc->cmd == LC_UUID) {
|
||||
const auto* uuid = reinterpret_cast<const struct uuid_command*>(lc);
|
||||
std::memcpy(g_ctx.buildId, uuid->uuid, sizeof(uuid->uuid));
|
||||
g_ctx.buildIdLen = sizeof(uuid->uuid);
|
||||
return;
|
||||
}
|
||||
lc = reinterpret_cast<const struct load_command*>(
|
||||
reinterpret_cast<const char*>(lc) + lc->cmdsize);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool segmentContains(const dl_phdr_info* info, uintptr_t addr) {
|
||||
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
||||
const ElfW(Phdr)& ph = info->dlpi_phdr[i];
|
||||
if (ph.p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
const uintptr_t start = info->dlpi_addr + ph.p_vaddr;
|
||||
if (addr >= start && addr < start + ph.p_memsz) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readGnuBuildId(const dl_phdr_info* info) {
|
||||
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
||||
const ElfW(Phdr)& ph = info->dlpi_phdr[i];
|
||||
if (ph.p_type != PT_NOTE) {
|
||||
continue;
|
||||
}
|
||||
const auto* p = reinterpret_cast<const uint8_t*>(info->dlpi_addr + ph.p_vaddr);
|
||||
const uint8_t* end = p + ph.p_memsz;
|
||||
while (p + sizeof(ElfW(Nhdr)) <= end) {
|
||||
const auto* nh = reinterpret_cast<const ElfW(Nhdr)*>(p);
|
||||
const char* name = reinterpret_cast<const char*>(nh + 1);
|
||||
const uint8_t* desc =
|
||||
reinterpret_cast<const uint8_t*>(name + ((nh->n_namesz + 3) & ~3u));
|
||||
if (nh->n_type == NT_GNU_BUILD_ID && nh->n_namesz == 4 &&
|
||||
std::memcmp(name, "GNU", 4) == 0) {
|
||||
unsigned n = nh->n_descsz;
|
||||
if (n > sizeof(g_ctx.buildId)) {
|
||||
n = sizeof(g_ctx.buildId);
|
||||
}
|
||||
std::memcpy(g_ctx.buildId, desc, n);
|
||||
g_ctx.buildIdLen = n;
|
||||
return true;
|
||||
}
|
||||
p = desc + ((nh->n_descsz + 3) & ~3u);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) {
|
||||
const auto self = *static_cast<const uintptr_t*>(arg);
|
||||
if (!segmentContains(info, self)) {
|
||||
return 0;
|
||||
}
|
||||
readGnuBuildId(info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void captureBuildId() {
|
||||
uintptr_t self = reinterpret_cast<uintptr_t>(&install);
|
||||
dl_iterate_phdr(&elfBuildIdCallback, &self);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const char* signalName(int sig) {
|
||||
switch (sig) {
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV (segmentation fault)";
|
||||
case SIGBUS:
|
||||
return "SIGBUS (bus error)";
|
||||
case SIGABRT:
|
||||
return "SIGABRT (abort)";
|
||||
case SIGILL:
|
||||
return "SIGILL (illegal instruction)";
|
||||
case SIGFPE:
|
||||
return "SIGFPE (floating point exception)";
|
||||
default:
|
||||
return "unknown signal";
|
||||
}
|
||||
}
|
||||
|
||||
void emit(int fd, int sig, siginfo_t* info, const uintptr_t* frames, int frameCount,
|
||||
uintptr_t pc) {
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
const uintptr_t faultAddr =
|
||||
info != nullptr ? reinterpret_cast<uintptr_t>(info->si_addr) : 0;
|
||||
emitHeader(fd, signalName(sig), 0, false, faultAddr, pc, pc != 0);
|
||||
for (int i = 0; i < frameCount; ++i) {
|
||||
emitFrame(fd, i, frames[i]);
|
||||
}
|
||||
emitFooter(fd);
|
||||
}
|
||||
|
||||
void chainPrevious(int sig, siginfo_t* info, void* uc) {
|
||||
for (int i = 0; i < kSignalCount; ++i) {
|
||||
if (kSignals[i] != sig) {
|
||||
continue;
|
||||
}
|
||||
const struct sigaction& o = g_prev[i];
|
||||
if ((o.sa_flags & SA_SIGINFO) != 0) {
|
||||
if (o.sa_sigaction != nullptr) {
|
||||
o.sa_sigaction(sig, info, uc);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (o.sa_handler == SIG_IGN) {
|
||||
return;
|
||||
}
|
||||
if (o.sa_handler != SIG_DFL && o.sa_handler != nullptr) {
|
||||
o.sa_handler(sig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
::signal(sig, SIG_DFL);
|
||||
::raise(sig);
|
||||
}
|
||||
|
||||
void handler(int sig, siginfo_t* info, void* ucv) {
|
||||
if (g_inHandler != 0) {
|
||||
_exit(128 + sig);
|
||||
}
|
||||
g_inHandler = 1;
|
||||
|
||||
uintptr_t pc = 0;
|
||||
uintptr_t lr = 0;
|
||||
uintptr_t fp = 0;
|
||||
crashRegs(ucv, pc, lr, fp);
|
||||
uintptr_t frames[kMaxFrames];
|
||||
int frameCount = captureBacktraceFP(pc, lr, fp, frames, kMaxFrames);
|
||||
if (frameCount < 2) {
|
||||
frameCount = captureBacktrace(frames, kMaxFrames, 2);
|
||||
}
|
||||
|
||||
emit(kStderrFd, sig, info, frames, frameCount, pc);
|
||||
const int logFd = dusk::GetLogFileDescriptor();
|
||||
if (logFd >= 0) {
|
||||
emit(logFd, sig, info, frames, frameCount, pc);
|
||||
::fsync(logFd);
|
||||
}
|
||||
|
||||
chainPrevious(sig, info, ucv);
|
||||
}
|
||||
|
||||
void writeTerminateMessage(int fd, const char* body, const char* what) {
|
||||
writeStr(fd, "\nterminate: ");
|
||||
writeStr(fd, body);
|
||||
writeStr(fd, what);
|
||||
writeStr(fd, "\n");
|
||||
}
|
||||
|
||||
void onTerminate() {
|
||||
const char* body = "unknown reason";
|
||||
const char* what = nullptr;
|
||||
if (std::exception_ptr ep = std::current_exception()) {
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::exception& e) {
|
||||
body = "uncaught exception: ";
|
||||
what = e.what();
|
||||
} catch (...) {
|
||||
body = "uncaught non-std exception";
|
||||
}
|
||||
} else {
|
||||
body = "no active exception";
|
||||
}
|
||||
writeTerminateMessage(kStderrFd, body, what);
|
||||
writeTerminateMessage(dusk::GetLogFileDescriptor(), body, what);
|
||||
if (g_prevTerminate != nullptr) {
|
||||
g_prevTerminate();
|
||||
}
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
void install() {
|
||||
#if defined(_WIN32)
|
||||
g_ctx.moduleBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
GetModuleFileNameA(nullptr, g_ctx.modulePath, sizeof(g_ctx.modulePath) - 1);
|
||||
captureBuildId();
|
||||
#if defined(DUSK_CRASH_DBGHELP)
|
||||
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
|
||||
SymInitialize(GetCurrentProcess(), nullptr, TRUE);
|
||||
#endif
|
||||
g_prevFilter = SetUnhandledExceptionFilter(&windowsHandler);
|
||||
#else
|
||||
Dl_info moduleInfo;
|
||||
if (dladdr(reinterpret_cast<void*>(&install), &moduleInfo) != 0) {
|
||||
g_ctx.moduleBase = reinterpret_cast<uintptr_t>(moduleInfo.dli_fbase);
|
||||
if (moduleInfo.dli_fname != nullptr) {
|
||||
std::strncpy(g_ctx.modulePath, moduleInfo.dli_fname,
|
||||
sizeof(g_ctx.modulePath) - 1);
|
||||
}
|
||||
}
|
||||
captureBuildId();
|
||||
prewarmUnwinder();
|
||||
|
||||
static stack_t altStack;
|
||||
altStack.ss_sp = g_altStack;
|
||||
altStack.ss_size = sizeof(g_altStack);
|
||||
altStack.ss_flags = 0;
|
||||
sigaltstack(&altStack, nullptr);
|
||||
|
||||
struct sigaction sa;
|
||||
std::memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = &handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
|
||||
for (int i = 0; i < kSignalCount; ++i) {
|
||||
sigaction(kSignals[i], &sa, &g_prev[i]);
|
||||
}
|
||||
|
||||
g_prevTerminate = std::set_terminate(&onTerminate);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dusk::crash_handler
|
||||
@@ -1,14 +1,15 @@
|
||||
#include "dusk/frame_interpolation.h"
|
||||
|
||||
#include <memory>
|
||||
#include "mtx.h"
|
||||
#include "f_op/f_op_camera_mng.h"
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
#include "mtx.h"
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Recording {
|
||||
std::unordered_map<uintptr_t, Mtx> matrix_values;
|
||||
absl::flat_hash_map<uintptr_t, Mtx> matrix_values;
|
||||
};
|
||||
|
||||
bool s_initialized = false;
|
||||
@@ -26,7 +27,7 @@ uint64_t g_sim_tick_seq = 0;
|
||||
Recording g_current_recording;
|
||||
Recording g_previous_recording;
|
||||
|
||||
std::unordered_map<uintptr_t, Mtx> g_replacements;
|
||||
absl::flat_hash_map<uintptr_t, Mtx> g_replacements;
|
||||
|
||||
struct CameraSnapshot {
|
||||
cXyz eye{};
|
||||
|
||||
@@ -208,6 +208,27 @@ namespace dusk {
|
||||
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
|
||||
daHorse_c* horse = dComIfGp_getHorseActor();
|
||||
|
||||
double speedXzy = 0.0;
|
||||
if (player != nullptr) {
|
||||
speedXzy = sqrtf(player->speed.x * player->speed.x
|
||||
+ player->speed.z * player->speed.z
|
||||
+ player->speed.y * player->speed.y);
|
||||
}
|
||||
|
||||
ImGui::Text("Global");
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Stage: {}\n", dComIfGp_getStartStageName())
|
||||
: "Stage: ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Layer: {0}\n", dComIfG_play_c::getLayerNo(0))
|
||||
: "Layer: ?\n"
|
||||
);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Link");
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
@@ -217,14 +238,38 @@ namespace dusk {
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Angle: {0}\n", player->shape_angle.y)
|
||||
: "Angle: ?\n"
|
||||
? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", player->speed.x, player->speed.y, player->speed.z)
|
||||
: "Velocity (XYZ): ?, ?, ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Speed: {: .4f}\n", player->speedF)
|
||||
: "Speed: ?\n"
|
||||
? fmt::format("Speed (SpeedF): {: .4f}\n", player->speedF)
|
||||
: "Speed (SpeedF): ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Speed (3D): {: .4f}\n", speedXzy)
|
||||
: "Speed (3D): ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Angle: {0}\n", player->shape_angle.y)
|
||||
: "Angle: ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(player))
|
||||
: "Room: ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Entry: {0}\n", dComIfGp_getStartStagePoint())
|
||||
: "Entry: ?\n"
|
||||
);
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -235,6 +280,18 @@ namespace dusk {
|
||||
: "Position: ?, ?, ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", horse->speed.x, horse->speed.y, horse->speed.z)
|
||||
: "Velocity (XYZ): ?, ?, ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Speed (SpeedF): {: .4f}\n", horse->speedF)
|
||||
: "Speed (SpeedF): ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Angle: {0}\n", horse->shape_angle.y)
|
||||
@@ -243,8 +300,20 @@ namespace dusk {
|
||||
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Speed: {: .4f}\n", horse->speedF)
|
||||
: "Speed: ?\n"
|
||||
? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(horse))
|
||||
: "Room: ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Saved Stage: {}\n", dComIfGs_getHorseRestartStageName())
|
||||
: "Saved Stage: ?\n"
|
||||
);
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Saved Room: {0}\n", dComIfGs_getHorseRestartRoomNo())
|
||||
: "Saved Room: ?\n"
|
||||
);
|
||||
|
||||
ShowCornerContextMenu(m_playerInfoOverlayCorner, m_debugOverlayCorner);
|
||||
|
||||
@@ -33,10 +33,17 @@ static constexpr std::string_view StubFragments[] = {
|
||||
"but selective updates are not implemented"sv,
|
||||
};
|
||||
|
||||
#if _WIN32
|
||||
#define DUSK_FILENO _fileno
|
||||
#else
|
||||
#define DUSK_FILENO fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// On macOS, std::mutex becomes poisoned when its dtor is run.
|
||||
// We use this to check if the LogState is destroyed before attempting to acquire it.
|
||||
std::atomic g_logStateAlive(true);
|
||||
std::atomic<int> g_logFd(-1);
|
||||
|
||||
struct LogState {
|
||||
std::mutex mutex;
|
||||
@@ -54,6 +61,7 @@ struct LogState {
|
||||
}
|
||||
std::lock_guard lock(mutex);
|
||||
if (file != nullptr) {
|
||||
g_logFd.store(-1, std::memory_order_release);
|
||||
std::fflush(file);
|
||||
std::fclose(file);
|
||||
file = nullptr;
|
||||
@@ -232,6 +240,7 @@ void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraL
|
||||
}
|
||||
|
||||
g_logState.filePath = logPath.u8string();
|
||||
g_logFd.store(DUSK_FILENO(g_logState.file), std::memory_order_release);
|
||||
aurora::g_config.logCallback = &aurora_log_callback;
|
||||
aurora::g_config.logLevel = logLevel;
|
||||
WriteLogLine(g_logState.file, "INFO", "dusk", "File logging initialized", 24);
|
||||
@@ -252,3 +261,7 @@ const char* dusk::GetLogFilePath() {
|
||||
return reinterpret_cast<const char*>(
|
||||
g_logState.filePath.empty() ? nullptr : g_logState.filePath.c_str());
|
||||
}
|
||||
|
||||
int dusk::GetLogFileDescriptor() {
|
||||
return g_logFd.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,8 @@ UserSettings g_userSettings = {
|
||||
.invertCameraYAxis {"game.invertCameraYAxis", false},
|
||||
.invertFirstPersonXAxis {"game.invertFirstPersonXAxis", false},
|
||||
.invertFirstPersonYAxis {"game.invertFirstPersonYAxis", false},
|
||||
.invertAirSwimX {"game.invertAirSwimX", false},
|
||||
.invertAirSwimY {"game.invertAirSwimY", false},
|
||||
.freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f},
|
||||
.debugFlyCam {"game.debugFlyCam", false},
|
||||
.debugFlyCamLockEvents {"game.debugFlyCamLockEvents", true},
|
||||
@@ -219,6 +221,8 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.invertCameraYAxis);
|
||||
Register(g_userSettings.game.invertFirstPersonXAxis);
|
||||
Register(g_userSettings.game.invertFirstPersonYAxis);
|
||||
Register(g_userSettings.game.invertAirSwimX);
|
||||
Register(g_userSettings.game.invertAirSwimY);
|
||||
Register(g_userSettings.game.freeCameraSensitivity);
|
||||
Register(g_userSettings.game.minimalHUD);
|
||||
Register(g_userSettings.game.pauseOnFocusLost);
|
||||
|
||||
@@ -275,6 +275,7 @@ ControllerConfigWindow::ControllerConfigWindow(bool prelaunch) {
|
||||
void ControllerConfigWindow::hide(bool close) {
|
||||
stop_rumble_test();
|
||||
cancel_pending_binding();
|
||||
config::Save();
|
||||
Window::hide(close);
|
||||
}
|
||||
|
||||
|
||||
@@ -957,6 +957,10 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
|
||||
"Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings).");
|
||||
addOption("Invert First Person Y Axis", getSettings().game.invertFirstPersonYAxis,
|
||||
"Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings).");
|
||||
addOption("Invert Air/Swim X Axis", getSettings().game.invertAirSwimX,
|
||||
"Invert horizontal movement while flying or swimming.");
|
||||
addOption("Invert Air/Swim Y Axis", getSettings().game.invertAirSwimY,
|
||||
"Invert vertical movement while flying or swimming.");
|
||||
|
||||
leftPane.add_section("Gyro");
|
||||
leftPane.register_control(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "input.hpp"
|
||||
#include "prelaunch.hpp"
|
||||
#include "window.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
@@ -60,6 +61,7 @@ bool initialize() noexcept {
|
||||
}
|
||||
|
||||
void shutdown() noexcept {
|
||||
config::Save();
|
||||
sDocumentStack.clear();
|
||||
sPassiveDocuments.clear();
|
||||
sConnectedGamepads.clear();
|
||||
|
||||
@@ -754,7 +754,7 @@ void myGXVerifyCallback(GXWarningLevel param_1, u32 param_2, const char* param_3
|
||||
#endif
|
||||
|
||||
int mDoMch_Create() {
|
||||
#if !TARGET_PC // We want crash logs.
|
||||
#ifdef NDEBUG
|
||||
if (mDoMain::developmentMode == 0 || !(OSGetConsoleType() & 0x10000000)) {
|
||||
OSReportDisable();
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <thread>
|
||||
#include "SSystem/SComponent/c_API.h"
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/crash_handler.h"
|
||||
#include "dusk/crash_reporting.h"
|
||||
#include "dusk/data.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
@@ -565,6 +566,7 @@ int game_main(int argc, char* argv[]) {
|
||||
}
|
||||
ApplyCVarOverrides(parsed_arg_options["cvar"]);
|
||||
dusk::crash_reporting::initialize();
|
||||
dusk::crash_handler::install();
|
||||
// TODO: How to handle this?
|
||||
// PADSetDefaultMapping(&defaultPadMapping, PAD_TYPE_STANDARD);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user