Merge branch 'main' of https://github.com/TakaRikka/dusk into speedrun-timer

This commit is contained in:
TakaRikka
2026-04-14 04:10:28 -07:00
104 changed files with 8617 additions and 246 deletions
+30 -4
View File
@@ -115,7 +115,14 @@ static Z2WolfHowlLine sNewSong3[9] = {
{HOWL_LINE_MID, 45},
};
static Z2WolfHowlData sGuideData[9] = {
#if TARGET_PC
static Z2WolfHowlLine sHowlTimeSong[6] = {
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40},
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40},
};
#endif
static Z2WolfHowlData sGuideData[9 IF_DUSK(+1)] = {
{ARRAY_SIZE(sHowlTobikusa), sHowlTobikusa},
{ARRAY_SIZE(sHowlUmakusa), sHowlUmakusa},
{ARRAY_SIZE(sHowlZeldaSong), sHowlZeldaSong},
@@ -125,6 +132,9 @@ static Z2WolfHowlData sGuideData[9] = {
{ARRAY_SIZE(sNewSong1), sNewSong1},
{ARRAY_SIZE(sNewSong2), sNewSong2},
{ARRAY_SIZE(sNewSong3), sNewSong3},
#if TARGET_PC
{ARRAY_SIZE(sHowlTimeSong), sHowlTimeSong},
#endif
};
Z2WolfHowlMgr::Z2WolfHowlMgr() : JASGlobalInstance(true) {
@@ -356,6 +366,13 @@ void Z2WolfHowlMgr::setCorrectData(s8 curveID, Z2WolfHowlData* data) {
cPitchCenter = 0.94387f;
cPitchDown = 0.74915f;
break;
#if TARGET_PC
case Z2WOLFHOWL_TIMESONG:
cPitchUp = 1.259906f;
cPitchCenter = 0.94387f;
cPitchDown = 0.840885f;
break;
#endif
default:
cPitchUp = 1.1892f;
cPitchCenter = 1.0f;
@@ -400,7 +417,7 @@ u8 Z2WolfHowlMgr::getCorrectLineNum() {
return 0;
}
static JAISoundID sCorrectPhrase[9] = {
static JAISoundID sCorrectPhrase[9 IF_DUSK(+1)] = {
Z2BGM_HOWL_TOBIKUSA,
Z2BGM_HOWL_UMAKUSA,
Z2BGM_HOWL_ZELDASONG,
@@ -410,9 +427,12 @@ static JAISoundID sCorrectPhrase[9] = {
Z2BGM_NEW_01_HOWL,
Z2BGM_NEW_02_HOWL,
Z2BGM_NEW_03_HOWL,
#if TARGET_PC
0xFFFFFFFF,
#endif
};
static JAISoundID sWindStoneSound[9] = {
static JAISoundID sWindStoneSound[9 IF_DUSK(+1)] = {
0xFFFFFFFF,
0xFFFFFFFF,
Z2BGM_STONE_ZELDASONG,
@@ -422,9 +442,12 @@ static JAISoundID sWindStoneSound[9] = {
Z2BGM_NEW_01_STONE,
Z2BGM_NEW_02_STONE,
Z2BGM_NEW_03_STONE,
#if TARGET_PC
0xFFFFFFFF,
#endif
};
static JAISoundID sCorrectDuo[9] = {
static JAISoundID sCorrectDuo[9 IF_DUSK(+1)] = {
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
@@ -434,6 +457,9 @@ static JAISoundID sCorrectDuo[9] = {
Z2BGM_NEW_01_DUO,
Z2BGM_NEW_02_DUO,
Z2BGM_NEW_03_DUO,
#if TARGET_PC
0xFFFFFFFF,
#endif
};
s8 Z2WolfHowlMgr::checkLine() {
+63 -7
View File
@@ -4394,13 +4394,34 @@ void daAlink_c::setSelectEquipItem(BOOL param_0) {
if (mClothesChangeWaitTimer == 0) {
if (checkZoraWearAbility()) {
if (checkZoraWearMaskDraw()) {
field_0x06f0->show();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->show();
}
if (!checkEquipHeavyBoots()) {
field_0x06e4->show();
#if TARGET_PC
if (field_0x06e4 != NULL)
#endif
{
field_0x06e4->show();
}
}
} else {
field_0x06f0->hide();
field_0x06e4->hide();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->hide();
}
#if TARGET_PC
if (field_0x06e4 != NULL)
#endif
{
field_0x06e4->hide();
}
}
}
@@ -19295,11 +19316,20 @@ void daAlink_c::setWaterDropColor(const J3DGXColorS10* i_color) {
if (!checkNoResetFlg2(FLG2_UNK_80000)) {
if (checkZoraWearAbility()) {
#if TARGET_PC
if (field_0x064C->getMaterialNum() >= 14)
#endif
{
field_0x064C->getMaterialNodePointer(13)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(0)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(1)->setTevColor(1, i_color);
mpLinkHatModel->getModelData()->getMaterialNodePointer(1)->setTevColor(1, i_color);
}
} else if (checkMagicArmorWearAbility()) {
#if TARGET_PC
if (field_0x064C->getMaterialNum() >= 12)
#endif
{
field_0x064C->getMaterialNodePointer(11)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(10)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(9)->setTevColor(1, i_color);
@@ -19307,11 +19337,21 @@ void daAlink_c::setWaterDropColor(const J3DGXColorS10* i_color) {
field_0x064C->getMaterialNodePointer(6)->setTevColor(1, i_color);
mpLinkHatModel->getModelData()->getMaterialNodePointer(2)->setTevColor(1, i_color);
mpLinkHatModel->getModelData()->getMaterialNodePointer(1)->setTevColor(1, i_color);
}
} else if (checkCasualWearFlg()) {
#if TARGET_PC
if (field_0x064C->getMaterialNum() >= 8)
#endif
{
field_0x064C->getMaterialNodePointer(7)->setTevColor(1, i_color);
mpLinkHatModel->getModelData()->getMaterialNodePointer(0)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(5)->setTevColor(1, var_r31);
}
} else {
#if TARGET_PC
if (field_0x064C->getMaterialNum() >= 18)
#endif
{
field_0x064C->getMaterialNodePointer(17)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(9)->setTevColor(1, i_color);
field_0x064C->getMaterialNodePointer(0)->setTevColor(1, i_color);
@@ -19321,6 +19361,7 @@ void daAlink_c::setWaterDropColor(const J3DGXColorS10* i_color) {
field_0x064C->getMaterialNodePointer(16)->setTevColor(1, var_r31);
field_0x064C->getMaterialNodePointer(15)->setTevColor(1, var_r31);
field_0x064C->getMaterialNodePointer(14)->setTevColor(1, var_r31);
}
}
}
}
@@ -19506,7 +19547,12 @@ int daAlink_c::draw() {
field_0x06e8->hide();
}
field_0x06f0->hide();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->hide();
}
#if PLATFORM_SHIELD
if (mProcID == PROC_HOOKSHOT_WALL_SHOOT || mProcID == PROC_HOOKSHOT_SUBJECT) {
@@ -19536,7 +19582,12 @@ int daAlink_c::draw() {
}
if (!checkZoraWearMaskDraw() && checkZoraWearAbility()) {
field_0x06f0->hide();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->hide();
}
}
}
@@ -19545,7 +19596,12 @@ int daAlink_c::draw() {
}
if (checkZoraWearMaskDraw() || !checkZoraWearAbility()) {
field_0x06f0->show();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->show();
}
}
}
+59
View File
@@ -4,6 +4,65 @@
#include "d/d_meter2_draw.h"
#include "d/d_meter2_info.h"
void daAlink_c::handleWolfHowl() {
if (checkWolf()) {
if (!dusk::getSettings().game.sunsSong) {
return;
}
// Check to see if Link has the ability to transform.
if (!dComIfGs_isEventBit(dSv_event_flag_c::M_077)) {
return;
}
// Ensure there is a proper pointer to the mMeterClass and mpMeterDraw structs in
// g_meter2_info.
const auto meterClassPtr = g_meter2_info.getMeterClass();
if (!meterClassPtr) {
return;
}
const auto meterDrawPtr = meterClassPtr->getMeterDrawPtr();
if (!meterDrawPtr) {
return;
}
// Ensure that link is not in a cutscene.
if (checkEventRun()) {
Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
return;
}
mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags = 0;
// Ensure that the Z Button is not dimmed
if (meterDrawPtr->getButtonZAlpha() != 1.f) {
Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
return;
}
bool canTransform = false;
if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) {
if (!checkForestOldCentury()) {
if (checkMidnaRide()) {
if ((checkWolf() &&
(checkModeFlg(MODE_UNK_1000) || dComIfGp_checkPlayerStatus0(0, 0x10))) ||
(!checkWolf() &&
(checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) &&
(checkModeFlg(4) || dComIfGp_checkPlayerStatus0(0, 0x10))))
{
canTransform = true;
}
}
}
}
getWolfHowlMgrP()->setCorrectCurve(9);
procWolfHowlDemoInit();
}
}
void daAlink_c::handleQuickTransform() {
if (!dusk::getSettings().game.enableQuickTransform) {
return;
+19
View File
@@ -3954,7 +3954,13 @@ int daAlink_c::procWolfHowlDemoInit() {
mZ2WolfHowlMgr.setCorrectCurve(-1);
}
} else {
#if TARGET_PC
if (mZ2WolfHowlMgr.getCorrectCurveID() != 9) {
mZ2WolfHowlMgr.setCorrectCurve(-1);
}
#else
mZ2WolfHowlMgr.setCorrectCurve(-1);
#endif
}
mNormalSpeed = 0.0f;
@@ -4095,6 +4101,19 @@ int daAlink_c::procWolfHowlDemo() {
dStage_changeScene(mProcVar0.mHowlExitID, 0.0f, 0, fopAcM_GetRoomNo(this),
shape_angle.y, -1);
} else {
#if TARGET_PC
if (daAlink_getAlinkActorClass()->getCorrectCurveID() == 9) {
if (dComIfGp_roomControl_getTimePass()) {
g_env_light.time_change_rate = 1.0f;
dComIfGp_event_reset();
dCam_getBody()->EndEventCamera(fopAcM_GetID(this));
} else {
setWolfHowlNotHappen(isSkipEdge);
}
return 1;
}
#endif
fopAc_ac_c* actor_p = NULL;
if (gwolf_p == NULL) {
fopAcIt_Executor((fopAcIt_ExecutorFunc)daAlink_searchWolfHowl,
+41 -34
View File
@@ -10,35 +10,38 @@
static const char* l_arcName = "CatDoor";
u32 const daObjCatDoor_c::M_attr = 0x001E0578;
const daObjCatDoor_Attr_c daObjCatDoor_c::M_attr = {
30,
1400,
};
daObjCatDoor_c::~daObjCatDoor_c() {
if (mDoor1.bgw.ChkUsed()) {
dComIfG_Bgsp().Release(&mDoor1.bgw);
}
if (mDoor2.bgw.ChkUsed()) {
dComIfG_Bgsp().Release(&mDoor2.bgw);
}
dComIfG_resDelete(&mPhaseReq, l_arcName);
}
int daObjCatDoor_c::createHeap() {
J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes(l_arcName, 4);
JUT_ASSERT(174, modelData != NULL);
ASSERT(modelData != NULL);
mDoor1.pmodel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000084);
mDoor2.pmodel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000084);
if (mDoor1.pmodel == NULL || mDoor2.pmodel == NULL) {
return 0;
}
cBgD_t* cbgd = (cBgD_t*)dComIfG_getObjectRes(l_arcName, 7);
if (mDoor1.bgw.Set(cbgd, 1, &mDoor1.mtx)) {
if (mDoor1.bgw.Set((cBgD_t*)dComIfG_getObjectRes(l_arcName, 7), 1, &mDoor1.mtx)) {
return 0;
}
cBgD_t* cbgd2 = (cBgD_t*)dComIfG_getObjectRes(l_arcName, 7);
if (mDoor2.bgw.Set(cbgd2, 1, &mDoor2.mtx)) {
if (mDoor2.bgw.Set((cBgD_t*)dComIfG_getObjectRes(l_arcName, 7), 1, &mDoor2.mtx)) {
return 0;
}
@@ -48,9 +51,9 @@ int daObjCatDoor_c::createHeap() {
int daObjCatDoor_c::draw() {
g_env_light.settingTevStruct(0x10, &current.pos, &tevStr);
fopAc_ac_c* p1 = static_cast<fopAc_ac_c*>(this);
g_env_light.setLightTevColorType_MAJI(mDoor1.pmodel, &p1->tevStr);
g_env_light.setLightTevColorType_MAJI(mDoor2.pmodel, &p1->tevStr);
fopAc_ac_c* actor = (fopAc_ac_c*)this;
g_env_light.setLightTevColorType_MAJI(mDoor1.pmodel, &actor->tevStr);
g_env_light.setLightTevColorType_MAJI(mDoor2.pmodel, &actor->tevStr);
dComIfGd_setListBG();
mDoExt_modelUpdateDL(mDoor1.pmodel);
@@ -60,22 +63,22 @@ int daObjCatDoor_c::draw() {
}
int daObjCatDoor_c::execute() {
if (dComIfGs_isSwitch(fopAcM_GetParam(this) & 0xFF, fopAcM_GetHomeRoomNo(this)) ||
mRotSpeed == 0)
{
if (fopAcM_isSwitch(this, getSwitchNo()) || mRotSpeed == 0) {
return 1;
}
calcOpen();
setBaseMtx();
return 1;
}
const s16* daObjCatDoor_c::attr() const {
return (const s16*)&daObjCatDoor_c::M_attr;
const daObjCatDoor_Attr_c* daObjCatDoor_c::attr() const {
return &daObjCatDoor_c::M_attr;
}
static int createSolidHeap(fopAc_ac_c* i_this) {
return static_cast<daObjCatDoor_c*>(i_this)->createHeap();
static int createSolidHeap(fopAc_ac_c* actor) {
daObjCatDoor_c* i_this = (daObjCatDoor_c*)actor;
return i_this->createHeap();
}
int daObjCatDoor_c::create() {
@@ -84,7 +87,7 @@ int daObjCatDoor_c::create() {
int phase_state = dComIfG_resLoad(&mPhaseReq, l_arcName);
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, createSolidHeap, 0x2520)) {
phase_state = cPhs_ERROR_e;
return cPhs_ERROR_e;
} else {
create_init();
}
@@ -93,7 +96,8 @@ int daObjCatDoor_c::create() {
}
void daObjCatDoor_c::create_init() {
ASSERT(getSwitchNo() != 0xff);
JUT_ASSERT(295, getSwitchNo() != 0xff);
fopAcM_setCullSizeBox(this, -200.0f, 0.0f, -20.0f, 200.0f, 260.0f, 100.0f);
if (fopAcM_isSwitch(this, getSwitchNo())) {
mDoor1.angle = 0x8800;
@@ -106,37 +110,40 @@ void daObjCatDoor_c::create_init() {
mDoor2.bgw.SetRoomId(fopAcM_GetRoomNo(this));
dComIfG_Bgsp().Regist(&mDoor2.bgw, this);
}
initBaseMtx();
}
void daObjCatDoor_c::initBaseMtx() {
cullMtx = mMtx;
fopAcM_SetMtx(this, mMtx);
mDoMtx_stack_c::transS(current.pos);
mDoMtx_YrotM(mDoMtx_stack_c::get(), shape_angle.y);
mDoMtx_copy(mDoMtx_stack_c::get(), mMtx);
mDoMtx_stack_c::YrotM(shape_angle.y);
cMtx_copy(mDoMtx_stack_c::get(), mMtx);
setBaseMtx();
}
void daObjCatDoor_c::setBaseMtx() {
mDoMtx_stack_c::transS(current.pos);
mDoMtx_YrotM(mDoMtx_stack_c::get(), shape_angle.y);
mDoMtx_stack_c::YrotM(shape_angle.y);
for (int i = 0; i < 2; i++) {
daObjCatDoor_Door_c* door = i == 0 ? &mDoor1 : &mDoor2;
f32 xOff = i == 0 ? -97.0f : 97.0f;
s16 rot = i == 0 ? door->angle : s16(door->angle + 0x8000);
mDoMtx_stack_c::push();
mDoMtx_stack_c::transM(xOff, 0.0, 0.0);
mDoMtx_YrotM(mDoMtx_stack_c::get(), (s16)rot);
mDoMtx_copy(mDoMtx_stack_c::get(), door->pmodel->mBaseTransformMtx);
mDoMtx_copy(mDoMtx_stack_c::get(), door->mtx);
mDoMtx_stack_c::YrotM((s16)rot);
door->pmodel->setBaseTRMtx(mDoMtx_stack_c::get());
cMtx_copy(mDoMtx_stack_c::get(), door->mtx);
door->bgw.Move();
mDoMtx_stack_c::pop();
}
}
void daObjCatDoor_c::calcOpen() {
s16 prev = mRotSpeed;
int res = cLib_chaseS(&mRotSpeed, 0, *attr());
s16 prev = (s16)mRotSpeed;
int res = cLib_chaseS(&mRotSpeed, 0, attr()->speed);
for (int i = 0; i < 2; i++) {
daObjCatDoor_Door_c* door = i == 0 ? &mDoor1 : &mDoor2;
if (i == 0) {
@@ -159,20 +166,20 @@ static int daObjCatDoor_Execute(daObjCatDoor_c* i_this) {
return static_cast<daObjCatDoor_c*>(i_this)->execute();
}
static BOOL daObjCatDoor_IsDelete(daObjCatDoor_c* i_this) {
return TRUE;
static int daObjCatDoor_IsDelete(daObjCatDoor_c* i_this) {
return 1;
}
static int daObjCatDoor_Delete(daObjCatDoor_c* i_this) {
fopAcM_GetID(i_this);
fpc_ProcID id = fopAcM_GetID(i_this);
i_this->~daObjCatDoor_c();
return 1;
}
static int daObjCatDoor_Create(fopAc_ac_c* i_this) {
fopAcM_GetID(i_this);
daObjCatDoor_c* a_this = static_cast<daObjCatDoor_c*>(i_this);
return a_this->create();
static int daObjCatDoor_Create(fopAc_ac_c* actor) {
daObjCatDoor_c* i_this = (daObjCatDoor_c*)actor;
fpc_ProcID id = fopAcM_GetID(actor);
return i_this->create();
}
static actor_method_class l_daObjCatDoor_Method = {
+1 -1
View File
@@ -539,7 +539,7 @@ const char* daObj_GrA_c::getResName() {
u8 daObj_GrA_c::getMode() {
u32 uVar1 = fopAcM_GetParam(this) >> 28 & 3;
strcpy(&field_0x744, "Obj_grA");
strcpy(field_0x744, "Obj_grA");
switch (uVar1) {
case 1:
+5
View File
@@ -277,6 +277,11 @@ void daOnsTaru_c::mode_init_drop() {
cLib_offBit<u32>(attention_info.flags, fopAc_AttnFlag_CARRY_e);
gravity = -7.0f;
mMode = MODE_DROP_e;
#if TARGET_PC
// TODO: figure out why this is needed and where exactly the UB happens
mCcStts.ClrCcMove();
#endif
}
void daOnsTaru_c::mode_proc_drop() {
+37 -1
View File
@@ -20,7 +20,6 @@
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_graphic.h"
#include "m_Do/m_Do_lib.h"
#include "dusk/frame_interpolation.h"
#include <cmath>
#include <cstring>
@@ -29,6 +28,11 @@
#include "d/d_debug_camera.h"
#endif
#if TARGET_PC
#include "dusk/frame_interpolation.h"
#include "dusk/logging.h"
#endif
namespace {
static f32 limitf(f32 value, f32 min, f32 max) {
@@ -2048,6 +2052,18 @@ s32 dCamera_c::nextType(s32 i_curType) {
bool dCamera_c::onTypeChange(s32 i_curType, s32 i_nextType) {
daAlink_c* unusedPlayer = daAlink_getAlinkActorClass();
#if TARGET_PC
const s32 event_type_id = specialType[CAM_TYPE_EVENT];
DuskLog.debug(
"frameInterp: onTypeChange {} -> {} (event_type_id={}, leaving_event={}, entering_event={})",
static_cast<int>(i_curType),
static_cast<int>(i_nextType),
static_cast<int>(event_type_id),
i_curType == event_type_id,
i_nextType == event_type_id
);
#endif
if (i_curType == specialType[CAM_TYPE_EVENT]) {
if (mCamSetup.CheckFlag(0x4000)) {
mGear = 0;
@@ -10161,6 +10177,26 @@ bool dCamera_c::eventCamera(s32 param_0) {
ActionNames[var_r29]);
#endif
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation) {
switch (var_r29) {
case 3:
case 4:
case 5:
case 12:
dusk::frame_interp::request_presentation_sync();
break;
default:
DuskLog.debug(
"frameInterp: presentation sync not requested for ZEV event [{}] (staff idx {})",
static_cast<const char*>(ActionNames[var_r29]),
static_cast<int>(mEventData.mStaffIdx)
);
break;
}
}
#endif
if (getEvFloatData(&sp28, "KeepDist") != 0 && mViewCache.mDirection.R() < sp28)
{
mViewCache.mDirection.R(sp28);
+24 -32
View File
@@ -1440,16 +1440,17 @@ void dDlst_shadowSimple_c::set(cXyz* param_0, f32 param_1, f32 param_2, cXyz* pa
void dDlst_shadowControl_c::init() {
#if TARGET_PC
u16 resMult = dusk::getSettings().game.shadowResolutionMultiplier;
mTexResScale = dusk::getSettings().game.shadowResolutionMultiplier;
// Increase shadow map resolution
u16 l_realImageSize[2] =
{
static_cast<u16>(192 * resMult),
static_cast<u16>(64 * resMult)
static_cast<u16>(192 * mTexResScale),
static_cast<u16>(64 * mTexResScale)
};
#else
static u16 l_realImageSize[2] = {192, 64};
#endif
for (int i = 0; i < 2; i++) {
u16 size = l_realImageSize[i];
@@ -1458,10 +1459,13 @@ void dDlst_shadowControl_c::init() {
#else
u32 buffer_size = GXGetTexBufferSize(size, size, 5, GX_DISABLE, 0);
#endif
field_0x15ef0[i] = JKR_NEW_ARRAY_ARGS(u8, buffer_size, 0x20);
GXInitTexObj(&field_0x15eb0[i], field_0x15ef0[i], size, size, GX_TF_RGB5A3, GX_CLAMP,
JKR_DELETE_ARRAY(mShadowTexData[i]);
mShadowTexData[i] = JKR_NEW_ARRAY_ARGS(u8, buffer_size, 0x20);
mShadowTexObj[i].reset();
GXInitTexObj(&mShadowTexObj[i], mShadowTexData[i], size, size, GX_TF_RGB5A3, GX_CLAMP,
GX_CLAMP, GX_DISABLE);
GXInitTexObjLOD(&field_0x15eb0[i], GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE,
GXInitTexObjLOD(&mShadowTexObj[i], GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE,
GX_FALSE, GX_ANISO_1);
}
}
@@ -1479,25 +1483,13 @@ void dDlst_shadowControl_c::reset() {
mRealNum = 0;
field_0x4 = NULL;
#ifdef TARGET_PC
field_0x15eb0[0].reset();
field_0x15eb0[1].reset();
#if TARGET_PC
if (mTexResScale != dusk::getSettings().game.shadowResolutionMultiplier)
init();
#endif
}
#if TARGET_PC
int lastShadowValue = 0;
#endif
void dDlst_shadowControl_c::imageDraw(Mtx param_0) {
#if TARGET_PC
if (lastShadowValue != dusk::getSettings().game.shadowResolutionMultiplier) {
reset();
init();
lastShadowValue = dusk::getSettings().game.shadowResolutionMultiplier;
}
#endif
static u8 l_matDL[] ATTRIBUTE_ALIGN(32) = {
0x10, 0x00, 0x00, 0x10, 0x0E, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10,
0x00, 0x00, 0x04, 0x00, 0x61, 0x28, 0x38, 0x00, 0x00, 0x61, 0xC0, 0x08, 0xFF, 0xF2,
@@ -1530,7 +1522,7 @@ void dDlst_shadowControl_c::imageDraw(Mtx param_0) {
j3dSys.setDrawModeOpaTexEdge();
J3DShape::resetVcdVatCache();
dDlst_shadowReal_c* shadowReal = field_0x4;
int r29 = 0;
int chan = 0;
int tex = 0;
u16 r27;
u16 r26;
@@ -1539,8 +1531,8 @@ void dDlst_shadowControl_c::imageDraw(Mtx param_0) {
#endif
for (; shadowReal; shadowReal = shadowReal->getZsortNext()) {
if (shadowReal->isUse()) {
if (r29 == 0) {
r27 = GXGetTexObjWidth(field_0x15eb0 + tex);
if (chan == 0) {
r27 = GXGetTexObjWidth(&mShadowTexObj[tex]);
r26 = r27 * 2;
#ifdef TARGET_PC
GXCreateFrameBuffer(r26, r26);
@@ -1549,27 +1541,27 @@ void dDlst_shadowControl_c::imageDraw(Mtx param_0) {
GXSetViewport(0.0f, 0.0f, r26, r26, 0.0f, 1.0f);
GXSetScissor(0, 0, r26, r26);
}
GXSetTevColor(GX_TEVREG0, l_imageDrawColor[r29]);
if (r29 == 3) {
GXSetTevColor(GX_TEVREG0, l_imageDrawColor[chan]);
if (chan == 3) {
GXSetColorUpdate(GX_DISABLE);
GXSetAlphaUpdate(GX_ENABLE);
}
shadowReal->imageDraw(param_0);
r29 = (r29 + 1) % 4;
if (r29 == 0) {
chan = (chan + 1) % 4;
if (chan == 0) {
GXSetTexCopySrc(0, 0, r26, r26);
GXSetTexCopyDst(r27, r27, GX_TF_RGB5A3, GX_TRUE);
GXSetColorUpdate(GX_ENABLE);
GXCopyTex(field_0x15ef0[tex++], GX_TRUE);
GXCopyTex(mShadowTexData[tex++], GX_TRUE);
GXPixModeSync();
GXSetAlphaUpdate(GX_DISABLE);
}
}
}
if (r29) {
if (chan) {
GXSetTexCopySrc(0, 0, r26, r26);
GXSetTexCopyDst(r27, r27, GX_TF_RGB5A3, GX_TRUE);
GXCopyTex(field_0x15ef0[tex], GX_TRUE);
GXCopyTex(mShadowTexData[tex], GX_TRUE);
GXPixModeSync();
GXSetAlphaUpdate(GX_DISABLE);
}
@@ -1621,7 +1613,7 @@ void dDlst_shadowControl_c::draw(Mtx param_0) {
for (int i2 = 0, i3 = 0; real != NULL; real = real->getZsortNext()) {
if (real->isUse()) {
if (i2 == 0) {
TGXTexObj* obj = &field_0x15eb0[i3];
TGXTexObj* obj = &mShadowTexObj[i3];
i3++;
GXLoadTexObj(obj, GX_TEXMAP0);
+13
View File
@@ -1540,8 +1540,21 @@ void dScnKy_env_light_c::setDaytime() {
}
if (dComIfGp_roomControl_getTimePass() && !field_0x130a && temp_r29) {
#if TARGET_PC
f32 prev = daytime;
#endif
daytime += time_change_rate;
#if TARGET_PC
if (time_change_rate == 1.0f &&
(std::fmod(daytime - 90.0f + 360.0f, 360.0f) < std::fmod(prev - 90.0f + 360.0f, 360.0f) ||
std::fmod(daytime - 285.0f + 360.0f, 360.0f) < std::fmod(prev - 285.0f + 360.0f, 360.0f)))
{
g_env_light.time_change_rate = 0.012f;
}
#endif
// Stage is Fishing Pond or Hena's Hut
if (!strcmp(dComIfGp_getStartStageName(), "F_SP127") ||
!strcmp(dComIfGp_getStartStageName(), "R_SP127"))
+15 -2
View File
@@ -48,13 +48,20 @@ public:
GXPixModeSync();
#if TARGET_PC
// init mTexObj at capture time so the gpu ref survives window resizes
GXInitTexObj(&mTexObj, mDoGph_gInf_c::getFrameBufferTex(), mDoGph_gInf_c::getWidth(), mDoGph_gInf_c::getHeight(),
mCaptureWidth = mDoGph_gInf_c::getWidth();
mCaptureHeight = mDoGph_gInf_c::getHeight();
GXInitTexObj(&mTexObj, mDoGph_gInf_c::getFrameBufferTex(), mCaptureWidth, mCaptureHeight,
(GXTexFmt)mDoGph_gInf_c::getFrameBufferTimg()->format, GX_CLAMP, GX_CLAMP, GX_FALSE);
GXInitTexObjLOD(&mTexObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
#endif
} else {
#if TARGET_PC
// reuse the persistent mTexObj
// If the window was resized since capture, force a re-capture at the new size
if (mCaptureWidth != (u16)mDoGph_gInf_c::getWidth() ||
mCaptureHeight != (u16)mDoGph_gInf_c::getHeight()) {
mFlag = 1;
return;
}
GXLoadTexObj(&mTexObj, GX_TEXMAP0);
#else
TGXTexObj tex;
@@ -112,6 +119,10 @@ public:
mFlag = 0;
mAlpha = 255;
mTopFlag = 0;
#if TARGET_PC
mCaptureWidth = 0;
mCaptureHeight = 0;
#endif
}
void setCaptureFlag() { mFlag = 1; }
@@ -126,6 +137,8 @@ private:
/* 0x5 */ u8 mAlpha;
/* 0x6 */ u8 mTopFlag;
#if TARGET_PC
u16 mCaptureWidth;
u16 mCaptureHeight;
TGXTexObj mTexObj;
#endif
};
+40 -1
View File
@@ -5,6 +5,7 @@
#include "aurora/lib/logging.hpp"
#include "dusk/io.hpp"
#include "dusk/settings.h"
#include <limits>
#include <string>
@@ -37,9 +38,24 @@ const ConfigImplBase* ConfigVarBase::getImpl() const noexcept {
return impl;
}
template <typename T>
static T sanitizeEnumValue(const ConfigVar<T>& cVar, T value) {
if constexpr (std::is_enum_v<T>) {
using Underlying = std::underlying_type_t<T>;
const Underlying raw = static_cast<Underlying>(value);
const Underlying min = static_cast<Underlying>(ConfigEnumRange<T>::min);
const Underlying max = static_cast<Underlying>(ConfigEnumRange<T>::max);
if (raw < min || raw > max) {
return cVar.getDefaultValue();
}
}
return value;
}
template<ConfigValue T>
void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
cVar.setValue(jsonValue.get<T>(), false);
cVar.setValue(sanitizeEnumValue(cVar, jsonValue.get<T>()), false);
}
template<ConfigValue T>
@@ -85,6 +101,28 @@ static void loadFromArgImpl(ConfigVar<std::string>& cVar, const std::string_view
cVar.setOverrideValue(std::string(stringValue));
}
template<ConfigValue T> requires std::is_enum_v<T>
static void loadFromArgImpl(ConfigVar<T>& cVar, const std::string_view stringValue) {
using Underlying = std::underlying_type_t<T>;
const std::string str(stringValue);
if constexpr (std::is_signed_v<Underlying>) {
const auto result = std::stoll(str);
if (result >= std::numeric_limits<Underlying>::min() && result <= std::numeric_limits<Underlying>::max()) {
cVar.setOverrideValue(sanitizeEnumValue(cVar, static_cast<T>(result)));
} else {
throw std::out_of_range("Value is too large");
}
} else {
const auto result = std::stoull(str);
if (result <= std::numeric_limits<Underlying>::max()) {
cVar.setOverrideValue(sanitizeEnumValue(cVar, static_cast<T>(result)));
} else {
throw std::out_of_range("Value is too large");
}
}
}
template<ConfigValue T>
void ConfigImpl<T>::loadFromArg(ConfigVar<T>& cVar, const std::string_view stringValue) {
loadFromArgImpl(cVar, stringValue);
@@ -115,6 +153,7 @@ namespace dusk::config {
template class ConfigImpl<f32>;
template class ConfigImpl<f64>;
template class ConfigImpl<std::string>;
template class ConfigImpl<dusk::BloomMode>;
}
void dusk::config::Register(ConfigVarBase& configVar) {
+187
View File
@@ -0,0 +1,187 @@
#include "dusk/crash_reporting.h"
#include "dusk/app_info.hpp"
#include "dusk/dusk.h"
#include "dusk/logging.h"
#include "dusk/settings.h"
#include "version.h"
#include <cstdlib>
#include <filesystem>
#include <string>
#include <string_view>
#include <system_error>
#include "SDL3/SDL_filesystem.h"
#if DUSK_ENABLE_SENTRY_NATIVE
#include <sentry.h>
#endif
namespace dusk {
namespace {
#if DUSK_ENABLE_SENTRY_NATIVE
bool g_sentryInitialized = false;
bool IsTruthy(std::string_view value) {
return value == "1" || value == "true" || value == "TRUE" || value == "yes"
|| value == "YES" || value == "on" || value == "ON";
}
std::string GetEnvOrEmpty(const char* name) {
if (const char* value = std::getenv(name)) {
return value;
}
return {};
}
bool GetEffectiveEnabled() {
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_ENABLED");
if (!env.empty()) {
return IsTruthy(env);
}
return getSettings().backend.enableCrashReporting;
}
std::string GetEffectiveDsn() {
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DSN");
if (!env.empty()) {
return env;
}
return DUSK_SENTRY_DSN;
}
bool GetEffectiveDebug() {
const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DEBUG");
if (!env.empty()) {
return IsTruthy(env);
}
return false;
}
std::string GetReleaseName() {
return std::string(AppName) + "@" DUSK_WC_DESCRIBE;
}
std::filesystem::path GetSentryDatabasePath() {
return std::filesystem::path(configPath) / "sentry";
}
std::filesystem::path GetLogAttachmentPath() {
if (const char* logPath = GetLogFilePath()) {
return logPath;
}
return {};
}
std::filesystem::path GetCrashpadHandlerPath() {
const char* basePath = SDL_GetBasePath();
if (!basePath) {
return {};
}
const std::filesystem::path handlerDir(basePath);
#if _WIN32
return handlerDir / "crashpad_handler.exe";
#else
return handlerDir / "crashpad_handler";
#endif
}
void ConfigurePathOptions(sentry_options_t* options) {
const auto databasePath = GetSentryDatabasePath();
std::error_code ec;
std::filesystem::create_directories(databasePath, ec);
if (ec) {
DuskLog.warn("Unable to create Sentry database path '{}': {}",
databasePath.string(), ec.message());
}
#if _WIN32
const std::wstring databasePathWide = databasePath.wstring();
sentry_options_set_database_pathw(options, databasePathWide.c_str());
const auto handlerPath = GetCrashpadHandlerPath();
if (!handlerPath.empty()) {
const std::wstring handlerPathWide = handlerPath.wstring();
sentry_options_set_handler_pathw(options, handlerPathWide.c_str());
}
#else
const std::string databasePathUtf8 = databasePath.string();
sentry_options_set_database_path(options, databasePathUtf8.c_str());
const auto handlerPath = GetCrashpadHandlerPath();
if (!handlerPath.empty()) {
const std::string handlerPathUtf8 = handlerPath.string();
sentry_options_set_handler_path(options, handlerPathUtf8.c_str());
}
#endif
const auto logPath = GetLogAttachmentPath();
if (!logPath.empty()) {
#if _WIN32
sentry_options_add_attachmentw(options, logPath.wstring().c_str());
#else
sentry_options_add_attachment(options, logPath.string().c_str());
#endif
}
}
#endif
} // namespace
void InitializeCrashReporting() {
#if DUSK_ENABLE_SENTRY_NATIVE
if (g_sentryInitialized) {
return;
}
if (!GetEffectiveEnabled()) {
return;
}
const std::string dsn = GetEffectiveDsn();
if (dsn.empty()) {
DuskLog.warn("Crash reporting is enabled but no Sentry DSN is configured");
return;
}
const std::string release = GetReleaseName();
sentry_options_t* options = sentry_options_new();
sentry_options_set_dsn(options, dsn.c_str());
sentry_options_set_release(options, release.c_str());
sentry_options_set_environment(options, DUSK_SENTRY_ENVIRONMENT);
sentry_options_set_debug(options, GetEffectiveDebug() ? 1 : 0);
sentry_options_set_cache_keep(options, 1);
sentry_options_set_max_breadcrumbs(options, 100);
ConfigurePathOptions(options);
if (sentry_init(options) != 0) {
DuskLog.warn("Failed to initialize Sentry crash reporting");
return;
}
sentry_set_tag("git_branch", DUSK_WC_BRANCH);
sentry_set_tag("build_type", DUSK_BUILD_TYPE);
sentry_set_tag("tp_version", DUSK_TP_VERSION);
g_sentryInitialized = true;
DuskLog.info("Initialized Sentry crash reporting");
#endif
}
void ShutdownCrashReporting() {
#if DUSK_ENABLE_SENTRY_NATIVE
if (!g_sentryInitialized) {
return;
}
sentry_close();
g_sentryInitialized = false;
#endif
}
} // namespace dusk
+1 -1
View File
@@ -54,7 +54,7 @@ void *_memcpy(void* dest, void const* src, int n) {
}
void DCZeroRange(void* addr, uint32_t nBytes) {
#ifdef _MSC_VER
#if defined(_MSC_VER) || TARGET_ANDROID
memset(addr, 0, nBytes);
#else
bzero(addr, nBytes);
+35 -11
View File
@@ -63,6 +63,10 @@ bool s_initialized = false;
bool g_enabled = false;
bool g_recording = false;
bool g_interpolating = false;
bool g_sync_presentation = false;
uint32_t g_presentation_counter = 0;
uint32_t g_presentation_sync_end = 0;
float g_step = 0.0f;
uint32_t g_pending_presentation_ui_ticks = 0;
uint32_t g_current_presentation_ui_ticks = 0;
@@ -235,7 +239,7 @@ void interpolate_branch(const Path& old_path, const Path& new_path, float step)
}
const Mtx* resolve_replacement(const Mtx* source, Mtx* scratch) {
if (!g_interpolating || source == nullptr) {
if (!g_interpolating || source == nullptr || dusk::frame_interp::presentation_sync_active()) {
return source;
}
@@ -268,6 +272,7 @@ void begin_record() {
ensure_initialized();
if (!g_enabled) {
g_interpolating = false;
g_sync_presentation = false;
g_previous_recording = {};
g_current_recording = {};
g_current_path.clear();
@@ -275,6 +280,10 @@ void begin_record() {
return;
}
if (g_sync_presentation && g_presentation_counter > g_presentation_sync_end) {
g_sync_presentation = false;
}
g_previous_recording = std::move(g_current_recording);
g_current_recording = {};
g_current_path.clear();
@@ -292,21 +301,38 @@ void interpolate(float step) {
ensure_initialized();
clear_replacements();
g_step = std::clamp(step, 0.0f, 1.0f);
g_interpolating = g_enabled && !g_recording && has_recording_data(g_current_recording);
g_interpolating = g_enabled && !g_recording && !g_sync_presentation && has_recording_data(g_current_recording);
if (!g_interpolating) {
return;
}
const Path& old_root = has_recording_data(g_previous_recording) ? g_previous_recording.root : g_current_recording.root;
interpolate_branch(old_root, g_current_recording.root, g_step);
}
if (!has_recording_data(g_previous_recording)) {
interpolate_branch(g_current_recording.root, g_current_recording.root, g_step);
void notify_presentation_frame() {
ensure_initialized();
++g_presentation_counter;
}
void request_presentation_sync() {
ensure_initialized();
if (!g_enabled) {
return;
}
g_sync_presentation = true;
g_presentation_sync_end = g_presentation_counter + 1;
}
interpolate_branch(g_previous_recording.root, g_current_recording.root, g_step);
bool presentation_sync_active() {
if (!s_initialized || !g_enabled) {
return false;
}
return g_sync_presentation;
}
float get_interpolation_step() {
return g_step;
ensure_initialized();
return presentation_sync_active() ? 1.0f : g_step;
}
void notify_sim_tick_complete() {
@@ -371,7 +397,7 @@ void record_final_mtx_raw(const Mtx* dest, const Mtx src) {
}
bool lookup_replacement(const void* source, Mtx out) {
if (!s_initialized || !g_interpolating || source == nullptr) {
if (presentation_sync_active() || !g_interpolating || source == nullptr) {
return false;
}
@@ -385,7 +411,7 @@ bool lookup_replacement(const void* source, Mtx out) {
}
bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
if (!s_initialized || !g_interpolating || lhs == nullptr || rhs == nullptr) {
if (presentation_sync_active() || !g_interpolating || lhs == nullptr || rhs == nullptr) {
return false;
}
@@ -393,9 +419,7 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) {
Mtx rhs_scratch;
const Mtx* resolved_lhs = resolve_replacement(reinterpret_cast<const Mtx*>(lhs), &lhs_scratch);
const Mtx* resolved_rhs = resolve_replacement(reinterpret_cast<const Mtx*>(rhs), &rhs_scratch);
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) &&
resolved_rhs == reinterpret_cast<const Mtx*>(rhs))
{
if (resolved_lhs == reinterpret_cast<const Mtx*>(lhs) && resolved_rhs == reinterpret_cast<const Mtx*>(rhs)) {
return false;
}
+2 -3
View File
@@ -61,9 +61,8 @@ void read(float dt, bool context_active) {
float yaw_rate = apply_deadband(s_smooth_gy);
float pitch_rate = apply_deadband(s_smooth_gx);
const float sens = dusk::getSettings().game.gyroAimSensitivity;
s_pending_yaw_rad += yaw_rate * dt * sens;
s_pending_pitch_rad += pitch_rate * dt * sens;
s_pending_yaw_rad += yaw_rate * dt * dusk::getSettings().game.gyroAimSensitivityX;
s_pending_pitch_rad += pitch_rate * dt * dusk::getSettings().game.gyroAimSensitivityY;
}
void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) {
+1
View File
@@ -290,6 +290,7 @@ namespace dusk {
m_menuTools.ShowAudioDebug();
m_menuTools.ShowSaveEditor();
}
m_menuTools.ShowStateShare();
DuskDebugPad(); // temporary, remove later
// Only show cursor when menu or any windows are open
+11
View File
@@ -47,4 +47,15 @@ void DuskDebugPad() {
pad.mMainStickValue = 1.0f;
pad.mMainStickAngle = -0x4000;
}
if (ImGui::IsKeyDown(ImGuiKey_Q)) {
pad.mTriggerLeft = 1.0;
pad.mTrigLockL = 1;
pad.mHoldLockL = 1;
}
if (ImGui::IsKeyDown(ImGuiKey_E)) {
pad.mTriggerRight = 1.0;
pad.mTrigLockR = 1;
pad.mHoldLockR = 1;
}
}
+17 -9
View File
@@ -29,13 +29,22 @@ bool AssetExists(const std::string& path) {
SDL_PathInfo pathInfo{};
return SDL_GetPathInfo(path.c_str(), &pathInfo) && pathInfo.type == SDL_PATHTYPE_FILE;
}
ImTextureID AddTexture(const char* assetName) {
auto image = GetImage(GetAssetPath(assetName));
if (image.data == nullptr || image.width == 0 || image.height == 0) {
return 0;
}
return aurora_imgui_add_texture(image.width, image.height, image.data.get());
}
} // namespace
ImFont* ImGuiEngine::fontNormal;
ImFont* ImGuiEngine::fontLarge;
ImFont* ImGuiEngine::fontExtraLarge;
ImFont* ImGuiEngine::fontMono;
ImTextureID ImGuiEngine::duskIcon = 0;
ImTextureID ImGuiEngine::orgIcon = 0;
ImTextureID ImGuiEngine::duskLogo = 0;
inline ImFont* CreateFont(float size, const std::string& fontPath, std::string_view fontName) {
bool fontFileExists = !fontPath.empty() && AssetExists(fontPath);
@@ -60,7 +69,7 @@ inline ImFont* CreateFont(float size, const std::string& fontPath, std::string_v
void ImGuiEngine_Initialize(float scale) {
// Round font scale to nearest integer
scale = std::ceilf(scale);
scale = std::ceil(scale);
ImGui::GetCurrentContext();
ImGuiIO& io = ImGui::GetIO();
@@ -149,6 +158,7 @@ void ImGuiEngine_Initialize(float scale) {
Image GetImage(const std::string& path) {
if (!AssetExists(path)) {
DuskLog.warn("Image '{}' does not exist", path);
return {};
}
@@ -187,13 +197,11 @@ Image GetImage(const std::string& path) {
}
void ImGuiEngine_AddTextures() {
if (ImGuiEngine::duskIcon == 0) {
auto icon = GetImage(GetAssetPath("icon.png"));
if (icon.data == nullptr || icon.width == 0 || icon.height == 0) {
ImGuiEngine::duskIcon = 0;
return;
}
ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get());
if (ImGuiEngine::orgIcon == 0) {
ImGuiEngine::orgIcon = AddTexture("org-icon.png");
}
if (ImGuiEngine::duskLogo == 0) {
ImGuiEngine::duskLogo = AddTexture("logo.png");
}
}
} // namespace dusk
+3 -2
View File
@@ -11,7 +11,8 @@ public:
static ImFont* fontLarge;
static ImFont* fontExtraLarge;
static ImFont* fontMono;
static ImTextureID duskIcon;
static ImTextureID orgIcon;
static ImTextureID duskLogo;
};
void ImGuiEngine_Initialize(float scale);
@@ -23,5 +24,5 @@ struct Image {
uint32_t width;
uint32_t height;
};
Image GetImage(std::string_view path);
Image GetImage(const std::string& path);
} // namespace dusk
+4
View File
@@ -12,11 +12,13 @@ namespace dusk {
static void ApplyPresetClassic() {
auto& s = getSettings();
s.video.lockAspectRatio.setValue(true);
s.game.bloomMode.setValue(BloomMode::Classic);
VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH);
}
static void ApplyPresetHD() {
auto& s = getSettings();
s.game.bloomMode.setValue(BloomMode::Classic);
s.game.hideTvSettingsScreen.setValue(true);
s.game.skipWarningScreen.setValue(true);
s.game.noReturnRupees.setValue(true);
@@ -37,6 +39,8 @@ static void ApplyPresetDusk() {
s.game.instantSaves.setValue(true);
s.game.midnasLamentNonStop.setValue(true);
s.game.enableFrameInterpolation.setValue(true);
s.game.sunsSong.setValue(true);
s.game.bloomMode.setValue(BloomMode::Dusk);
}
// =========================================================================
+11 -1
View File
@@ -13,6 +13,15 @@ namespace dusk {
if (ImGui::BeginMenu("Enhancements")) {
if (ImGui::BeginMenu("Quality of Life")) {
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows you to quickly transform between forms\n"
"without having to talk to Midna.");
}
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
}
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
if (ImGui::IsItemHovered()) {
@@ -121,7 +130,8 @@ namespace dusk {
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.");
}
config::ImGuiSliderFloat("Gyro Sensitivity", getSettings().game.gyroAimSensitivity, 0.25f, 4.0f, "%.2f");
config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroAimSensitivityY, 0.25f, 4.0f, "%.2f");
config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroAimSensitivityX, 0.25f, 4.0f, "%.2f");
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
+146 -2
View File
@@ -16,6 +16,7 @@
#include "m_Do/m_Do_graphic.h"
#include <aurora/gfx.h>
#include <SDL3/SDL_gamepad.h>
#include "dusk/main.h"
@@ -62,7 +63,30 @@ namespace dusk {
config::Save();
}
config::ImGuiCheckbox("Native Bloom", getSettings().game.enableBloom);
constexpr const char* bloomModeNames[] = {"Off", "Classic", "Dusk"};
int bloomMode = static_cast<int>(getSettings().game.bloomMode.getValue());
if (ImGui::BeginCombo("Bloom", bloomModeNames[bloomMode])) {
for (int i = 0; i < IM_ARRAYSIZE(bloomModeNames); i++) {
const bool selected = bloomMode == i;
if (ImGui::Selectable(bloomModeNames[i], selected)) {
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(i));
config::Save();
}
if (selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
bool bloomOff = bloomMode == static_cast<int>(BloomMode::Off);
if (bloomOff) ImGui::BeginDisabled();
float mult = getSettings().game.bloomMultiplier.getValue();
if (ImGui::SliderFloat("Bloom Brightness", &mult, 0.0f, 1.0f, "%.2f")) {
getSettings().game.bloomMultiplier.setValue(mult);
config::Save();
}
if (bloomOff) ImGui::EndDisabled();
config::ImGuiCheckbox("Enable Water Refraction", getSettings().game.enableWaterRefraction);
@@ -110,6 +134,9 @@ namespace dusk {
if (ImGui::BeginMenu("Interface")) {
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
#if DUSK_ENABLE_SENTRY_NATIVE
config::ImGuiCheckbox("Enable Crash Reporting", getSettings().backend.enableCrashReporting);
#endif
ImGui::EndMenu();
}
@@ -148,6 +175,122 @@ namespace dusk {
ImGui::EndChild();
}
struct SpecificButtonName {
SDL_GamepadType Type;
const char* Name;
};
struct ButtonNames {
SDL_GamepadButton Button;
std::vector<SpecificButtonName> Names;
};
// clang-format off
static const std::vector<ButtonNames> GamepadButtonNames = {
{ SDL_GAMEPAD_BUTTON_LEFT_STICK, {
{SDL_GAMEPAD_TYPE_PS3, "L3"},
{SDL_GAMEPAD_TYPE_PS4, "L3"},
{SDL_GAMEPAD_TYPE_PS5, "L3"},
{SDL_GAMEPAD_TYPE_XBOX360, "Left Stick"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Left Stick"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Control Stick"},
}},
{ SDL_GAMEPAD_BUTTON_RIGHT_STICK, {
{SDL_GAMEPAD_TYPE_PS3, "R3"},
{SDL_GAMEPAD_TYPE_PS4, "R3"},
{SDL_GAMEPAD_TYPE_PS5, "R3"},
{SDL_GAMEPAD_TYPE_XBOX360, "Right Stick"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Right Stick"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "C Stick"},
}},
{ SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, {
{SDL_GAMEPAD_TYPE_PS3, "L1"},
{SDL_GAMEPAD_TYPE_PS4, "L1"},
{SDL_GAMEPAD_TYPE_PS5, "L1"},
{SDL_GAMEPAD_TYPE_XBOX360, "LB"},
{SDL_GAMEPAD_TYPE_XBOXONE, "LB"},
}},
{ SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, {
{SDL_GAMEPAD_TYPE_PS3, "R1"},
{SDL_GAMEPAD_TYPE_PS4, "R1"},
{SDL_GAMEPAD_TYPE_PS5, "R1"},
{SDL_GAMEPAD_TYPE_XBOX360, "RB"},
{SDL_GAMEPAD_TYPE_XBOXONE, "RB"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Z"},
}},
{ SDL_GAMEPAD_BUTTON_BACK, {
{SDL_GAMEPAD_TYPE_PS3, "Select"},
{SDL_GAMEPAD_TYPE_PS4, "Share"},
{SDL_GAMEPAD_TYPE_PS5, "Create"},
{SDL_GAMEPAD_TYPE_XBOX360, "Back"},
{SDL_GAMEPAD_TYPE_XBOXONE, "View"},
}},
{ SDL_GAMEPAD_BUTTON_START, {
{SDL_GAMEPAD_TYPE_PS3, "Start"},
{SDL_GAMEPAD_TYPE_PS4, "Options"},
{SDL_GAMEPAD_TYPE_PS5, "Options"},
{SDL_GAMEPAD_TYPE_XBOX360, "Start"},
{SDL_GAMEPAD_TYPE_XBOXONE, "Menu"},
{SDL_GAMEPAD_TYPE_GAMECUBE, "Start/Pause"},
}},
};
// clang-format on
static const char* GetNameForGamepadButton(SDL_Gamepad* gamepad, u32 buttonUntyped) {
if (buttonUntyped == PAD_NATIVE_BUTTON_INVALID) {
return "Not bound";
}
auto button = static_cast<SDL_GamepadButton>(buttonUntyped);
auto label = SDL_GetGamepadButtonLabel(gamepad, button);
switch (label) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
return "A";
case SDL_GAMEPAD_BUTTON_LABEL_B:
return "B";
case SDL_GAMEPAD_BUTTON_LABEL_X:
return "X";
case SDL_GAMEPAD_BUTTON_LABEL_Y:
return "Y";
case SDL_GAMEPAD_BUTTON_LABEL_CROSS:
return "Cross";
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE:
return "Circle";
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE:
return "Triangle";
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE:
return "Square";
default:; // Fall through
}
auto padType = SDL_GetGamepadType(gamepad);
for (const auto& buttonNames : GamepadButtonNames) {
if (buttonNames.Button != button) {
continue;
}
for (const auto& name : buttonNames.Names) {
if (name.Type == padType) {
return name.Name;
}
}
}
switch (button) {
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return "D-pad left";
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return "D-pad right";
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return "D-pad up";
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return "D-pad down";
default:
return PADGetNativeButtonName(buttonUntyped);
}
}
void ImGuiMenuGame::windowControllerConfig() {
if (!m_showControllerConfig) {
return;
@@ -277,6 +420,7 @@ namespace dusk {
ImGuiBeginGroupPanel("Buttons", ImVec2(150 * scale, 20 * scale));
SDL_Gamepad* gamepad = PADGetSDLGamepadForIndex(PADGetIndexForPort(m_controllerConfig.m_selectedPort));
u32 buttonCount;
PADButtonMapping* btnMappingList = PADGetButtonMappings(m_controllerConfig.m_selectedPort, &buttonCount);
if (btnMappingList != nullptr) {
@@ -296,7 +440,7 @@ namespace dusk {
if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) {
dispName = fmt::format("Press a Key...##{}", btnName);
} else {
const char* nativeName = PADGetNativeButtonName(btnMappingList[i].nativeButton);
const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton);
if (nativeName == nullptr) {
nativeName = "[unbound]";
}
+1
View File
@@ -53,6 +53,7 @@ namespace dusk {
ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader);
ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo);
ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor);
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug);
if (!dusk::IsGameLaunched) {
+5
View File
@@ -6,6 +6,7 @@
#include "imgui.h"
#include "ImGuiSaveEditor.hpp"
#include "ImGuiStateShare.hpp"
namespace dusk {
class ImGuiMenuTools {
@@ -23,6 +24,7 @@ namespace dusk {
void ShowPlayerInfo();
void ShowAudioDebug();
void ShowSaveEditor();
void ShowStateShare();
private:
bool m_showDebugOverlay = false;
@@ -57,6 +59,9 @@ namespace dusk {
bool m_showSaveEditor = false;
ImGuiSaveEditor m_saveEditor;
bool m_showStateShare = false;
ImGuiStateShare m_stateShare;
};
}
+18 -6
View File
@@ -47,7 +47,11 @@ void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unu
ImGuiPreLaunchWindow::ImGuiPreLaunchWindow() = default;
bool ImGuiPreLaunchWindow::isSelectedPathValid() const {
#if TARGET_ANDROID
return !m_selectedIsoPath.empty(); // unsure why SDL_GetPathInfo doesnt work here
#else
return !m_selectedIsoPath.empty() && SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr);
#endif
}
void ImGuiPreLaunchWindow::draw() {
@@ -80,12 +84,20 @@ void ImGuiPreLaunchWindow::draw() {
float iconSize = 150.f;
ImGui::SameLine(windowSize.x / 2 - iconSize + (iconSize / 2));
if (ImGuiEngine::duskIcon != 0)
ImGui::Image(ImGuiEngine::duskIcon, ImVec2{iconSize, iconSize});
if (ImGuiEngine::orgIcon != 0) {
ImGui::Image(ImGuiEngine::orgIcon, ImVec2{iconSize, iconSize});
}
ImGuiTextCenter("Twilit Realm presents");
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
ImGuiTextCenter("Dusk");
ImGui::PopFont();
if (ImGuiEngine::duskLogo) {
ImGui::NewLine();
float width = iconSize * 2.5f;
ImGui::SameLine(windowSize.x / 2 - width + (width / 2));
ImGui::Image(ImGuiEngine::duskLogo, ImVec2{width, iconSize});
} else {
ImGui::PushFont(ImGuiEngine::fontExtraLarge);
ImGuiTextCenter("Dusk");
ImGui::PopFont();
}
(this->*drawTable[m_CurMenu])();
@@ -98,7 +110,7 @@ void ImGuiPreLaunchWindow::drawMainMenu() {
ImGui::PushFont(ImGuiEngine::fontLarge);
if (m_selectedIsoPath.empty() || !SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr)) {
if (!isSelectedPathValid()) {
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
+133
View File
@@ -0,0 +1,133 @@
#include "ImGuiStateShare.hpp"
#include "ImGuiMenuTools.hpp"
#include "ImGuiConsole.hpp"
#include "imgui.h"
#include "fmt/format.h"
#include "absl/strings/escaping.h"
#include "d/d_com_inf_game.h"
#include "dusk/main.h"
#include <zstd.h>
namespace dusk {
#pragma pack(push, 1)
struct StateSharePacket {
char stageName[8];
int8_t roomNo;
int8_t layer;
int16_t startPoint;
// followed by raw dSv_info_c bytes
};
#pragma pack(pop)
static constexpr size_t PACKET_TOTAL = sizeof(StateSharePacket) + sizeof(dSv_info_c);
void ImGuiStateShare::copyState() {
StateSharePacket pkt = {};
strncpy(pkt.stageName, dComIfGp_getStartStageName(), 7);
pkt.roomNo = dComIfGp_getStartStageRoomNo();
pkt.layer = dComIfGp_getStartStageLayer();
pkt.startPoint = dComIfGp_getStartStagePoint();
std::string raw(PACKET_TOTAL, '\0');
memcpy(raw.data(), &pkt, sizeof(pkt));
memcpy(raw.data() + sizeof(pkt), &g_dComIfG_gameInfo.info, sizeof(dSv_info_c));
size_t bound = ZSTD_compressBound(raw.size());
std::string compressed(bound, '\0');
compressed.resize(ZSTD_compress(compressed.data(), bound, raw.data(), raw.size(), 1));
std::string encoded = absl::Base64Escape(compressed);
ImGui::SetClipboardText(encoded.c_str());
m_statusMsg = "Copied to clipboard.";
}
bool ImGuiStateShare::pasteState() {
const char* clip = ImGui::GetClipboardText();
if (!clip || clip[0] == '\0') {
m_statusMsg = "Clipboard is empty.";
return false;
}
std::string decoded;
if (!absl::Base64Unescape(clip, &decoded)) {
m_statusMsg = "Invalid base64.";
return false;
}
unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size());
if (dSize == ZSTD_CONTENTSIZE_ERROR || dSize == ZSTD_CONTENTSIZE_UNKNOWN || dSize < PACKET_TOTAL) {
m_statusMsg = "Not a valid state string.";
return false;
}
std::string raw(static_cast<size_t>(dSize), '\0');
size_t result = ZSTD_decompress(raw.data(), raw.size(), decoded.data(), decoded.size());
if (ZSTD_isError(result)) {
m_statusMsg = fmt::format("Decompression failed: {}", ZSTD_getErrorName(result));
return false;
}
StateSharePacket pkt;
memcpy(&pkt, raw.data(), sizeof(pkt));
pkt.stageName[7] = '\0';
memcpy(&g_dComIfG_gameInfo.info, raw.data() + sizeof(pkt), sizeof(dSv_info_c));
s16 spawnPoint = pkt.startPoint == -4 ? -1 : pkt.startPoint;
if (spawnPoint == -1) {
dComIfGs_setRestartRoomParam(pkt.roomNo & 0x3F);
}
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer);
m_pendingInfo = g_dComIfG_gameInfo.info;
m_statusMsg = fmt::format("Warping to {} room {} layer {}.", pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
return true;
}
void ImGuiStateShare::tickPendingApply() {
if (!m_pendingInfo.has_value() || dComIfGp_isEnableNextStage())
return;
g_dComIfG_gameInfo.info = *m_pendingInfo;
m_pendingInfo.reset();
}
void ImGuiStateShare::draw(bool& open) {
if (dusk::IsGameLaunched)
tickPendingApply();
if (!open)
return;
if (!ImGui::Begin("State Share", &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
ImGui::End();
return;
}
if (!dusk::IsGameLaunched) ImGui::BeginDisabled();
if (ImGui::Button("Copy State")) copyState();
ImGui::SameLine();
if (ImGui::Button("Import State")) pasteState();
if (!dusk::IsGameLaunched) ImGui::EndDisabled();
if (!m_statusMsg.empty()) {
ImGui::Spacing();
ImGui::Separator();
ImGui::TextWrapped("%s", m_statusMsg.c_str());
}
ImGui::End();
}
void ImGuiMenuTools::ShowStateShare() {
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare))
return;
m_stateShare.draw(m_showStateShare);
}
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef DUSK_IMGUI_STATESHARE_HPP
#define DUSK_IMGUI_STATESHARE_HPP
#include "d/d_save.h"
#include <optional>
#include <string>
namespace dusk {
class ImGuiStateShare {
public:
void draw(bool& open);
private:
void copyState();
bool pasteState();
void tickPendingApply();
std::string m_statusMsg;
std::optional<dSv_info_c> m_pendingInfo;
};
}
#endif
+154 -11
View File
@@ -1,9 +1,20 @@
#include "dusk/logging.h"
#include <array>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <mutex>
#include <string>
#include "tracy/Tracy.hpp"
#if TARGET_ANDROID
#include "android/log.h"
#include <vector>
#include <sstream>
#endif
bool StubLogEnabled = true;
using namespace std::literals::string_view_literals;
@@ -20,6 +31,60 @@ static constexpr std::string_view StubFragments[] = {
"but selective updates are not implemented"sv,
};
namespace {
std::mutex g_logMutex;
FILE* g_logFile = nullptr;
std::string g_logFilePath;
const char* LogLevelString(AuroraLogLevel level) {
switch (level) {
case LOG_DEBUG:
return "DEBUG";
case LOG_INFO:
return "INFO";
case LOG_WARNING:
return "WARNING";
case LOG_ERROR:
return "ERROR";
case LOG_FATAL:
return "FATAL";
}
return "??";
}
FILE* LogStreamForLevel(AuroraLogLevel level) {
return level >= LOG_ERROR ? stderr : stdout;
}
std::string MakeTimestampedLogName() {
const auto now = std::chrono::system_clock::now();
const std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
std::tm localTime{};
#if _WIN32
localtime_s(&localTime, &nowTime);
#else
localtime_r(&nowTime, &localTime);
#endif
std::array<char, 32> buffer{};
std::strftime(buffer.data(), buffer.size(), "dusk-%Y%m%d-%H%M%S.log", &localTime);
return buffer.data();
}
void WriteLogLine(FILE* out, const char* levelStr, const char* module, const char* message, unsigned int len) {
if (out == nullptr) {
return;
}
std::fprintf(out, "[%s | %s] ", levelStr, module);
std::fwrite(message, 1, len, out);
std::fputc('\n', out);
std::fflush(out);
}
} // namespace
static bool IsForStubLog(const char* message) {
std::string_view msg_view(message);
@@ -32,6 +97,7 @@ static bool IsForStubLog(const char* message) {
return false;
}
#if TARGET_ANDROID
void aurora_log_callback(AuroraLogLevel level, const char* module, const char* message,
unsigned int len) {
ZoneScoped;
@@ -40,32 +106,109 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
return;
}
const char* levelStr = "??";
FILE* out = stdout;
int android_log_level = 0;
switch (level) {
case LOG_DEBUG:
levelStr = "DEBUG";
android_log_level = ANDROID_LOG_DEBUG;
break;
case LOG_INFO:
levelStr = "INFO";
android_log_level = ANDROID_LOG_INFO;
break;
case LOG_WARNING:
levelStr = "WARNING";
android_log_level = ANDROID_LOG_WARN;
break;
case LOG_ERROR:
levelStr = "ERROR";
out = stderr;
android_log_level = ANDROID_LOG_ERROR;
break;
case LOG_FATAL:
levelStr = "FATAL";
out = stderr;
android_log_level = ANDROID_LOG_FATAL;
break;
}
fprintf(out, "[%s | %s] %s\n", levelStr, module, message);
std::stringstream msgStream(message);
std::string segment;
while(std::getline(msgStream, segment)) {
__android_log_print(android_log_level, module, "%s\n", segment.c_str());
}
if (level == LOG_FATAL) {
fflush(out);
abort();
}
}
#else
void aurora_log_callback(AuroraLogLevel level, const char* module, const char* message,
unsigned int len) {
ZoneScoped;
if (StubLogEnabled && level != LOG_FATAL && IsForStubLog(message)) {
dusk::SendToStubLog(level, module, message);
return;
}
if (module == nullptr) {
module = "";
}
const char* levelStr = LogLevelString(level);
FILE* out = LogStreamForLevel(level);
WriteLogLine(out, levelStr, module, message, len);
{
std::lock_guard lock(g_logMutex);
if (g_logFile != nullptr) {
WriteLogLine(g_logFile, levelStr, module, message, len);
}
}
if (level == LOG_FATAL) {
abort();
}
}
#endif
aurora::Module DuskLog("dusk");
void dusk::InitializeFileLogging(const char* configDir, AuroraLogLevel logLevel) {
std::lock_guard lock(g_logMutex);
if (g_logFile != nullptr || configDir == nullptr) {
return;
}
std::error_code ec;
const std::filesystem::path logsDir = std::filesystem::path(configDir) / "logs";
std::filesystem::create_directories(logsDir, ec);
if (ec) {
std::fprintf(stderr, "[WARNING | dusk] Failed to create log directory '%s': %s\n",
logsDir.string().c_str(), ec.message().c_str());
return;
}
const std::filesystem::path logPath = logsDir / MakeTimestampedLogName();
g_logFile = std::fopen(logPath.string().c_str(), "wb");
if (g_logFile == nullptr) {
std::fprintf(stderr, "[WARNING | dusk] Failed to open log file '%s'\n",
logPath.string().c_str());
return;
}
g_logFilePath = logPath.string();
aurora::g_config.logCallback = &aurora_log_callback;
aurora::g_config.logLevel = logLevel;
WriteLogLine(g_logFile, "INFO", "dusk", "File logging initialized", 24);
}
void dusk::ShutdownFileLogging() {
std::lock_guard lock(g_logMutex);
if (g_logFile == nullptr) {
return;
}
std::fflush(g_logFile);
std::fclose(g_logFile);
g_logFile = nullptr;
}
const char* dusk::GetLogFilePath() {
std::lock_guard lock(g_logMutex);
return g_logFilePath.empty() ? nullptr : g_logFilePath.c_str();
}
+111 -11
View File
@@ -1,26 +1,126 @@
#if _WIN32
#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
#endif
#include <aurora/main.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <string_view>
#include <vector>
int game_main(int argc, char* argv[]);
void WindowsSetupConsole();
namespace {
int main(int argc, char* argv[]) {
WindowsSetupConsole();
#if _WIN32
bool ShouldShowWindowsConsole(int argc, char* argv[]) {
if (const auto* env = std::getenv("DUSK_CONSOLE")) {
if (env[0] != '\0' && env[0] != '0') {
return true;
}
}
for (int i = 1; i < argc; ++i) {
if (std::string_view(argv[i]) == "--console") {
return true;
}
}
return false;
}
void SetupWindowsConsoleStreams() {
FILE* stream = nullptr;
freopen_s(&stream, "CONIN$", "r", stdin);
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONOUT$", "w", stderr);
}
void WindowsSetupConsole(bool showConsole) {
if (!showConsole) {
return;
}
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
AllocConsole();
}
SetupWindowsConsoleStreams();
SetConsoleOutputCP(CP_UTF8);
if (const HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != nullptr) {
DWORD consoleMode = 0;
if (GetConsoleMode(stdoutHandle, &consoleMode)) {
SetConsoleMode(stdoutHandle,
consoleMode | ENABLE_PROCESSED_OUTPUT
| ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
}
}
int DuskMain(int argc, char* argv[]) {
WindowsSetupConsole(ShouldShowWindowsConsole(argc, argv));
return game_main(argc, argv);
}
void WindowsSetupConsole() {
#if _WIN32
SetConsoleOutputCP(CP_UTF8);
std::vector<std::string> WideArgsToUtf8(int argc, wchar_t** argv) {
std::vector<std::string> utf8Args;
utf8Args.reserve(argc);
auto stdout = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD consoleMode;
GetConsoleMode(stdout, &consoleMode);
SetConsoleMode(stdout, consoleMode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
for (int i = 0; i < argc; ++i) {
const int requiredSize =
WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr);
if (requiredSize <= 0) {
utf8Args.emplace_back();
continue;
}
std::vector<char> utf8Buffer(static_cast<size_t>(requiredSize));
WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8Buffer.data(), requiredSize, nullptr,
nullptr);
utf8Args.emplace_back(utf8Buffer.data());
}
return utf8Args;
}
int RunWindowsGuiEntryPoint() {
int argc = 0;
wchar_t** wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (wideArgv == nullptr) {
return DuskMain(__argc, __argv);
}
std::vector<std::string> utf8Args = WideArgsToUtf8(argc, wideArgv);
LocalFree(wideArgv);
std::vector<char*> argv;
argv.reserve(utf8Args.size());
for (auto& arg : utf8Args) {
argv.push_back(arg.data());
}
return DuskMain(argc, argv.data());
}
#else
int DuskMain(int argc, char* argv[]) {
return game_main(argc, argv);
}
#endif
} // namespace
int main(int argc, char* argv[]) {
return DuskMain(argc, argv);
}
#if _WIN32
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) {
return RunWindowsGuiEntryPoint();
}
#endif
}
+13 -5
View File
@@ -35,13 +35,15 @@ UserSettings g_userSettings = {
.noMissClimbing {"game.noMissClimbing", false},
.fastTears {"game.fastTears", false},
.instantSaves {"game.instantSaves", false},
.sunsSong {"game.sunsSong", false},
// Preferences
.enableMirrorMode {"game.enableMirrorMode", false},
.invertCameraXAxis {"game.invertCameraXAxis", false},
// Graphics
.enableBloom {"game.enableBloom", true},
.bloomMode {"game.bloomMode", BloomMode::Classic},
.bloomMultiplier {"game.bloomMultiplier", 1.0f},
.enableWaterRefraction {"game.enableWaterRefraction", true},
.enableFrameInterpolation = {"game.enableFrameInterpolation", false},
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
@@ -52,7 +54,8 @@ UserSettings g_userSettings = {
// Input
.enableGyroAim {"game.enableGyroAim", false},
.gyroAimSensitivity {"game.gyroAimSensitivity", 1.0f},
.gyroAimSensitivityX {"game.gyroAimSensitivityX", 1.0f},
.gyroAimSensitivityY {"game.gyroAimSensitivityY", 1.0f},
.gyroAimInvertPitch {"game.gyroAimInvertPitch", false},
.gyroAimInvertYaw {"game.gyroAimInvertYaw", false},
@@ -79,7 +82,8 @@ UserSettings g_userSettings = {
.graphicsBackend {"backend.graphicsBackend", "auto"},
.skipPreLaunchUI {"backend.skipPreLaunchUI", false},
.showPipelineCompilation {"backend.showPipelineCompilation", false},
.wasPresetChosen {"backend.wasPresetChosen", false}
.wasPresetChosen {"backend.wasPresetChosen", false},
.enableCrashReporting {"backend.enableCrashReporting", true}
}
};
@@ -115,9 +119,11 @@ void registerSettings() {
Register(g_userSettings.game.fastClimbing);
Register(g_userSettings.game.fastTears);
Register(g_userSettings.game.instantSaves);
Register(g_userSettings.game.sunsSong);
Register(g_userSettings.game.enableMirrorMode);
Register(g_userSettings.game.invertCameraXAxis);
Register(g_userSettings.game.enableBloom);
Register(g_userSettings.game.bloomMode);
Register(g_userSettings.game.bloomMultiplier);
Register(g_userSettings.game.enableWaterRefraction);
Register(g_userSettings.game.shadowResolutionMultiplier);
Register(g_userSettings.game.enableFastIronBoots);
@@ -134,7 +140,8 @@ void registerSettings() {
Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.enableFrameInterpolation);
Register(g_userSettings.game.enableGyroAim);
Register(g_userSettings.game.gyroAimSensitivity);
Register(g_userSettings.game.gyroAimSensitivityX);
Register(g_userSettings.game.gyroAimSensitivityY);
Register(g_userSettings.game.gyroAimInvertPitch);
Register(g_userSettings.game.gyroAimInvertYaw);
@@ -143,6 +150,7 @@ void registerSettings() {
Register(g_userSettings.backend.skipPreLaunchUI);
Register(g_userSettings.backend.showPipelineCompilation);
Register(g_userSettings.backend.wasPresetChosen);
Register(g_userSettings.backend.enableCrashReporting);
}
// Transient settings
+6
View File
@@ -749,6 +749,12 @@ void fapGm_Execute() {
#endif
#if TARGET_PC
if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) {
if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) {
dynamic_cast<daAlink_c*>(link)->handleWolfHowl();
}
}
if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigY(PAD_1)) {
if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) {
dynamic_cast<daAlink_c*>(link)->handleQuickTransform();
+291 -17
View File
@@ -1232,12 +1232,12 @@ void mDoGph_drawFilterQuad(s8 param_0, s8 param_1) {
void mDoGph_gInf_c::bloom_c::create() {
if (m_buffer == NULL) {
#ifdef TARGET_PC
u32 size = 0x20; // No need to allocate memory for texture
m_buffer = (void*)1;
#else
u32 size = GXGetTexBufferSize(FB_WIDTH / 2, FB_HEIGHT / 2, GX_TF_RGBA8, GX_FALSE, 0);
#endif
m_buffer = mDoExt_getArchiveHeap()->alloc(size, -32);
JUT_ASSERT(1621, m_buffer != NULL);
#endif
mEnable = false;
mMode = 0;
@@ -1249,20 +1249,289 @@ void mDoGph_gInf_c::bloom_c::create() {
}
void mDoGph_gInf_c::bloom_c::remove() {
#if !TARGET_PC
if (m_buffer != NULL) {
mDoExt_getArchiveHeap()->free(m_buffer);
m_buffer = NULL;
}
#endif
mMonoColor.a = 0;
}
void mDoGph_gInf_c::bloom_c::draw() {
#if TARGET_PC
static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, u16 dstWidth, u16 dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) {
GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE);
GXCopyTex((void*)texID, false);
GXInitTexObj(pDst, (void*)texID, dstWidth, dstHeight, dstFmt, GX_CLAMP, GX_CLAMP, GX_FALSE);
GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1);
}
void mDoGph_gInf_c::bloom_c::draw2() {
ZoneScoped;
if (!dusk::getSettings().game.enableBloom) {
// if (!dusk::getSettings().game.enableBloom) {
// return;
// }
bool enabled = mEnable;
if (mMonoColor.a == 0 && !enabled)
return;
f32 width = mDoGph_gInf_c::getWidth();
f32 height = mDoGph_gInf_c::getHeight();
GXLoadTexObj(getFrameBufferTexObj(), GX_TEXMAP0);
GXSetNumChans(0);
GXSetNumTexGens(1);
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_GREEN);
GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA);
GXSetZCompLoc(1);
GXSetZMode(0, GX_ALWAYS, 0);
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0);
GXSetFog(GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, g_clearColor);
GXSetFogRangeAdj(0, 0, 0);
GXSetCullMode(GX_CULL_NONE);
GXSetDither(1);
Mtx44 ortho;
C_MTXOrtho(ortho, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 10.0f);
GXLoadPosMtxImm(cMtx_getIdentity(), 0);
GXSetProjection(ortho, GX_ORTHOGRAPHIC);
GXSetCurrentMtx(0);
GXClearVtxDesc();
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S8, 0);
// Set up viewports for our pyramid.
enum { MaxDivNum = 10 };
enum {
Pass0,
FinalPass,
BlurPass0, BlurPassN = BlurPass0 + MaxDivNum,
MaxTexNum,
};
struct {
u16 x;
u16 y;
u16 w;
u16 h;
} divRects[MaxDivNum];
divRects[0] = {
0,
0,
static_cast<u16>(width),
static_cast<u16>(height),
};
divRects[1] = {
0,
0,
static_cast<u16>(divRects[0].w / 2),
static_cast<u16>(divRects[0].h / 2),
};
divRects[2] = {
divRects[1].w,
0,
static_cast<u16>(divRects[1].w / 2),
static_cast<u16>(divRects[1].h / 2),
};
for (int i = 3; i < ARRAY_SIZE(divRects); i++) {
const auto& prev = divRects[i - 1];
divRects[i] = {
prev.x,
static_cast<u16>(prev.y + prev.h),
static_cast<u16>(prev.w / 2),
static_cast<u16>(prev.h / 2),
};
}
auto divCopySrc = [&](int divNo) {
auto const& rect = divRects[divNo];
GXSetTexCopySrc(rect.x, rect.y, rect.w, rect.h);
};
TGXTexObj tmpTex[MaxTexNum];
auto divCopyTex = [&](uintptr_t texNo, int divNo) -> GXTexObj * {
auto const& rect = divRects[divNo];
CopyToTexObj(&tmpTex[texNo], texNo, rect.w, rect.h);
return &tmpTex[texNo];
};
auto divQuad = [&](int divNo) {
auto const& rect = divRects[divNo];
f32 x0 = rect.x / width;
f32 y0 = rect.y / height;
f32 x1 = (rect.x + rect.w) / width;
f32 y1 = (rect.y + rect.h) / height;
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
GXPosition3f32(x0, y0, -5);
GXTexCoord2s8(0, 0);
GXPosition3f32(x1, y0, -5);
GXTexCoord2s8(1, 0);
GXPosition3f32(x1, y1, -5);
GXTexCoord2s8(1, 1);
GXPosition3f32(x0, y1, -5);
GXTexCoord2s8(0, 1);
GXEnd();
};
if (mMonoColor.a != 0) {
GXSetNumTevStages(1);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C2, GX_CC_ZERO);
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A2);
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP1, GX_TEV_SWAP1);
GXSetTevColor(GX_TEVREG2, mMonoColor);
GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_OR);
divQuad(0);
}
if (enabled) {
GXCreateFrameBuffer(width * 0.75f, height * 0.5f);
GXSetViewport(0.0f, 0.0f, width, height, 0.1f, 1.0f); // use oversized viewport to make the math easier
GXSetNumTevStages(3);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_TEXA, GX_CC_HALF, GX_CC_ZERO);
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO);
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP1, GX_TEV_SWAP1);
GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_TEXC, GX_CC_CPREV, GX_CC_HALF, GX_CC_C0);
GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO);
GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP3, GX_TEV_SWAP3);
GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_CPREV, GX_CC_ZERO);
GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO);
GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_OR);
GXColorS10 tevColor0 = {(s16)-mPoint, (s16)-mPoint, (s16)-mPoint, 0x40};
GXSetTevColorS10(GX_TEVREG0, tevColor0);
GXPixModeSync();
// Create thresholded 1/2 image.
divQuad(1);
GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
// Downsample and filter from 1/2 EFB into 1/4.
divCopySrc(1);
GXTexObj* texPass0 = divCopyTex(Pass0, 2);
GXLoadTexObj(texPass0, GX_TEXMAP0);
f32 blurScale = mBlureSize * ((448.0f / getHeight()) / 6400.0f);
// Setup blur filter TEV.
GXSetNumTexGens(8);
u32 texMtxID = GX_TEXMTX0;
int angle = 0;
for (int texCoord = (int)GX_TEXCOORD0; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) {
GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID);
mDoMtx_stack_c::transS((blurScale * cM_scos(angle)) * getInvScale(),
blurScale * cM_ssin(angle), 0.0f);
GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4);
texMtxID += 3;
angle += 0x2000;
}
GXSetNumTevStages(8);
for (int stage = 0; stage < 8; stage++) {
GXTevStageID tevStage = (GXTevStageID)stage;
GXSetTevOrder(tevStage, (GXTexCoordID)stage, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, stage == 0 ? GX_CC_ZERO : GX_CC_CPREV);
GXSetTevColorOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0);
GXSetTevAlphaOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
}
// Successively downsample and apply blurs.
static int divStart = 2;
static int divNum = 6; // inclusive
// The original mBlureRatio is multiplied into each sample, of which there are 8 samples originally.
// This is applied over two passes, the second one with an alpha of 25%; however, the clipping that this introduces is a bit integral to the look,
// so we do the same thing, letting it clip.
float brightnessF32 = (mBlureRatio * 16 / 255.0f);
// Distribute the brightness through the total number of passes.
f32 totalNumPasses = (divNum - divStart + 1);
float brightnessPerPass = 255.0f * powf(brightnessF32, 1.0f / totalNumPasses);
GXSetTevColorS10(GX_TEVREG1, {0, 0, 0, s16(brightnessPerPass / 8)});
for (int i = divStart; i < divNum; i++) {
// Apply blur filter.
divQuad(i);
// Copy to next layer.
divCopySrc(i);
// Set up for the next pass down.
GXTexObj * blurTex = divCopyTex(BlurPass0 + i, i + 1);
GXLoadTexObj(blurTex, GX_TEXMAP0);
}
// All the way down at the bottom.
divQuad(divNum);
// Now successively alpha blend back up, don't blur anymore.
GXSetNumTevStages(1);
GXSetTevColorS10(GX_TEVREG1, {0, 0, 0, s16(255)});
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_OR);
for (int i = divNum; i > divStart; i--) {
float alpha = 255.0f * powf(0.25f * dusk::getSettings().game.bloomMultiplier.getValue(), 1.0f / (divNum - i + 1));
GXSetTevColorS10(GX_TEVREG0, {0, 0, 0, s16(alpha)});
divCopySrc(i);
GXTexObj* upTex = divCopyTex(BlurPass0 + i, i);
GXLoadTexObj(upTex, GX_TEXMAP0);
divQuad(i - 1);
}
// Now that we've upsampled and filtered our final bloom, copy 1/4 buffer.
divCopySrc(2);
GXTexObj* texFinal = divCopyTex(FinalPass, 2);
GXLoadTexObj(texFinal, GX_TEXMAP0);
GXRestoreFrameBuffer();
GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f);
// Now blend our bloom into the real FB.
GXSetTevColor(GX_TEVREG0, mBlendColor);
GXSetNumTevStages(1);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C0, GX_CC_ZERO);
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0);
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GXSetBlendMode(GX_BM_BLEND, mMode == 1 ? GX_BL_INVDSTCLR : GX_BL_ONE, GX_BL_SRCALPHA,
GX_LO_OR);
GXPixModeSync();
GXInvalidateTexAll();
divQuad(0);
}
}
#endif
void mDoGph_gInf_c::bloom_c::draw() {
ZoneScoped;
if (dusk::getSettings().game.bloomMode.getValue() == dusk::BloomMode::Dusk) {
draw2();
return;
}
#endif
if (dusk::getSettings().game.bloomMode.getValue() != dusk::BloomMode::Classic) {
return;
}
bool enabled = mEnable && m_buffer != NULL;
if (mMonoColor.a != 0 || enabled) {
#if TARGET_PC
@@ -1351,7 +1620,12 @@ void mDoGph_gInf_c::bloom_c::draw() {
GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE,
GX_TEVPREV);
GXSetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_OR);
GXColorS10 tevColor0 = {(s16)-mPoint, (s16)-mPoint, (s16)-mPoint, 0x40};
#if TARGET_PC
s16 bloomAlpha = s16(0x40 * dusk::getSettings().game.bloomMultiplier.getValue());
#else
s16 bloomAlpha = 0x40;
#endif
GXColorS10 tevColor0 = {(s16)-mPoint, (s16)-mPoint, (s16)-mPoint, bloomAlpha};
GXSetTevColorS10(GX_TEVREG0, tevColor0);
GXColor tevColor1 = {mBlureRatio, mBlureRatio, mBlureRatio, mBlureRatio};
GXSetTevColor(GX_TEVREG1, tevColor1);
@@ -1376,24 +1650,24 @@ void mDoGph_gInf_c::bloom_c::draw() {
GXLoadTexObj(&tmp_tex1, GX_TEXMAP0);
GXSetNumTexGens(8);
u32 iVar11 = 0x1e;
int sVar10 = 0;
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c);
u32 texMtxID = GX_TEXMTX0;
int angle = 0;
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
for (int texCoord = (int)GX_TEXCOORD1; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) {
GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, iVar11);
GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID);
#if TARGET_PC
f32 dVar15 = mBlureSize * ((448.0f / getHeight()) / 6400.0f);
f32 dVar15 = mBlureSize * ((448.0f / height) / 6400.0f);
#else
f32 dVar15 = mBlureSize * (1.0f / 6400.0f);
#endif
mDoMtx_stack_c::transS((dVar15 * cM_scos(sVar10)) * getInvScale(),
dVar15 * cM_ssin(sVar10), 0.0f);
GXLoadTexMtxImm(mDoMtx_stack_c::get(), iVar11, GX_MTX2x4);
mDoMtx_stack_c::transS((dVar15 * cM_scos(angle)) * getInvScale(),
dVar15 * cM_ssin(angle), 0.0f);
GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4);
iVar11 += 3;
sVar10 += 0x2492;
texMtxID += 3;
angle += 0x2492;
}
GXSetNumTevStages(8);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
+56 -1
View File
@@ -43,9 +43,12 @@
#include <cstring>
#include <chrono>
#include <filesystem>
#include <system_error>
#include <thread>
#include "SSystem/SComponent/c_API.h"
#include "dusk/app_info.hpp"
#include "dusk/crash_reporting.h"
#include "dusk/dusk.h"
#include "dusk/frame_interpolation.h"
#include "dusk/gyro_aim.h"
@@ -240,6 +243,8 @@ void main01(void) {
}
if (dusk::getSettings().game.enableFrameInterpolation) {
dusk::frame_interp::notify_presentation_frame();
while (accumulator >= kSimStepSeconds) {
mDoCPd_c::read();
if (dusk::getSettings().game.enableGyroAim) {
@@ -363,6 +368,48 @@ static const char* CalculateConfigPath() {
return result;
}
static void EnsureInitialPipelineCache(const char* configDir) {
if (configDir == nullptr) {
return;
}
const std::filesystem::path configPathFs(configDir);
const std::filesystem::path pipelineCachePath = configPathFs / "pipeline_cache.db";
if (std::filesystem::exists(pipelineCachePath)) {
return;
}
const char* basePath = SDL_GetBasePath();
if (basePath == nullptr) {
DuskLog.warn("Unable to resolve base path while seeding pipeline cache: {}", SDL_GetError());
return;
}
const std::filesystem::path initialPipelineCachePath =
std::filesystem::path(basePath) / "initial_pipeline_cache.db";
if (!std::filesystem::exists(initialPipelineCachePath)) {
DuskLog.info("No bundled initial pipeline cache found at '{}'", initialPipelineCachePath.string());
return;
}
std::error_code ec;
std::filesystem::create_directories(configPathFs, ec);
if (ec) {
DuskLog.warn("Failed to create config directory '{}' for pipeline cache: {}",
configPathFs.string(), ec.message());
return;
}
std::filesystem::copy_file(initialPipelineCachePath, pipelineCachePath, std::filesystem::copy_options::none, ec);
if (ec) {
DuskLog.warn("Failed to seed pipeline cache from '{}' to '{}': {}",
initialPipelineCachePath.string(), pipelineCachePath.string(), ec.message());
return;
}
DuskLog.info("Seeded pipeline cache from '{}'", initialPipelineCachePath.string());
}
static constexpr PADDefaultMapping defaultPadMapping = {
.buttons = {
{SDL_GAMEPAD_BUTTON_SOUTH, PAD_BUTTON_A},
@@ -418,6 +465,7 @@ int game_main(int argc, char* argv[]) {
arg_options.add_options()
("l,log-level", "Log level from " + std::to_string(AuroraLogLevel::LOG_DEBUG) + " to " + std::to_string(AuroraLogLevel::LOG_FATAL), cxxopts::value<uint8_t>()->default_value("0"))
("h,help", "Print usage")
("console", "Show the Windows console window for logs", cxxopts::value<bool>()->default_value("false")->implicit_value("true"))
("dvd", "Path to DVD image file", cxxopts::value<std::string>())
("backend", "Graphics API backend to use (auto, d3d12, metal, vulkan, null)", cxxopts::value<std::string>())
("cvar", "Override configuration variables without modifying config", cxxopts::value<std::vector<std::string>>());
@@ -440,9 +488,13 @@ int game_main(int argc, char* argv[]) {
}
configPath = CalculateConfigPath();
const auto startupLogLevel = static_cast<AuroraLogLevel>(parsed_arg_options["log-level"].as<uint8_t>());
dusk::InitializeFileLogging(configPath, startupLogLevel);
dusk::config::LoadFromUserPreferences();
ApplyCVarOverrides(parsed_arg_options["cvar"]);
dusk::InitializeCrashReporting();
EnsureInitialPipelineCache(configPath);
AuroraConfig config{};
config.appName = dusk::AppName;
@@ -455,7 +507,7 @@ int game_main(int argc, char* argv[]) {
config.windowHeight = defaultWindowHeight * 2;
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
config.logCallback = &aurora_log_callback;
config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as<uint8_t>();
config.logLevel = startupLogLevel;
config.mem1Size = 256 * 1024 * 1024;
config.mem2Size = 24 * 1024 * 1024;
config.allowJoystickBackgroundEvents = true;
@@ -498,6 +550,7 @@ int game_main(int argc, char* argv[]) {
if (!dvd_opened) {
// pre game launch ui main loop
if (!launchUILoop()) {
dusk::ShutdownCrashReporting();
aurora_shutdown();
return 0;
}
@@ -535,6 +588,8 @@ int game_main(int argc, char* argv[]) {
main01();
dusk::ShutdownCrashReporting();
dusk::ShutdownFileLogging();
fflush(stdout);
fflush(stderr);