Merge remote-tracking branch 'origin/main' into android-building

# Conflicts:
#	CMakeLists.txt
This commit is contained in:
CraftyBoss
2026-04-14 01:03:50 -07:00
48 changed files with 1526 additions and 247 deletions
+109 -13
View File
@@ -48,12 +48,17 @@ else ()
message(STATUS "Unable to find git, commit information will not be available")
endif ()
if (DUSK_WC_DESCRIBE)
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+)\-([0-9]+).*" "\\1.\\2.\\3.\\4" DUSK_VERSION_STRING "${DUSK_WC_DESCRIBE}")
string(REGEX REPLACE "v([0-9]+)\.([0-9]+)\.([0-9]+).*" "\\1.\\2.\\3" DUSK_SHORT_VERSION_STRING "${DUSK_WC_DESCRIBE}")
if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(-([0-9]+).*)?$")
set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
if (CMAKE_MATCH_5)
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${CMAKE_MATCH_5}")
else ()
set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.0")
endif ()
else ()
set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION")
set(DUSK_VERSION_STRING "0.0.0")
set(DUSK_VERSION_STRING "0.0.0.0")
set(DUSK_SHORT_VERSION_STRING "0.0.0")
endif ()
# Add version information to CI environment variables
@@ -103,6 +108,10 @@ if(ANDROID)
set(NOD_COMPRESS_ZSTD OFF CACHE BOOL "" FORCE)
endif ()
option(DUSK_ENABLE_SENTRY_NATIVE "Enable sentry-native crash reporting support" OFF)
set(DUSK_SENTRY_DSN "" CACHE STRING "Sentry DSN")
set(DUSK_SENTRY_ENVIRONMENT "development" CACHE STRING "Sentry environment")
if (DUSK_MOVIE_SUPPORT)
find_package(libjpeg-turbo QUIET)
if (libjpeg-turbo_FOUND)
@@ -156,21 +165,23 @@ elseif (APPLE)
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_BUILD_RPATH "$ORIGIN")
elseif (MSVC)
add_compile_options(/bigobj)
add_compile_options(/Zc:strictStrings-)
add_compile_options(/MP)
add_compile_options(/FS)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:/bigobj>
$<$<COMPILE_LANGUAGE:C,CXX>:/Zc:strictStrings->
$<$<COMPILE_LANGUAGE:C,CXX>:/MP>
$<$<COMPILE_LANGUAGE:C,CXX>:/FS>
)
if (NOT DUSK_BUILD_WARNINGS)
add_compile_options(/W0)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/W0>)
else ()
# Disable warnings
add_compile_options(/wd4068) # unknown pragma
add_compile_options(/wd4291) # no matching delete operator, leaks if exception thrown
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4068>) # unknown pragma
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wd4291>) # no matching delete operator, leaks if exception thrown
# Only show warnings once
add_compile_options(/wo4244) # narrowing conversion, possible data loss
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/wo4244>) # narrowing conversion, possible data loss
endif ()
add_compile_options(/utf-8)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>)
endif ()
@@ -191,6 +202,46 @@ FetchContent_Declare(json
)
FetchContent_MakeAvailable(cxxopts json)
if (DUSK_ENABLE_SENTRY_NATIVE)
message(STATUS "dusk: Fetching sentry-native")
set(SENTRY_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(SENTRY_BACKEND crashpad CACHE STRING "" FORCE)
if (WIN32)
set(SENTRY_TRANSPORT winhttp CACHE STRING "" FORCE)
endif ()
set(SENTRY_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(SENTRY_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(SENTRY_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE)
FetchContent_Declare(sentry_native
GIT_REPOSITORY https://github.com/getsentry/sentry-native.git
GIT_TAG 0.13.6
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
GIT_SUBMODULES_RECURSE TRUE
)
if (NOT sentry_native_POPULATED)
FetchContent_Populate(sentry_native)
set(_dusk_skip_install_rules ${CMAKE_SKIP_INSTALL_RULES})
set(CMAKE_SKIP_INSTALL_RULES ON)
add_subdirectory(${sentry_native_SOURCE_DIR} ${sentry_native_BINARY_DIR} EXCLUDE_FROM_ALL)
set(CMAKE_SKIP_INSTALL_RULES ${_dusk_skip_install_rules})
endif ()
endif ()
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
set(PLATFORM_NAME win32)
elseif (CMAKE_SYSTEM_NAME STREQUAL Darwin)
if (IOS)
set(PLATFORM_NAME ios)
elseif (TVOS)
set(PLATFORM_NAME tvos)
else ()
set(PLATFORM_NAME macos)
endif ()
else ()
string(TOLOWER CMAKE_SYSTEM_NAME PLATFORM_NAME)
endif ()
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
include(files.cmake)
@@ -199,6 +250,10 @@ include(files.cmake)
set(DUSK_BUNDLE_NAME Dusk)
set(DUSK_BUNDLE_IDENTIFIER dev.decomp.dusk)
set(DUSK_COMPANY_NAME "Twilit Realm")
set(DUSK_FILE_DESCRIPTION "Dusk")
set(DUSK_PRODUCT_NAME "Dusk")
set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
set(DUSK_GAME_NAME "GZ2E")
set(DUSK_GAME_VERSION "01")
set(DUSK_TP_VERSION ${DUSK_GAME_NAME}${DUSK_GAME_VERSION})
@@ -224,6 +279,13 @@ set(GAME_INCLUDE_DIRS
set(GAME_LIBS aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd
aurora::card freeverb cxxopts::cxxopts absl::flat_hash_map nlohmann_json::nlohmann_json TracyClient)
list(APPEND GAME_LIBS libzstd_static)
if (DUSK_ENABLE_SENTRY_NATIVE)
list(APPEND GAME_LIBS sentry)
list(APPEND GAME_COMPILE_DEFS DUSK_ENABLE_SENTRY_NATIVE=1 SENTRY_BUILD_STATIC=1)
endif ()
if (DUSK_MOVIE_SUPPORT)
if (TARGET libjpeg-turbo::turbojpeg-static)
list(APPEND GAME_LIBS libjpeg-turbo::turbojpeg-static)
@@ -282,6 +344,9 @@ endif ()
target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0)
target_include_directories(dusk PRIVATE include)
target_link_libraries(dusk PRIVATE game aurora::main)
if (TARGET crashpad_handler)
add_dependencies(dusk crashpad_handler)
endif ()
if (ANDROID)
# SDLActivity loads SDL_main via dlsym on Android. Since aurora::main is a static
@@ -296,6 +361,35 @@ add_custom_command(TARGET dusk POST_BUILD
COMMENT "Copying resources"
)
if (WIN32)
set(DUSK_WINDOWS_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/windows)
set(DUSK_WINDOWS_ICON_PNG ${CMAKE_CURRENT_SOURCE_DIR}/res/icon.png)
set(DUSK_WINDOWS_ICON_ICO ${CMAKE_CURRENT_BINARY_DIR}/dusk.ico)
set(DUSK_WINDOWS_RC ${CMAKE_CURRENT_BINARY_DIR}/dusk.rc)
set(DUSK_WINDOWS_MANIFEST ${CMAKE_CURRENT_BINARY_DIR}/dusk.manifest)
add_custom_command(
OUTPUT ${DUSK_WINDOWS_ICON_ICO}
COMMAND powershell -ExecutionPolicy Bypass -File
${DUSK_WINDOWS_RESOURCE_DIR}/Create-IcoFromPng.ps1
-InputPng ${DUSK_WINDOWS_ICON_PNG}
-OutputIco ${DUSK_WINDOWS_ICON_ICO}
DEPENDS ${DUSK_WINDOWS_ICON_PNG} ${DUSK_WINDOWS_RESOURCE_DIR}/Create-IcoFromPng.ps1
VERBATIM
COMMENT "Generating Windows icon"
)
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.manifest.in ${DUSK_WINDOWS_MANIFEST} @ONLY)
configure_file(${DUSK_WINDOWS_RESOURCE_DIR}/dusk.rc.in ${DUSK_WINDOWS_RC} @ONLY)
target_sources(dusk PRIVATE ${DUSK_WINDOWS_ICON_ICO} ${DUSK_WINDOWS_RC})
set_target_properties(dusk PROPERTIES WIN32_EXECUTABLE TRUE)
if (MSVC)
target_link_options(dusk PRIVATE /MANIFEST:NO)
endif ()
endif ()
if (APPLE)
if (IOS)
set(DUSK_RESOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios)
@@ -384,6 +478,8 @@ if (TARGET crashpad_handler)
list(APPEND EXTRA_TARGETS crashpad_handler)
endif ()
install(TARGETS ${BINARY_TARGETS} ${EXTRA_TARGETS} DESTINATION ${CMAKE_INSTALL_PREFIX})
aurora_install_runtime_dlls(dusk ${CMAKE_INSTALL_PREFIX})
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res DESTINATION ${CMAKE_INSTALL_PREFIX})
if (CMAKE_BUILD_TYPE STREQUAL Debug OR CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
set(DEBUG_FILES_LIST "")
foreach (target IN LISTS BINARY_TARGETS EXTRA_TARGETS)
+4 -4
View File
@@ -88,7 +88,7 @@
"name": "windows-msvc",
"displayName": "Windows (MSVC)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"binaryDir": "${sourceDir}/build/${presetName}",
"architecture": {
"value": "x64",
"strategy": "external"
@@ -96,7 +96,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "cl",
"CMAKE_CXX_COMPILER": "cl",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install"
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install"
},
"vendor": {
"microsoft.com/VisualStudioSettings/CMake/1.0": {
@@ -126,7 +126,7 @@
"name": "windows-arm64-msvc",
"displayName": "Windows ARM64 (MSVC)",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"binaryDir": "${sourceDir}/build/${presetName}",
"architecture": {
"value": "arm64",
"strategy": "external"
@@ -134,7 +134,7 @@
"cacheVariables": {
"CMAKE_C_COMPILER": "cl",
"CMAKE_CXX_COMPILER": "cl",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install",
"AURORA_DAWN_PROVIDER": "vendor"
},
"vendor": {
+1 -1
+3
View File
@@ -1338,6 +1338,7 @@ set(DUSK_FILES
src/d/actor/d_a_alink_dusk.cpp
src/dusk/asserts.cpp
src/dusk/config.cpp
src/dusk/crash_reporting.cpp
src/dusk/endian.cpp
src/dusk/extras.c
src/dusk/extras.cpp
@@ -1373,6 +1374,8 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/imgui/ImGuiMapLoader.cpp
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
+1 -2
View File
@@ -119,8 +119,7 @@ public:
public:
/* 0x56C */ dBgS_ObjAcch mAcch;
/* 0x744 */ char field_0x744;
/* 0x745 */ u8 field_0x745[0x74c - 0x745];
/* 0x744 */ char field_0x744[8];
/* 0x74C */ request_of_phase_process_class mPhase;
/* 0x754 */ mDoExt_McaMorfSO* mpModelMorf;
/* 0x758 */ Z2Creature mSound;
+24 -3
View File
@@ -12,6 +12,27 @@ public:
/* 0xf4 */ s16 angle;
};
class daObjCatDoor_Attr_c {
public:
/* 0x0 */ s16 speed;
/* 0x2 */ s16 decay_rate;
};
class daObjCatDoor_Hio_c : public mDoHIO_entry_c {
public:
daObjCatDoor_Hio_c();
~daObjCatDoor_Hio_c();
void genMessage(JORMContext*);
void ct();
void dt();
void default_set();
/* 0x8 */ int field_0x8;
/* 0xC */ daObjCatDoor_Attr_c m_attr;
};
/**
* @ingroup actors-objects
* @class daObjCatDoor_c
@@ -24,7 +45,7 @@ class daObjCatDoor_c : public fopAc_ac_c {
public:
inline ~daObjCatDoor_c();
const s16* attr() const;
const daObjCatDoor_Attr_c* attr() const;
void initBaseMtx();
void setBaseMtx();
void calcOpen();
@@ -45,7 +66,7 @@ public:
}
void setDoorOpen() {
mRotSpeed = attr()[1];
mRotSpeed = attr()->decay_rate;
dBgW* bgw1 = &mDoor1.bgw;
if (bgw1->ChkUsed()) {
dComIfG_Bgsp().Release(bgw1);
@@ -67,7 +88,7 @@ private:
/* 0x790 */ s16 mRotSpeed;
public:
static s16 const M_attr[2];
static const daObjCatDoor_Attr_c M_attr;
};
#endif /* D_A_OBJ_CATDOOR_H */
+5 -2
View File
@@ -308,8 +308,11 @@ private:
/* 0x0000C */ dDlst_shadowSimple_c mSimple[128];
/* 0x0340C */ int mNextID;
/* 0x03410 */ dDlst_shadowReal_c mReal[8];
/* 0x15EB0 */ TGXTexObj field_0x15eb0[2];
/* 0x15EF0 */ void* field_0x15ef0[2];
/* 0x15EB0 */ TGXTexObj mShadowTexObj[2];
/* 0x15EF0 */ void* mShadowTexData[2];
#if TARGET_PC
int mTexResScale;
#endif
};
class dDlst_window_c {
+11
View File
@@ -146,6 +146,12 @@ concept ConfigValue =
template <ConfigValue T>
const ConfigImplBase* GetConfigImpl();
template <typename T>
struct ConfigEnumRange {
static constexpr auto min = std::numeric_limits<std::underlying_type_t<T>>::min();
static constexpr auto max = std::numeric_limits<std::underlying_type_t<T>>::max();
};
/**
* \brief A CVar storing values.
*
@@ -189,6 +195,11 @@ public:
}
}
[[nodiscard]] constexpr const T& getDefaultValue() const noexcept {
checkRegistered();
return defaultValue;
}
/**
* \brief Change the value of a CVar.
*
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace dusk {
void InitializeCrashReporting();
void ShutdownCrashReporting();
} // namespace dusk
+5
View File
@@ -20,6 +20,11 @@ void begin_record();
void end_record();
void interpolate(float step);
float get_interpolation_step();
void notify_presentation_frame();
void request_presentation_sync();
bool presentation_sync_active();
void notify_sim_tick_complete();
uint32_t begin_presentation_ui_pass();
uint32_t get_presentation_ui_advance_ticks();
+1
View File
@@ -17,6 +17,7 @@ constexpr const char* SHOW_HEAP_VIEWER = "F4";
constexpr const char* SHOW_STUB_LOG = "F5";
constexpr const char* SHOW_CAMERA_DEBUG = "F6";
constexpr const char* SHOW_AUDIO_DEBUG = "F7";
constexpr const char* SHOW_STATE_SHARE = "F8";
constexpr const char* TURBO = "Tab";
+3
View File
@@ -7,6 +7,9 @@
void aurora_log_callback(AuroraLogLevel level, const char* module, const char* message, unsigned int len);
namespace dusk {
void InitializeFileLogging(const char* configDir, AuroraLogLevel logLevel);
void ShutdownFileLogging();
const char* GetLogFilePath();
void SendToStubLog(AuroraLogLevel level, const char* module, const char* message);
}
+17 -1
View File
@@ -7,6 +7,20 @@ namespace dusk {
using namespace config;
enum class BloomMode : int {
Off = 0,
Classic = 1,
Dusk = 2,
};
namespace config {
template <>
struct ConfigEnumRange<BloomMode> {
static constexpr auto min = BloomMode::Off;
static constexpr auto max = BloomMode::Dusk;
};
}
// Persistent user settings
struct UserSettings {
@@ -53,7 +67,8 @@ struct UserSettings {
ConfigVar<bool> invertCameraXAxis;
// Graphics
ConfigVar<bool> enableBloom;
ConfigVar<BloomMode> bloomMode;
ConfigVar<float> bloomMultiplier;
ConfigVar<bool> enableWaterRefraction;
ConfigVar<bool> enableFrameInterpolation;
ConfigVar<int> shadowResolutionMultiplier;
@@ -88,6 +103,7 @@ struct UserSettings {
ConfigVar<bool> skipPreLaunchUI;
ConfigVar<bool> showPipelineCompilation;
ConfigVar<bool> wasPresetChosen;
ConfigVar<bool> enableCrashReporting;
} backend;
};
+3
View File
@@ -31,6 +31,9 @@ public:
void create();
void remove();
void draw();
#if TARGET_PC
void draw2();
#endif
u8 getEnable() { return mEnable; }
void setEnable(u8 i_enable) { mEnable = i_enable; }
@@ -2,7 +2,11 @@
#include "JSystem/JStudio/JStudio/jstudio-object.h"
#if TARGET_PC
#include "dusk/audio.h"
#include "dusk/frame_interpolation.h"
#include "dusk/settings.h"
#endif
namespace JStudio {
namespace {
@@ -650,10 +654,25 @@ value_or_fun:
return;
value:
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation && u <= 5 &&
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
{
dusk::frame_interp::request_presentation_sync();
}
#endif
adaptor->adaptor_setVariableValue(control, u, operation, param_2, param_3);
return;
value_n:
#if TARGET_PC
if (dusk::getSettings().game.enableFrameInterpolation &&
(pN == TAdaptor_camera::sauVariableValue_3_POSITION_XYZ || pN == TAdaptor_camera::sauVariableValue_3_TARGET_POSITION_XYZ) &&
(operation == data::UNK_0x2 || operation == data::UNK_0x3 || operation == data::UNK_0x12))
{
dusk::frame_interp::request_presentation_sync();
}
#endif
adaptor->adaptor_setVariableValue_n(control, pN, u, operation, param_2, param_3);
return;
+91
View File
@@ -0,0 +1,91 @@
param(
[Parameter(Mandatory = $true)]
[string]$InputPng,
[Parameter(Mandatory = $true)]
[string]$OutputIco
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
Add-Type -AssemblyName System.Drawing
$iconSizes = @(16, 24, 32, 48, 64, 128, 256)
$inputPath = (Resolve-Path $InputPng).Path
$outputPath = [System.IO.Path]::GetFullPath($OutputIco)
$outputDir = Split-Path -Parent $outputPath
if (-not [string]::IsNullOrEmpty($outputDir)) {
[System.IO.Directory]::CreateDirectory($outputDir) | Out-Null
}
$sourceImage = [System.Drawing.Image]::FromFile($inputPath)
try {
$entries = New-Object System.Collections.Generic.List[object]
foreach ($size in $iconSizes) {
$bitmap = New-Object System.Drawing.Bitmap $size, $size
try {
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
try {
$graphics.Clear([System.Drawing.Color]::Transparent)
$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
$graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
$graphics.DrawImage($sourceImage, 0, 0, $size, $size)
} finally {
$graphics.Dispose()
}
$memory = New-Object System.IO.MemoryStream
try {
$bitmap.Save($memory, [System.Drawing.Imaging.ImageFormat]::Png)
$entries.Add([pscustomobject]@{
Size = $size
Bytes = $memory.ToArray()
}) | Out-Null
} finally {
$memory.Dispose()
}
} finally {
$bitmap.Dispose()
}
}
$fileStream = [System.IO.File]::Open($outputPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
try {
$writer = New-Object System.IO.BinaryWriter $fileStream
try {
$writer.Write([UInt16]0)
$writer.Write([UInt16]1)
$writer.Write([UInt16]$entries.Count)
$dataOffset = 6 + (16 * $entries.Count)
foreach ($entry in $entries) {
$dimension = if ($entry.Size -ge 256) { 0 } else { [byte]$entry.Size }
$writer.Write([byte]$dimension)
$writer.Write([byte]$dimension)
$writer.Write([byte]0)
$writer.Write([byte]0)
$writer.Write([UInt16]1)
$writer.Write([UInt16]32)
$writer.Write([UInt32]$entry.Bytes.Length)
$writer.Write([UInt32]$dataOffset)
$dataOffset += $entry.Bytes.Length
}
foreach ($entry in $entries) {
$writer.Write($entry.Bytes)
}
} finally {
$writer.Dispose()
}
} finally {
$fileStream.Dispose()
}
} finally {
$sourceImage.Dispose()
}
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@.@PROJECT_VERSION_TWEAK@"
processorArchitecture="*"
name="@DUSK_BUNDLE_IDENTIFIER@"
type="win32"/>
<description>@DUSK_FILE_DESCRIPTION@</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"/>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
</assembly>
+38
View File
@@ -0,0 +1,38 @@
#include <windows.h>
#include <winver.h>
IDI_MAIN_ICON ICON "@DUSK_WINDOWS_ICON_ICO@"
1 RT_MANIFEST "@DUSK_WINDOWS_MANIFEST@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "@DUSK_COMPANY_NAME@\0"
VALUE "FileDescription", "@DUSK_FILE_DESCRIPTION@\0"
VALUE "FileVersion", "@DUSK_VERSION_STRING@\0"
VALUE "InternalName", "dusk\0"
VALUE "LegalCopyright", "@DUSK_COPYRIGHT@\0"
VALUE "OriginalFilename", "dusk.exe\0"
VALUE "ProductName", "@DUSK_PRODUCT_NAME@\0"
VALUE "ProductVersion", "@DUSK_VERSION_STRING@\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1200
END
END
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

+43 -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();
}
}
}
@@ -19506,7 +19527,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 +19562,12 @@ int daAlink_c::draw() {
}
if (!checkZoraWearMaskDraw() && checkZoraWearAbility()) {
field_0x06f0->hide();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->hide();
}
}
}
@@ -19545,7 +19576,12 @@ int daAlink_c::draw() {
}
if (checkZoraWearMaskDraw() || !checkZoraWearAbility()) {
field_0x06f0->show();
#if TARGET_PC
if (field_0x06f0 != NULL)
#endif
{
field_0x06f0->show();
}
}
}
+41 -34
View File
@@ -10,35 +10,38 @@
static const char* l_arcName = "CatDoor";
s16 const daObjCatDoor_c::M_attr[2] = {0x001E, 0x0578};
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 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,
delete 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);
+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
+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;
}
+1
View File
@@ -269,6 +269,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
+3
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,7 @@ static void ApplyPresetDusk() {
s.game.instantSaves.setValue(true);
s.game.midnasLamentNonStop.setValue(true);
s.game.enableFrameInterpolation.setValue(true);
s.game.bloomMode.setValue(BloomMode::Dusk);
}
// =========================================================================
+27 -1
View File
@@ -62,7 +62,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 +133,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();
}
+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;
};
}
+13 -5
View File
@@ -84,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])();
+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
+118 -22
View File
@@ -1,6 +1,11 @@
#include "dusk/logging.h"
#include <array>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <mutex>
#include <string>
#include "tracy/Tracy.hpp"
@@ -26,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);
@@ -85,30 +144,22 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
return;
}
const char* levelStr = "??";
FILE* out = stdout;
switch (level) {
case LOG_DEBUG:
levelStr = "DEBUG";
break;
case LOG_INFO:
levelStr = "INFO";
break;
case LOG_WARNING:
levelStr = "WARNING";
break;
case LOG_ERROR:
levelStr = "ERROR";
out = stderr;
break;
case LOG_FATAL:
levelStr = "FATAL";
out = stderr;
break;
if (module == nullptr) {
module = "";
}
fprintf(out, "[%s | %s] %s\n", levelStr, module, message);
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) {
fflush(out);
abort();
}
}
@@ -116,3 +167,48 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
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
}
+7 -3
View File
@@ -41,7 +41,8 @@ UserSettings g_userSettings = {
.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},
@@ -75,7 +76,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}
}
};
@@ -113,7 +115,8 @@ void registerSettings() {
Register(g_userSettings.game.instantSaves);
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);
@@ -137,6 +140,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
+170 -75
View File
@@ -1258,20 +1258,19 @@ void mDoGph_gInf_c::bloom_c::remove() {
mMonoColor.a = 0;
}
#if TARGET_PC
static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, int dstWidth, int dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) {
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::draw() {
void mDoGph_gInf_c::bloom_c::draw2() {
ZoneScoped;
if (!dusk::getSettings().game.enableBloom) {
return;
}
// if (!dusk::getSettings().game.enableBloom) {
// return;
// }
bool enabled = mEnable;
if (mMonoColor.a == 0 && !enabled)
@@ -1279,13 +1278,11 @@ void mDoGph_gInf_c::bloom_c::draw() {
f32 width = mDoGph_gInf_c::getWidth();
f32 height = mDoGph_gInf_c::getHeight();
GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f);
GXSetScissor(0, 0, width, height);
GXLoadTexObj(getFrameBufferTexObj(), GX_TEXMAP0);
GXSetNumChans(0);
GXSetNumTexGens(1);
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c);
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);
@@ -1296,7 +1293,7 @@ void mDoGph_gInf_c::bloom_c::draw() {
GXSetCullMode(GX_CULL_NONE);
GXSetDither(1);
Mtx44 ortho;
C_MTXOrtho(ortho, 0.0f, height, 0.0f, width, 0.0f, 10.0f);
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);
@@ -1306,16 +1303,74 @@ void mDoGph_gInf_c::bloom_c::draw() {
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S8, 0);
auto filterQuad = [&](int div) {
f32 x = width / div, y = height / div;
// 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(0, 0, -5);
GXPosition3f32(x0, y0, -5);
GXTexCoord2s8(0, 0);
GXPosition3f32(x, 0, -5);
GXPosition3f32(x1, y0, -5);
GXTexCoord2s8(1, 0);
GXPosition3f32(x, y, -5);
GXPosition3f32(x1, y1, -5);
GXTexCoord2s8(1, 1);
GXPosition3f32(0, y, -5);
GXPosition3f32(x0, y1, -5);
GXTexCoord2s8(0, 1);
GXEnd();
};
@@ -1330,13 +1385,12 @@ void mDoGph_gInf_c::bloom_c::draw() {
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);
filterQuad(1);
divQuad(0);
}
if (enabled) {
enum PassID { Pass0, Pass1, Pass2 };
GXCreateFrameBuffer(width, height);
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);
@@ -1359,75 +1413,97 @@ void mDoGph_gInf_c::bloom_c::draw() {
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);
GXColor tevColor1 = {mBlureRatio, mBlureRatio, mBlureRatio, mBlureRatio};
GXSetTevColor(GX_TEVREG1, tevColor1);
GXPixModeSync();
filterQuad(2);
// 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.
TGXTexObj tmpTex[3];
GXSetTexCopySrc(0, 0, width / 2, height / 2);
CopyToTexObj(&tmpTex[0], Pass0, width / 4, height / 4);
GXLoadTexObj(&tmpTex[0], GX_TEXMAP0);
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;
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, texMtxID);
f32 dVar15 = mBlureSize * ((448.0f / getHeight()) / 6400.0f);
mDoMtx_stack_c::transS((dVar15 * cM_scos(angle)) * getInvScale(),
dVar15 * cM_ssin(angle), 0.0f);
GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4);
texMtxID += 3;
angle += 0x2492;
}
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);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, 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);
for (int tevStage = (int)GX_TEVSTAGE1; tevStage < 8; tevStage++) {
GXSetTevOrder((GXTevStageID)tevStage, (GXTexCoordID)tevStage, GX_TEXMAP0,
GX_COLOR_NULL);
GXSetTevColorIn((GXTevStageID)tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, GX_CC_CPREV);
GXSetTevColorOp((GXTevStageID)tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE,
GX_TEVPREV);
GXSetTevAlphaIn((GXTevStageID)tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0);
GXSetTevAlphaOp((GXTevStageID)tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE,
GX_TEVPREV);
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);
}
GXPixModeSync();
// Blur filter from tmp_tex1 1/4 to EFB 1/4.
filterQuad(4);
// Successively downsample and apply blurs.
static int divStart = 2;
static int divNum = 6; // inclusive
GXSetTexCopySrc(0, 0, width / 4, height / 4);
CopyToTexObj(&tmpTex[1], Pass1, width / 8, height / 8);
GXLoadTexObj(&tmpTex[1], GX_TEXMAP0);
// 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)});
// Upsample 1/8 buffer back up to 1/4 buffer.
GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_OR);
GXPixModeSync();
GXInvalidateTexAll();
filterQuad(4);
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.
GXSetTexCopySrc(0, 0, width / 4, height / 4);
CopyToTexObj(&tmpTex[2], Pass2, width / 4, height / 4);
GXLoadTexObj(&tmpTex[2], GX_TEXMAP0);
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);
@@ -1437,14 +1513,25 @@ void mDoGph_gInf_c::bloom_c::draw() {
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);
GXSetBlendMode(GX_BM_BLEND, mMode == 1 ? GX_BL_INVDSTCLR : GX_BL_ONE, GX_BL_SRCALPHA,
GX_LO_OR);
GXPixModeSync();
GXInvalidateTexAll();
filterQuad(1);
divQuad(0);
}
}
#else
#endif
void mDoGph_gInf_c::bloom_c::draw() {
ZoneScoped;
if (dusk::getSettings().game.bloomMode.getValue() == dusk::BloomMode::Dusk) {
draw2();
return;
}
if (dusk::getSettings().game.bloomMode.getValue() != dusk::BloomMode::Classic) {
return;
}
bool enabled = mEnable && m_buffer != NULL;
if (mMonoColor.a != 0 || enabled) {
#if TARGET_PC
@@ -1533,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);
@@ -1563,8 +1655,12 @@ void mDoGph_gInf_c::bloom_c::draw() {
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, texMtxID);
#if TARGET_PC
f32 dVar15 = mBlureSize * ((448.0f / height) / 6400.0f);
#else
f32 dVar15 = mBlureSize * (1.0f / 6400.0f);
#endif
mDoMtx_stack_c::transS((dVar15 * cM_scos(angle)) * getInvScale(),
dVar15 * cM_ssin(angle), 0.0f);
@@ -1673,7 +1769,6 @@ void mDoGph_gInf_c::bloom_c::draw() {
}
}
}
#endif
static void retry_captue_frame(view_class* param_0, view_port_class* param_1, int param_2) {
UNUSED(param_0);
+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);
+9 -3
View File
@@ -10,11 +10,17 @@
#define DUSK_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
#if defined(__x86_64__) || defined(_M_AMD64)
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-x86_64"
#define DUSK_ARCH "x86_64"
#elif defined(__i386__) || defined(_M_IX86)
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-x86"
#define DUSK_ARCH "x86"
#elif defined(__aarch64__) || defined(_M_ARM64)
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-@PLATFORM_NAME@-arm64"
#define DUSK_ARCH "arm64"
#endif
#define DUSK_PLATFORM_NAME "@PLATFORM_NAME@"
#define DUSK_DLPACKAGE "dusk-@DUSK_WC_DESCRIBE@-" DUSK_PLATFORM_NAME "-" DUSK_ARCH
#define DUSK_SENTRY_DSN "@DUSK_SENTRY_DSN@"
#define DUSK_SENTRY_ENVIRONMENT "@DUSK_SENTRY_ENVIRONMENT@"
#endif