mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-04 19:36:27 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2be3275da | |||
| d4b2fbd7e0 | |||
| 5a0319d6a5 | |||
| 4bb9c903b1 | |||
| f350e4c083 | |||
| 02c9adf491 | |||
| b580311623 | |||
| 7e39b8d0f0 | |||
| aaa3fe8a7f | |||
| d6b5e48c9c | |||
| 9db65da794 | |||
| 3163b987ea | |||
| 5b633163c7 | |||
| 122700da62 | |||
| 5b986eb59e | |||
| a838ff6b70 | |||
| d37b66dfbb | |||
| 7a8a0665ae | |||
| d623fe8fe4 | |||
| 440f4f51f2 | |||
| 85b01a26d1 | |||
| dd030de217 | |||
| 7d7d03f707 | |||
| 7176a7189c | |||
| 95426a6bcf | |||
| 9a7a4fdc1b | |||
| cc8e479b9a | |||
| c8a2ef9cbe | |||
| 17a3e35342 | |||
| 5ceb287e95 | |||
| af7febe7a5 | |||
| e6f5678019 | |||
| e3882da0e4 | |||
| 7bd4cf8d36 | |||
| 213307545d | |||
| d77df48b73 | |||
| b45b4c13f2 | |||
| d511b11c50 | |||
| aec09e6a21 | |||
| 670095c3e8 | |||
| f3e76e8d84 | |||
| 48fefaf4d9 | |||
| 615d77b356 | |||
| 06593e9889 | |||
| 0673767a41 | |||
| b332007f89 | |||
| ad3929f11d | |||
| 32a8445767 | |||
| 88f867607e | |||
| b3e09d7108 | |||
| aa9d93b509 | |||
| 48f5f71858 | |||
| 7e225f4520 | |||
| 6bc5f936c6 | |||
| 8ed685ac8c | |||
| facf8fdf57 |
@@ -84,8 +84,6 @@ option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
|
|||||||
# Lossless Scaling frame generation. Only Android.
|
# Lossless Scaling frame generation. Only Android.
|
||||||
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
|
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
|
||||||
|
|
||||||
option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON)
|
|
||||||
|
|
||||||
# non-linux bundled qt are static
|
# non-linux bundled qt are static
|
||||||
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
||||||
set(YUZU_STATIC_BUILD ON)
|
set(YUZU_STATIC_BUILD ON)
|
||||||
|
|||||||
@@ -246,11 +246,6 @@
|
|||||||
"repo": "stachenov/quazip",
|
"repo": "stachenov/quazip",
|
||||||
"version": "2e95c9001b"
|
"version": "2e95c9001b"
|
||||||
},
|
},
|
||||||
"reshade": {
|
|
||||||
"hash": "a1bd3fcf135fb6d018c1831ae45a8942d9777d0418b55e1189921e2ab775d1b53b0a9808924e09ef1c3e68ea11d69b3bcbebc7f4b59de14f6deec2dc24531d5e",
|
|
||||||
"repo": "crosire/reshade",
|
|
||||||
"version": "v6.7.3"
|
|
||||||
},
|
|
||||||
"sdl3": {
|
"sdl3": {
|
||||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||||
"min_version": "3.2.10",
|
"min_version": "3.2.10",
|
||||||
|
|||||||
Vendored
-62
@@ -1,62 +0,0 @@
|
|||||||
texture EdenBackBufferTex : COLOR;
|
|
||||||
sampler EdenBackBuffer { Texture = EdenBackBufferTex; };
|
|
||||||
|
|
||||||
uniform float Saturation <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Saturation";
|
|
||||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 1.0;
|
|
||||||
|
|
||||||
uniform float Brightness <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Brightness";
|
|
||||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 1.0;
|
|
||||||
|
|
||||||
uniform float Contrast <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Contrast";
|
|
||||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 1.0;
|
|
||||||
|
|
||||||
uniform float Gamma <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Gamma";
|
|
||||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 1.0;
|
|
||||||
|
|
||||||
void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
|
|
||||||
{
|
|
||||||
uv = float2(0.0, 0.0);
|
|
||||||
if (id == 2)
|
|
||||||
{
|
|
||||||
uv.x = 2.0;
|
|
||||||
}
|
|
||||||
if (id == 1)
|
|
||||||
{
|
|
||||||
uv.y = 2.0;
|
|
||||||
}
|
|
||||||
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 PS_ColorGrade(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
||||||
{
|
|
||||||
float3 rgb = tex2D(EdenBackBuffer, uv).rgb;
|
|
||||||
|
|
||||||
float luma = dot(rgb, float3(0.2126, 0.7152, 0.0722));
|
|
||||||
rgb = lerp(float3(luma, luma, luma), rgb, Saturation);
|
|
||||||
rgb *= Brightness;
|
|
||||||
rgb = (rgb - 0.5) * Contrast + 0.5;
|
|
||||||
rgb = pow(max(rgb, 0.0), 1.0 / max(Gamma, 0.0001));
|
|
||||||
|
|
||||||
return float4(saturate(rgb), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
technique EdenColorGrade
|
|
||||||
{
|
|
||||||
pass
|
|
||||||
{
|
|
||||||
VertexShader = VS_Eden;
|
|
||||||
PixelShader = PS_ColorGrade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-45
@@ -1,45 +0,0 @@
|
|||||||
texture EdenBackBufferTex : COLOR;
|
|
||||||
sampler EdenBackBuffer { Texture = EdenBackBufferTex; };
|
|
||||||
|
|
||||||
uniform float Amount <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Amount";
|
|
||||||
ui_min = 0.0; ui_max = 3.0; ui_step = 0.01;
|
|
||||||
> = 0.6;
|
|
||||||
|
|
||||||
void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
|
|
||||||
{
|
|
||||||
uv = float2(0.0, 0.0);
|
|
||||||
if (id == 2)
|
|
||||||
{
|
|
||||||
uv.x = 2.0;
|
|
||||||
}
|
|
||||||
if (id == 1)
|
|
||||||
{
|
|
||||||
uv.y = 2.0;
|
|
||||||
}
|
|
||||||
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 PS_Sharpen(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
||||||
{
|
|
||||||
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
|
|
||||||
|
|
||||||
float3 centre = tex2D(EdenBackBuffer, uv).rgb;
|
|
||||||
float3 blur = tex2D(EdenBackBuffer, uv + float2(-texel.x, 0.0)).rgb;
|
|
||||||
blur += tex2D(EdenBackBuffer, uv + float2(texel.x, 0.0)).rgb;
|
|
||||||
blur += tex2D(EdenBackBuffer, uv + float2(0.0, -texel.y)).rgb;
|
|
||||||
blur += tex2D(EdenBackBuffer, uv + float2(0.0, texel.y)).rgb;
|
|
||||||
blur *= 0.25;
|
|
||||||
|
|
||||||
return float4(centre + (centre - blur) * Amount, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
technique EdenSharpen
|
|
||||||
{
|
|
||||||
pass
|
|
||||||
{
|
|
||||||
VertexShader = VS_Eden;
|
|
||||||
PixelShader = PS_Sharpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-50
@@ -1,50 +0,0 @@
|
|||||||
texture EdenBackBufferTex : COLOR;
|
|
||||||
sampler EdenBackBuffer { Texture = EdenBackBufferTex; };
|
|
||||||
|
|
||||||
uniform float Strength <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Strength";
|
|
||||||
ui_tooltip = "How dark the corners become.";
|
|
||||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 0.6;
|
|
||||||
|
|
||||||
uniform float Aspect <
|
|
||||||
ui_type = "slider";
|
|
||||||
ui_label = "Aspect";
|
|
||||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
|
||||||
> = 1.0;
|
|
||||||
|
|
||||||
void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
|
|
||||||
{
|
|
||||||
uv = float2(0.0, 0.0);
|
|
||||||
if (id == 2)
|
|
||||||
{
|
|
||||||
uv.x = 2.0;
|
|
||||||
}
|
|
||||||
if (id == 1)
|
|
||||||
{
|
|
||||||
uv.y = 2.0;
|
|
||||||
}
|
|
||||||
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 PS_Vignette(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
|
||||||
{
|
|
||||||
float2 diff = uv - 0.5;
|
|
||||||
diff.x *= Aspect;
|
|
||||||
diff.y /= max(Aspect, 0.0001);
|
|
||||||
|
|
||||||
float falloff = 1.0 - min(1.0, Strength * dot(diff, diff) * 2.0);
|
|
||||||
float3 rgb = tex2D(EdenBackBuffer, uv).rgb;
|
|
||||||
|
|
||||||
return float4(rgb * falloff, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
technique EdenVignette
|
|
||||||
{
|
|
||||||
pass
|
|
||||||
{
|
|
||||||
VertexShader = VS_Eden;
|
|
||||||
PixelShader = PS_Vignette;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-33
@@ -195,39 +195,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# reshadefx
|
|
||||||
if (ENABLE_RESHADE)
|
|
||||||
AddJsonPackage(NAME reshade DOWNLOAD_ONLY)
|
|
||||||
|
|
||||||
set(RESHADEFX_SHIM_DIR ${CMAKE_CURRENT_BINARY_DIR}/reshadefx_shim)
|
|
||||||
file(WRITE ${RESHADEFX_SHIM_DIR}/spirv.hpp "#include <spirv/unified1/spirv.hpp>\n")
|
|
||||||
file(WRITE ${RESHADEFX_SHIM_DIR}/GLSL.std.450.h "#include <spirv/unified1/GLSL.std.450.h>\n")
|
|
||||||
|
|
||||||
add_library(reshadefx STATIC
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_codegen_spirv.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_expression.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_lexer.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_parser_exp.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_parser_stmt.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_preprocessor.cpp
|
|
||||||
${reshade_SOURCE_DIR}/source/effect_symbol_table.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(reshadefx SYSTEM PUBLIC ${reshade_SOURCE_DIR}/source)
|
|
||||||
target_include_directories(reshadefx PRIVATE ${RESHADEFX_SHIM_DIR})
|
|
||||||
target_link_libraries(reshadefx PUBLIC SPIRV-Headers::SPIRV-Headers)
|
|
||||||
|
|
||||||
if (NOT MSVC)
|
|
||||||
target_compile_options(reshadefx PRIVATE -w)
|
|
||||||
else()
|
|
||||||
target_compile_options(reshadefx PRIVATE /w)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT TARGET reshadefx::reshadefx)
|
|
||||||
add_library(reshadefx::reshadefx ALIAS reshadefx)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Catch2
|
# Catch2
|
||||||
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
||||||
AddJsonPackage(catch2)
|
AddJsonPackage(catch2)
|
||||||
|
|||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.model
|
|
||||||
|
|
||||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
abstract class FxUniformSetting(
|
|
||||||
protected val index: Int,
|
|
||||||
protected val uniform: NativePostProcessing.Uniform,
|
|
||||||
protected val component: Int
|
|
||||||
) : AbstractSetting {
|
|
||||||
override val key: String
|
|
||||||
get() = "fx_${index}_${uniform.name}_$component"
|
|
||||||
|
|
||||||
override val isRuntimeModifiable: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override val pairedSettingKey: String
|
|
||||||
get() = ""
|
|
||||||
|
|
||||||
override val isSwitchable: Boolean
|
|
||||||
get() = false
|
|
||||||
|
|
||||||
override val isSaveable: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override var global: Boolean
|
|
||||||
get() = true
|
|
||||||
set(_) {}
|
|
||||||
|
|
||||||
protected fun currentValue(): Float {
|
|
||||||
if (NativePostProcessing.hasValue(index, uniform.name)) {
|
|
||||||
return NativePostProcessing.getValue(index, uniform.name, component)
|
|
||||||
}
|
|
||||||
return uniform.defaultAt(component)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun commit(value: Float) {
|
|
||||||
NativePostProcessing.setValue(index, uniform.name, component, value)
|
|
||||||
NativePostProcessing.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reset() = commit(uniform.defaultAt(component))
|
|
||||||
}
|
|
||||||
|
|
||||||
class FxUniformSliderSetting(
|
|
||||||
index: Int,
|
|
||||||
uniform: NativePostProcessing.Uniform,
|
|
||||||
component: Int
|
|
||||||
) : FxUniformSetting(index, uniform, component), AbstractIntSetting {
|
|
||||||
override val defaultValue: Any
|
|
||||||
get() = ((uniform.defaultAt(component) - uniform.min) / uniform.step).roundToInt()
|
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int =
|
|
||||||
((currentValue() - uniform.min) / uniform.step).roundToInt()
|
|
||||||
|
|
||||||
override fun setInt(value: Int) = commit(uniform.min + value * uniform.step)
|
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String {
|
|
||||||
if (uniform.kind == NativePostProcessing.KIND_FLOAT) {
|
|
||||||
return String.format("%.3f", currentValue())
|
|
||||||
}
|
|
||||||
return currentValue().roundToInt().toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FxUniformChoiceSetting(
|
|
||||||
index: Int,
|
|
||||||
uniform: NativePostProcessing.Uniform,
|
|
||||||
component: Int
|
|
||||||
) : FxUniformSetting(index, uniform, component), AbstractIntSetting {
|
|
||||||
override val defaultValue: Any
|
|
||||||
get() = uniform.defaultAt(component).roundToInt()
|
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int = currentValue().roundToInt()
|
|
||||||
|
|
||||||
override fun setInt(value: Int) = commit(value.toFloat())
|
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
class FxUniformBooleanSetting(
|
|
||||||
index: Int,
|
|
||||||
uniform: NativePostProcessing.Uniform,
|
|
||||||
component: Int
|
|
||||||
) : FxUniformSetting(index, uniform, component), AbstractBooleanSetting {
|
|
||||||
override val defaultValue: Any
|
|
||||||
get() = uniform.defaultAt(component) != 0f
|
|
||||||
|
|
||||||
override fun getBoolean(needsGlobal: Boolean): Boolean = currentValue() != 0f
|
|
||||||
|
|
||||||
override fun setBoolean(value: Boolean) {
|
|
||||||
if (value) {
|
|
||||||
commit(1f)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
commit(0f)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = getBoolean().toString()
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ object Settings {
|
|||||||
SECTION_SYSTEM(R.string.preferences_system),
|
SECTION_SYSTEM(R.string.preferences_system),
|
||||||
SECTION_RENDERER(R.string.preferences_graphics),
|
SECTION_RENDERER(R.string.preferences_graphics),
|
||||||
SECTION_FRAME_GEN(R.string.frame_gen),
|
SECTION_FRAME_GEN(R.string.frame_gen),
|
||||||
SECTION_POST_PROCESSING(R.string.post_processing),
|
|
||||||
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
||||||
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
||||||
SECTION_SOC_OVERLAY(R.string.soc_overlay_options),
|
SECTION_SOC_OVERLAY(R.string.soc_overlay_options),
|
||||||
|
|||||||
-1
@@ -13,7 +13,6 @@ enum class StringSetting(override val key: String) : AbstractStringSetting {
|
|||||||
DEVICE_NAME("device_name"),
|
DEVICE_NAME("device_name"),
|
||||||
LOG_FILTER("log_filter"),
|
LOG_FILTER("log_filter"),
|
||||||
PROGRAM_ARGS("program_args"),
|
PROGRAM_ARGS("program_args"),
|
||||||
POST_SHADER_CHAIN("post_shader_chain"),
|
|
||||||
|
|
||||||
WEB_TOKEN("eden_token"),
|
WEB_TOKEN("eden_token"),
|
||||||
WEB_USERNAME("eden_username")
|
WEB_USERNAME("eden_username")
|
||||||
|
|||||||
-218
@@ -18,9 +18,6 @@ import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
|
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.FxUniformBooleanSetting
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.FxUniformChoiceSetting
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.FxUniformSliderSetting
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||||
@@ -33,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
|
|||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
@@ -167,7 +163,6 @@ class SettingsFragmentPresenter(
|
|||||||
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||||
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||||
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
||||||
MenuTag.SECTION_POST_PROCESSING -> addPostProcessingSettings(sl)
|
|
||||||
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
||||||
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
||||||
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
||||||
@@ -195,219 +190,6 @@ class SettingsFragmentPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addPostProcessingSettings(sl: ArrayList<SettingsItem>) {
|
|
||||||
val usable = NativePostProcessing.catalog().filter { it.valid }
|
|
||||||
|
|
||||||
sl.apply {
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_reload,
|
|
||||||
descriptionId = R.string.post_processing_reload_description,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.reload()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (usable.isEmpty()) {
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_empty,
|
|
||||||
descriptionString = NativePostProcessing.getShaderDirectory(),
|
|
||||||
isRunnable = false
|
|
||||||
) {}
|
|
||||||
)
|
|
||||||
return@apply
|
|
||||||
}
|
|
||||||
|
|
||||||
val labels = mutableListOf<String>()
|
|
||||||
val files = mutableListOf<String>()
|
|
||||||
val techniques = mutableListOf<String>()
|
|
||||||
for (effect in usable) {
|
|
||||||
for (technique in effect.techniques) {
|
|
||||||
if (effect.techniques.size == 1) {
|
|
||||||
labels.add(effect.name)
|
|
||||||
} else {
|
|
||||||
labels.add(effect.name + " \u00b7 " + technique)
|
|
||||||
}
|
|
||||||
files.add(effect.file)
|
|
||||||
techniques.add(technique)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val chain = NativePostProcessing.chain()
|
|
||||||
for (index in chain.indices) {
|
|
||||||
val entry = chain[index]
|
|
||||||
val effect = usable.firstOrNull { it.file == entry.file }
|
|
||||||
|
|
||||||
var header = entry.file
|
|
||||||
if (effect != null) {
|
|
||||||
header = effect.name
|
|
||||||
}
|
|
||||||
add(HeaderSetting(titleString = header))
|
|
||||||
|
|
||||||
add(
|
|
||||||
IntSingleChoiceSetting(
|
|
||||||
buildSlotSelector(index, entry, files, techniques),
|
|
||||||
titleId = R.string.post_processing_effect,
|
|
||||||
choices = labels.toTypedArray(),
|
|
||||||
values = labels.indices.toList().toTypedArray()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (effect != null) {
|
|
||||||
for (uniform in effect.uniforms) {
|
|
||||||
addUniform(this, index, uniform)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 0) {
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_move_up,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.move(index, -1)
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (index < chain.size - 1) {
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_move_down,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.move(index, 1)
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_reset,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.resetValues(index)
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_remove,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.remove(index)
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
|
||||||
RunnableSetting(
|
|
||||||
titleId = R.string.post_processing_add,
|
|
||||||
isRunnable = true
|
|
||||||
) {
|
|
||||||
NativePostProcessing.append(files[0], techniques[0])
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildSlotSelector(
|
|
||||||
index: Int,
|
|
||||||
entry: NativePostProcessing.ChainEntry,
|
|
||||||
files: List<String>,
|
|
||||||
techniques: List<String>
|
|
||||||
): AbstractIntSetting = object : AbstractIntSetting {
|
|
||||||
override val key = "fx_slot_$index"
|
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int {
|
|
||||||
for (i in files.indices) {
|
|
||||||
if (files[i] == entry.file && techniques[i] == entry.technique) {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setInt(value: Int) {
|
|
||||||
NativePostProcessing.replace(index, files[value], techniques[value])
|
|
||||||
NativePostProcessing.store()
|
|
||||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val defaultValue = 0
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString()
|
|
||||||
override fun reset() {}
|
|
||||||
override val isRuntimeModifiable = true
|
|
||||||
override val pairedSettingKey = ""
|
|
||||||
override val isSwitchable = false
|
|
||||||
override val isSaveable = true
|
|
||||||
override var global: Boolean
|
|
||||||
get() = true
|
|
||||||
set(_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addUniform(
|
|
||||||
sl: ArrayList<SettingsItem>,
|
|
||||||
index: Int,
|
|
||||||
uniform: NativePostProcessing.Uniform
|
|
||||||
) {
|
|
||||||
if (uniform.uiType == NativePostProcessing.UI_CHECKBOX ||
|
|
||||||
uniform.kind == NativePostProcessing.KIND_BOOL
|
|
||||||
) {
|
|
||||||
sl.add(
|
|
||||||
SwitchSetting(
|
|
||||||
FxUniformBooleanSetting(index, uniform, 0),
|
|
||||||
titleString = uniform.label,
|
|
||||||
descriptionString = uniform.tooltip
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uniform.items.isNotEmpty() &&
|
|
||||||
(uniform.uiType == NativePostProcessing.UI_COMBO ||
|
|
||||||
uniform.uiType == NativePostProcessing.UI_RADIO)
|
|
||||||
) {
|
|
||||||
sl.add(
|
|
||||||
IntSingleChoiceSetting(
|
|
||||||
FxUniformChoiceSetting(index, uniform, 0),
|
|
||||||
titleString = uniform.label,
|
|
||||||
descriptionString = uniform.tooltip,
|
|
||||||
choices = uniform.items.toTypedArray(),
|
|
||||||
values = uniform.items.indices.toList().toTypedArray()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for (component in 0 until uniform.components) {
|
|
||||||
var title = uniform.label
|
|
||||||
if (uniform.components > 1) {
|
|
||||||
title = uniform.label + " [" + component + "]"
|
|
||||||
}
|
|
||||||
sl.add(
|
|
||||||
SliderSetting(
|
|
||||||
FxUniformSliderSetting(index, uniform, component),
|
|
||||||
titleString = title,
|
|
||||||
descriptionString = uniform.tooltip,
|
|
||||||
min = 0,
|
|
||||||
max = uniform.steps
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
sl.apply {
|
||||||
add(
|
add(
|
||||||
|
|||||||
@@ -383,20 +383,6 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
|
||||||
SubmenuProperty(
|
|
||||||
R.string.post_processing,
|
|
||||||
R.string.post_processing_per_game_description,
|
|
||||||
R.drawable.ic_post_processing,
|
|
||||||
action = {
|
|
||||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
|
||||||
args.game,
|
|
||||||
Settings.MenuTag.SECTION_POST_PROCESSING
|
|
||||||
)
|
|
||||||
binding.root.findNavController().navigate(action)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (GpuDriverHelper.isAdrenoGpu()) {
|
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||||
add(
|
add(
|
||||||
|
|||||||
@@ -171,20 +171,6 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
add(
|
|
||||||
HomeSetting(
|
|
||||||
R.string.post_processing,
|
|
||||||
R.string.post_processing_description,
|
|
||||||
R.drawable.ic_post_processing,
|
|
||||||
{
|
|
||||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
|
||||||
null,
|
|
||||||
Settings.MenuTag.SECTION_POST_PROCESSING
|
|
||||||
)
|
|
||||||
binding.root.findNavController().navigate(action)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
add(
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.lossless_scaling,
|
R.string.lossless_scaling,
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
|
||||||
|
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONObject
|
|
||||||
|
|
||||||
object NativePostProcessing {
|
|
||||||
const val KIND_BOOL = 0
|
|
||||||
const val KIND_INT = 1
|
|
||||||
const val KIND_FLOAT = 2
|
|
||||||
|
|
||||||
const val UI_HIDDEN = 0
|
|
||||||
const val UI_SLIDER = 1
|
|
||||||
const val UI_DRAG = 2
|
|
||||||
const val UI_COMBO = 3
|
|
||||||
const val UI_RADIO = 4
|
|
||||||
const val UI_CHECKBOX = 5
|
|
||||||
const val UI_COLOR = 6
|
|
||||||
const val UI_INPUT_BOX = 7
|
|
||||||
|
|
||||||
external fun getCatalogJson(): String
|
|
||||||
|
|
||||||
external fun getChainJson(): String
|
|
||||||
|
|
||||||
external fun append(file: String, technique: String)
|
|
||||||
|
|
||||||
external fun replace(index: Int, file: String, technique: String)
|
|
||||||
|
|
||||||
external fun remove(index: Int)
|
|
||||||
|
|
||||||
external fun move(index: Int, delta: Int)
|
|
||||||
|
|
||||||
external fun resetValues(index: Int)
|
|
||||||
|
|
||||||
external fun getValue(index: Int, uniform: String, component: Int): Float
|
|
||||||
|
|
||||||
external fun hasValue(index: Int, uniform: String): Boolean
|
|
||||||
|
|
||||||
external fun setValue(index: Int, uniform: String, component: Int, value: Float)
|
|
||||||
|
|
||||||
external fun store()
|
|
||||||
|
|
||||||
external fun reload()
|
|
||||||
|
|
||||||
external fun getShaderDirectory(): String
|
|
||||||
|
|
||||||
data class Uniform(
|
|
||||||
val name: String,
|
|
||||||
val label: String,
|
|
||||||
val tooltip: String,
|
|
||||||
val category: String,
|
|
||||||
val kind: Int,
|
|
||||||
val uiType: Int,
|
|
||||||
val components: Int,
|
|
||||||
val min: Float,
|
|
||||||
val max: Float,
|
|
||||||
val step: Float,
|
|
||||||
val items: List<String>,
|
|
||||||
val defaults: List<Float>
|
|
||||||
) {
|
|
||||||
val steps: Int
|
|
||||||
get() {
|
|
||||||
val span = max - min
|
|
||||||
if (step <= 0f) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
val count = Math.round(span / step)
|
|
||||||
if (count < 1) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
fun defaultAt(component: Int): Float {
|
|
||||||
if (component < defaults.size) {
|
|
||||||
return defaults[component]
|
|
||||||
}
|
|
||||||
return 0f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Effect(
|
|
||||||
val file: String,
|
|
||||||
val name: String,
|
|
||||||
val error: String,
|
|
||||||
val techniques: List<String>,
|
|
||||||
val uniforms: List<Uniform>
|
|
||||||
) {
|
|
||||||
val valid: Boolean
|
|
||||||
get() = error.isEmpty() && techniques.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ChainEntry(val file: String, val technique: String)
|
|
||||||
|
|
||||||
fun catalog(): List<Effect> {
|
|
||||||
val out = mutableListOf<Effect>()
|
|
||||||
val array = JSONArray(getCatalogJson())
|
|
||||||
for (i in 0 until array.length()) {
|
|
||||||
val obj = array.getJSONObject(i)
|
|
||||||
out.add(
|
|
||||||
Effect(
|
|
||||||
file = obj.optString("file"),
|
|
||||||
name = obj.optString("name"),
|
|
||||||
error = obj.optString("error"),
|
|
||||||
techniques = obj.optJSONArray("techniques").toStringList(),
|
|
||||||
uniforms = obj.optJSONArray("uniforms").toUniformList()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fun chain(): List<ChainEntry> {
|
|
||||||
val out = mutableListOf<ChainEntry>()
|
|
||||||
val array = JSONArray(getChainJson())
|
|
||||||
for (i in 0 until array.length()) {
|
|
||||||
val obj = array.getJSONObject(i)
|
|
||||||
out.add(ChainEntry(obj.optString("file"), obj.optString("technique")))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findEffect(file: String): Effect? = catalog().firstOrNull { it.file == file }
|
|
||||||
|
|
||||||
private fun JSONArray?.toStringList(): List<String> {
|
|
||||||
if (this == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
val out = mutableListOf<String>()
|
|
||||||
for (i in 0 until length()) {
|
|
||||||
out.add(optString(i))
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JSONArray?.toFloatList(): List<Float> {
|
|
||||||
if (this == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
val out = mutableListOf<Float>()
|
|
||||||
for (i in 0 until length()) {
|
|
||||||
out.add(optDouble(i, 0.0).toFloat())
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun JSONArray?.toUniformList(): List<Uniform> {
|
|
||||||
if (this == null) {
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
val out = mutableListOf<Uniform>()
|
|
||||||
for (i in 0 until length()) {
|
|
||||||
val obj: JSONObject = optJSONObject(i) ?: continue
|
|
||||||
out.add(
|
|
||||||
Uniform(
|
|
||||||
name = obj.optString("name"),
|
|
||||||
label = obj.optString("label"),
|
|
||||||
tooltip = obj.optString("tooltip"),
|
|
||||||
category = obj.optString("category"),
|
|
||||||
kind = obj.optInt("kind", KIND_FLOAT),
|
|
||||||
uiType = obj.optInt("uiType", UI_HIDDEN),
|
|
||||||
components = obj.optInt("components", 1),
|
|
||||||
min = obj.optDouble("min", 0.0).toFloat(),
|
|
||||||
max = obj.optDouble("max", 1.0).toFloat(),
|
|
||||||
step = obj.optDouble("step", 0.01).toFloat(),
|
|
||||||
items = obj.optJSONArray("items").toStringList(),
|
|
||||||
defaults = obj.optJSONArray("defaults").toFloatList()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,6 @@ add_library(yuzu-android SHARED
|
|||||||
android_config.cpp
|
android_config.cpp
|
||||||
android_config.h
|
android_config.h
|
||||||
native_input.cpp
|
native_input.cpp
|
||||||
native_post_processing.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
#include "common/android/android_common.h"
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
nlohmann::json SerializeUniform(const VideoCore::FxUniformDesc& uniform) {
|
|
||||||
nlohmann::json out;
|
|
||||||
out["name"] = uniform.name;
|
|
||||||
out["label"] = uniform.label;
|
|
||||||
out["tooltip"] = uniform.tooltip;
|
|
||||||
out["category"] = uniform.category;
|
|
||||||
out["kind"] = static_cast<int>(uniform.kind);
|
|
||||||
out["uiType"] = static_cast<int>(uniform.ui_type);
|
|
||||||
out["components"] = uniform.components;
|
|
||||||
out["min"] = uniform.ui_min;
|
|
||||||
out["max"] = uniform.ui_max;
|
|
||||||
out["step"] = uniform.ui_step;
|
|
||||||
out["items"] = uniform.items;
|
|
||||||
|
|
||||||
nlohmann::json defaults = nlohmann::json::array();
|
|
||||||
for (u32 i = 0; i < uniform.components; ++i) {
|
|
||||||
defaults.push_back(uniform.default_value[i]);
|
|
||||||
}
|
|
||||||
out["defaults"] = defaults;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<f32, 4> DefaultValueOf(size_t index, const std::string& uniform) {
|
|
||||||
const auto entries = VideoCore::FxChain::Instance().Entries();
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const VideoCore::FxEffectDesc* effect = VideoCore::FindFxEffect(entries[index].file);
|
|
||||||
if (effect == nullptr) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const VideoCore::FxUniformDesc* desc = VideoCore::FindFxUniform(*effect, uniform);
|
|
||||||
if (desc == nullptr) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return desc->default_value;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getCatalogJson(JNIEnv* env,
|
|
||||||
jobject obj) {
|
|
||||||
nlohmann::json out = nlohmann::json::array();
|
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
for (const auto& effect : VideoCore::GetFxCatalog()) {
|
|
||||||
nlohmann::json entry;
|
|
||||||
entry["file"] = effect.file;
|
|
||||||
entry["name"] = effect.name;
|
|
||||||
entry["error"] = effect.error;
|
|
||||||
entry["techniques"] = effect.techniques;
|
|
||||||
|
|
||||||
nlohmann::json uniforms = nlohmann::json::array();
|
|
||||||
for (const auto& uniform : effect.uniforms) {
|
|
||||||
uniforms.push_back(SerializeUniform(uniform));
|
|
||||||
}
|
|
||||||
entry["uniforms"] = uniforms;
|
|
||||||
|
|
||||||
out.push_back(entry);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return Common::Android::ToJString(env, out.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getChainJson(JNIEnv* env, jobject obj) {
|
|
||||||
nlohmann::json out = nlohmann::json::array();
|
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
for (const auto& entry : VideoCore::FxChain::Instance().Entries()) {
|
|
||||||
nlohmann::json item;
|
|
||||||
item["file"] = entry.file;
|
|
||||||
item["technique"] = entry.technique;
|
|
||||||
|
|
||||||
nlohmann::json values = nlohmann::json::object();
|
|
||||||
for (const auto& [name, value] : entry.values) {
|
|
||||||
values[name] = {value[0], value[1], value[2], value[3]};
|
|
||||||
}
|
|
||||||
item["values"] = values;
|
|
||||||
|
|
||||||
out.push_back(item);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return Common::Android::ToJString(env, out.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_append(JNIEnv* env, jobject obj,
|
|
||||||
jstring jfile, jstring jtechnique) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().Append(Common::Android::GetJString(env, jfile),
|
|
||||||
Common::Android::GetJString(env, jtechnique));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_replace(JNIEnv* env, jobject obj,
|
|
||||||
jint index, jstring jfile,
|
|
||||||
jstring jtechnique) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().Replace(static_cast<size_t>(index),
|
|
||||||
Common::Android::GetJString(env, jfile),
|
|
||||||
Common::Android::GetJString(env, jtechnique));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_remove(JNIEnv* env, jobject obj,
|
|
||||||
jint index) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().Remove(static_cast<size_t>(index));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_move(JNIEnv* env, jobject obj, jint index,
|
|
||||||
jint delta) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().Move(static_cast<size_t>(index), delta);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_resetValues(JNIEnv* env, jobject obj,
|
|
||||||
jint index) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().ResetValues(static_cast<size_t>(index));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
jfloat Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getValue(JNIEnv* env, jobject obj,
|
|
||||||
jint index, jstring juniform,
|
|
||||||
jint component) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
if (component < 0 || component >= 4) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
const auto value = VideoCore::FxChain::Instance().GetValue(
|
|
||||||
static_cast<size_t>(index), Common::Android::GetJString(env, juniform));
|
|
||||||
return value[static_cast<size_t>(component)];
|
|
||||||
#else
|
|
||||||
return 0.0f;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_hasValue(JNIEnv* env, jobject obj,
|
|
||||||
jint index,
|
|
||||||
jstring juniform) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
return static_cast<jboolean>(VideoCore::FxChain::Instance().HasValue(
|
|
||||||
static_cast<size_t>(index), Common::Android::GetJString(env, juniform)));
|
|
||||||
#else
|
|
||||||
return static_cast<jboolean>(false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_setValue(JNIEnv* env, jobject obj,
|
|
||||||
jint index, jstring juniform,
|
|
||||||
jint component, jfloat value) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
if (component < 0 || component >= 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const std::string uniform = Common::Android::GetJString(env, juniform);
|
|
||||||
auto& chain = VideoCore::FxChain::Instance();
|
|
||||||
const auto slot = static_cast<size_t>(index);
|
|
||||||
|
|
||||||
auto current = chain.GetValue(slot, uniform);
|
|
||||||
if (!chain.HasValue(slot, uniform)) {
|
|
||||||
current = DefaultValueOf(slot, uniform);
|
|
||||||
}
|
|
||||||
|
|
||||||
current[static_cast<size_t>(component)] = value;
|
|
||||||
chain.SetValue(slot, uniform, current);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_store(JNIEnv* env, jobject obj) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::FxChain::Instance().StoreToSettings();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_reload(JNIEnv* env, jobject obj) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::ReloadFxCatalog();
|
|
||||||
VideoCore::FxChain::Instance().DropUnknownEntries();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getShaderDirectory(JNIEnv* env,
|
|
||||||
jobject obj) {
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
return Common::Android::ToJString(env, VideoCore::GetFxRootDirectory().string());
|
|
||||||
#else
|
|
||||||
return Common::Android::ToJString(env, "");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // extern "C"
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="?attr/colorControlNormal"
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M8.5,3 L19.4,3 Q21,3 21,4.6 L21,16.5 L19.4,16.5 L19.4,4.6 L8.5,4.6 Z M5,7 L15,7 Q17,7 17,9 L17,19 Q17,21 15,21 L5,21 Q3,21 3,19 L3,9 Q3,7 5,7 Z M5.2,8.6 L14.8,8.6 Q15.4,8.6 15.4,9.2 L15.4,18.8 Q15.4,19.4 14.8,19.4 L5.2,19.4 Q4.6,19.4 4.6,18.8 L4.6,9.2 Q4.6,8.6 5.2,8.6 Z M10,10.9 A3.1,3.1 0 0 1 10,17.1 Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -298,18 +298,6 @@
|
|||||||
<string name="gpu_driver_fetcher">GPU driver fetcher</string>
|
<string name="gpu_driver_fetcher">GPU driver fetcher</string>
|
||||||
<string name="gpu_driver_manager">GPU driver manager</string>
|
<string name="gpu_driver_manager">GPU driver manager</string>
|
||||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||||
<string name="post_processing">Post-Processing Effects</string>
|
|
||||||
<string name="post_processing_description">ReShade FX effects applied after rendering</string>
|
|
||||||
<string name="post_processing_per_game_description">Configure the effect chain for this game</string>
|
|
||||||
<string name="post_processing_effect">Effect</string>
|
|
||||||
<string name="post_processing_add">Add effect</string>
|
|
||||||
<string name="post_processing_remove">Remove</string>
|
|
||||||
<string name="post_processing_move_up">Move up</string>
|
|
||||||
<string name="post_processing_move_down">Move down</string>
|
|
||||||
<string name="post_processing_reset">Reset to defaults</string>
|
|
||||||
<string name="post_processing_reload">Reload from disk</string>
|
|
||||||
<string name="post_processing_reload_description">Rescan the shader folder for .fx files</string>
|
|
||||||
<string name="post_processing_empty">No effects found. Place .fx files in this folder:</string>
|
|
||||||
<string name="frame_gen">Frame generation</string>
|
<string name="frame_gen">Frame generation</string>
|
||||||
<string name="frame_gen_per_game_description">Configure frame generation for this game</string>
|
<string name="frame_gen_per_game_description">Configure frame generation for this game</string>
|
||||||
<string name="frame_gen_description">Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled.</string>
|
<string name="frame_gen_description">Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled.</string>
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ add_library(
|
|||||||
settings_input.h
|
settings_input.h
|
||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
socket_types.h
|
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "web_service/web_result.h"
|
#include "web_service/web_result.h"
|
||||||
|
|
||||||
namespace AnnounceMultiplayerRoom {
|
namespace AnnounceMultiplayerRoom {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#define LOSSLESS_DIR "lossless"
|
#define LOSSLESS_DIR "lossless"
|
||||||
#define NAND_DIR "nand"
|
#define NAND_DIR "nand"
|
||||||
#define PLAY_TIME_DIR "play_time"
|
#define PLAY_TIME_DIR "play_time"
|
||||||
#define POST_SHADER_DIR "post_shaders"
|
|
||||||
#define SCREENSHOTS_DIR "screenshots"
|
#define SCREENSHOTS_DIR "screenshots"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define SHADER_DIR "shader"
|
#define SHADER_DIR "shader"
|
||||||
|
|||||||
@@ -160,7 +160,6 @@ public:
|
|||||||
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
||||||
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
||||||
GenerateEdenPath(EdenPath::PlayTimeDir, eden_path / PLAY_TIME_DIR);
|
GenerateEdenPath(EdenPath::PlayTimeDir, eden_path / PLAY_TIME_DIR);
|
||||||
GenerateEdenPath(EdenPath::PostShaderDir, eden_path / POST_SHADER_DIR);
|
|
||||||
GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR);
|
GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR);
|
||||||
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
|
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
|
||||||
GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_DIR);
|
GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_DIR);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ enum class EdenPath {
|
|||||||
LosslessDir, // Where the user-supplied Lossless Scaling library is stored.
|
LosslessDir, // Where the user-supplied Lossless Scaling library is stored.
|
||||||
NANDDir, // Where the emulated NAND is stored.
|
NANDDir, // Where the emulated NAND is stored.
|
||||||
PlayTimeDir, // Where play time data is stored.
|
PlayTimeDir, // Where play time data is stored.
|
||||||
PostShaderDir, // Where user post-processing shaders are stored.
|
|
||||||
SaveDir, // Where save data is stored.
|
SaveDir, // Where save data is stored.
|
||||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||||
SDMCDir, // Where the emulated SDMC is stored.
|
SDMCDir, // Where the emulated SDMC is stored.
|
||||||
|
|||||||
@@ -388,14 +388,6 @@ struct Values {
|
|||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
|
|
||||||
SwitchableSetting<std::string> post_shader_chain{linkage,
|
|
||||||
std::string(),
|
|
||||||
"post_shader_chain",
|
|
||||||
Category::Renderer,
|
|
||||||
Specialization::Default,
|
|
||||||
true,
|
|
||||||
true};
|
|
||||||
|
|
||||||
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
||||||
Specialization::Default, true, false};
|
Specialization::Default, true, false};
|
||||||
|
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
/// Address families
|
|
||||||
enum class Domain : u8 {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
INET, ///< Address family for IPv4
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Socket types
|
|
||||||
enum class Type {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
STREAM,
|
|
||||||
DGRAM,
|
|
||||||
RAW,
|
|
||||||
SEQPACKET,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Protocol values for sockets
|
|
||||||
enum class Protocol : u8 {
|
|
||||||
Unspecified, ///< Represents 0, usable in various places
|
|
||||||
IP,
|
|
||||||
ICMP,
|
|
||||||
TCP,
|
|
||||||
UDP,
|
|
||||||
IPV6,
|
|
||||||
RAW,
|
|
||||||
IGMP,
|
|
||||||
GGP,
|
|
||||||
IPV4,
|
|
||||||
ST,
|
|
||||||
EGP,
|
|
||||||
PIGP,
|
|
||||||
RCCMON,
|
|
||||||
NVPII,
|
|
||||||
PUP,
|
|
||||||
ARGUS,
|
|
||||||
EMCON,
|
|
||||||
XNET,
|
|
||||||
CHAOS,
|
|
||||||
MUX,
|
|
||||||
MEAS,
|
|
||||||
HMP,
|
|
||||||
PRM,
|
|
||||||
IDP,
|
|
||||||
TRUNK1,
|
|
||||||
TRUNK2,
|
|
||||||
LEAF1,
|
|
||||||
LEAF2,
|
|
||||||
RDP,
|
|
||||||
IRTP,
|
|
||||||
TP,
|
|
||||||
BLT,
|
|
||||||
NSP,
|
|
||||||
INP,
|
|
||||||
DCCP,
|
|
||||||
//TODO: 3PC,
|
|
||||||
IDPR,
|
|
||||||
XTP,
|
|
||||||
DDP,
|
|
||||||
CMTP,
|
|
||||||
TPXX,
|
|
||||||
IL,
|
|
||||||
SDRP,
|
|
||||||
ROUTING,
|
|
||||||
FRAGMENT,
|
|
||||||
IDRP,
|
|
||||||
RSVP,
|
|
||||||
GRE,
|
|
||||||
MHRP,
|
|
||||||
BHA,
|
|
||||||
ESP,
|
|
||||||
AH,
|
|
||||||
INLSP,
|
|
||||||
SWIPE,
|
|
||||||
NHRP,
|
|
||||||
MOBILE,
|
|
||||||
TLSP,
|
|
||||||
SKIP,
|
|
||||||
ICMPV6,
|
|
||||||
NONE,
|
|
||||||
DSTOPTS,
|
|
||||||
AHIP,
|
|
||||||
CFTP,
|
|
||||||
HELLO,
|
|
||||||
SATEXPAK,
|
|
||||||
KRYPTOLAN,
|
|
||||||
RVD,
|
|
||||||
IPPC,
|
|
||||||
ADFS,
|
|
||||||
SATMON,
|
|
||||||
VISA,
|
|
||||||
IPCV,
|
|
||||||
CPNX,
|
|
||||||
CPHB,
|
|
||||||
WSN,
|
|
||||||
PVP,
|
|
||||||
BRSATMON,
|
|
||||||
ND,
|
|
||||||
WBMON,
|
|
||||||
WBEXPAK,
|
|
||||||
EON,
|
|
||||||
VMTP,
|
|
||||||
SVMTP,
|
|
||||||
VINES,
|
|
||||||
TTP,
|
|
||||||
IGP,
|
|
||||||
DGP,
|
|
||||||
TCF,
|
|
||||||
IGRP,
|
|
||||||
OSPFIGP,
|
|
||||||
SRPC,
|
|
||||||
LARP,
|
|
||||||
MTP,
|
|
||||||
AX25,
|
|
||||||
IPEIP,
|
|
||||||
MICP,
|
|
||||||
SCCSP,
|
|
||||||
ETHERIP,
|
|
||||||
ENCAP,
|
|
||||||
APES,
|
|
||||||
GMTP,
|
|
||||||
IPCOMP,
|
|
||||||
SCTP,
|
|
||||||
MH,
|
|
||||||
UDPLITE,
|
|
||||||
HIP,
|
|
||||||
SHIM6,
|
|
||||||
PIM,
|
|
||||||
CARP,
|
|
||||||
PGM,
|
|
||||||
MPLS,
|
|
||||||
PFSYNC
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Shutdown mode
|
|
||||||
enum class ShutdownHow {
|
|
||||||
RD,
|
|
||||||
WR,
|
|
||||||
RDWR,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Array of IPv4 address
|
|
||||||
using IPv4Address = std::array<u8, 4>;
|
|
||||||
|
|
||||||
/// Cross-platform sockaddr structure
|
|
||||||
struct SockAddrIn {
|
|
||||||
Domain family;
|
|
||||||
IPv4Address ip;
|
|
||||||
u16 portno;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 FLAG_MSG_PEEK = 0x2;
|
|
||||||
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
|
|
||||||
constexpr u32 FLAG_O_NONBLOCK = 0x800;
|
|
||||||
|
|
||||||
/// Cross-platform addrinfo structure
|
|
||||||
struct AddrInfo {
|
|
||||||
Domain family;
|
|
||||||
Type socket_type;
|
|
||||||
Protocol protocol;
|
|
||||||
SockAddrIn addr;
|
|
||||||
std::optional<std::string> canon_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -1129,6 +1129,7 @@ add_library(core STATIC
|
|||||||
internal_network/network_interface.h
|
internal_network/network_interface.h
|
||||||
internal_network/socket_proxy.cpp
|
internal_network/socket_proxy.cpp
|
||||||
internal_network/socket_proxy.h
|
internal_network/socket_proxy.h
|
||||||
|
internal_network/socket_types.h
|
||||||
internal_network/sockets.h
|
internal_network/sockets.h
|
||||||
internal_network/wifi_scanner.h
|
internal_network/wifi_scanner.h
|
||||||
launch_timestamp_cache.cpp
|
launch_timestamp_cache.cpp
|
||||||
@@ -1167,6 +1168,12 @@ add_library(core STATIC
|
|||||||
tools/renderdoc.cpp
|
tools/renderdoc.cpp
|
||||||
tools/renderdoc.h)
|
tools/renderdoc.h)
|
||||||
|
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
target_sources(core PRIVATE
|
||||||
|
internal_network/socket_icmp.cpp
|
||||||
|
internal_network/socket_icmp.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WIFI_SCAN)
|
if (ENABLE_WIFI_SCAN)
|
||||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "common/container/unordered_map.h"
|
#include "common/container/unordered_map.h"
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/ldn/ldn_results.h"
|
#include "core/hle/service/ldn/ldn_results.h"
|
||||||
#include "core/hle/service/ldn/ldn_types.h"
|
#include "core/hle/service/ldn/ldn_types.h"
|
||||||
|
|||||||
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||||
|
for (size_t i = 0; i < num_threads; i++) {
|
||||||
|
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||||
|
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||||
server_manager->m_system.RunServer(std::move(server_manager));
|
server_manager->m_system.RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
|
||||||
for (size_t i = 0; i < num_threads; i++) {
|
|
||||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
|
||||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
|
|
||||||
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ServerManager::LoopProcess() {
|
Result ServerManager::LoopProcess() {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -48,8 +51,8 @@ public:
|
|||||||
Result ManageDeferral(Kernel::KEvent** out_event);
|
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
|
||||||
|
|
||||||
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
@@ -20,6 +20,9 @@
|
|||||||
#include "core/hle/service/sockets/sockets_translate.h"
|
#include "core/hle/service/sockets/sockets_translate.h"
|
||||||
#include "core/internal_network/network.h"
|
#include "core/internal_network/network.h"
|
||||||
#include "core/internal_network/socket_proxy.h"
|
#include "core/internal_network/socket_proxy.h"
|
||||||
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
|
#include "core/internal_network/socket_icmp.h"
|
||||||
|
#endif
|
||||||
#include "core/internal_network/sockets.h"
|
#include "core/internal_network/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include <common/settings.h>
|
#include <common/settings.h>
|
||||||
@@ -28,15 +31,18 @@ namespace Service::Sockets {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool IsConnectionBased(Type type) {
|
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::STREAM:
|
case Network::Type::STREAM:
|
||||||
|
case Network::Type::SEQPACKET:
|
||||||
return true;
|
return true;
|
||||||
case Type::DGRAM:
|
case Network::Type::RAW:
|
||||||
|
case Network::Type::DGRAM:
|
||||||
|
case Network::Type::RDM:
|
||||||
|
case Network::Type::Unspecified:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
UNREACHABLE();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +98,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
|
|||||||
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,10 +176,9 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
|
|||||||
const u32 domain = rp.Pop<u32>();
|
const u32 domain = rp.Pop<u32>();
|
||||||
const u32 type = rp.Pop<u32>();
|
const u32 type = rp.Pop<u32>();
|
||||||
const u32 protocol = rp.Pop<u32>();
|
const u32 protocol = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -189,8 +194,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
if (bsd_errno == Errno::SUCCESS) {
|
if (bsd_errno == Network::Errno::E_SUCCESS) {
|
||||||
bsd_errno = ShutdownImpl(fd, 0);
|
bsd_errno = ShutdownImpl(fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +269,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -282,13 +287,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -296,21 +301,19 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const auto level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
const auto optname = Network::OptName(rp.Pop<u32>());
|
||||||
|
|
||||||
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
|
||||||
optval.size());
|
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||||
|
|
||||||
const Errno err = GetSockOptImpl(fd, level, optname, optval);
|
|
||||||
|
|
||||||
ctx.WriteBuffer(optval);
|
ctx.WriteBuffer(optval);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(err);
|
rb.PushEnum(err);
|
||||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||||
}
|
}
|
||||||
@@ -333,7 +336,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
|
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -345,13 +348,11 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
|
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
|
||||||
const auto optval = ctx.ReadBuffer();
|
const auto optval = ctx.ReadBuffer();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
|
||||||
static_cast<u32>(optname), optval.size());
|
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +474,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
s32 ret;
|
s32 ret;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x8);
|
static_assert(sizeof(OutputParameters) == 0x8);
|
||||||
|
|
||||||
@@ -485,7 +486,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (is_user) {
|
if (is_user) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = Errno::INVAL,
|
.bsd_errno = Network::Errno::E_INVAL,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -494,10 +495,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = *res,
|
.ret = *res,
|
||||||
.bsd_errno = Errno::SUCCESS,
|
.bsd_errno = Network::Errno::E_SUCCESS,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto* err = std::get_if<Errno>(&res_v);
|
auto* err = std::get_if<Network::Errno>(&res_v);
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = *err,
|
.bsd_errno = *err,
|
||||||
@@ -512,7 +513,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, Errno::SUCCESS);
|
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
@@ -521,132 +522,143 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
|
|||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
|
||||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||||
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
|
||||||
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
|
||||||
|
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
|
||||||
// fine, can use on bsd:s and bsd:u
|
// fine, can use on bsd:s and bsd:u
|
||||||
} else {
|
} else {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
|
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
|
||||||
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
||||||
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
|
type = Network::Type(u32(type) & ~0x20000000);
|
||||||
|
|
||||||
const s32 fd = FindFreeFileDescriptorHandle();
|
const s32 fd = FindFreeFileDescriptorHandle();
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
|
||||||
|
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, Network::Errno::E_NOTCONN};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[fd] = FileDescriptor{};
|
file_descriptors[fd] = FileDescriptor{};
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// ENONMEM might be thrown here
|
// ENONMEM might be thrown here
|
||||||
|
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
|
||||||
|
|
||||||
LOG_INFO(Service, "New socket fd={}", fd);
|
// While room is important -- we need to remember ICMP takes priority over **everything else**
|
||||||
|
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
|
||||||
|
// to browse the internet while playing LDN or something stupid like that?
|
||||||
auto room_member = Network::GetRoomMember().lock();
|
auto room_member = Network::GetRoomMember().lock();
|
||||||
if (room_member && room_member->IsConnected()) {
|
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
|
||||||
|
&& (room_member && room_member->IsConnected())) {
|
||||||
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
} else {
|
} else {
|
||||||
descriptor.socket = std::make_shared<Network::Socket>();
|
descriptor.socket = std::make_shared<Network::Socket>();
|
||||||
}
|
}
|
||||||
|
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
|
||||||
|
|
||||||
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
// ...only unix has this issue it seems, ICMP works otherwise fine on win
|
||||||
|
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
|
||||||
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
|
&& bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
LOG_WARNING(Network, "Using ICMP emulated socket");
|
||||||
return {-1, Errno::NOTCONN};
|
descriptor.socket = std::make_shared<Network::IcmpSocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return {fd, Errno::SUCCESS};
|
descriptor.is_connection_based = IsConnectionBased(type);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
|
||||||
|
#else
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
|
||||||
|
#endif
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, bsd_errno};
|
||||||
|
}
|
||||||
|
return {fd, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
|
||||||
s32 nfds, s32 timeout) {
|
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
|
||||||
if (nfds <= 0) {
|
if (nfds <= 0) {
|
||||||
// When no entries are provided, -1 is returned with errno zero
|
// When no entries are provided, -1 is returned with errno zero
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
if (read_buffer.size() < nfds * sizeof(PollFD)) {
|
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (write_buffer.size() < nfds * sizeof(PollFD)) {
|
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PollFD> fds(nfds);
|
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
|
||||||
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
|
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
|
||||||
|
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
|
||||||
|
|
||||||
if (timeout >= 0) {
|
if (timeout >= 0) {
|
||||||
const s64 seconds = timeout / 1000;
|
const s64 seconds = timeout / 1000;
|
||||||
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
|
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (nanoseconds > 999'999'999) {
|
if (nanoseconds > 999'999'999) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
} else if (timeout != -1) {
|
} else if (timeout != -1) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PollFD& pollfd : fds) {
|
for (size_t i = 0; i < in_fds.size(); ++i) {
|
||||||
ASSERT(False(pollfd.revents));
|
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
|
||||||
|
if (!IsFileDescriptorValid(in_fds[i].fd)) {
|
||||||
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
|
out_fds[i].revents = {};
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
|
if (!file_descriptors[in_fds[i].fd])
|
||||||
pollfd.revents = PollEvents{};
|
out_fds[i].revents = Network::PollEvents::NVAL;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
|
||||||
|
|
||||||
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
|
|
||||||
if (!descriptor) {
|
|
||||||
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
|
|
||||||
pollfd.revents = PollEvents::Nval;
|
|
||||||
return {0, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::PollFD> host_pollfds(fds.size());
|
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
|
||||||
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
|
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
|
||||||
Network::PollFD result;
|
Network::HostPollFD result{};
|
||||||
result.socket = file_descriptors[pollfd.fd]->socket.get();
|
result.socket = file_descriptors[e.fd]->socket.get();
|
||||||
result.events = Translate(pollfd.events);
|
result.events = e.events;
|
||||||
result.revents = Network::PollEvents{};
|
result.revents = {};
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
auto const res = Network::Poll(host_pollfds, timeout);
|
||||||
const auto result = Network::Poll(host_pollfds, timeout);
|
for (size_t i = 0; i < in_fds.size(); ++i)
|
||||||
|
out_fds[i].revents = host_pollfds[i].revents;
|
||||||
const size_t num = host_pollfds.size();
|
return res;
|
||||||
for (size_t i = 0; i < num; ++i) {
|
|
||||||
fds[i].revents = Translate(host_pollfds[i].revents);
|
|
||||||
}
|
|
||||||
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
|
|
||||||
|
|
||||||
return Translate(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (new_fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
auto [result, bsd_errno] = descriptor.socket->Accept();
|
auto [result, bsd_errno] = descriptor.socket->Accept();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, Translate(bsd_errno)};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{};
|
file_descriptors[new_fd] = FileDescriptor{};
|
||||||
@@ -654,267 +666,218 @@ std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer)
|
|||||||
new_descriptor.socket = std::move(result.socket);
|
new_descriptor.socket = std::move(result.socket);
|
||||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||||
|
|
||||||
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
PutValue(write_buffer, result.sockaddr_in);
|
||||||
PutValue(write_buffer, guest_addr_in);
|
return {new_fd, Network::Errno::E_SUCCESS};
|
||||||
|
|
||||||
return {new_fd, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
return file_descriptors[fd]->socket->Bind(addr_in);
|
||||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
|
||||||
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
if (result == Network::Errno::E_ISCONN) {
|
||||||
|
|
||||||
if (result == Errno::ISCONN) {
|
|
||||||
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
||||||
|
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
return file_descriptors[fd]->socket->Listen(backlog);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
std::pair<s32, Network::Errno> BSD_USA::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
|
||||||
|
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case FcntlCmd::GETFL:
|
case Network::FcntlCmd::GETFL:
|
||||||
ASSERT(arg == 0);
|
ASSERT(arg == 0);
|
||||||
return {descriptor.flags, Errno::SUCCESS};
|
return {descriptor.flags, Network::Errno::E_SUCCESS};
|
||||||
case FcntlCmd::SETFL: {
|
case Network::FcntlCmd::SETFL: {
|
||||||
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
|
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
|
||||||
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
|
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, bsd_errno};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
descriptor.flags = arg;
|
descriptor.flags = arg;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
Network::Errno BSD_USA::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
|
||||||
|
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
if (level != Network::SocketLevel::SOCKET) {
|
||||||
UNIMPLEMENTED_MSG("Unknown getsockopt level");
|
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case OptName::ERROR_: {
|
case Network::OptName::ERROR_: {
|
||||||
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
||||||
if (getsockopt_err == Network::Errno::SUCCESS) {
|
if (getsockopt_err == Network::Errno::E_SUCCESS) {
|
||||||
Errno translated_pending_err = Translate(pending_err);
|
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
optval.size() == sizeof(Errno), { return Errno::INVAL; },
|
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
|
||||||
"Incorrect getsockopt option size");
|
"Incorrect getsockopt option size");
|
||||||
optval.resize(sizeof(Errno));
|
optval.resize(sizeof(Network::Errno));
|
||||||
PutValue(optval, translated_pending_err);
|
PutValue(optval, pending_err);
|
||||||
}
|
}
|
||||||
return Translate(getsockopt_err);
|
return getsockopt_err;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
Network::Errno BSD_USA::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
return socket->SetSockOpt(level, optname, optval);
|
||||||
if (optname == OptName::LINGER) {
|
|
||||||
ASSERT(optval.size() == sizeof(Linger));
|
|
||||||
auto linger = GetValue<Linger>(optval);
|
|
||||||
ASSERT(linger.onoff == 0 || linger.onoff == 1);
|
|
||||||
|
|
||||||
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(optval.size() == sizeof(u32));
|
|
||||||
auto value = GetValue<u32>(optval);
|
|
||||||
|
|
||||||
switch (optname) {
|
|
||||||
case OptName::REUSEADDR:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetReuseAddr(value != 0));
|
|
||||||
case OptName::KEEPALIVE:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetKeepAlive(value != 0));
|
|
||||||
case OptName::BROADCAST:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetBroadcast(value != 0));
|
|
||||||
case OptName::SNDBUF:
|
|
||||||
return Translate(socket->SetSndBuf(value));
|
|
||||||
case OptName::RCVBUF:
|
|
||||||
return Translate(socket->SetRcvBuf(value));
|
|
||||||
case OptName::SNDTIMEO:
|
|
||||||
return Translate(socket->SetSndTimeo(value));
|
|
||||||
case OptName::RCVTIMEO:
|
|
||||||
return Translate(socket->SetRcvTimeo(value));
|
|
||||||
case OptName::NOSIGPIPE:
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
||||||
|
LOG_DEBUG(Network, "fd={},how={}", fd, how);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
|
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
|
||||||
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
|
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
|
||||||
std::vector<u8>& addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
@@ -929,19 +892,17 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
|
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,58 +911,59 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
|
|||||||
addr.clear();
|
addr.clear();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
const SockAddrIn result = Translate(addr_in);
|
PutValue(addr, addr_in);
|
||||||
PutValue(addr, result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
return file_descriptors[fd]->socket->Send(message, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
|
||||||
std::span<const u8> addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr_in;
|
Network::SockAddrIn addr_in{};
|
||||||
Network::SockAddrIn* p_addr_in = nullptr;
|
Network::SockAddrIn* p_addr_in = nullptr;
|
||||||
if (!addr.empty()) {
|
if (!addr.empty()) {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
addr_in = Translate(guest_addr_in);
|
addr_in = guest_addr_in;
|
||||||
p_addr_in = &addr_in;
|
p_addr_in = &addr_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::CloseImpl(s32 fd) {
|
Network::Errno BSD_USA::CloseImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
auto const bsd_errno = file_descriptors[fd]->socket->Close();
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,15 +973,16 @@ Errno BSD_USA::CloseImpl(s32 fd) {
|
|||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (!IsFileDescriptorValid(new_fd)) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return Errno::MFILE;
|
return Network::Errno::E_MFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{
|
file_descriptors[new_fd] = FileDescriptor{
|
||||||
@@ -1031,6 +994,7 @@ std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -1042,41 +1006,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
||||||
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
// first three file descriptors are reserved for:
|
||||||
if (!file_descriptors[fd]) {
|
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
|
||||||
|
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
|
||||||
|
if (!file_descriptors[fd])
|
||||||
return fd;
|
return fd;
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
if (fd < 0 || fd >= s32(file_descriptors.size())) {
|
||||||
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
LOG_ERROR(Service, "Invalid handle={}", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]) {
|
if (!file_descriptors[fd]) {
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
|
LOG_ERROR(Service, "handle={} is not allocated", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||||
for (auto& optional_descriptor : file_descriptors) {
|
for (auto& optional_descriptor : file_descriptors) {
|
||||||
if (!optional_descriptor.has_value()) {
|
if (optional_descriptor.has_value()) {
|
||||||
continue;
|
FileDescriptor& descriptor = *optional_descriptor;
|
||||||
|
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||||
}
|
}
|
||||||
FileDescriptor& descriptor = *optional_descriptor;
|
|
||||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
@@ -35,8 +35,8 @@ public:
|
|||||||
// These methods are called from SSL; the first two are also called from
|
// These methods are called from SSL; the first two are also called from
|
||||||
// this class for the corresponding IPC methods.
|
// this class for the corresponding IPC methods.
|
||||||
// On the real device, the SSL service makes IPC calls to this service.
|
// On the real device, the SSL service makes IPC calls to this service.
|
||||||
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
|
||||||
Errno CloseImpl(s32 fd);
|
Network::Errno CloseImpl(s32 fd);
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -58,7 +58,7 @@ private:
|
|||||||
std::span<const u8> read_buffer;
|
std::span<const u8> read_buffer;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AcceptWork {
|
struct AcceptWork {
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
s32 fd;
|
s32 fd;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectWork {
|
struct ConnectWork {
|
||||||
@@ -77,7 +77,7 @@ private:
|
|||||||
|
|
||||||
s32 fd;
|
s32 fd;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvWork {
|
struct RecvWork {
|
||||||
@@ -88,7 +88,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvFromWork {
|
struct RecvFromWork {
|
||||||
@@ -100,7 +100,7 @@ private:
|
|||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
std::vector<u8> addr;
|
std::vector<u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendWork {
|
struct SendWork {
|
||||||
@@ -111,7 +111,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendToWork {
|
struct SendToWork {
|
||||||
@@ -123,7 +123,7 @@ private:
|
|||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterClient(HLERequestContext& ctx);
|
void RegisterClient(HLERequestContext& ctx);
|
||||||
@@ -155,29 +155,29 @@ private:
|
|||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(HLERequestContext& ctx, Work work);
|
void ExecuteWork(HLERequestContext& ctx, Work work);
|
||||||
|
|
||||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
|
||||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||||
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno BindImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno ListenImpl(s32 fd, s32 backlog);
|
Network::Errno ListenImpl(s32 fd, s32 backlog);
|
||||||
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
|
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
|
||||||
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
|
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
|
||||||
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
|
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
|
||||||
Errno ShutdownImpl(s32 fd, s32 how);
|
Network::Errno ShutdownImpl(s32 fd, s32 how);
|
||||||
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||||
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||||
std::vector<u8>& addr);
|
std::vector<u8>& addr);
|
||||||
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||||
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||||
std::span<const u8> addr);
|
std::span<const u8> addr);
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
|
|
||||||
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
||||||
|
|
||||||
|
|||||||
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
|
|||||||
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
||||||
}
|
}
|
||||||
|
|
||||||
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return NetDbError::TryAgain;
|
return NetDbError::TryAgain;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
default:
|
default:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
||||||
// some reason, but that doesn't seem useful to implement.
|
// some reason, but that doesn't seem useful to implement.
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return Errno::INVAL;
|
return Network::Errno::E_INVAL;
|
||||||
default:
|
default:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
|||||||
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
||||||
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
||||||
// doesn't implement h_errno.
|
// doesn't implement h_errno.
|
||||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
|
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
// h_name: use the input hostname (append nul-terminated)
|
// h_name: use the input hostname (append nul-terminated)
|
||||||
AppendNulTerminated(data, host);
|
AppendNulTerminated(data, host);
|
||||||
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
|
|
||||||
Append<u32_be>(data, 0); // count of h_aliases
|
Append<u32_be>(data, 0); // count of h_aliases
|
||||||
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
||||||
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
|
||||||
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
||||||
// h_addr_list:
|
// h_addr_list:
|
||||||
size_t count = vec.size();
|
size_t count = vec.size();
|
||||||
ASSERT(count <= UINT32_MAX);
|
ASSERT(count <= UINT32_MAX);
|
||||||
Append<u32_be>(data, static_cast<uint32_t>(count));
|
Append<u32_be>(data, u32(count));
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// On the Switch, this is passed through htonl despite already being
|
// On the Switch, this is passed through htonl despite already being
|
||||||
// big-endian, so it ends up as little-endian.
|
// big-endian, so it ends up as little-endian.
|
||||||
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||||
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||||
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
|
|
||||||
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
// Adapted from
|
// Adapted from
|
||||||
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// serialized addrinfo:
|
// serialized addrinfo:
|
||||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||||
Append<u32_be>(data, 0); // ai_flags
|
Append<u32_be>(data, 0); // ai_flags
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
|
||||||
Append<u32_be>(data, 16); // ai_addrlen
|
Append<u32_be>(data, 16); // ai_addrlen
|
||||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||||
|
|
||||||
// ai_addr:
|
// ai_addr:
|
||||||
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
|
||||||
// On the Switch, the following fields are passed through htonl despite
|
// On the Switch, the following fields are passed through htonl despite
|
||||||
// already being big-endian, so they end up as little-endian.
|
// already being big-endian, so they end up as little-endian.
|
||||||
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
||||||
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> service = std::nullopt;
|
std::optional<std::string> service = std::nullopt;
|
||||||
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, service);
|
auto res_v = Network::GetAddressInfo(host, service);
|
||||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||||
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
GetAddrInfoError gai_errno;
|
Network::GetAddrInfoError gai_errno;
|
||||||
};
|
};
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto input = rp.PopRaw<InputParameters>();
|
auto input = rp.PopRaw<InputParameters>();
|
||||||
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x10);
|
static_assert(sizeof(OutputParameters) == 0x10);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@@ -15,242 +16,6 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
enum class Errno : u32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
BADF = 9,
|
|
||||||
AGAIN = 11,
|
|
||||||
INVAL = 22,
|
|
||||||
MFILE = 24,
|
|
||||||
PIPE = 32,
|
|
||||||
MSGSIZE = 90,
|
|
||||||
CONNABORTED = 103,
|
|
||||||
CONNRESET = 104,
|
|
||||||
NOTCONN = 107,
|
|
||||||
TIMEDOUT = 110,
|
|
||||||
CONNREFUSED = 111,
|
|
||||||
INPROGRESS = 115,
|
|
||||||
ISCONN = 106,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError : s32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
ADDRFAMILY = 1,
|
|
||||||
AGAIN = 2,
|
|
||||||
BADFLAGS = 3,
|
|
||||||
FAIL = 4,
|
|
||||||
FAMILY = 5,
|
|
||||||
MEMORY = 6,
|
|
||||||
NODATA = 7,
|
|
||||||
NONAME = 8,
|
|
||||||
SERVICE = 9,
|
|
||||||
SOCKTYPE = 10,
|
|
||||||
SYSTEM = 11,
|
|
||||||
BADHINTS = 12,
|
|
||||||
PROTOCOL = 13,
|
|
||||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
|
||||||
OTHER = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Domain : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
INET = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Type : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
STREAM = 1,
|
|
||||||
DGRAM = 2,
|
|
||||||
RAW = 3,
|
|
||||||
SEQPACKET = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Protocol : u32 {
|
|
||||||
IP = 0,
|
|
||||||
ICMP = 1,
|
|
||||||
TCP = 6,
|
|
||||||
UDP = 17,
|
|
||||||
//
|
|
||||||
IPV6 = 41,
|
|
||||||
RAW = 255,
|
|
||||||
//
|
|
||||||
HOPOPTS = 0,
|
|
||||||
IGMP = 2,
|
|
||||||
GGP = 3,
|
|
||||||
IPV4 = 4,
|
|
||||||
ST = 7,
|
|
||||||
EGP = 8,
|
|
||||||
PIGP = 9,
|
|
||||||
RCCMON = 10,
|
|
||||||
NVPII = 11,
|
|
||||||
PUP = 12,
|
|
||||||
ARGUS = 13,
|
|
||||||
EMCON = 14,
|
|
||||||
XNET = 15,
|
|
||||||
CHAOS = 16,
|
|
||||||
MUX = 18,
|
|
||||||
MEAS = 19,
|
|
||||||
HMP = 20,
|
|
||||||
PRM = 21,
|
|
||||||
IDP = 22,
|
|
||||||
TRUNK1 = 23,
|
|
||||||
TRUNK2 = 24,
|
|
||||||
LEAF1 = 25,
|
|
||||||
LEAF2 = 26,
|
|
||||||
RDP = 27,
|
|
||||||
IRTP = 28,
|
|
||||||
TP = 29,
|
|
||||||
BLT = 30,
|
|
||||||
NSP = 31,
|
|
||||||
INP = 32,
|
|
||||||
DCCP = 33,
|
|
||||||
//3PC = 34,
|
|
||||||
IDPR = 35,
|
|
||||||
XTP = 36,
|
|
||||||
DDP = 37,
|
|
||||||
CMTP = 38,
|
|
||||||
TPXX = 39,
|
|
||||||
IL = 40,
|
|
||||||
SDRP = 42,
|
|
||||||
ROUTING = 43,
|
|
||||||
FRAGMENT = 44,
|
|
||||||
IDRP = 45,
|
|
||||||
RSVP = 46,
|
|
||||||
GRE = 47,
|
|
||||||
MHRP = 48,
|
|
||||||
BHA = 49,
|
|
||||||
ESP = 50,
|
|
||||||
AH = 51,
|
|
||||||
INLSP = 52,
|
|
||||||
SWIPE = 53,
|
|
||||||
NHRP = 54,
|
|
||||||
MOBILE = 55,
|
|
||||||
TLSP = 56,
|
|
||||||
SKIP = 57,
|
|
||||||
ICMPV6 = 58,
|
|
||||||
NONE = 59,
|
|
||||||
DSTOPTS = 60,
|
|
||||||
AHIP = 61,
|
|
||||||
CFTP = 62,
|
|
||||||
HELLO = 63,
|
|
||||||
SATEXPAK = 64,
|
|
||||||
KRYPTOLAN = 65,
|
|
||||||
RVD = 66,
|
|
||||||
IPPC = 67,
|
|
||||||
ADFS = 68,
|
|
||||||
SATMON = 69,
|
|
||||||
VISA = 70,
|
|
||||||
IPCV = 71,
|
|
||||||
CPNX = 72,
|
|
||||||
CPHB = 73,
|
|
||||||
WSN = 74,
|
|
||||||
PVP = 75,
|
|
||||||
BRSATMON = 76,
|
|
||||||
ND = 77,
|
|
||||||
WBMON = 78,
|
|
||||||
WBEXPAK = 79,
|
|
||||||
EON = 80,
|
|
||||||
VMTP = 81,
|
|
||||||
SVMTP = 82,
|
|
||||||
VINES = 83,
|
|
||||||
TTP = 84,
|
|
||||||
IGP = 85,
|
|
||||||
DGP = 86,
|
|
||||||
TCF = 87,
|
|
||||||
IGRP = 88,
|
|
||||||
OSPFIGP = 89,
|
|
||||||
SRPC = 90,
|
|
||||||
LARP = 91,
|
|
||||||
MTP = 92,
|
|
||||||
AX25 = 93,
|
|
||||||
IPEIP = 94,
|
|
||||||
MICP = 95,
|
|
||||||
SCCSP = 96,
|
|
||||||
ETHERIP = 97,
|
|
||||||
ENCAP = 98,
|
|
||||||
APES = 99,
|
|
||||||
GMTP = 100,
|
|
||||||
IPCOMP = 108,
|
|
||||||
SCTP = 132,
|
|
||||||
MH = 135,
|
|
||||||
UDPLITE = 136,
|
|
||||||
HIP = 139,
|
|
||||||
SHIM6 = 140,
|
|
||||||
PIM = 103,
|
|
||||||
CARP = 112,
|
|
||||||
PGM = 113,
|
|
||||||
MPLS = 137,
|
|
||||||
PFSYNC = 240,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketLevel : u32 {
|
|
||||||
IP = 0,
|
|
||||||
TCP = 6,
|
|
||||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OptName : u32 {
|
|
||||||
REUSEADDR = 0x4,
|
|
||||||
KEEPALIVE = 0x8,
|
|
||||||
BROADCAST = 0x20,
|
|
||||||
LINGER = 0x80,
|
|
||||||
SNDBUF = 0x1001,
|
|
||||||
RCVBUF = 0x1002,
|
|
||||||
SNDTIMEO = 0x1005,
|
|
||||||
RCVTIMEO = 0x1006,
|
|
||||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
|
||||||
NOSIGPIPE = 0x800, // at least according to libnx
|
|
||||||
ACCEPTFILTER = 0x1000,
|
|
||||||
BINTIME = 0x2000,
|
|
||||||
NO_OFFLOAD = 0x4000,
|
|
||||||
NO_DDP = 0x8000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShutdownHow : s32 {
|
|
||||||
RD = 0,
|
|
||||||
WR = 1,
|
|
||||||
RDWR = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FcntlCmd : s32 {
|
|
||||||
GETFL = 3,
|
|
||||||
SETFL = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SockAddrIn {
|
|
||||||
u8 len;
|
|
||||||
u8 family;
|
|
||||||
u16 portno;
|
|
||||||
std::array<u8, 4> ip;
|
|
||||||
std::array<u8, 248> zeroes;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
In = 1 << 0,
|
|
||||||
Pri = 1 << 1,
|
|
||||||
Out = 1 << 2,
|
|
||||||
Err = 1 << 3,
|
|
||||||
Hup = 1 << 4,
|
|
||||||
Nval = 1 << 5,
|
|
||||||
RdNorm = 1 << 6,
|
|
||||||
RdBand = 1 << 7,
|
|
||||||
WrBand = 1 << 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
s32 fd;
|
|
||||||
PollEvents events;
|
|
||||||
PollEvents revents;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Linger {
|
|
||||||
u32 onoff;
|
|
||||||
u32 linger;
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
void LoopProcess(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -15,388 +15,42 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
Errno Translate(Network::Errno value) {
|
const char* Translate(Network::GetAddrInfoError error) {
|
||||||
switch (value) {
|
|
||||||
case Network::Errno::SUCCESS:
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
case Network::Errno::BADF:
|
|
||||||
return Errno::BADF;
|
|
||||||
case Network::Errno::AGAIN:
|
|
||||||
return Errno::AGAIN;
|
|
||||||
case Network::Errno::INVAL:
|
|
||||||
return Errno::INVAL;
|
|
||||||
case Network::Errno::MFILE:
|
|
||||||
return Errno::MFILE;
|
|
||||||
case Network::Errno::PIPE:
|
|
||||||
return Errno::PIPE;
|
|
||||||
case Network::Errno::CONNREFUSED:
|
|
||||||
return Errno::CONNREFUSED;
|
|
||||||
case Network::Errno::NOTCONN:
|
|
||||||
return Errno::NOTCONN;
|
|
||||||
case Network::Errno::TIMEDOUT:
|
|
||||||
return Errno::TIMEDOUT;
|
|
||||||
case Network::Errno::CONNABORTED:
|
|
||||||
return Errno::CONNABORTED;
|
|
||||||
case Network::Errno::CONNRESET:
|
|
||||||
return Errno::CONNRESET;
|
|
||||||
case Network::Errno::INPROGRESS:
|
|
||||||
return Errno::INPROGRESS;
|
|
||||||
case Network::Errno::ISCONN:
|
|
||||||
return Errno::ISCONN;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
|
||||||
return {value.first, Translate(value.second)};
|
|
||||||
}
|
|
||||||
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
|
|
||||||
switch (error) {
|
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
|
||||||
return GetAddrInfoError::SUCCESS;
|
|
||||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
|
||||||
return GetAddrInfoError::ADDRFAMILY;
|
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
|
||||||
return GetAddrInfoError::AGAIN;
|
|
||||||
case Network::GetAddrInfoError::BADFLAGS:
|
|
||||||
return GetAddrInfoError::BADFLAGS;
|
|
||||||
case Network::GetAddrInfoError::FAIL:
|
|
||||||
return GetAddrInfoError::FAIL;
|
|
||||||
case Network::GetAddrInfoError::FAMILY:
|
|
||||||
return GetAddrInfoError::FAMILY;
|
|
||||||
case Network::GetAddrInfoError::MEMORY:
|
|
||||||
return GetAddrInfoError::MEMORY;
|
|
||||||
case Network::GetAddrInfoError::NODATA:
|
|
||||||
return GetAddrInfoError::NODATA;
|
|
||||||
case Network::GetAddrInfoError::NONAME:
|
|
||||||
return GetAddrInfoError::NONAME;
|
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
|
||||||
return GetAddrInfoError::SERVICE;
|
|
||||||
case Network::GetAddrInfoError::SOCKTYPE:
|
|
||||||
return GetAddrInfoError::SOCKTYPE;
|
|
||||||
case Network::GetAddrInfoError::SYSTEM:
|
|
||||||
return GetAddrInfoError::SYSTEM;
|
|
||||||
case Network::GetAddrInfoError::BADHINTS:
|
|
||||||
return GetAddrInfoError::BADHINTS;
|
|
||||||
case Network::GetAddrInfoError::PROTOCOL:
|
|
||||||
return GetAddrInfoError::PROTOCOL;
|
|
||||||
case Network::GetAddrInfoError::OVERFLOW_:
|
|
||||||
return GetAddrInfoError::OVERFLOW_;
|
|
||||||
case Network::GetAddrInfoError::OTHER:
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Translate(GetAddrInfoError error) {
|
|
||||||
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return "Success";
|
return "Success";
|
||||||
case GetAddrInfoError::ADDRFAMILY:
|
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||||
return "Address family for hostname not supported";
|
return "Address family for hostname not supported";
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return "Temporary failure in name resolution";
|
return "Temporary failure in name resolution";
|
||||||
case GetAddrInfoError::BADFLAGS:
|
case Network::GetAddrInfoError::BADFLAGS:
|
||||||
return "Invalid value for ai_flags";
|
return "Invalid value for ai_flags";
|
||||||
case GetAddrInfoError::FAIL:
|
case Network::GetAddrInfoError::FAIL:
|
||||||
return "Non-recoverable failure in name resolution";
|
return "Non-recoverable failure in name resolution";
|
||||||
case GetAddrInfoError::FAMILY:
|
case Network::GetAddrInfoError::FAMILY:
|
||||||
return "ai_family not supported";
|
return "ai_family not supported";
|
||||||
case GetAddrInfoError::MEMORY:
|
case Network::GetAddrInfoError::MEMORY:
|
||||||
return "Memory allocation failure";
|
return "Memory allocation failure";
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return "No address associated with hostname";
|
return "No address associated with hostname";
|
||||||
case GetAddrInfoError::NONAME:
|
case Network::GetAddrInfoError::NONAME:
|
||||||
return "hostname nor servname provided, or not known";
|
return "hostname nor servname provided, or not known";
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return "servname not supported for ai_socktype";
|
return "servname not supported for ai_socktype";
|
||||||
case GetAddrInfoError::SOCKTYPE:
|
case Network::GetAddrInfoError::SOCKTYPE:
|
||||||
return "ai_socktype not supported";
|
return "ai_socktype not supported";
|
||||||
case GetAddrInfoError::SYSTEM:
|
case Network::GetAddrInfoError::SYSTEM:
|
||||||
return "System error returned in errno";
|
return "System error returned in errno";
|
||||||
case GetAddrInfoError::BADHINTS:
|
case Network::GetAddrInfoError::BADHINTS:
|
||||||
return "Invalid value for hints";
|
return "Invalid value for hints";
|
||||||
case GetAddrInfoError::PROTOCOL:
|
case Network::GetAddrInfoError::PROTOCOL:
|
||||||
return "Resolved protocol is unknown";
|
return "Resolved protocol is unknown";
|
||||||
case GetAddrInfoError::OVERFLOW_:
|
case Network::GetAddrInfoError::OVERFLOW_:
|
||||||
return "Argument buffer overflow";
|
return "Argument buffer overflow";
|
||||||
default:
|
default:
|
||||||
return "Unknown error";
|
return "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Domain Translate(Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Domain::Unspecified:
|
|
||||||
return Network::Domain::Unspecified;
|
|
||||||
case Domain::INET:
|
|
||||||
return Network::Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Domain Translate(Network::Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Network::Domain::Unspecified:
|
|
||||||
return Domain::Unspecified;
|
|
||||||
case Network::Domain::INET:
|
|
||||||
return Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::Type Translate(Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Type::Unspecified:
|
|
||||||
return Network::Type::Unspecified;
|
|
||||||
case Type::STREAM:
|
|
||||||
return Network::Type::STREAM;
|
|
||||||
case Type::DGRAM:
|
|
||||||
return Network::Type::DGRAM;
|
|
||||||
case Type::RAW:
|
|
||||||
return Network::Type::RAW;
|
|
||||||
case Type::SEQPACKET:
|
|
||||||
return Network::Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Network::Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Type Translate(Network::Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Network::Type::Unspecified: return Type::Unspecified;
|
|
||||||
case Network::Type::STREAM: return Type::STREAM;
|
|
||||||
case Network::Type::DGRAM: return Type::DGRAM;
|
|
||||||
case Network::Type::RAW: return Type::RAW;
|
|
||||||
case Network::Type::SEQPACKET: return Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
|
|
||||||
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
|
|
||||||
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
|
|
||||||
Network::PollEvents Translate(PollEvents flags) {
|
|
||||||
Network::PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
translate(PollEvents::In, Network::PollEvents::In);
|
|
||||||
translate(PollEvents::Pri, Network::PollEvents::Pri);
|
|
||||||
translate(PollEvents::Out, Network::PollEvents::Out);
|
|
||||||
translate(PollEvents::Err, Network::PollEvents::Err);
|
|
||||||
translate(PollEvents::Hup, Network::PollEvents::Hup);
|
|
||||||
translate(PollEvents::Nval, Network::PollEvents::Nval);
|
|
||||||
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
|
|
||||||
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
|
|
||||||
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
PollEvents Translate(Network::PollEvents flags) {
|
|
||||||
PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
translate(Network::PollEvents::In, PollEvents::In);
|
|
||||||
translate(Network::PollEvents::Pri, PollEvents::Pri);
|
|
||||||
translate(Network::PollEvents::Out, PollEvents::Out);
|
|
||||||
translate(Network::PollEvents::Err, PollEvents::Err);
|
|
||||||
translate(Network::PollEvents::Hup, PollEvents::Hup);
|
|
||||||
translate(Network::PollEvents::Nval, PollEvents::Nval);
|
|
||||||
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
|
|
||||||
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
|
|
||||||
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value) {
|
|
||||||
// All lengths are valid, from [0 upto 256]
|
|
||||||
return {
|
|
||||||
.family = Translate(Domain(value.family)),
|
|
||||||
.ip = value.ip,
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value) {
|
|
||||||
return {
|
|
||||||
.len = 16,
|
|
||||||
.family = static_cast<u8>(Translate(value.family)),
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
.ip = value.ip,
|
|
||||||
.zeroes = {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how) {
|
|
||||||
switch (how) {
|
|
||||||
case ShutdownHow::RD:
|
|
||||||
return Network::ShutdownHow::RD;
|
|
||||||
case ShutdownHow::WR:
|
|
||||||
return Network::ShutdownHow::WR;
|
|
||||||
case ShutdownHow::RDWR:
|
|
||||||
return Network::ShutdownHow::RDWR;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,49 +14,7 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
/// Translate abstract errno to guest errno
|
|
||||||
Errno Translate(Network::Errno value);
|
|
||||||
|
|
||||||
/// Translate abstract return value errno pair to guest return value errno pair
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
|
||||||
|
|
||||||
/// Translate abstract getaddrinfo error to guest getaddrinfo error
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError value);
|
|
||||||
|
|
||||||
/// Translate guest error to string
|
/// Translate guest error to string
|
||||||
const char* Translate(GetAddrInfoError value);
|
const char* Translate(Network::GetAddrInfoError value);
|
||||||
|
|
||||||
/// Translate guest domain to abstract domain
|
|
||||||
Network::Domain Translate(Domain domain);
|
|
||||||
|
|
||||||
/// Translate abstract domain to guest domain
|
|
||||||
Domain Translate(Network::Domain domain);
|
|
||||||
|
|
||||||
/// Translate guest type to abstract type
|
|
||||||
Network::Type Translate(Type type);
|
|
||||||
|
|
||||||
/// Translate abstract type to guest type
|
|
||||||
Type Translate(Network::Type type);
|
|
||||||
|
|
||||||
/// Translate guest protocol to abstract protocol
|
|
||||||
Network::Protocol Translate(Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate abstract protocol to guest protocol
|
|
||||||
Protocol Translate(Network::Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate guest poll event flags to abstract poll event flags
|
|
||||||
Network::PollEvents Translate(PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate abstract poll event flags to guest poll event flags
|
|
||||||
PollEvents Translate(Network::PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate guest socket address structure to abstract socket address structure
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate abstract socket address structure to guest socket address structure
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate guest shutdown mode to abstract shutdown mode
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how);
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public:
|
|||||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||||
if (bsd) {
|
if (bsd) {
|
||||||
auto err = bsd->CloseImpl(fd);
|
auto err = bsd->CloseImpl(fd);
|
||||||
if (err != Service::Sockets::Errno::SUCCESS) {
|
if (err != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ private:
|
|||||||
|
|
||||||
const bool non_block = mode == IoMode::NonBlocking;
|
const bool non_block = mode == IoMode::NonBlocking;
|
||||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||||
if (error != Network::Errno::SUCCESS) {
|
if (error != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
|||||||
@@ -290,10 +290,10 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
return 1;
|
return 1;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
@@ -309,13 +309,13 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
if (actual == 0) {
|
if (actual == 0) {
|
||||||
self->got_read_eof = true;
|
self->got_read_eof = true;
|
||||||
}
|
}
|
||||||
return actual ? 1 : 0;
|
return actual ? 1 : 0;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ public:
|
|||||||
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
|
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
|
||||||
const auto [actual, err] = socket->Recv(0, read_span);
|
const auto [actual, err] = socket->Recv(0, read_span);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
ASSERT(static_cast<size_t>(actual) <= fill_size);
|
ASSERT(static_cast<size_t>(actual) <= fill_size);
|
||||||
ciphertext_read_buf.resize(offset + actual);
|
ciphertext_read_buf.resize(offset + actual);
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
ciphertext_read_buf.resize(offset);
|
ciphertext_read_buf.resize(offset);
|
||||||
return ResultWouldBlock;
|
return ResultWouldBlock;
|
||||||
default:
|
default:
|
||||||
@@ -165,12 +165,12 @@ public:
|
|||||||
while (!ciphertext_write_buf.empty()) {
|
while (!ciphertext_write_buf.empty()) {
|
||||||
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
|
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
|
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
|
||||||
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
|
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
|
||||||
ciphertext_write_buf.begin() + actual);
|
ciphertext_write_buf.begin() + actual);
|
||||||
break;
|
break;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
return ResultWouldBlock;
|
return ResultWouldBlock;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
|
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public:
|
|||||||
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
|
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
|
||||||
actual, cur.size(), static_cast<s32>(err));
|
actual, cur.size(), static_cast<s32>(err));
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
offset += actual;
|
offset += actual;
|
||||||
if (actual == 0) {
|
if (actual == 0) {
|
||||||
ASSERT(is_read);
|
ASSERT(is_read);
|
||||||
@@ -203,7 +203,7 @@ public:
|
|||||||
return errSecEndOfData;
|
return errSecEndOfData;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
*dataLength = offset;
|
*dataLength = offset;
|
||||||
return errSSLWouldBlock;
|
return errSSLWouldBlock;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <common/socket_types.h>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -31,68 +31,10 @@ namespace Network {
|
|||||||
class SocketBase;
|
class SocketBase;
|
||||||
class Socket;
|
class Socket;
|
||||||
|
|
||||||
/// Error code for network functions
|
struct HostPollFD {
|
||||||
enum class Errno {
|
SocketBase* socket = nullptr;
|
||||||
SUCCESS,
|
Network::PollEvents events = {};
|
||||||
BADF,
|
Network::PollEvents revents = {};
|
||||||
INVAL,
|
|
||||||
MFILE,
|
|
||||||
PIPE,
|
|
||||||
NOTCONN,
|
|
||||||
AGAIN,
|
|
||||||
CONNREFUSED,
|
|
||||||
CONNRESET,
|
|
||||||
CONNABORTED,
|
|
||||||
HOSTUNREACH,
|
|
||||||
NETDOWN,
|
|
||||||
NETUNREACH,
|
|
||||||
TIMEDOUT,
|
|
||||||
MSGSIZE,
|
|
||||||
INPROGRESS,
|
|
||||||
ISCONN,
|
|
||||||
OTHER,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError {
|
|
||||||
SUCCESS,
|
|
||||||
ADDRFAMILY,
|
|
||||||
AGAIN,
|
|
||||||
BADFLAGS,
|
|
||||||
FAIL,
|
|
||||||
FAMILY,
|
|
||||||
MEMORY,
|
|
||||||
NODATA,
|
|
||||||
NONAME,
|
|
||||||
SERVICE,
|
|
||||||
SOCKTYPE,
|
|
||||||
SYSTEM,
|
|
||||||
BADHINTS,
|
|
||||||
PROTOCOL,
|
|
||||||
OVERFLOW_,
|
|
||||||
OTHER,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Cross-platform poll fd structure
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
In = 1 << 0,
|
|
||||||
Pri = 1 << 1,
|
|
||||||
Out = 1 << 2,
|
|
||||||
Err = 1 << 3,
|
|
||||||
Hup = 1 << 4,
|
|
||||||
Nval = 1 << 5,
|
|
||||||
RdNorm = 1 << 6,
|
|
||||||
RdBand = 1 << 7,
|
|
||||||
WrBand = 1 << 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
SocketBase* socket;
|
|
||||||
PollEvents events;
|
|
||||||
PollEvents revents;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkInstance {
|
class NetworkInstance {
|
||||||
@@ -101,6 +43,10 @@ public:
|
|||||||
~NetworkInstance();
|
~NetworkInstance();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
|
||||||
|
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
|
||||||
|
s32 TranslateMsgOptToNative(s32 flags);
|
||||||
|
|
||||||
void CancelPendingSocketOperations();
|
void CancelPendingSocketOperations();
|
||||||
void RestartSocketOperations();
|
void RestartSocketOperations();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Network {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
|
|
||||||
ULONG buf_size = 0;
|
ULONG buf_size = 0;
|
||||||
if (GetAdaptersAddresses(
|
if (GetAdaptersAddresses(
|
||||||
@@ -66,7 +66,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> result;
|
std::vector<NetworkInterface> result;
|
||||||
|
|
||||||
for (auto* a = addrs; a; a = a->Next) {
|
for (auto* a = addrs; a; a = a->Next) {
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
||||||
->sin_addr;
|
->sin_addr;
|
||||||
|
|
||||||
result.emplace_back(Network::NetworkInterface{
|
result.emplace_back(NetworkInterface{
|
||||||
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
||||||
.ip_address = ip,
|
.ip_address = ip,
|
||||||
.subnet_mask = mask,
|
.subnet_mask = mask,
|
||||||
@@ -103,7 +103,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
#if defined(__ANDROID__) || defined(__linux__)
|
#if defined(__ANDROID__) || defined(__linux__)
|
||||||
struct ifaddrs* ifaddr = nullptr;
|
struct ifaddrs* ifaddr = nullptr;
|
||||||
if (getifaddrs(&ifaddr) != 0) {
|
if (getifaddrs(&ifaddr) != 0) {
|
||||||
@@ -135,7 +135,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<Network::NetworkInterface> ifaces;
|
std::vector<NetworkInterface> ifaces;
|
||||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||||
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
||||||
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
||||||
@@ -149,7 +149,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
});
|
});
|
||||||
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
||||||
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
||||||
ifaces.emplace_back(Network::NetworkInterface{
|
ifaces.emplace_back(NetworkInterface{
|
||||||
.name = ifa->ifa_name,
|
.name = ifa->ifa_name,
|
||||||
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
||||||
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
||||||
@@ -159,7 +159,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
freeifaddrs(ifaddr);
|
freeifaddrs(ifaddr);
|
||||||
return ifaces;
|
return ifaces;
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
std::vector<Network::NetworkInterface> ifaces;
|
std::vector<NetworkInterface> ifaces;
|
||||||
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
||||||
@@ -191,7 +191,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
||||||
char const* p = (char const*)(ifm + 1);
|
char const* p = (char const*)(ifm + 1);
|
||||||
|
|
||||||
Network::NetworkInterface iface{};
|
NetworkInterface iface{};
|
||||||
for (size_t i = 0; i < RTAX_MAX; i++)
|
for (size_t i = 0; i < RTAX_MAX; i++)
|
||||||
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
||||||
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
||||||
@@ -220,7 +220,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
|
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||||
auto const& sel_if = Settings::values.network_interface.GetValue();
|
auto const& sel_if = Settings::values.network_interface.GetValue();
|
||||||
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
||||||
if (sel_if.empty())
|
if (sel_if.empty())
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#ifdef __unix__
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "core/internal_network/socket_icmp.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern char **environ;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
u16 ComputeChecksum(std::span<const u8> data) {
|
||||||
|
u32 sum = 0;
|
||||||
|
for (size_t i = 0; i < data.size(); i += 2) {
|
||||||
|
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
|
||||||
|
sum += value;
|
||||||
|
}
|
||||||
|
if (data.size() % 2 != 0){
|
||||||
|
sum += u16(data[data.size() - 1]) << 8;
|
||||||
|
}
|
||||||
|
while ((sum >> 16) != 0)
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
return (~sum) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IcmpSocket::~IcmpSocket() {
|
||||||
|
if (fd == INVALID_SOCKET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
|
||||||
|
if (optname == Network::OptName::RCVTIMEO) {
|
||||||
|
if (optval.size() < sizeof(Network::Timeval))
|
||||||
|
return Errno::E_INVAL;
|
||||||
|
std::memcpy(&rcv_timeo, optval.data(), sizeof(rcv_timeo));
|
||||||
|
}
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
connected_addr = addr_in;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Listen(s32 backlog) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Shutdown(ShutdownHow how) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
return connected_addr.has_value()
|
||||||
|
? RecvFrom(flags, message, nullptr)
|
||||||
|
: std::make_pair(s32(0), Errno::E_NOTCONN);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
ASSERT(flags == 0);
|
||||||
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
|
||||||
|
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
|
||||||
|
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||||
|
const auto timestamp = std::chrono::steady_clock::now();
|
||||||
|
while (true) {
|
||||||
|
{
|
||||||
|
std::lock_guard guard(pings_mutex);
|
||||||
|
// find ping process that is finished running
|
||||||
|
for (auto it = pings.begin(); it != pings.end();) {
|
||||||
|
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
|
||||||
|
// ping process is still running, go to next
|
||||||
|
if (result != it->ping_pid) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ping process is finished, remove and handle it
|
||||||
|
it = pings.erase(it);
|
||||||
|
if (it->ping_status == 0) {
|
||||||
|
if (addr) {
|
||||||
|
addr->family = it->family;
|
||||||
|
addr->ip = it->ip;
|
||||||
|
addr->portno = it->portno;
|
||||||
|
addr->len = 16;
|
||||||
|
addr->zeroes = {};
|
||||||
|
}
|
||||||
|
std::array<u8, 8> data{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, //checksum
|
||||||
|
0,
|
||||||
|
it->seq_ident[0],
|
||||||
|
it->seq_ident[1],
|
||||||
|
it->seq_ident[2],
|
||||||
|
it->seq_ident[3]
|
||||||
|
};
|
||||||
|
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
|
||||||
|
data[2] = u8(csum >> 8); //hi
|
||||||
|
data[3] = u8(csum); //lo
|
||||||
|
auto const n = std::min(data.size(), message.size());
|
||||||
|
std::copy(data.begin(), data.begin() + n, message.begin());
|
||||||
|
return {s32(n), Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blocking)
|
||||||
|
return {-1, Errno::E_AGAIN};
|
||||||
|
|
||||||
|
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||||
|
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||||
|
if (time_diff_ms > rcv_timeout_ms)
|
||||||
|
return {-1, Errno::E_TIMEDOUT};
|
||||||
|
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
if (connected_addr.has_value())
|
||||||
|
return SendTo(flags, message, std::addressof(connected_addr.value()));
|
||||||
|
return {s32(0), Errno::E_NOTCONN};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||||
|
// 0 -> 8 (IPv4), 128 (IPv6)
|
||||||
|
// 1 -> 0
|
||||||
|
// 2..4 -> checksum
|
||||||
|
// 4..6 -> ident
|
||||||
|
// 6..8 -> seq
|
||||||
|
|
||||||
|
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
|
||||||
|
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
|
||||||
|
#if !(defined(__OPENORBIS__) || defined(__ANDROID__)) && (defined(__FreeBSD__) || defined(__linux__))
|
||||||
|
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||||
|
if (!addr)
|
||||||
|
return {-1, Errno::E_DESTADDRREQ};
|
||||||
|
|
||||||
|
if (message.size() >= 8) {
|
||||||
|
std::string ip_str = fmt::format(
|
||||||
|
"{}.{}.{}.{}",
|
||||||
|
addr->ip[0],
|
||||||
|
addr->ip[1],
|
||||||
|
addr->ip[2],
|
||||||
|
addr->ip[3]
|
||||||
|
);
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
// ping -W option is a nonfractional int (milliseconds)
|
||||||
|
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
|
||||||
|
std::vector<char*> argv = {
|
||||||
|
const_cast<char*>("ping"),
|
||||||
|
const_cast<char*>("-c"),
|
||||||
|
const_cast<char*>("1"),
|
||||||
|
const_cast<char*>("-W"),
|
||||||
|
timeout_str.data(),
|
||||||
|
ip_str.data(),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
#elif defined(__linux__)
|
||||||
|
// ping -W option is a fractional float (seconds)
|
||||||
|
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
|
||||||
|
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
|
||||||
|
std::vector<char*> argv = {
|
||||||
|
const_cast<char*>("ping"),
|
||||||
|
const_cast<char*>("-c"),
|
||||||
|
const_cast<char*>("1"),
|
||||||
|
const_cast<char*>("-W"),
|
||||||
|
timeout_str.data(),
|
||||||
|
ip_str.data(),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
pid_t ping_pid;
|
||||||
|
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
|
||||||
|
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
|
||||||
|
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
std::lock_guard guard(pings_mutex);
|
||||||
|
if (pings.size() >= pings.max_size())
|
||||||
|
pings.erase(pings.begin());
|
||||||
|
pings.push_back(PingProcessData{
|
||||||
|
.ip = addr->ip,
|
||||||
|
.portno = addr->portno,
|
||||||
|
.ping_pid = ping_pid,
|
||||||
|
.ping_status = 0,
|
||||||
|
.seq_ident = {
|
||||||
|
message[4],
|
||||||
|
message[5],
|
||||||
|
message[6],
|
||||||
|
message[7]
|
||||||
|
},
|
||||||
|
.family = addr->family,
|
||||||
|
});
|
||||||
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Close() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
|
fd = INVALID_SOCKET;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
|
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IcmpSocket::IsOpened() const {
|
||||||
|
return fd != INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
}
|
||||||
|
Errno IcmpSocket::SetNonBlock(bool enable) {
|
||||||
|
blocking = !enable;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <span>
|
||||||
|
#include <utility>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
#include "core/internal_network/sockets.h"
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
struct PingProcessData {
|
||||||
|
IPv4Address ip;
|
||||||
|
u16 portno;
|
||||||
|
pid_t ping_pid;
|
||||||
|
pid_t ping_status;
|
||||||
|
std::array<u8, 4> seq_ident;
|
||||||
|
u8 family;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IcmpSocket : public Network::SocketBase {
|
||||||
|
public:
|
||||||
|
explicit IcmpSocket() noexcept = default;
|
||||||
|
~IcmpSocket() override;
|
||||||
|
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
||||||
|
Errno Close() override;
|
||||||
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
Errno Listen(s32 backlog) override;
|
||||||
|
Errno Shutdown(ShutdownHow how) override;
|
||||||
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||||
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
bool IsOpened() const override;
|
||||||
|
void HandleProxyPacket(const ProxyPacket& packet) override;
|
||||||
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
|
boost::container::static_vector<PingProcessData, 128> pings;
|
||||||
|
std::optional<SockAddrIn> connected_addr;
|
||||||
|
std::mutex pings_mutex;
|
||||||
|
Network::Timeval rcv_timeo;
|
||||||
|
bool blocking = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -47,71 +47,82 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|||||||
received_packets.push(decompressed);
|
received_packets.push(decompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
Errno ProxySocket::SetNonBlock(bool enable) {
|
||||||
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
|
blocking = !enable;
|
||||||
LOG_DEBUG(Network, "(STUBBED) called");
|
return Errno::E_SUCCESS;
|
||||||
return Errno::SUCCESS;
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
|
||||||
|
// numeric values?
|
||||||
|
if (optval.size() >= sizeof(u32)) {
|
||||||
|
u32 value;
|
||||||
|
std::memcpy(&value, optval.data(), sizeof(value));
|
||||||
|
if (optname == Network::OptName::BROADCAST)
|
||||||
|
broadcast = bool(value);
|
||||||
|
if (optname == Network::OptName::SNDTIMEO)
|
||||||
|
send_timeout = value;
|
||||||
|
if (optname == Network::OptName::RCVTIMEO)
|
||||||
|
receive_timeout = value;
|
||||||
|
}
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||||
protocol = socket_protocol;
|
protocol = socket_protocol;
|
||||||
SetSockOpt(fd, SO_TYPE, type);
|
return Errno::E_SUCCESS;
|
||||||
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {AcceptResult{}, Errno::SUCCESS};
|
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Connect(SockAddrIn addr_in) {
|
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {SockAddrIn{}, Errno::SUCCESS};
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
|
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {SockAddrIn{}, Errno::SUCCESS};
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Bind(SockAddrIn addr) {
|
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
|
||||||
if (is_bound) {
|
if (is_bound) {
|
||||||
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
local_endpoint = addr;
|
local_endpoint = addr;
|
||||||
is_bound = true;
|
is_bound = true;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Listen(s32 backlog) {
|
Errno ProxySocket::Listen(s32 backlog) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
return {s32(0), Errno::E_SUCCESS};
|
||||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
|
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
|
||||||
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
||||||
const auto timestamp = std::chrono::steady_clock::now();
|
const auto timestamp = std::chrono::steady_clock::now();
|
||||||
@@ -128,88 +139,85 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!blocking) {
|
if (!blocking) {
|
||||||
return {-1, Errno::AGAIN};
|
return {-1, Errno::E_AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
|
|
||||||
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||||
const auto time_diff_ms =
|
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
|
||||||
|
|
||||||
if (time_diff_ms > timeout) {
|
if (time_diff_ms > timeout) {
|
||||||
return {-1, Errno::TIMEDOUT};
|
return {-1, Errno::E_TIMEDOUT};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
|
||||||
std::size_t max_length) {
|
LOG_DEBUG(Network, "called");
|
||||||
ProxyPacket& packet = received_packets.front();
|
ProxyPacket& packet = received_packets.front();
|
||||||
if (addr) {
|
if (addr) {
|
||||||
addr->family = Domain::INET;
|
addr->len = 16;
|
||||||
|
addr->family = u8(Network::Domain::INET);
|
||||||
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
||||||
addr->portno = packet.local_endpoint.portno; // The senders port number
|
addr->portno = packet.local_endpoint.portno; // The senders port number
|
||||||
|
addr->zeroes = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool peek = (flags & FLAG_MSG_PEEK) != 0;
|
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
|
||||||
std::size_t read_bytes;
|
std::size_t read_bytes;
|
||||||
if (packet.data.size() > max_length) {
|
if (packet.data.size() > max_length) {
|
||||||
read_bytes = max_length;
|
read_bytes = max_length;
|
||||||
memcpy(message.data(), packet.data.data(), max_length);
|
std::memcpy(message.data(), packet.data.data(), max_length);
|
||||||
|
|
||||||
if (protocol == Protocol::UDP) {
|
if (protocol == Protocol::UDP) {
|
||||||
if (!peek) {
|
if (!peek) {
|
||||||
received_packets.pop();
|
received_packets.pop();
|
||||||
}
|
}
|
||||||
return {-1, Errno::MSGSIZE};
|
return {-1, Errno::E_MSGSIZE};
|
||||||
} else if (protocol == Protocol::TCP) {
|
} else if (protocol == Protocol::TCP) {
|
||||||
std::vector<u8> numArray(packet.data.size() - max_length);
|
std::vector<u8> numArray(packet.data.size() - max_length);
|
||||||
std::copy(packet.data.begin() + max_length, packet.data.end(),
|
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
|
||||||
std::back_inserter(numArray));
|
|
||||||
packet.data = numArray;
|
packet.data = numArray;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
read_bytes = packet.data.size();
|
read_bytes = packet.data.size();
|
||||||
memcpy(message.data(), packet.data.data(), read_bytes);
|
std::memcpy(message.data(), packet.data.data(), read_bytes);
|
||||||
if (!peek) {
|
if (!peek) {
|
||||||
received_packets.pop();
|
received_packets.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
|
return {u32(read_bytes), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
return {s32(0), Errno::E_SUCCESS};
|
||||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (room_member->IsConnected()) {
|
if (room_member->IsConnected()) {
|
||||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
|
||||||
packet.data.size());
|
|
||||||
room_member->SendProxyPacket(packet);
|
room_member->SendProxyPacket(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||||
const SockAddrIn* addr) {
|
LOG_DEBUG(Network, "called");
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
|
||||||
if (!is_bound) {
|
if (!is_bound) {
|
||||||
LOG_ERROR(Network, "ProxySocket is not bound!");
|
LOG_ERROR(Network, "ProxySocket is not bound!");
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (!room_member->IsConnected()) {
|
if (!room_member->IsConnected()) {
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,66 +242,20 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
|
|||||||
|
|
||||||
SendPacket(packet);
|
SendPacket(packet);
|
||||||
|
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Close() {
|
Errno ProxySocket::Close() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
fd = INVALID_SOCKET;
|
fd = INVALID_SOCKET;
|
||||||
closed = true;
|
closed = true;
|
||||||
|
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
|
|
||||||
struct Linger {
|
|
||||||
u16 linger_enable;
|
|
||||||
u16 linger_time;
|
|
||||||
} values;
|
|
||||||
values.linger_enable = enable ? 1 : 0;
|
|
||||||
values.linger_time = static_cast<u16>(linger);
|
|
||||||
|
|
||||||
return SetSockOpt(fd, SO_LINGER, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetReuseAddr(bool enable) {
|
|
||||||
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetBroadcast(bool enable) {
|
|
||||||
broadcast = enable;
|
|
||||||
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetSndBuf(u32 value) {
|
|
||||||
return SetSockOpt(fd, SO_SNDBUF, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetKeepAlive(bool enable) {
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetRcvBuf(u32 value) {
|
|
||||||
return SetSockOpt(fd, SO_RCVBUF, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetSndTimeo(u32 value) {
|
|
||||||
send_timeout = value;
|
|
||||||
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetRcvTimeo(u32 value) {
|
|
||||||
receive_timeout = value;
|
|
||||||
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetNonBlock(bool enable) {
|
|
||||||
blocking = !enable;
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
||||||
LOG_DEBUG(Network, "(STUBBED) called");
|
LOG_DEBUG(Network, "called");
|
||||||
return {Errno::SUCCESS, Errno::SUCCESS};
|
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxySocket::IsOpened() const {
|
bool ProxySocket::IsOpened() const {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -29,13 +29,13 @@ public:
|
|||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
|
||||||
Errno Connect(SockAddrIn addr_in) override;
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
Errno Bind(SockAddrIn addr) override;
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ public:
|
|||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
|
||||||
std::size_t max_length);
|
std::size_t max_length);
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
@@ -53,28 +53,11 @@ public:
|
|||||||
void SendPacket(ProxyPacket& packet);
|
void SendPacket(ProxyPacket& packet);
|
||||||
|
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||||
const SockAddrIn* addr) override;
|
const Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
Errno SetLinger(bool enable, u32 linger) override;
|
|
||||||
|
|
||||||
Errno SetReuseAddr(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetBroadcast(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetKeepAlive(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetSndBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetSndTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
template <typename T>
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
|
||||||
@@ -86,7 +69,7 @@ private:
|
|||||||
u32 send_timeout = 0;
|
u32 send_timeout = 0;
|
||||||
u32 receive_timeout = 0;
|
u32 receive_timeout = 0;
|
||||||
bool is_bound = false;
|
bool is_bound = false;
|
||||||
SockAddrIn local_endpoint{};
|
Network::SockAddrIn local_endpoint{};
|
||||||
bool blocking = true;
|
bool blocking = true;
|
||||||
std::queue<ProxyPacket> received_packets;
|
std::queue<ProxyPacket> received_packets;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
|
|||||||
@@ -0,0 +1,470 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
|
||||||
|
// Most of these structures are direct mappings of guest's
|
||||||
|
// expectations for these values, in other words, they're the
|
||||||
|
// values that HOS is expected to use AND handle.
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
enum class Errno : u32 {
|
||||||
|
E_SUCCESS = 0,
|
||||||
|
E_PERM = 1,
|
||||||
|
E_NOENT = 2,
|
||||||
|
E_SRCH = 3,
|
||||||
|
E_INTR = 4,
|
||||||
|
E_IO = 5,
|
||||||
|
E_NXIO = 6,
|
||||||
|
E_2BIG = 7,
|
||||||
|
E_NOEXEC = 8,
|
||||||
|
E_BADF = 9,
|
||||||
|
E_CHILD = 10,
|
||||||
|
E_AGAIN = 11,
|
||||||
|
E_NOMEM = 12,
|
||||||
|
E_ACCES = 13,
|
||||||
|
E_FAULT = 14,
|
||||||
|
E_NOTBLK = 15,
|
||||||
|
E_BUSY = 16,
|
||||||
|
E_EXIST = 17,
|
||||||
|
E_XDEV = 18,
|
||||||
|
E_NODEV = 19,
|
||||||
|
E_NOTDIR = 20,
|
||||||
|
E_ISDIR = 21,
|
||||||
|
E_INVAL = 22,
|
||||||
|
E_NFILE = 23,
|
||||||
|
E_MFILE = 24,
|
||||||
|
E_NOTTY = 25,
|
||||||
|
E_TXTBSY = 26,
|
||||||
|
E_FBIG = 27,
|
||||||
|
E_NOSPC = 28,
|
||||||
|
E_SPIPE = 29,
|
||||||
|
E_ROFS = 30,
|
||||||
|
E_MLINK = 31,
|
||||||
|
E_PIPE = 32,
|
||||||
|
E_DOM = 33,
|
||||||
|
E_RANGE = 34,
|
||||||
|
E_DEADLK = 35,
|
||||||
|
E_NAMETOOLONG = 36,
|
||||||
|
E_NOLCK = 37,
|
||||||
|
E_NOSYS = 38,
|
||||||
|
E_NOTEMPTY = 39,
|
||||||
|
E_LOOP = 40,
|
||||||
|
E_NOMSG = 42,
|
||||||
|
E_IDRM = 43,
|
||||||
|
E_CHRNG = 44,
|
||||||
|
E_L2NSYNC = 45,
|
||||||
|
E_L3HLT = 46,
|
||||||
|
E_L3RST = 47,
|
||||||
|
E_LNRNG = 48,
|
||||||
|
E_UNATCH = 49,
|
||||||
|
E_NOCSI = 50,
|
||||||
|
E_L2HLT = 51,
|
||||||
|
E_BADE = 52,
|
||||||
|
E_BADR = 53,
|
||||||
|
E_XFULL = 54,
|
||||||
|
E_NOANO = 55,
|
||||||
|
E_BADRQC = 56,
|
||||||
|
E_BADSSL = 57,
|
||||||
|
E_BFONT = 59,
|
||||||
|
E_NOSTR = 60,
|
||||||
|
E_NODATA = 61,
|
||||||
|
E_TIME = 62,
|
||||||
|
E_NOSR = 63,
|
||||||
|
E_NONET = 64,
|
||||||
|
E_NOPKG = 65,
|
||||||
|
E_REMOTE = 66,
|
||||||
|
E_NOLINK = 67,
|
||||||
|
E_ADV = 68,
|
||||||
|
E_SRMNT = 69,
|
||||||
|
E_COMM = 70,
|
||||||
|
E_PROTO = 71,
|
||||||
|
E_MULTIHOP = 72,
|
||||||
|
E_DOTDOT = 73,
|
||||||
|
E_BADMSG = 74,
|
||||||
|
E_OVERFLOW = 75,
|
||||||
|
E_NOTUNUQ = 76,
|
||||||
|
E_BADFD = 77,
|
||||||
|
E_REMCHG = 78,
|
||||||
|
E_LIBACC = 79,
|
||||||
|
E_LIBBAD = 80,
|
||||||
|
E_LIBSCN = 81,
|
||||||
|
E_LIBMAX = 82,
|
||||||
|
E_LIBEXEC = 83,
|
||||||
|
E_ILSEQ = 84,
|
||||||
|
E_RESTART = 85,
|
||||||
|
E_STRPIPE = 86,
|
||||||
|
E_USERS = 87,
|
||||||
|
E_NOTSOCK = 88,
|
||||||
|
E_DESTADDRREQ = 89,
|
||||||
|
E_MSGSIZE = 90,
|
||||||
|
E_PROTOTYPE = 91,
|
||||||
|
E_NOPROTOOPT = 92,
|
||||||
|
E_PROTONOSUPPORT = 93,
|
||||||
|
E_SOCKTNOSUPPORT = 94,
|
||||||
|
E_OPNOTSUPP = 95,
|
||||||
|
E_PFNOSUPPORT = 96,
|
||||||
|
E_AFNOSUPPORT = 97,
|
||||||
|
E_ADDRINUSE = 98,
|
||||||
|
E_ADDRNOTAVAIL = 99,
|
||||||
|
E_NETDOWN = 100,
|
||||||
|
E_NETUNREACH = 101,
|
||||||
|
E_NETRESET = 102,
|
||||||
|
E_CONNABORTED = 103,
|
||||||
|
E_CONNRESET = 104,
|
||||||
|
E_NOBUFS = 105,
|
||||||
|
E_ISCONN = 106,
|
||||||
|
E_NOTCONN = 107,
|
||||||
|
E_SHUTDOWN = 108,
|
||||||
|
E_TOOMANYREFS = 109,
|
||||||
|
E_TIMEDOUT = 110,
|
||||||
|
E_CONNREFUSED = 111,
|
||||||
|
E_HOSTDOWN = 112,
|
||||||
|
E_HOSTUNREACH = 113,
|
||||||
|
E_ALREADY = 114,
|
||||||
|
E_INPROGRESS = 115,
|
||||||
|
E_STALE = 116,
|
||||||
|
E_UCLEAN = 117,
|
||||||
|
E_NOTNAM = 118,
|
||||||
|
E_NAVAIL = 119,
|
||||||
|
E_ISNAM = 120,
|
||||||
|
E_REMOTEIO = 121,
|
||||||
|
E_DQUOT = 122,
|
||||||
|
E_NOMEDIUM = 123,
|
||||||
|
E_MEDIUMTYPE = 124,
|
||||||
|
E_CANCELED = 125,
|
||||||
|
E_NOKEY = 126,
|
||||||
|
E_KEYEXPIRED = 127,
|
||||||
|
E_KEYREVOKED = 128,
|
||||||
|
E_KEYREJECTED = 129,
|
||||||
|
E_OWNERDEAD = 130,
|
||||||
|
E_NOTRECOVERABLE = 131,
|
||||||
|
E_RFKILL = 132,
|
||||||
|
E_HWPOISON = 133,
|
||||||
|
E_PROCLIM = 156,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GetAddrInfoError : s32 {
|
||||||
|
SUCCESS = 0,
|
||||||
|
ADDRFAMILY = 1,
|
||||||
|
AGAIN = 2,
|
||||||
|
BADFLAGS = 3,
|
||||||
|
FAIL = 4,
|
||||||
|
FAMILY = 5,
|
||||||
|
MEMORY = 6,
|
||||||
|
NODATA = 7,
|
||||||
|
NONAME = 8,
|
||||||
|
SERVICE = 9,
|
||||||
|
SOCKTYPE = 10,
|
||||||
|
SYSTEM = 11,
|
||||||
|
BADHINTS = 12,
|
||||||
|
PROTOCOL = 13,
|
||||||
|
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
||||||
|
OTHER = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Domain : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
UNIX = 1,
|
||||||
|
INET = 2,
|
||||||
|
IMPLINK = 3,
|
||||||
|
PUP = 4,
|
||||||
|
CHAOS = 5,
|
||||||
|
NETBIOS = 6,
|
||||||
|
ISO = 7,
|
||||||
|
ECMA = 8,
|
||||||
|
DATAKIT = 9,
|
||||||
|
CCITT = 10,
|
||||||
|
SNA = 11,
|
||||||
|
DECnet = 12,
|
||||||
|
DLI = 13,
|
||||||
|
LAT = 14,
|
||||||
|
HYLINK = 15,
|
||||||
|
APPLETALK = 16,
|
||||||
|
ROUTE = 17,
|
||||||
|
LINK = 18,
|
||||||
|
COIP = 20,
|
||||||
|
CNT = 21,
|
||||||
|
IPX = 23,
|
||||||
|
SIP = 24,
|
||||||
|
ISDN = 26,
|
||||||
|
INET6 = 28,
|
||||||
|
NATM = 29,
|
||||||
|
ATM = 30,
|
||||||
|
NETGRAPH = 32,
|
||||||
|
SLOW = 33,
|
||||||
|
SCLUSTER = 34,
|
||||||
|
ARP = 35,
|
||||||
|
BLUETOOTH = 36,
|
||||||
|
IEEE80211 = 37,
|
||||||
|
NETLINK = 38,
|
||||||
|
INET_SDP = 40,
|
||||||
|
INET6_SDP = 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Type : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
STREAM = 1,
|
||||||
|
DGRAM = 2,
|
||||||
|
RAW = 3,
|
||||||
|
RDM = 4,
|
||||||
|
SEQPACKET = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Protocol : u32 {
|
||||||
|
IP = 0,
|
||||||
|
ICMP = 1,
|
||||||
|
TCP = 6,
|
||||||
|
UDP = 17,
|
||||||
|
//
|
||||||
|
IPV6 = 41,
|
||||||
|
RAW = 255,
|
||||||
|
//
|
||||||
|
HOPOPTS = 0,
|
||||||
|
IGMP = 2,
|
||||||
|
GGP = 3,
|
||||||
|
IPV4 = 4,
|
||||||
|
ST = 7,
|
||||||
|
EGP = 8,
|
||||||
|
PIGP = 9,
|
||||||
|
RCCMON = 10,
|
||||||
|
NVPII = 11,
|
||||||
|
PUP = 12,
|
||||||
|
ARGUS = 13,
|
||||||
|
EMCON = 14,
|
||||||
|
XNET = 15,
|
||||||
|
CHAOS = 16,
|
||||||
|
MUX = 18,
|
||||||
|
MEAS = 19,
|
||||||
|
HMP = 20,
|
||||||
|
PRM = 21,
|
||||||
|
IDP = 22,
|
||||||
|
TRUNK1 = 23,
|
||||||
|
TRUNK2 = 24,
|
||||||
|
LEAF1 = 25,
|
||||||
|
LEAF2 = 26,
|
||||||
|
RDP = 27,
|
||||||
|
IRTP = 28,
|
||||||
|
TP = 29,
|
||||||
|
BLT = 30,
|
||||||
|
NSP = 31,
|
||||||
|
INP = 32,
|
||||||
|
DCCP = 33,
|
||||||
|
//3PC = 34,
|
||||||
|
IDPR = 35,
|
||||||
|
XTP = 36,
|
||||||
|
DDP = 37,
|
||||||
|
CMTP = 38,
|
||||||
|
TPXX = 39,
|
||||||
|
IL = 40,
|
||||||
|
SDRP = 42,
|
||||||
|
ROUTING = 43,
|
||||||
|
FRAGMENT = 44,
|
||||||
|
IDRP = 45,
|
||||||
|
RSVP = 46,
|
||||||
|
GRE = 47,
|
||||||
|
MHRP = 48,
|
||||||
|
BHA = 49,
|
||||||
|
ESP = 50,
|
||||||
|
AH = 51,
|
||||||
|
INLSP = 52,
|
||||||
|
SWIPE = 53,
|
||||||
|
NHRP = 54,
|
||||||
|
MOBILE = 55,
|
||||||
|
TLSP = 56,
|
||||||
|
SKIP = 57,
|
||||||
|
ICMPV6 = 58,
|
||||||
|
NONE = 59,
|
||||||
|
DSTOPTS = 60,
|
||||||
|
AHIP = 61,
|
||||||
|
CFTP = 62,
|
||||||
|
HELLO = 63,
|
||||||
|
SATEXPAK = 64,
|
||||||
|
KRYPTOLAN = 65,
|
||||||
|
RVD = 66,
|
||||||
|
IPPC = 67,
|
||||||
|
ADFS = 68,
|
||||||
|
SATMON = 69,
|
||||||
|
VISA = 70,
|
||||||
|
IPCV = 71,
|
||||||
|
CPNX = 72,
|
||||||
|
CPHB = 73,
|
||||||
|
WSN = 74,
|
||||||
|
PVP = 75,
|
||||||
|
BRSATMON = 76,
|
||||||
|
ND = 77,
|
||||||
|
WBMON = 78,
|
||||||
|
WBEXPAK = 79,
|
||||||
|
EON = 80,
|
||||||
|
VMTP = 81,
|
||||||
|
SVMTP = 82,
|
||||||
|
VINES = 83,
|
||||||
|
TTP = 84,
|
||||||
|
IGP = 85,
|
||||||
|
DGP = 86,
|
||||||
|
TCF = 87,
|
||||||
|
IGRP = 88,
|
||||||
|
OSPFIGP = 89,
|
||||||
|
SRPC = 90,
|
||||||
|
LARP = 91,
|
||||||
|
MTP = 92,
|
||||||
|
AX25 = 93,
|
||||||
|
IPEIP = 94,
|
||||||
|
MICP = 95,
|
||||||
|
SCCSP = 96,
|
||||||
|
ETHERIP = 97,
|
||||||
|
ENCAP = 98,
|
||||||
|
APES = 99,
|
||||||
|
GMTP = 100,
|
||||||
|
IPCOMP = 108,
|
||||||
|
SCTP = 132,
|
||||||
|
MH = 135,
|
||||||
|
UDPLITE = 136,
|
||||||
|
HIP = 139,
|
||||||
|
SHIM6 = 140,
|
||||||
|
PIM = 103,
|
||||||
|
CARP = 112,
|
||||||
|
PGM = 113,
|
||||||
|
MPLS = 137,
|
||||||
|
PFSYNC = 240,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SocketLevel : u32 {
|
||||||
|
IP = 0,
|
||||||
|
ICMP = 1,
|
||||||
|
TCP = 6,
|
||||||
|
UDP = 17,
|
||||||
|
CONFIG = 0xfffe,
|
||||||
|
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MsgOpt : u32 {
|
||||||
|
OOB = 0x00001,
|
||||||
|
PEEK = 0x00002,
|
||||||
|
DONTROUTE = 0x00004,
|
||||||
|
EOR_ = 0x00008,
|
||||||
|
TRUNC = 0x00010,
|
||||||
|
CTRUNC = 0x00020,
|
||||||
|
WAITALL = 0x00040,
|
||||||
|
DONTWAIT = 0x00080,
|
||||||
|
EOF_ = 0x00100,
|
||||||
|
NOSIGNAL = 0x20000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OptName : u32 {
|
||||||
|
DEBUG = 0x0001,
|
||||||
|
ACCEPTCONN = 0x0002,
|
||||||
|
REUSEADDR = 0x0004,
|
||||||
|
KEEPALIVE = 0x0008,
|
||||||
|
DONTROUTE = 0x0010,
|
||||||
|
BROADCAST = 0x0020,
|
||||||
|
USELOOPBACK = 0x0040,
|
||||||
|
LINGER = 0x0080,
|
||||||
|
OOBINLINE = 0x0100,
|
||||||
|
REUSEPORT = 0x0200,
|
||||||
|
TIMESTAMP = 0x0400,
|
||||||
|
NOSIGPIPE = 0x0800, // at least according to libnx
|
||||||
|
ACCEPTFILER = 0x1000,
|
||||||
|
SNDBUF = 0x1001,
|
||||||
|
RCVBUF = 0x1002,
|
||||||
|
SNDTIMEO = 0x1005,
|
||||||
|
RCVTIMEO = 0x1006,
|
||||||
|
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
||||||
|
ACCEPTFILTER = 0x1000,
|
||||||
|
BINTIME = 0x2000,
|
||||||
|
NO_OFFLOAD = 0x4000,
|
||||||
|
NO_DDP = 0x8000,
|
||||||
|
};
|
||||||
|
enum class TcpOptName : u32 {
|
||||||
|
NODELAY = 0x0001,
|
||||||
|
MAXSEG = 0x0002,
|
||||||
|
NOPUSH = 0x0004,
|
||||||
|
NOOPT = 0x0008,
|
||||||
|
MS5SIG = 0x0010,
|
||||||
|
INFO = 0x0020
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShutdownHow : s32 {
|
||||||
|
RD = 0,
|
||||||
|
WR = 1,
|
||||||
|
RDWR = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FcntlCmd : s32 {
|
||||||
|
GETFL = 3,
|
||||||
|
SETFL = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FcntlFlags : u32 {
|
||||||
|
NONBLOCK = 0x004,
|
||||||
|
NONBLOCK_NX = 0x800,
|
||||||
|
// Provided for convenience
|
||||||
|
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Array of IPv4 address
|
||||||
|
using IPv4Address = std::array<u8, 4>;
|
||||||
|
|
||||||
|
struct SockAddrIn {
|
||||||
|
u8 len;
|
||||||
|
u8 family;
|
||||||
|
u16 portno;
|
||||||
|
IPv4Address ip;
|
||||||
|
std::array<u8, 248> zeroes;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||||
|
|
||||||
|
enum class PollEvents : u16 {
|
||||||
|
// Using Pascal case because IN is a macro on Windows.
|
||||||
|
IN_ = 0x0001,
|
||||||
|
PRI_ = 0x0002,
|
||||||
|
OUT_ = 0x0004,
|
||||||
|
ERR_ = 0x0008,
|
||||||
|
HUP_ = 0x0010,
|
||||||
|
NVAL = 0x0020,
|
||||||
|
RDNORM = 0x0040,
|
||||||
|
RDBAND = 0x0080,
|
||||||
|
WRBAND = 0x0100,
|
||||||
|
IGNEOF = 0x2000,
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||||
|
|
||||||
|
struct PollFD {
|
||||||
|
s32 fd;
|
||||||
|
Network::PollEvents events;
|
||||||
|
Network::PollEvents revents;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PollFD) == 8);
|
||||||
|
|
||||||
|
struct Linger {
|
||||||
|
s32 onoff;
|
||||||
|
s32 linger;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Linger) == 8);
|
||||||
|
|
||||||
|
struct Timeval {
|
||||||
|
u64 tv_sec;
|
||||||
|
u64 tv_usec;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Timeval) == 16);
|
||||||
|
|
||||||
|
/// @brief Cross-platform addrinfo structure (not guest)
|
||||||
|
struct AddrInfo {
|
||||||
|
Domain family;
|
||||||
|
Type socket_type;
|
||||||
|
Protocol protocol;
|
||||||
|
SockAddrIn addr;
|
||||||
|
std::optional<std::string> canon_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
|
|
||||||
struct AcceptResult {
|
struct AcceptResult {
|
||||||
std::unique_ptr<SocketBase> socket;
|
std::unique_ptr<SocketBase> socket;
|
||||||
SockAddrIn sockaddr_in;
|
Network::SockAddrIn sockaddr_in;
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketBase() = default;
|
SocketBase() = default;
|
||||||
@@ -46,13 +46,13 @@ public:
|
|||||||
|
|
||||||
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
||||||
|
|
||||||
virtual Errno Connect(SockAddrIn addr_in) = 0;
|
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
|
||||||
|
|
||||||
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
|
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
|
||||||
|
|
||||||
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
|
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
|
||||||
|
|
||||||
virtual Errno Bind(SockAddrIn addr) = 0;
|
virtual Errno Bind(Network::SockAddrIn addr) = 0;
|
||||||
|
|
||||||
virtual Errno Listen(s32 backlog) = 0;
|
virtual Errno Listen(s32 backlog) = 0;
|
||||||
|
|
||||||
@@ -60,31 +60,16 @@ public:
|
|||||||
|
|
||||||
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
|
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
|
||||||
const SockAddrIn* addr) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetLinger(bool enable, u32 linger) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetReuseAddr(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetKeepAlive(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetBroadcast(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetSndBuf(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetRcvBuf(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetSndTimeo(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetRcvTimeo(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetNonBlock(bool enable) = 0;
|
virtual Errno SetNonBlock(bool enable) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
|
||||||
|
|
||||||
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
||||||
|
|
||||||
virtual bool IsOpened() const = 0;
|
virtual bool IsOpened() const = 0;
|
||||||
@@ -95,7 +80,6 @@ public:
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
SOCKET fd = INVALID_SOCKET;
|
SOCKET fd = INVALID_SOCKET;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,13 +98,13 @@ public:
|
|||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
|
||||||
Errno Connect(SockAddrIn addr_in) override;
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
Errno Bind(SockAddrIn addr) override;
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
@@ -128,38 +112,19 @@ public:
|
|||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||||
const SockAddrIn* addr) override;
|
|
||||||
|
|
||||||
Errno SetLinger(bool enable, u32 linger) override;
|
|
||||||
|
|
||||||
Errno SetReuseAddr(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetKeepAlive(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetBroadcast(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetSndBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetSndTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
template <typename T>
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
|
||||||
template <typename T>
|
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
|
||||||
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
|
|
||||||
|
|
||||||
bool IsOpened() const override;
|
bool IsOpened() const override;
|
||||||
|
|
||||||
@@ -169,6 +134,6 @@ private:
|
|||||||
bool is_non_blocking = false;
|
bool is_non_blocking = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
|
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "network/verify_user.h"
|
#include "network/verify_user.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "enet/enet.h"
|
#include "enet/enet.h"
|
||||||
#include "network/packet.h"
|
#include "network/packet.h"
|
||||||
#include "network/room_member.h"
|
#include "network/room_member.h"
|
||||||
@@ -358,19 +358,23 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
|
|||||||
// Parse the ProxyPacket from the packet
|
// Parse the ProxyPacket from the packet
|
||||||
u8 local_family;
|
u8 local_family;
|
||||||
packet.Read(local_family);
|
packet.Read(local_family);
|
||||||
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
|
proxy_packet.local_endpoint.len = 16;
|
||||||
|
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
|
||||||
packet.Read(proxy_packet.local_endpoint.ip);
|
packet.Read(proxy_packet.local_endpoint.ip);
|
||||||
packet.Read(proxy_packet.local_endpoint.portno);
|
packet.Read(proxy_packet.local_endpoint.portno);
|
||||||
|
proxy_packet.local_endpoint.zeroes = {};
|
||||||
|
|
||||||
u8 remote_family;
|
u8 remote_family;
|
||||||
packet.Read(remote_family);
|
packet.Read(remote_family);
|
||||||
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
|
proxy_packet.remote_endpoint.len = 16;
|
||||||
|
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
|
||||||
packet.Read(proxy_packet.remote_endpoint.ip);
|
packet.Read(proxy_packet.remote_endpoint.ip);
|
||||||
packet.Read(proxy_packet.remote_endpoint.portno);
|
packet.Read(proxy_packet.remote_endpoint.portno);
|
||||||
|
proxy_packet.local_endpoint.zeroes = {};
|
||||||
|
|
||||||
u8 protocol_type;
|
u8 protocol_type;
|
||||||
packet.Read(protocol_type);
|
packet.Read(protocol_type);
|
||||||
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
|
proxy_packet.protocol = Network::Protocol(protocol_type);
|
||||||
|
|
||||||
packet.Read(proxy_packet.broadcast);
|
packet.Read(proxy_packet.broadcast);
|
||||||
packet.Read(proxy_packet.data);
|
packet.Read(proxy_packet.data);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "network/room.h"
|
#include "network/room.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
@@ -39,8 +39,8 @@ struct LDNPacket {
|
|||||||
|
|
||||||
/// Information about the received proxy packets.
|
/// Information about the received proxy packets.
|
||||||
struct ProxyPacket {
|
struct ProxyPacket {
|
||||||
SockAddrIn local_endpoint;
|
Network::SockAddrIn local_endpoint;
|
||||||
SockAddrIn remote_endpoint;
|
Network::SockAddrIn remote_endpoint;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
bool broadcast;
|
bool broadcast;
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|||||||
@@ -150,7 +150,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
|
INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"),
|
||||||
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA "
|
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA "
|
||||||
"can produce a more stable picture in lower resolutions."));
|
"can produce a more stable picture in lower resolutions."));
|
||||||
INSERT(Settings, post_shader_chain, QString(), QString());
|
|
||||||
INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
|
INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"),
|
||||||
tr("The method used to render the window in fullscreen.\nBorderless offers the best "
|
tr("The method used to render the window in fullscreen.\nBorderless offers the best "
|
||||||
"compatibility with the on-screen keyboard that some games request for "
|
"compatibility with the on-screen keyboard that some games request for "
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -12,16 +15,18 @@ TEST_CASE("Network::Errors", "[core]") {
|
|||||||
Network::Socket socks[2];
|
Network::Socket socks[2];
|
||||||
for (Network::Socket& sock : socks) {
|
for (Network::Socket& sock : socks) {
|
||||||
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
||||||
Network::Protocol::TCP) == Network::Errno::SUCCESS);
|
Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr{
|
Network::SockAddrIn addr{
|
||||||
Network::Domain::INET,
|
16,
|
||||||
{127, 0, 0, 1},
|
u8(Network::Domain::INET),
|
||||||
1, // hopefully nobody running this test has something listening on port 1
|
1, // hopefully nobody running this test has something listening on port 1
|
||||||
|
{127, 0, 0, 1},
|
||||||
|
{},
|
||||||
};
|
};
|
||||||
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
|
REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
|
||||||
|
|
||||||
std::vector<u8> message{1, 2, 3, 4};
|
std::vector<u8> message{1, 2, 3, 4};
|
||||||
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
|
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,21 +299,6 @@ if (ENABLE_LSFG)
|
|||||||
target_compile_definitions(video_core PUBLIC HAS_LSFG)
|
target_compile_definitions(video_core PUBLIC HAS_LSFG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_RESHADE)
|
|
||||||
target_sources(video_core PRIVATE
|
|
||||||
post_processing/fx_chain.cpp
|
|
||||||
post_processing/fx_chain.h
|
|
||||||
post_processing/fx_compile.cpp
|
|
||||||
post_processing/fx_compile.h
|
|
||||||
post_processing/fx_effect.cpp
|
|
||||||
post_processing/fx_effect.h
|
|
||||||
renderer_vulkan/present/post_process.cpp
|
|
||||||
renderer_vulkan/present/post_process.h
|
|
||||||
)
|
|
||||||
target_link_libraries(video_core PRIVATE reshadefx::reshadefx)
|
|
||||||
target_compile_definitions(video_core PUBLIC HAS_RESHADE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (ENABLE_OPENGL)
|
if (ENABLE_OPENGL)
|
||||||
target_sources(video_core PRIVATE
|
target_sources(video_core PRIVATE
|
||||||
renderer_opengl/present/filters.cpp
|
renderer_opengl/present/filters.cpp
|
||||||
|
|||||||
@@ -1,293 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "common/settings.h"
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
std::vector<std::string_view> Split(std::string_view value, char separator) {
|
|
||||||
std::vector<std::string_view> out;
|
|
||||||
size_t start = 0;
|
|
||||||
while (start <= value.size()) {
|
|
||||||
size_t end = value.find(separator, start);
|
|
||||||
if (end == std::string_view::npos) {
|
|
||||||
end = value.size();
|
|
||||||
}
|
|
||||||
out.push_back(value.substr(start, end - start));
|
|
||||||
start = end + 1;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSerializableName(std::string_view value) {
|
|
||||||
return value.find_first_of(";|,=") == std::string_view::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
std::vector<FxChainEntry> ParseFxChain(std::string_view value) {
|
|
||||||
std::vector<FxChainEntry> parsed;
|
|
||||||
|
|
||||||
for (const std::string_view record : Split(value, ';')) {
|
|
||||||
if (record.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto fields = Split(record, '|');
|
|
||||||
if (fields.size() < 2 || fields[0].empty() || fields[1].empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxChainEntry entry;
|
|
||||||
entry.file = std::string(fields[0]);
|
|
||||||
entry.technique = std::string(fields[1]);
|
|
||||||
|
|
||||||
if (fields.size() >= 3) {
|
|
||||||
for (const std::string_view assignment : Split(fields[2], ',')) {
|
|
||||||
const size_t equals = assignment.find('=');
|
|
||||||
if (equals == std::string_view::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const std::string name(assignment.substr(0, equals));
|
|
||||||
if (name.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<f32, 4> components{};
|
|
||||||
size_t index = 0;
|
|
||||||
for (const std::string_view piece : Split(assignment.substr(equals + 1), '/')) {
|
|
||||||
if (index >= components.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::string text(piece);
|
|
||||||
if (!text.empty()) {
|
|
||||||
components[index] = std::strtof(text.c_str(), nullptr);
|
|
||||||
}
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
entry.values.emplace(name, components);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed.push_back(std::move(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SerializeFxChain(std::span<const FxChainEntry> entries) {
|
|
||||||
std::string out;
|
|
||||||
|
|
||||||
for (const auto& entry : entries) {
|
|
||||||
if (!IsSerializableName(entry.file) || !IsSerializableName(entry.technique)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!out.empty()) {
|
|
||||||
out += ';';
|
|
||||||
}
|
|
||||||
out += entry.file;
|
|
||||||
out += '|';
|
|
||||||
out += entry.technique;
|
|
||||||
out += '|';
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
for (const auto& [name, value] : entry.values) {
|
|
||||||
if (!IsSerializableName(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!first) {
|
|
||||||
out += ',';
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
out += name;
|
|
||||||
out += '=';
|
|
||||||
for (size_t i = 0; i < value.size(); ++i) {
|
|
||||||
if (i > 0) {
|
|
||||||
out += '/';
|
|
||||||
}
|
|
||||||
out += fmt::format("{}", value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxChain& FxChain::Instance() {
|
|
||||||
static FxChain instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxChainSnapshot FxChain::Snapshot() const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
return FxChainSnapshot{
|
|
||||||
.entries = entries,
|
|
||||||
.generation = generation.load(std::memory_order_relaxed),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<FxChainEntry> FxChain::Entries() const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t FxChain::Size() const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
return entries.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::Append(std::string_view file, std::string_view technique) {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
FxChainEntry entry;
|
|
||||||
entry.file = std::string(file);
|
|
||||||
entry.technique = std::string(technique);
|
|
||||||
entries.push_back(std::move(entry));
|
|
||||||
}
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::Replace(size_t index, std::string_view file, std::string_view technique) {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entries[index].file == file && entries[index].technique == technique) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FxChainEntry entry;
|
|
||||||
entry.file = std::string(file);
|
|
||||||
entry.technique = std::string(technique);
|
|
||||||
entries[index] = std::move(entry);
|
|
||||||
}
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::Remove(size_t index) {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entries.erase(entries.begin() + static_cast<std::ptrdiff_t>(index));
|
|
||||||
}
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::Move(size_t index, int delta) {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const std::ptrdiff_t target = static_cast<std::ptrdiff_t>(index) + delta;
|
|
||||||
if (target < 0 || target >= static_cast<std::ptrdiff_t>(entries.size())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::swap(entries[index], entries[static_cast<size_t>(target)]);
|
|
||||||
}
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::Clear() {
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
entries.clear();
|
|
||||||
}
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::SetValue(size_t index, std::string_view uniform, const std::array<f32, 4>& value) {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entries[index].values[std::string(uniform)] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<f32, 4> FxChain::GetValue(size_t index, std::string_view uniform) const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto it = entries[index].values.find(std::string(uniform));
|
|
||||||
if (it == entries[index].values.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, std::array<f32, 4>> FxChain::EntryValues(size_t index) const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return entries[index].values;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FxChain::HasValue(size_t index, std::string_view uniform) const {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return entries[index].values.contains(std::string(uniform));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::ResetValues(size_t index) {
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (index >= entries.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
entries[index].values.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::LoadFromSettings() {
|
|
||||||
auto loaded = ParseFxChain(Settings::values.post_shader_chain.GetValue());
|
|
||||||
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
entries = std::move(loaded);
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::StoreToSettings() const {
|
|
||||||
std::string serialized;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
serialized = SerializeFxChain(entries);
|
|
||||||
}
|
|
||||||
Settings::values.post_shader_chain.SetValue(serialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FxChain::DropUnknownEntries() {
|
|
||||||
bool changed = false;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
const auto removed = std::remove_if(entries.begin(), entries.end(), [](const FxChainEntry& entry) {
|
|
||||||
const FxEffectDesc* effect = FindFxEffect(entry.file);
|
|
||||||
if (effect == nullptr || !effect->Valid()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return std::find(effect->techniques.begin(), effect->techniques.end(),
|
|
||||||
entry.technique) == effect->techniques.end();
|
|
||||||
});
|
|
||||||
if (removed != entries.end()) {
|
|
||||||
entries.erase(removed, entries.end());
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
generation.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <atomic>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
struct FxChainEntry {
|
|
||||||
std::string file;
|
|
||||||
std::string technique;
|
|
||||||
std::map<std::string, std::array<f32, 4>> values;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FxChainSnapshot {
|
|
||||||
std::vector<FxChainEntry> entries;
|
|
||||||
u64 generation{};
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<FxChainEntry> ParseFxChain(std::string_view value);
|
|
||||||
|
|
||||||
std::string SerializeFxChain(std::span<const FxChainEntry> entries);
|
|
||||||
|
|
||||||
class FxChain {
|
|
||||||
public:
|
|
||||||
static FxChain& Instance();
|
|
||||||
|
|
||||||
FxChainSnapshot Snapshot() const;
|
|
||||||
|
|
||||||
std::vector<FxChainEntry> Entries() const;
|
|
||||||
|
|
||||||
size_t Size() const;
|
|
||||||
|
|
||||||
void Append(std::string_view file, std::string_view technique);
|
|
||||||
|
|
||||||
void Replace(size_t index, std::string_view file, std::string_view technique);
|
|
||||||
|
|
||||||
void Remove(size_t index);
|
|
||||||
|
|
||||||
void Move(size_t index, int delta);
|
|
||||||
|
|
||||||
void Clear();
|
|
||||||
|
|
||||||
void SetValue(size_t index, std::string_view uniform, const std::array<f32, 4>& value);
|
|
||||||
|
|
||||||
std::array<f32, 4> GetValue(size_t index, std::string_view uniform) const;
|
|
||||||
|
|
||||||
std::map<std::string, std::array<f32, 4>> EntryValues(size_t index) const;
|
|
||||||
|
|
||||||
bool HasValue(size_t index, std::string_view uniform) const;
|
|
||||||
|
|
||||||
void ResetValues(size_t index);
|
|
||||||
|
|
||||||
void LoadFromSettings();
|
|
||||||
|
|
||||||
void StoreToSettings() const;
|
|
||||||
|
|
||||||
void DropUnknownEntries();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FxChain() = default;
|
|
||||||
|
|
||||||
mutable std::mutex mutex;
|
|
||||||
std::vector<FxChainEntry> entries;
|
|
||||||
std::atomic<u64> generation{1};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "effect_codegen.hpp"
|
|
||||||
#include "effect_parser.hpp"
|
|
||||||
#include "effect_preprocessor.hpp"
|
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
|
||||||
#include "common/fs/fs_util.h"
|
|
||||||
#include "video_core/post_processing/fx_compile.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
FxCompileResult CompileFxEffect(const std::filesystem::path& path, u32 width, u32 height,
|
|
||||||
u32 color_bit_depth) {
|
|
||||||
FxCompileResult result;
|
|
||||||
|
|
||||||
if (!Common::FS::Exists(path)) {
|
|
||||||
result.error = "Effect file not found: " + Common::FS::PathToUTF8String(path);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
reshadefx::preprocessor preprocessor;
|
|
||||||
preprocessor.add_macro_definition("__RESHADE__", "50000");
|
|
||||||
preprocessor.add_macro_definition("__RESHADE_PERFORMANCE_MODE__", "1");
|
|
||||||
preprocessor.add_macro_definition("__RENDERER__", "0x20000");
|
|
||||||
preprocessor.add_macro_definition("__VENDOR__", "0");
|
|
||||||
preprocessor.add_macro_definition("__DEVICE__", "0");
|
|
||||||
preprocessor.add_macro_definition("__APPLICATION__", "0");
|
|
||||||
preprocessor.add_macro_definition("BUFFER_WIDTH", std::to_string(width));
|
|
||||||
preprocessor.add_macro_definition("BUFFER_HEIGHT", std::to_string(height));
|
|
||||||
preprocessor.add_macro_definition("BUFFER_RCP_WIDTH", "(1.0 / BUFFER_WIDTH)");
|
|
||||||
preprocessor.add_macro_definition("BUFFER_RCP_HEIGHT", "(1.0 / BUFFER_HEIGHT)");
|
|
||||||
preprocessor.add_macro_definition("BUFFER_COLOR_DEPTH", std::to_string(color_bit_depth));
|
|
||||||
preprocessor.add_macro_definition("BUFFER_COLOR_BIT_DEPTH", std::to_string(color_bit_depth));
|
|
||||||
|
|
||||||
for (const auto& include : GetFxIncludePaths(path)) {
|
|
||||||
preprocessor.add_include_path(include);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preprocessor.append_file(path)) {
|
|
||||||
result.error = preprocessor.errors();
|
|
||||||
if (result.error.empty()) {
|
|
||||||
result.error = "Failed to preprocess " + Common::FS::PathToUTF8String(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<reshadefx::codegen> backend(
|
|
||||||
reshadefx::create_codegen_spirv(true, false, false, false, true));
|
|
||||||
|
|
||||||
reshadefx::parser parser;
|
|
||||||
if (!parser.parse(preprocessor.output(), backend.get())) {
|
|
||||||
result.error = parser.errors();
|
|
||||||
if (result.error.empty()) {
|
|
||||||
result.error = "Failed to parse " + Common::FS::PathToUTF8String(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.module = backend->module();
|
|
||||||
|
|
||||||
std::set<std::string> wanted;
|
|
||||||
for (const auto& technique : result.module.techniques) {
|
|
||||||
for (const auto& pass : technique.passes) {
|
|
||||||
if (!pass.vs_entry_point.empty()) {
|
|
||||||
wanted.insert(pass.vs_entry_point);
|
|
||||||
}
|
|
||||||
if (!pass.ps_entry_point.empty()) {
|
|
||||||
wanted.insert(pass.ps_entry_point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& name : wanted) {
|
|
||||||
std::string binary;
|
|
||||||
std::string assembly;
|
|
||||||
std::string errors;
|
|
||||||
if (!backend->assemble_code_for_entry_point(name, binary, assembly, errors)) {
|
|
||||||
result.error = "Failed to assemble entry point '" + name + "': " + errors;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (binary.size() % sizeof(u32) != 0) {
|
|
||||||
result.error = "Entry point '" + name + "' produced a malformed SPIR-V module";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u32> words(binary.size() / sizeof(u32));
|
|
||||||
std::memcpy(words.data(), binary.data(), binary.size());
|
|
||||||
result.entry_points.emplace(name, std::move(words));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.entry_points.empty()) {
|
|
||||||
result.error = "Effect declares no usable entry points";
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "effect_module.hpp"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
struct FxCompileResult {
|
|
||||||
reshadefx::effect_module module;
|
|
||||||
std::map<std::string, std::vector<u32>> entry_points;
|
|
||||||
std::string error;
|
|
||||||
|
|
||||||
bool Succeeded() const {
|
|
||||||
return error.empty() && !entry_points.empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FxCompileResult CompileFxEffect(const std::filesystem::path& path, u32 width, u32 height,
|
|
||||||
u32 color_bit_depth);
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -1,283 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
|
||||||
#include "common/fs/fs_util.h"
|
|
||||||
#include "common/fs/path_util.h"
|
|
||||||
#include "common/logging.h"
|
|
||||||
#include "video_core/post_processing/fx_compile.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr u32 CATALOG_PROBE_WIDTH = 1280;
|
|
||||||
constexpr u32 CATALOG_PROBE_HEIGHT = 720;
|
|
||||||
constexpr u32 CATALOG_PROBE_DEPTH = 8;
|
|
||||||
|
|
||||||
std::vector<FxEffectDesc> catalog;
|
|
||||||
|
|
||||||
const reshadefx::annotation* FindAnnotation(const std::vector<reshadefx::annotation>& annotations,
|
|
||||||
std::string_view name) {
|
|
||||||
const auto it = std::find_if(annotations.begin(), annotations.end(),
|
|
||||||
[&](const reshadefx::annotation& a) { return a.name == name; });
|
|
||||||
if (it == annotations.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &*it;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AnnotationString(const std::vector<reshadefx::annotation>& annotations,
|
|
||||||
std::string_view name) {
|
|
||||||
const reshadefx::annotation* a = FindAnnotation(annotations, name);
|
|
||||||
if (a == nullptr) {
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
return a->value.string_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AnnotationFloat(const std::vector<reshadefx::annotation>& annotations, std::string_view name,
|
|
||||||
f32& out) {
|
|
||||||
const reshadefx::annotation* a = FindAnnotation(annotations, name);
|
|
||||||
if (a == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (a->type.is_floating_point()) {
|
|
||||||
out = a->value.as_float[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (a->type.is_integral()) {
|
|
||||||
out = static_cast<f32>(a->value.as_int[0]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxUiType ParseUiType(std::string_view value) {
|
|
||||||
if (value == "slider") {
|
|
||||||
return FxUiType::Slider;
|
|
||||||
}
|
|
||||||
if (value == "drag") {
|
|
||||||
return FxUiType::Drag;
|
|
||||||
}
|
|
||||||
if (value == "combo") {
|
|
||||||
return FxUiType::Combo;
|
|
||||||
}
|
|
||||||
if (value == "radio") {
|
|
||||||
return FxUiType::Radio;
|
|
||||||
}
|
|
||||||
if (value == "check" || value == "checkbox") {
|
|
||||||
return FxUiType::CheckBox;
|
|
||||||
}
|
|
||||||
if (value == "color") {
|
|
||||||
return FxUiType::Color;
|
|
||||||
}
|
|
||||||
if (value == "input") {
|
|
||||||
return FxUiType::InputBox;
|
|
||||||
}
|
|
||||||
return FxUiType::Hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> SplitItems(const std::string& items) {
|
|
||||||
std::vector<std::string> out;
|
|
||||||
std::string current;
|
|
||||||
for (const char c : items) {
|
|
||||||
if (c == '\0') {
|
|
||||||
out.push_back(current);
|
|
||||||
current.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
current += c;
|
|
||||||
}
|
|
||||||
if (!current.empty()) {
|
|
||||||
out.push_back(current);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxUniformDesc DescribeUniform(const reshadefx::uniform& info) {
|
|
||||||
FxUniformDesc desc;
|
|
||||||
desc.name = info.name;
|
|
||||||
desc.components = std::min<u32>(info.type.components(), 4);
|
|
||||||
|
|
||||||
if (info.type.is_boolean()) {
|
|
||||||
desc.kind = FxUniformKind::Boolean;
|
|
||||||
} else if (info.type.is_integral()) {
|
|
||||||
desc.kind = FxUniformKind::Integer;
|
|
||||||
} else {
|
|
||||||
desc.kind = FxUniformKind::Floating;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc.label = AnnotationString(info.annotations, "ui_label");
|
|
||||||
if (desc.label.empty()) {
|
|
||||||
desc.label = info.name;
|
|
||||||
}
|
|
||||||
desc.tooltip = AnnotationString(info.annotations, "ui_tooltip");
|
|
||||||
desc.category = AnnotationString(info.annotations, "ui_category");
|
|
||||||
desc.ui_type = ParseUiType(AnnotationString(info.annotations, "ui_type"));
|
|
||||||
desc.items = SplitItems(AnnotationString(info.annotations, "ui_items"));
|
|
||||||
|
|
||||||
if (desc.kind == FxUniformKind::Boolean) {
|
|
||||||
desc.ui_min = 0.0f;
|
|
||||||
desc.ui_max = 1.0f;
|
|
||||||
desc.ui_step = 1.0f;
|
|
||||||
} else if (desc.kind == FxUniformKind::Integer) {
|
|
||||||
desc.ui_min = 0.0f;
|
|
||||||
desc.ui_max = 100.0f;
|
|
||||||
desc.ui_step = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void(AnnotationFloat(info.annotations, "ui_min", desc.ui_min));
|
|
||||||
void(AnnotationFloat(info.annotations, "ui_max", desc.ui_max));
|
|
||||||
void(AnnotationFloat(info.annotations, "ui_step", desc.ui_step));
|
|
||||||
|
|
||||||
if (desc.ui_step <= 0.0f) {
|
|
||||||
desc.ui_step = 0.01f;
|
|
||||||
if (desc.kind != FxUniformKind::Floating) {
|
|
||||||
desc.ui_step = 1.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (desc.ui_max < desc.ui_min) {
|
|
||||||
std::swap(desc.ui_min, desc.ui_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.has_initializer_value) {
|
|
||||||
for (u32 i = 0; i < desc.components; ++i) {
|
|
||||||
if (desc.kind == FxUniformKind::Floating) {
|
|
||||||
desc.default_value[i] = info.initializer_value.as_float[i];
|
|
||||||
} else {
|
|
||||||
desc.default_value[i] = static_cast<f32>(info.initializer_value.as_int[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxEffectDesc DescribeEffect(const std::filesystem::path& path, const std::filesystem::path& root) {
|
|
||||||
FxEffectDesc desc;
|
|
||||||
desc.file = Common::FS::PathToUTF8String(std::filesystem::relative(path, root));
|
|
||||||
desc.name = Common::FS::PathToUTF8String(path.stem());
|
|
||||||
|
|
||||||
const auto compiled =
|
|
||||||
CompileFxEffect(path, CATALOG_PROBE_WIDTH, CATALOG_PROBE_HEIGHT, CATALOG_PROBE_DEPTH);
|
|
||||||
if (!compiled.Succeeded()) {
|
|
||||||
desc.error = compiled.error;
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& technique : compiled.module.techniques) {
|
|
||||||
desc.techniques.push_back(technique.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& uniform : compiled.module.uniforms) {
|
|
||||||
FxUniformDesc uniform_desc = DescribeUniform(uniform);
|
|
||||||
if (uniform_desc.ui_type == FxUiType::Hidden) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
desc.uniforms.push_back(std::move(uniform_desc));
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
std::filesystem::path GetFxRootDirectory() {
|
|
||||||
return Common::FS::GetEdenPath(Common::FS::EdenPath::PostShaderDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::filesystem::path> GetFxIncludePaths(const std::filesystem::path& effect_path) {
|
|
||||||
const auto root = GetFxRootDirectory();
|
|
||||||
std::vector<std::filesystem::path> paths;
|
|
||||||
paths.push_back(effect_path.parent_path());
|
|
||||||
paths.push_back(root);
|
|
||||||
paths.push_back(root / "Shaders");
|
|
||||||
|
|
||||||
const auto last = std::unique(paths.begin(), paths.end());
|
|
||||||
paths.erase(last, paths.end());
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path ResolveFxTexturePath(const std::filesystem::path& effect_path,
|
|
||||||
std::string_view source) {
|
|
||||||
const auto root = GetFxRootDirectory();
|
|
||||||
const std::filesystem::path name{source};
|
|
||||||
|
|
||||||
const std::array candidates{
|
|
||||||
effect_path.parent_path() / name,
|
|
||||||
root / "Textures" / name,
|
|
||||||
root / name,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& candidate : candidates) {
|
|
||||||
if (Common::FS::Exists(candidate)) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::filesystem::path();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReloadFxCatalog() {
|
|
||||||
catalog.clear();
|
|
||||||
|
|
||||||
const auto root = GetFxRootDirectory();
|
|
||||||
if (!Common::FS::Exists(root)) {
|
|
||||||
void(Common::FS::CreateDirs(root));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::filesystem::path> effect_files;
|
|
||||||
Common::FS::IterateDirEntriesRecursively(
|
|
||||||
root,
|
|
||||||
[&](const std::filesystem::directory_entry& entry) {
|
|
||||||
if (entry.path().extension() == ".fx") {
|
|
||||||
effect_files.push_back(entry.path());
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Common::FS::DirEntryFilter::File);
|
|
||||||
|
|
||||||
std::sort(effect_files.begin(), effect_files.end());
|
|
||||||
|
|
||||||
for (const auto& file : effect_files) {
|
|
||||||
FxEffectDesc desc = DescribeEffect(file, root);
|
|
||||||
if (!desc.error.empty()) {
|
|
||||||
LOG_WARNING(Render, "Post-processing effect '{}' failed to compile:\n{}", desc.file,
|
|
||||||
desc.error);
|
|
||||||
}
|
|
||||||
catalog.push_back(std::move(desc));
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t usable = std::count_if(catalog.begin(), catalog.end(),
|
|
||||||
[](const FxEffectDesc& d) { return d.Valid(); });
|
|
||||||
LOG_INFO(Render, "Loaded {} post-processing effects ({} usable)", catalog.size(), usable);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<FxEffectDesc>& GetFxCatalog() {
|
|
||||||
return catalog;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FxEffectDesc* FindFxEffect(std::string_view file) {
|
|
||||||
const auto it = std::find_if(catalog.begin(), catalog.end(),
|
|
||||||
[&](const FxEffectDesc& d) { return d.file == file; });
|
|
||||||
if (it == catalog.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &*it;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FxUniformDesc* FindFxUniform(const FxEffectDesc& effect, std::string_view name) {
|
|
||||||
const auto it = std::find_if(effect.uniforms.begin(), effect.uniforms.end(),
|
|
||||||
[&](const FxUniformDesc& u) { return u.name == name; });
|
|
||||||
if (it == effect.uniforms.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &*it;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
|
|
||||||
enum class FxUniformKind {
|
|
||||||
Boolean,
|
|
||||||
Integer,
|
|
||||||
Floating,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FxUiType {
|
|
||||||
Hidden,
|
|
||||||
Slider,
|
|
||||||
Drag,
|
|
||||||
Combo,
|
|
||||||
Radio,
|
|
||||||
CheckBox,
|
|
||||||
Color,
|
|
||||||
InputBox,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FxUniformDesc {
|
|
||||||
std::string name;
|
|
||||||
std::string label;
|
|
||||||
std::string tooltip;
|
|
||||||
std::string category;
|
|
||||||
FxUniformKind kind{FxUniformKind::Floating};
|
|
||||||
u32 components{1};
|
|
||||||
FxUiType ui_type{FxUiType::Hidden};
|
|
||||||
f32 ui_min{0.0f};
|
|
||||||
f32 ui_max{1.0f};
|
|
||||||
f32 ui_step{0.01f};
|
|
||||||
std::vector<std::string> items;
|
|
||||||
std::array<f32, 4> default_value{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FxEffectDesc {
|
|
||||||
std::string file;
|
|
||||||
std::string name;
|
|
||||||
std::vector<std::string> techniques;
|
|
||||||
std::vector<FxUniformDesc> uniforms;
|
|
||||||
std::string error;
|
|
||||||
|
|
||||||
bool Valid() const {
|
|
||||||
return error.empty() && !techniques.empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::filesystem::path GetFxRootDirectory();
|
|
||||||
|
|
||||||
std::vector<std::filesystem::path> GetFxIncludePaths(const std::filesystem::path& effect_path);
|
|
||||||
|
|
||||||
std::filesystem::path ResolveFxTexturePath(const std::filesystem::path& effect_path,
|
|
||||||
std::string_view source);
|
|
||||||
|
|
||||||
void ReloadFxCatalog();
|
|
||||||
|
|
||||||
const std::vector<FxEffectDesc>& GetFxCatalog();
|
|
||||||
|
|
||||||
const FxEffectDesc* FindFxEffect(std::string_view file);
|
|
||||||
|
|
||||||
const FxUniformDesc* FindFxUniform(const FxEffectDesc& effect, std::string_view name);
|
|
||||||
|
|
||||||
} // namespace VideoCore
|
|
||||||
@@ -18,10 +18,6 @@
|
|||||||
#include "video_core/renderer_vulkan/present/sgsr.h"
|
#include "video_core/renderer_vulkan/present/sgsr.h"
|
||||||
#include "video_core/renderer_vulkan/present/fxaa.h"
|
#include "video_core/renderer_vulkan/present/fxaa.h"
|
||||||
#include "video_core/renderer_vulkan/present/layer.h"
|
#include "video_core/renderer_vulkan/present/layer.h"
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/renderer_vulkan/present/post_process.h"
|
|
||||||
#endif
|
|
||||||
#include "video_core/renderer_vulkan/present/present_push_constants.h"
|
#include "video_core/renderer_vulkan/present/present_push_constants.h"
|
||||||
#include "video_core/renderer_vulkan/present/smaa.h"
|
#include "video_core/renderer_vulkan/present/smaa.h"
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
@@ -97,9 +93,6 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
|
|
||||||
RefreshResources(device, framebuffer);
|
RefreshResources(device, framebuffer);
|
||||||
SetAntiAliasPass(device);
|
SetAntiAliasPass(device);
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
SetPostProcessPass(device);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
@@ -122,12 +115,6 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
smaa->Draw(device, scheduler, image_index, &source_image, &source_image_view);
|
smaa->Draw(device, scheduler, image_index, &source_image, &source_image_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
if (post_process.has_value()) {
|
|
||||||
post_process->Draw(device, scheduler, image_index, &source_image, &source_image_view);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
|
auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
|
||||||
const VkExtent2D render_extent{
|
const VkExtent2D render_extent{
|
||||||
.width = scaled_width,
|
.width = scaled_width,
|
||||||
@@ -227,40 +214,6 @@ void Layer::SetAntiAliasPass(const Device& device) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
void Layer::SetPostProcessPass(const Device& device) {
|
|
||||||
const VkExtent2D render_area{
|
|
||||||
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
|
||||||
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
|
||||||
};
|
|
||||||
|
|
||||||
const u64 generation = VideoCore::FxChain::Instance().Snapshot().generation;
|
|
||||||
|
|
||||||
if (post_process_generation == generation && post_process_extent.width == render_area.width &&
|
|
||||||
post_process_extent.height == render_area.height) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const u64 tick : resource_ticks) {
|
|
||||||
scheduler.Wait(tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
post_process_generation = generation;
|
|
||||||
post_process_extent = render_area;
|
|
||||||
post_process.reset();
|
|
||||||
|
|
||||||
if (VideoCore::FxChain::Instance().Size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
post_process.emplace(device, memory_allocator, scheduler, image_count, render_area);
|
|
||||||
|
|
||||||
if (post_process->Empty()) {
|
|
||||||
post_process.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Layer::ReleaseRawImages() {
|
void Layer::ReleaseRawImages() {
|
||||||
for (const u64 tick : resource_ticks) {
|
for (const u64 tick : resource_ticks) {
|
||||||
scheduler.Wait(tick);
|
scheduler.Wait(tick);
|
||||||
|
|||||||
@@ -15,9 +15,6 @@
|
|||||||
#include "video_core/renderer_vulkan/present/fsr.h"
|
#include "video_core/renderer_vulkan/present/fsr.h"
|
||||||
#include "video_core/renderer_vulkan/present/sgsr.h"
|
#include "video_core/renderer_vulkan/present/sgsr.h"
|
||||||
#include "video_core/renderer_vulkan/present/fxaa.h"
|
#include "video_core/renderer_vulkan/present/fxaa.h"
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
#include "video_core/renderer_vulkan/present/post_process.h"
|
|
||||||
#endif
|
|
||||||
#include "video_core/renderer_vulkan/present/smaa.h"
|
#include "video_core/renderer_vulkan/present/smaa.h"
|
||||||
|
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
@@ -69,9 +66,6 @@ private:
|
|||||||
|
|
||||||
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
||||||
void SetAntiAliasPass(const Device& device);
|
void SetAntiAliasPass(const Device& device);
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
void SetPostProcessPass(const Device& device);
|
|
||||||
#endif
|
|
||||||
void ReleaseRawImages();
|
void ReleaseRawImages();
|
||||||
|
|
||||||
u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
|
u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
|
||||||
@@ -101,11 +95,6 @@ private:
|
|||||||
Settings::AntiAliasing anti_alias_setting{};
|
Settings::AntiAliasing anti_alias_setting{};
|
||||||
std::variant<std::monostate, FXAA, SMAA> anti_alias{};
|
std::variant<std::monostate, FXAA, SMAA> anti_alias{};
|
||||||
std::variant<std::monostate, SGSR, FSR> sr_filter{};
|
std::variant<std::monostate, SGSR, FSR> sr_filter{};
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
std::optional<PostProcessChain> post_process{};
|
|
||||||
u64 post_process_generation{};
|
|
||||||
VkExtent2D post_process_extent{};
|
|
||||||
#endif
|
|
||||||
std::vector<u64> resource_ticks{};
|
std::vector<u64> resource_ticks{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,899 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
|
||||||
#include "common/fs/fs_util.h"
|
|
||||||
#include "common/logging.h"
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/post_processing/fx_compile.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
#include "video_core/renderer_vulkan/present/post_process.h"
|
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_device.h"
|
|
||||||
|
|
||||||
namespace Vulkan {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr VkFormat BACKBUFFER_FORMAT = VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
||||||
constexpr size_t NO_TEXTURE = ~size_t{0};
|
|
||||||
|
|
||||||
VkFormat ToVkFormat(reshadefx::texture_format format) {
|
|
||||||
switch (format) {
|
|
||||||
case reshadefx::texture_format::r8:
|
|
||||||
return VK_FORMAT_R8_UNORM;
|
|
||||||
case reshadefx::texture_format::r16f:
|
|
||||||
return VK_FORMAT_R16_SFLOAT;
|
|
||||||
case reshadefx::texture_format::r32f:
|
|
||||||
return VK_FORMAT_R32_SFLOAT;
|
|
||||||
case reshadefx::texture_format::rg8:
|
|
||||||
return VK_FORMAT_R8G8_UNORM;
|
|
||||||
case reshadefx::texture_format::rg16:
|
|
||||||
return VK_FORMAT_R16G16_UNORM;
|
|
||||||
case reshadefx::texture_format::rg16f:
|
|
||||||
return VK_FORMAT_R16G16_SFLOAT;
|
|
||||||
case reshadefx::texture_format::rg32f:
|
|
||||||
return VK_FORMAT_R32G32_SFLOAT;
|
|
||||||
case reshadefx::texture_format::rgba8:
|
|
||||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
case reshadefx::texture_format::rgba16:
|
|
||||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
|
||||||
case reshadefx::texture_format::rgba16f:
|
|
||||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
||||||
case reshadefx::texture_format::rgba32f:
|
|
||||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
||||||
case reshadefx::texture_format::rgb10a2:
|
|
||||||
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
|
||||||
default:
|
|
||||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkSamplerAddressMode ToAddressMode(reshadefx::texture_address_mode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case reshadefx::texture_address_mode::wrap:
|
|
||||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
|
||||||
case reshadefx::texture_address_mode::mirror:
|
|
||||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
|
||||||
case reshadefx::texture_address_mode::border:
|
|
||||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
|
||||||
case reshadefx::texture_address_mode::clamp:
|
|
||||||
default:
|
|
||||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkBlendFactor ToBlendFactor(reshadefx::blend_factor func) {
|
|
||||||
switch (func) {
|
|
||||||
case reshadefx::blend_factor::zero:
|
|
||||||
return VK_BLEND_FACTOR_ZERO;
|
|
||||||
case reshadefx::blend_factor::source_color:
|
|
||||||
return VK_BLEND_FACTOR_SRC_COLOR;
|
|
||||||
case reshadefx::blend_factor::source_alpha:
|
|
||||||
return VK_BLEND_FACTOR_SRC_ALPHA;
|
|
||||||
case reshadefx::blend_factor::one_minus_source_color:
|
|
||||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
|
|
||||||
case reshadefx::blend_factor::one_minus_source_alpha:
|
|
||||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
||||||
case reshadefx::blend_factor::dest_color:
|
|
||||||
return VK_BLEND_FACTOR_DST_COLOR;
|
|
||||||
case reshadefx::blend_factor::dest_alpha:
|
|
||||||
return VK_BLEND_FACTOR_DST_ALPHA;
|
|
||||||
case reshadefx::blend_factor::one_minus_dest_color:
|
|
||||||
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
|
||||||
case reshadefx::blend_factor::one_minus_dest_alpha:
|
|
||||||
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
|
||||||
case reshadefx::blend_factor::one:
|
|
||||||
default:
|
|
||||||
return VK_BLEND_FACTOR_ONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkBlendOp ToBlendOp(reshadefx::blend_op op) {
|
|
||||||
switch (op) {
|
|
||||||
case reshadefx::blend_op::subtract:
|
|
||||||
return VK_BLEND_OP_SUBTRACT;
|
|
||||||
case reshadefx::blend_op::reverse_subtract:
|
|
||||||
return VK_BLEND_OP_REVERSE_SUBTRACT;
|
|
||||||
case reshadefx::blend_op::min:
|
|
||||||
return VK_BLEND_OP_MIN;
|
|
||||||
case reshadefx::blend_op::max:
|
|
||||||
return VK_BLEND_OP_MAX;
|
|
||||||
case reshadefx::blend_op::add:
|
|
||||||
default:
|
|
||||||
return VK_BLEND_OP_ADD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkPrimitiveTopology ToTopology(reshadefx::primitive_topology topology) {
|
|
||||||
switch (topology) {
|
|
||||||
case reshadefx::primitive_topology::point_list:
|
|
||||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
||||||
case reshadefx::primitive_topology::line_list:
|
|
||||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
|
||||||
case reshadefx::primitive_topology::line_strip:
|
|
||||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
|
||||||
case reshadefx::primitive_topology::triangle_strip:
|
|
||||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
||||||
case reshadefx::primitive_topology::triangle_list:
|
|
||||||
default:
|
|
||||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::RenderPass CreateFxRenderPass(const Device& device, VkFormat format, bool clear) {
|
|
||||||
VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
|
|
||||||
VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
||||||
if (clear) {
|
|
||||||
load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
||||||
initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkAttachmentDescription attachment{
|
|
||||||
.flags = 0,
|
|
||||||
.format = format,
|
|
||||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
.loadOp = load_op,
|
|
||||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
||||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
||||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
||||||
.initialLayout = initial_layout,
|
|
||||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkAttachmentReference reference{
|
|
||||||
.attachment = 0,
|
|
||||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkSubpassDescription subpass{
|
|
||||||
.flags = 0,
|
|
||||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
||||||
.inputAttachmentCount = 0,
|
|
||||||
.pInputAttachments = nullptr,
|
|
||||||
.colorAttachmentCount = 1,
|
|
||||||
.pColorAttachments = &reference,
|
|
||||||
.pResolveAttachments = nullptr,
|
|
||||||
.pDepthStencilAttachment = nullptr,
|
|
||||||
.preserveAttachmentCount = 0,
|
|
||||||
.pPreserveAttachments = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.GetLogical().CreateRenderPass(VkRenderPassCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = &attachment,
|
|
||||||
.subpassCount = 1,
|
|
||||||
.pSubpasses = &subpass,
|
|
||||||
.dependencyCount = 0,
|
|
||||||
.pDependencies = nullptr,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Pipeline CreateFxPipeline(const Device& device, vk::RenderPass& renderpass,
|
|
||||||
vk::PipelineLayout& layout, VkShaderModule vertex_shader,
|
|
||||||
VkShaderModule fragment_shader,
|
|
||||||
const reshadefx::pass& pass) {
|
|
||||||
const std::array<VkPipelineShaderStageCreateInfo, 2> stages{{
|
|
||||||
{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
||||||
.module = vertex_shader,
|
|
||||||
.pName = pass.vs_entry_point.c_str(),
|
|
||||||
.pSpecializationInfo = nullptr,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
||||||
.module = fragment_shader,
|
|
||||||
.pName = pass.ps_entry_point.c_str(),
|
|
||||||
.pSpecializationInfo = nullptr,
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr VkPipelineVertexInputStateCreateInfo vertex_input{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.vertexBindingDescriptionCount = 0,
|
|
||||||
.pVertexBindingDescriptions = nullptr,
|
|
||||||
.vertexAttributeDescriptionCount = 0,
|
|
||||||
.pVertexAttributeDescriptions = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.topology = ToTopology(pass.topology),
|
|
||||||
.primitiveRestartEnable = VK_FALSE,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr VkPipelineViewportStateCreateInfo viewport_state{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.viewportCount = 1,
|
|
||||||
.pViewports = nullptr,
|
|
||||||
.scissorCount = 1,
|
|
||||||
.pScissors = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr VkPipelineRasterizationStateCreateInfo rasterization{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.depthClampEnable = VK_FALSE,
|
|
||||||
.rasterizerDiscardEnable = VK_FALSE,
|
|
||||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
|
||||||
.cullMode = VK_CULL_MODE_NONE,
|
|
||||||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
|
||||||
.depthBiasEnable = VK_FALSE,
|
|
||||||
.depthBiasConstantFactor = 0.0f,
|
|
||||||
.depthBiasClamp = 0.0f,
|
|
||||||
.depthBiasSlopeFactor = 0.0f,
|
|
||||||
.lineWidth = 1.0f,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr VkPipelineMultisampleStateCreateInfo multisampling{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
.sampleShadingEnable = VK_FALSE,
|
|
||||||
.minSampleShading = 0.0f,
|
|
||||||
.pSampleMask = nullptr,
|
|
||||||
.alphaToCoverageEnable = VK_FALSE,
|
|
||||||
.alphaToOneEnable = VK_FALSE,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkBool32 blend_enable = VK_FALSE;
|
|
||||||
if (pass.blend_enable[0]) {
|
|
||||||
blend_enable = VK_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkPipelineColorBlendAttachmentState blending{
|
|
||||||
.blendEnable = blend_enable,
|
|
||||||
.srcColorBlendFactor = ToBlendFactor(pass.source_color_blend_factor[0]),
|
|
||||||
.dstColorBlendFactor = ToBlendFactor(pass.dest_color_blend_factor[0]),
|
|
||||||
.colorBlendOp = ToBlendOp(pass.color_blend_op[0]),
|
|
||||||
.srcAlphaBlendFactor = ToBlendFactor(pass.source_alpha_blend_factor[0]),
|
|
||||||
.dstAlphaBlendFactor = ToBlendFactor(pass.dest_alpha_blend_factor[0]),
|
|
||||||
.alphaBlendOp = ToBlendOp(pass.alpha_blend_op[0]),
|
|
||||||
.colorWriteMask = static_cast<VkColorComponentFlags>(pass.render_target_write_mask[0] & 0xF),
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkPipelineColorBlendStateCreateInfo color_blend{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.logicOpEnable = VK_FALSE,
|
|
||||||
.logicOp = VK_LOGIC_OP_COPY,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = &blending,
|
|
||||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array dynamic_states{
|
|
||||||
VK_DYNAMIC_STATE_VIEWPORT,
|
|
||||||
VK_DYNAMIC_STATE_SCISSOR,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VkPipelineDynamicStateCreateInfo dynamic_state{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
|
|
||||||
.pDynamicStates = dynamic_states.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
return device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.stageCount = static_cast<u32>(stages.size()),
|
|
||||||
.pStages = stages.data(),
|
|
||||||
.pVertexInputState = &vertex_input,
|
|
||||||
.pInputAssemblyState = &input_assembly,
|
|
||||||
.pTessellationState = nullptr,
|
|
||||||
.pViewportState = &viewport_state,
|
|
||||||
.pRasterizationState = &rasterization,
|
|
||||||
.pMultisampleState = &multisampling,
|
|
||||||
.pDepthStencilState = nullptr,
|
|
||||||
.pColorBlendState = &color_blend,
|
|
||||||
.pDynamicState = &dynamic_state,
|
|
||||||
.layout = *layout,
|
|
||||||
.renderPass = *renderpass,
|
|
||||||
.subpass = 0,
|
|
||||||
.basePipelineHandle = nullptr,
|
|
||||||
.basePipelineIndex = 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
PostProcessChain::PostProcessChain(const Device& device, MemoryAllocator& allocator,
|
|
||||||
Scheduler& scheduler, size_t image_count, VkExtent2D extent)
|
|
||||||
: m_extent(extent)
|
|
||||||
, m_image_count(u32(image_count))
|
|
||||||
{
|
|
||||||
m_start = std::chrono::steady_clock::now();
|
|
||||||
m_previous = m_start;
|
|
||||||
|
|
||||||
CreatePingPongImages(device, allocator);
|
|
||||||
|
|
||||||
m_fallback_sampler = CreateWrappedSampler(device);
|
|
||||||
m_fallback_image = CreateWrappedImage(allocator, VkExtent2D{1, 1}, VK_FORMAT_R8G8B8A8_UNORM);
|
|
||||||
m_fallback_view = CreateWrappedImageView(device, m_fallback_image, VK_FORMAT_R8G8B8A8_UNORM);
|
|
||||||
|
|
||||||
if (!BuildEffects(device, allocator, scheduler)) {
|
|
||||||
m_effects.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PostProcessChain::~PostProcessChain() = default;
|
|
||||||
|
|
||||||
bool PostProcessChain::Empty() const {
|
|
||||||
return m_effects.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessChain::CreatePingPongImages(const Device& device, MemoryAllocator& allocator) {
|
|
||||||
m_frames.resize(m_image_count);
|
|
||||||
for (auto& frame : m_frames) {
|
|
||||||
for (size_t i = 0; i < frame.images.size(); ++i) {
|
|
||||||
frame.images[i] = CreateWrappedImage(allocator, m_extent, BACKBUFFER_FORMAT);
|
|
||||||
frame.views[i] = CreateWrappedImageView(device, frame.images[i], BACKBUFFER_FORMAT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PostProcessChain::BuildEffects(const Device& device, MemoryAllocator& allocator,
|
|
||||||
Scheduler& scheduler) {
|
|
||||||
const auto snapshot = VideoCore::FxChain::Instance().Snapshot();
|
|
||||||
if (snapshot.entries.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto root = VideoCore::GetFxRootDirectory();
|
|
||||||
|
|
||||||
for (size_t entry_index = 0; entry_index < snapshot.entries.size(); ++entry_index) {
|
|
||||||
const auto& entry = snapshot.entries[entry_index];
|
|
||||||
const auto path = root / entry.file;
|
|
||||||
|
|
||||||
const auto compiled = VideoCore::CompileFxEffect(path, m_extent.width, m_extent.height, 8);
|
|
||||||
if (!compiled.Succeeded()) {
|
|
||||||
LOG_ERROR(Render_Vulkan, "Post-processing effect '{}' failed to compile:\n{}",
|
|
||||||
entry.file, compiled.error);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& module = compiled.module;
|
|
||||||
|
|
||||||
const auto technique = std::find_if(
|
|
||||||
module.techniques.begin(), module.techniques.end(),
|
|
||||||
[&](const reshadefx::technique& t) { return t.name == entry.technique; });
|
|
||||||
if (technique == module.techniques.end()) {
|
|
||||||
LOG_ERROR(Render_Vulkan, "Effect '{}' has no technique '{}'", entry.file,
|
|
||||||
entry.technique);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Effect effect;
|
|
||||||
effect.entry_index = entry_index;
|
|
||||||
effect.file = entry.file;
|
|
||||||
effect.uniform_size = module.total_uniform_size;
|
|
||||||
for (const auto& [name, words] : compiled.entry_points) {
|
|
||||||
effect.shaders.emplace(name, CreateWrappedShaderModule(device, words));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& texture : module.textures) {
|
|
||||||
Texture out;
|
|
||||||
out.name = texture.unique_name;
|
|
||||||
out.extent = VkExtent2D{texture.width, texture.height};
|
|
||||||
out.format = ToVkFormat(texture.format);
|
|
||||||
|
|
||||||
if (texture.semantic == "COLOR") {
|
|
||||||
out.is_backbuffer = true;
|
|
||||||
effect.textures.push_back(std::move(out));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (texture.semantic == "DEPTH") {
|
|
||||||
effect.textures.push_back(std::move(out));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.image = CreateWrappedImage(allocator, out.extent, out.format);
|
|
||||||
out.view = CreateWrappedImageView(device, out.image, out.format);
|
|
||||||
effect.textures.push_back(std::move(out));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& sampler : module.samplers) {
|
|
||||||
Sampler out;
|
|
||||||
out.texture_index = NO_TEXTURE;
|
|
||||||
for (size_t i = 0; i < effect.textures.size(); ++i) {
|
|
||||||
if (effect.textures[i].name == sampler.texture_name) {
|
|
||||||
out.texture_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkFilter mag_filter = VK_FILTER_LINEAR;
|
|
||||||
VkFilter min_filter = VK_FILTER_LINEAR;
|
|
||||||
VkSamplerMipmapMode mip_mode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
|
||||||
const u32 filter = static_cast<u32>(sampler.filter);
|
|
||||||
if ((filter & 0x10) == 0) {
|
|
||||||
min_filter = VK_FILTER_NEAREST;
|
|
||||||
}
|
|
||||||
if ((filter & 0x04) == 0) {
|
|
||||||
mag_filter = VK_FILTER_NEAREST;
|
|
||||||
}
|
|
||||||
if ((filter & 0x01) == 0) {
|
|
||||||
mip_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.magFilter = mag_filter,
|
|
||||||
.minFilter = min_filter,
|
|
||||||
.mipmapMode = mip_mode,
|
|
||||||
.addressModeU = ToAddressMode(sampler.address_u),
|
|
||||||
.addressModeV = ToAddressMode(sampler.address_v),
|
|
||||||
.addressModeW = ToAddressMode(sampler.address_w),
|
|
||||||
.mipLodBias = sampler.lod_bias,
|
|
||||||
.anisotropyEnable = VK_FALSE,
|
|
||||||
.maxAnisotropy = 1.0f,
|
|
||||||
.compareEnable = VK_FALSE,
|
|
||||||
.compareOp = VK_COMPARE_OP_NEVER,
|
|
||||||
.minLod = sampler.min_lod,
|
|
||||||
.maxLod = sampler.max_lod,
|
|
||||||
.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
|
|
||||||
.unnormalizedCoordinates = VK_FALSE,
|
|
||||||
});
|
|
||||||
|
|
||||||
effect.samplers.push_back(std::move(out));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& uniform : module.uniforms) {
|
|
||||||
UniformWrite write;
|
|
||||||
write.name = uniform.name;
|
|
||||||
write.offset = uniform.offset;
|
|
||||||
write.components = std::min<u32>(uniform.type.components(), 4);
|
|
||||||
write.kind = UniformKind::Floating;
|
|
||||||
if (uniform.type.is_boolean()) {
|
|
||||||
write.kind = UniformKind::Boolean;
|
|
||||||
} else if (uniform.type.is_integral()) {
|
|
||||||
write.kind = UniformKind::Integer;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& annotation : uniform.annotations) {
|
|
||||||
if (annotation.name != "source") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const std::string& source = annotation.value.string_data;
|
|
||||||
if (source == "frametime") {
|
|
||||||
write.source = UniformSource::FrameTime;
|
|
||||||
} else if (source == "framecount") {
|
|
||||||
write.source = UniformSource::FrameCount;
|
|
||||||
} else if (source == "timer") {
|
|
||||||
write.source = UniformSource::Timer;
|
|
||||||
} else if (source == "random") {
|
|
||||||
write.source = UniformSource::Random;
|
|
||||||
} else if (source == "pingpong") {
|
|
||||||
write.source = UniformSource::PingPong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uniform.has_initializer_value) {
|
|
||||||
for (u32 i = 0; i < write.components; ++i) {
|
|
||||||
if (write.kind == UniformKind::Floating) {
|
|
||||||
write.fallback[i] = uniform.initializer_value.as_float[i];
|
|
||||||
} else {
|
|
||||||
write.fallback[i] = static_cast<f32>(uniform.initializer_value.as_int[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write.args = {0.0f, 1.0f, 1.0f, 0.0f};
|
|
||||||
for (const auto& annotation : uniform.annotations) {
|
|
||||||
if (annotation.name == "min" && annotation.type.is_floating_point()) {
|
|
||||||
write.args[0] = annotation.value.as_float[0];
|
|
||||||
}
|
|
||||||
if (annotation.name == "max" && annotation.type.is_floating_point()) {
|
|
||||||
write.args[1] = annotation.value.as_float[0];
|
|
||||||
}
|
|
||||||
if (annotation.name == "step" && annotation.type.is_floating_point()) {
|
|
||||||
write.args[2] = annotation.value.as_float[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write.state = write.args[0];
|
|
||||||
|
|
||||||
effect.uniforms.push_back(std::move(write));
|
|
||||||
}
|
|
||||||
|
|
||||||
effect.uniform_layout = CreateWrappedDescriptorSetLayout(
|
|
||||||
device, std::array{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER},
|
|
||||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
||||||
|
|
||||||
for (const auto& pass : technique->passes) {
|
|
||||||
Pass out;
|
|
||||||
out.num_vertices = pass.num_vertices;
|
|
||||||
out.clear = pass.clear_render_targets != 0;
|
|
||||||
out.target_texture = NO_TEXTURE;
|
|
||||||
out.extent = m_extent;
|
|
||||||
|
|
||||||
const std::string& target = pass.render_target_names[0];
|
|
||||||
if (target.empty()) {
|
|
||||||
out.writes_backbuffer = true;
|
|
||||||
} else {
|
|
||||||
for (size_t i = 0; i < effect.textures.size(); ++i) {
|
|
||||||
if (effect.textures[i].name == target) {
|
|
||||||
out.target_texture = i;
|
|
||||||
out.extent = effect.textures[i].extent;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (out.target_texture == NO_TEXTURE) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "Effect '{}' pass targets unknown texture '{}'",
|
|
||||||
entry.file, target);
|
|
||||||
out.writes_backbuffer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pass.viewport_width != 0 && pass.viewport_height != 0) {
|
|
||||||
out.extent = VkExtent2D{pass.viewport_width, pass.viewport_height};
|
|
||||||
}
|
|
||||||
|
|
||||||
VkFormat target_format = BACKBUFFER_FORMAT;
|
|
||||||
if (!out.writes_backbuffer) {
|
|
||||||
target_format = effect.textures[out.target_texture].format;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 binding_count = 0;
|
|
||||||
for (const auto& binding : pass.sampler_bindings) {
|
|
||||||
SamplerBinding entry_binding;
|
|
||||||
entry_binding.binding = binding.entry_point_binding;
|
|
||||||
entry_binding.sampler_index = binding.index;
|
|
||||||
out.sampler_bindings.push_back(entry_binding);
|
|
||||||
binding_count = std::max(binding_count, binding.entry_point_binding + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<VkDescriptorType> sampler_types(
|
|
||||||
std::max<size_t>(binding_count, 1), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
||||||
out.sampler_layout = CreateWrappedDescriptorSetLayout(
|
|
||||||
device, sampler_types, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
||||||
|
|
||||||
const std::array set_layouts{*effect.uniform_layout, *out.sampler_layout};
|
|
||||||
out.pipeline_layout =
|
|
||||||
device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.flags = 0,
|
|
||||||
.setLayoutCount = static_cast<u32>(set_layouts.size()),
|
|
||||||
.pSetLayouts = set_layouts.data(),
|
|
||||||
.pushConstantRangeCount = 0,
|
|
||||||
.pPushConstantRanges = nullptr,
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto vertex_shader = effect.shaders.find(pass.vs_entry_point);
|
|
||||||
const auto fragment_shader = effect.shaders.find(pass.ps_entry_point);
|
|
||||||
if (vertex_shader == effect.shaders.end() ||
|
|
||||||
fragment_shader == effect.shaders.end()) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "Effect '{}' pass references a missing entry point",
|
|
||||||
entry.file);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.renderpass = CreateFxRenderPass(device, target_format, out.clear);
|
|
||||||
out.pipeline = CreateFxPipeline(device, out.renderpass, out.pipeline_layout,
|
|
||||||
*vertex_shader->second, *fragment_shader->second, pass);
|
|
||||||
|
|
||||||
if (out.writes_backbuffer) {
|
|
||||||
out.backbuffer_slot = static_cast<u32>(effect.backbuffer_pass_count % 2);
|
|
||||||
++effect.backbuffer_pass_count;
|
|
||||||
for (u32 image = 0; image < m_image_count; ++image) {
|
|
||||||
for (size_t slot = 0; slot < 2; ++slot) {
|
|
||||||
out.framebuffers.push_back(CreateWrappedFramebuffer(
|
|
||||||
device, out.renderpass, m_frames[image].views[slot], out.extent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.framebuffers.push_back(
|
|
||||||
CreateWrappedFramebuffer(device, out.renderpass,
|
|
||||||
effect.textures[out.target_texture].view, out.extent));
|
|
||||||
}
|
|
||||||
|
|
||||||
effect.passes.push_back(std::move(out));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (effect.passes.empty()) {
|
|
||||||
LOG_WARNING(Render_Vulkan, "Effect '{}' technique '{}' has no passes", entry.file,
|
|
||||||
entry.technique);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 buffer_size = std::max<u32>(effect.uniform_size, 4);
|
|
||||||
for (u32 i = 0; i < m_image_count; ++i) {
|
|
||||||
effect.uniform_buffers.push_back(
|
|
||||||
CreateWrappedBuffer(allocator, buffer_size, MemoryUsage::Upload));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sampler_descriptor_count = 0;
|
|
||||||
size_t sampler_set_count = 0;
|
|
||||||
for (const auto& pass : effect.passes) {
|
|
||||||
sampler_descriptor_count +=
|
|
||||||
m_image_count * std::max<size_t>(pass.sampler_bindings.size(), 1);
|
|
||||||
sampler_set_count += m_image_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
effect.descriptor_pool = CreateWrappedDescriptorPool(
|
|
||||||
device, m_image_count + sampler_descriptor_count, m_image_count + sampler_set_count,
|
|
||||||
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
|
|
||||||
|
|
||||||
const std::vector<VkDescriptorSetLayout> uniform_layouts(m_image_count,
|
|
||||||
*effect.uniform_layout);
|
|
||||||
effect.uniform_sets = CreateWrappedDescriptorSets(effect.descriptor_pool, uniform_layouts);
|
|
||||||
|
|
||||||
for (auto& pass : effect.passes) {
|
|
||||||
const std::vector<VkDescriptorSetLayout> layouts(m_image_count, *pass.sampler_layout);
|
|
||||||
pass.sampler_sets = CreateWrappedDescriptorSets(effect.descriptor_pool, layouts);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_effects.push_back(std::move(effect));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessChain::PrepareImages(const Device& device, Scheduler& scheduler) {
|
|
||||||
if (m_images_ready) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduler.Record([this](vk::CommandBuffer cmdbuf) {
|
|
||||||
ClearColorImage(cmdbuf, *m_fallback_image);
|
|
||||||
for (auto& frame : m_frames) {
|
|
||||||
for (auto& image : frame.images) {
|
|
||||||
ClearColorImage(cmdbuf, *image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& effect : m_effects) {
|
|
||||||
for (auto& texture : effect.textures) {
|
|
||||||
if (texture.image) {
|
|
||||||
ClearColorImage(cmdbuf, *texture.image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
scheduler.Finish();
|
|
||||||
|
|
||||||
m_images_ready = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessChain::UpdateUniforms(Effect& effect, size_t image_index, f32 delta_seconds) {
|
|
||||||
if (effect.uniform_size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static thread_local std::mt19937 rng{std::random_device{}()};
|
|
||||||
|
|
||||||
std::vector<u8> staging(effect.uniform_size, 0);
|
|
||||||
const f32 elapsed =
|
|
||||||
std::chrono::duration<f32>(std::chrono::steady_clock::now() - m_start).count();
|
|
||||||
const auto overrides = VideoCore::FxChain::Instance().EntryValues(effect.entry_index);
|
|
||||||
|
|
||||||
for (auto& uniform : effect.uniforms) {
|
|
||||||
std::array<f32, 4> value = uniform.fallback;
|
|
||||||
|
|
||||||
const auto override = overrides.find(uniform.name);
|
|
||||||
if (override != overrides.end()) {
|
|
||||||
value = override->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (uniform.source) {
|
|
||||||
case UniformSource::FrameTime:
|
|
||||||
value[0] = delta_seconds * 1000.0f;
|
|
||||||
break;
|
|
||||||
case UniformSource::FrameCount:
|
|
||||||
value[0] = static_cast<f32>(m_frame_count);
|
|
||||||
break;
|
|
||||||
case UniformSource::Timer:
|
|
||||||
value[0] = elapsed * 1000.0f;
|
|
||||||
break;
|
|
||||||
case UniformSource::Random: {
|
|
||||||
const int low = static_cast<int>(uniform.args[0]);
|
|
||||||
int high = static_cast<int>(uniform.args[1]);
|
|
||||||
if (high <= low) {
|
|
||||||
high = low + 1;
|
|
||||||
}
|
|
||||||
std::uniform_int_distribution<int> dist(low, high);
|
|
||||||
value[0] = static_cast<f32>(dist(rng));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UniformSource::PingPong: {
|
|
||||||
const f32 min_value = uniform.args[0];
|
|
||||||
f32 max_value = uniform.args[1];
|
|
||||||
if (max_value <= min_value) {
|
|
||||||
max_value = min_value + 1.0f;
|
|
||||||
}
|
|
||||||
f32 step = uniform.args[2];
|
|
||||||
if (step == 0.0f) {
|
|
||||||
step = 1.0f;
|
|
||||||
}
|
|
||||||
uniform.state += uniform.direction * step * delta_seconds;
|
|
||||||
if (uniform.state >= max_value) {
|
|
||||||
uniform.state = max_value;
|
|
||||||
uniform.direction = -1.0f;
|
|
||||||
}
|
|
||||||
if (uniform.state <= min_value) {
|
|
||||||
uniform.state = min_value;
|
|
||||||
uniform.direction = 1.0f;
|
|
||||||
}
|
|
||||||
value[0] = uniform.state;
|
|
||||||
value[1] = uniform.direction;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UniformSource::Value:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i = 0; i < uniform.components; ++i) {
|
|
||||||
const size_t offset = uniform.offset + i * sizeof(u32);
|
|
||||||
if (offset + sizeof(u32) > staging.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (uniform.kind == UniformKind::Floating) {
|
|
||||||
const f32 element = value[i];
|
|
||||||
std::memcpy(staging.data() + offset, &element, sizeof(f32));
|
|
||||||
} else {
|
|
||||||
const s32 element = static_cast<s32>(value[i]);
|
|
||||||
std::memcpy(staging.data() + offset, &element, sizeof(s32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::span<u8> mapped = effect.uniform_buffers[image_index].Mapped();
|
|
||||||
if (mapped.size() >= staging.size()) {
|
|
||||||
std::memcpy(mapped.data(), staging.data(), staging.size());
|
|
||||||
effect.uniform_buffers[image_index].Flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessChain::UpdateDescriptors(const Device& device, Effect& effect, Pass& pass,
|
|
||||||
size_t image_index, VkImageView backbuffer_view) {
|
|
||||||
std::vector<VkDescriptorImageInfo> image_infos;
|
|
||||||
std::vector<VkWriteDescriptorSet> writes;
|
|
||||||
image_infos.reserve(pass.sampler_bindings.size() + 1);
|
|
||||||
|
|
||||||
const VkDescriptorSet sampler_set = pass.sampler_sets[image_index];
|
|
||||||
|
|
||||||
for (const auto& binding : pass.sampler_bindings) {
|
|
||||||
VkImageView view = *m_fallback_view;
|
|
||||||
VkSampler handle = *m_fallback_sampler;
|
|
||||||
|
|
||||||
if (binding.sampler_index < effect.samplers.size()) {
|
|
||||||
const Sampler& sampler = effect.samplers[binding.sampler_index];
|
|
||||||
if (sampler.sampler) {
|
|
||||||
handle = *sampler.sampler;
|
|
||||||
}
|
|
||||||
if (sampler.texture_index != NO_TEXTURE) {
|
|
||||||
const Texture& texture = effect.textures[sampler.texture_index];
|
|
||||||
if (texture.is_backbuffer) {
|
|
||||||
view = backbuffer_view;
|
|
||||||
} else if (texture.view) {
|
|
||||||
view = *texture.view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writes.push_back(
|
|
||||||
CreateWriteDescriptorSet(image_infos, handle, view, sampler_set, binding.binding));
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkDescriptorBufferInfo buffer_info{
|
|
||||||
.buffer = *effect.uniform_buffers[image_index],
|
|
||||||
.offset = 0,
|
|
||||||
.range = VK_WHOLE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
writes.push_back(VkWriteDescriptorSet{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.dstSet = effect.uniform_sets[image_index],
|
|
||||||
.dstBinding = 0,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
.pImageInfo = nullptr,
|
|
||||||
.pBufferInfo = &buffer_info,
|
|
||||||
.pTexelBufferView = nullptr,
|
|
||||||
});
|
|
||||||
|
|
||||||
device.GetLogical().UpdateDescriptorSets(writes, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void PostProcessChain::Draw(const Device& device, Scheduler& scheduler, size_t image_index,
|
|
||||||
VkImage* inout_image, VkImageView* inout_image_view) {
|
|
||||||
if (m_effects.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrepareImages(device, scheduler);
|
|
||||||
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
const f32 delta_seconds = std::chrono::duration<f32>(now - m_previous).count();
|
|
||||||
m_previous = now;
|
|
||||||
++m_frame_count;
|
|
||||||
|
|
||||||
FrameImages& frame = m_frames[image_index];
|
|
||||||
VkImage current_image = *inout_image;
|
|
||||||
VkImageView current_view = *inout_image_view;
|
|
||||||
u32 slot = 0;
|
|
||||||
|
|
||||||
for (auto& effect : m_effects) {
|
|
||||||
UpdateUniforms(effect, image_index, delta_seconds);
|
|
||||||
|
|
||||||
for (size_t pass_index = 0; pass_index < effect.passes.size(); ++pass_index) {
|
|
||||||
Pass& pass = effect.passes[pass_index];
|
|
||||||
|
|
||||||
UpdateDescriptors(device, effect, pass, image_index, current_view);
|
|
||||||
|
|
||||||
VkFramebuffer framebuffer{};
|
|
||||||
VkImage target_image{};
|
|
||||||
if (pass.writes_backbuffer) {
|
|
||||||
const u32 target_slot = (slot + 1) % 2;
|
|
||||||
framebuffer = *pass.framebuffers[image_index * 2 + target_slot];
|
|
||||||
target_image = *frame.images[target_slot];
|
|
||||||
} else {
|
|
||||||
framebuffer = *pass.framebuffers[0];
|
|
||||||
target_image = *effect.textures[pass.target_texture].image;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VkImage source_image = current_image;
|
|
||||||
const VkRenderPass renderpass = *pass.renderpass;
|
|
||||||
const VkPipeline pipeline = *pass.pipeline;
|
|
||||||
const VkPipelineLayout layout = *pass.pipeline_layout;
|
|
||||||
const VkDescriptorSet uniform_set = effect.uniform_sets[image_index];
|
|
||||||
const VkDescriptorSet sampler_set = pass.sampler_sets[image_index];
|
|
||||||
const VkExtent2D extent = pass.extent;
|
|
||||||
const u32 vertices = pass.num_vertices;
|
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
scheduler.Record([=](vk::CommandBuffer cmdbuf) {
|
|
||||||
TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
|
|
||||||
TransitionImageLayout(cmdbuf, target_image, VK_IMAGE_LAYOUT_GENERAL);
|
|
||||||
BeginRenderPass(cmdbuf, renderpass, framebuffer, extent);
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0,
|
|
||||||
std::array{uniform_set, sampler_set}, {});
|
|
||||||
cmdbuf.Draw(vertices, 1, 0, 0);
|
|
||||||
cmdbuf.EndRenderPass();
|
|
||||||
TransitionImageLayout(cmdbuf, target_image, VK_IMAGE_LAYOUT_GENERAL);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pass.writes_backbuffer) {
|
|
||||||
slot = (slot + 1) % 2;
|
|
||||||
current_image = *frame.images[slot];
|
|
||||||
current_view = *frame.views[slot];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*inout_image = current_image;
|
|
||||||
*inout_image_view = current_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <chrono>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
|
||||||
|
|
||||||
namespace Vulkan {
|
|
||||||
|
|
||||||
class Device;
|
|
||||||
class Scheduler;
|
|
||||||
|
|
||||||
class PostProcessChain {
|
|
||||||
public:
|
|
||||||
explicit PostProcessChain(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
|
|
||||||
size_t image_count, VkExtent2D extent);
|
|
||||||
~PostProcessChain();
|
|
||||||
|
|
||||||
void Draw(const Device& device, Scheduler& scheduler, size_t image_index, VkImage* inout_image,
|
|
||||||
VkImageView* inout_image_view);
|
|
||||||
|
|
||||||
bool Empty() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum class UniformKind : u32 {
|
|
||||||
Boolean,
|
|
||||||
Integer,
|
|
||||||
Floating,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class UniformSource : u32 {
|
|
||||||
Value,
|
|
||||||
FrameTime,
|
|
||||||
FrameCount,
|
|
||||||
Timer,
|
|
||||||
Random,
|
|
||||||
PingPong,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UniformWrite {
|
|
||||||
std::string name;
|
|
||||||
u32 offset{};
|
|
||||||
u32 components{};
|
|
||||||
UniformKind kind{UniformKind::Floating};
|
|
||||||
UniformSource source{UniformSource::Value};
|
|
||||||
std::array<f32, 4> fallback{};
|
|
||||||
std::array<f32, 4> args{};
|
|
||||||
f32 state{};
|
|
||||||
f32 direction{1.0f};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Texture {
|
|
||||||
std::string name;
|
|
||||||
vk::Image image{};
|
|
||||||
vk::ImageView view{};
|
|
||||||
VkExtent2D extent{};
|
|
||||||
VkFormat format{};
|
|
||||||
bool is_backbuffer{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sampler {
|
|
||||||
vk::Sampler sampler{};
|
|
||||||
size_t texture_index{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SamplerBinding {
|
|
||||||
u32 binding{};
|
|
||||||
size_t sampler_index{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Pass {
|
|
||||||
vk::RenderPass renderpass{};
|
|
||||||
vk::Pipeline pipeline{};
|
|
||||||
vk::DescriptorSetLayout sampler_layout{};
|
|
||||||
vk::PipelineLayout pipeline_layout{};
|
|
||||||
vk::DescriptorSets sampler_sets{};
|
|
||||||
std::vector<SamplerBinding> sampler_bindings{};
|
|
||||||
std::vector<vk::Framebuffer> framebuffers{};
|
|
||||||
size_t target_texture{};
|
|
||||||
VkExtent2D extent{};
|
|
||||||
u32 num_vertices{3};
|
|
||||||
bool clear{};
|
|
||||||
bool writes_backbuffer{};
|
|
||||||
u32 backbuffer_slot{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Effect {
|
|
||||||
size_t entry_index{};
|
|
||||||
std::string file{};
|
|
||||||
std::map<std::string, vk::ShaderModule> shaders{};
|
|
||||||
std::vector<Texture> textures{};
|
|
||||||
std::vector<Sampler> samplers{};
|
|
||||||
std::vector<Pass> passes{};
|
|
||||||
std::vector<UniformWrite> uniforms{};
|
|
||||||
u32 uniform_size{};
|
|
||||||
std::vector<vk::Buffer> uniform_buffers{};
|
|
||||||
vk::DescriptorSetLayout uniform_layout{};
|
|
||||||
vk::DescriptorPool descriptor_pool{};
|
|
||||||
vk::DescriptorSets uniform_sets{};
|
|
||||||
size_t backbuffer_pass_count{};
|
|
||||||
u32 backbuffer_slots{1};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FrameImages {
|
|
||||||
std::array<vk::Image, 2> images{};
|
|
||||||
std::array<vk::ImageView, 2> views{};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool BuildEffects(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler);
|
|
||||||
void CreatePingPongImages(const Device& device, MemoryAllocator& allocator);
|
|
||||||
void PrepareImages(const Device& device, Scheduler& scheduler);
|
|
||||||
void UpdateUniforms(Effect& effect, size_t image_index, f32 delta_seconds);
|
|
||||||
void UpdateDescriptors(const Device& device, Effect& effect, Pass& pass, size_t image_index,
|
|
||||||
VkImageView backbuffer_view);
|
|
||||||
|
|
||||||
const VkExtent2D m_extent;
|
|
||||||
const u32 m_image_count;
|
|
||||||
|
|
||||||
std::vector<Effect> m_effects{};
|
|
||||||
std::vector<FrameImages> m_frames{};
|
|
||||||
|
|
||||||
vk::Sampler m_fallback_sampler{};
|
|
||||||
vk::Image m_fallback_image{};
|
|
||||||
vk::ImageView m_fallback_view{};
|
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point m_start{};
|
|
||||||
std::chrono::steady_clock::time_point m_previous{};
|
|
||||||
u64 m_frame_count{};
|
|
||||||
bool m_images_ready{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
||||||
@@ -24,10 +24,6 @@
|
|||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/present.h"
|
#include "video_core/present.h"
|
||||||
#include "video_core/renderer_vulkan/present/util.h"
|
#include "video_core/renderer_vulkan/present/util.h"
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
#endif
|
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
@@ -187,12 +183,6 @@ try
|
|||||||
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
VideoCore::ReloadFxCatalog();
|
|
||||||
VideoCore::FxChain::Instance().LoadFromSettings();
|
|
||||||
VideoCore::FxChain::Instance().DropUnknownEntries();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Report();
|
Report();
|
||||||
} catch (const vk::Exception& exception) {
|
} catch (const vk::Exception& exception) {
|
||||||
LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
|
LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
|
||||||
|
|||||||
@@ -250,12 +250,6 @@ if (YUZU_CRASH_DUMPS)
|
|||||||
target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS)
|
target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_RESHADE)
|
|
||||||
target_sources(yuzu PRIVATE
|
|
||||||
configuration/configure_post_processing.cpp
|
|
||||||
configuration/configure_post_processing.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CXX_CLANG)
|
if (CXX_CLANG)
|
||||||
target_compile_definitions(yuzu PRIVATE
|
target_compile_definitions(yuzu PRIVATE
|
||||||
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>)
|
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>)
|
||||||
|
|||||||
@@ -1,378 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <QCheckBox>
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QGroupBox>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QSlider>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "common/fs/fs.h"
|
|
||||||
#include "common/fs/fs_util.h"
|
|
||||||
#include "video_core/post_processing/fx_chain.h"
|
|
||||||
#include "video_core/post_processing/fx_effect.h"
|
|
||||||
#include "yuzu/configuration/configure_post_processing.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
std::array<float, 4> CurrentValue(int index, const VideoCore::FxUniformDesc& uniform) {
|
|
||||||
auto& chain = VideoCore::FxChain::Instance();
|
|
||||||
if (chain.HasValue(static_cast<size_t>(index), uniform.name)) {
|
|
||||||
return chain.GetValue(static_cast<size_t>(index), uniform.name);
|
|
||||||
}
|
|
||||||
return uniform.default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SliderSteps(const VideoCore::FxUniformDesc& uniform) {
|
|
||||||
const float span = uniform.ui_max - uniform.ui_min;
|
|
||||||
const int steps = static_cast<int>(std::lround(span / uniform.ui_step));
|
|
||||||
if (steps < 1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FormatValue(const VideoCore::FxUniformDesc& uniform, float value) {
|
|
||||||
if (uniform.kind == VideoCore::FxUniformKind::Floating) {
|
|
||||||
return QString::number(value, 'f', 3);
|
|
||||||
}
|
|
||||||
return QString::number(static_cast<int>(std::lround(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SlotLabel(const VideoCore::FxEffectDesc& effect, const std::string& technique) {
|
|
||||||
const QString name = QString::fromStdString(effect.name);
|
|
||||||
if (effect.techniques.size() == 1) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return name + QStringLiteral(" · ") + QString::fromStdString(technique);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
ConfigurePostProcessing::ConfigurePostProcessing(QWidget* parent) : QDialog(parent) {
|
|
||||||
setWindowTitle(tr("Post-Processing Effects"));
|
|
||||||
setMinimumWidth(560);
|
|
||||||
setMinimumHeight(460);
|
|
||||||
|
|
||||||
auto* root = new QVBoxLayout(this);
|
|
||||||
|
|
||||||
auto* description = new QLabel(
|
|
||||||
tr("ReShade FX effects are loaded from the post_shaders folder in the Eden data "
|
|
||||||
"directory. Changes apply immediately while a game is running."),
|
|
||||||
this);
|
|
||||||
description->setWordWrap(true);
|
|
||||||
root->addWidget(description);
|
|
||||||
|
|
||||||
auto* scroll = new QScrollArea(this);
|
|
||||||
scroll->setWidgetResizable(true);
|
|
||||||
slots_container = new QWidget(scroll);
|
|
||||||
slots_layout = new QVBoxLayout(slots_container);
|
|
||||||
slots_layout->setAlignment(Qt::AlignTop);
|
|
||||||
scroll->setWidget(slots_container);
|
|
||||||
root->addWidget(scroll, 1);
|
|
||||||
|
|
||||||
auto* actions = new QHBoxLayout();
|
|
||||||
|
|
||||||
auto* add_button = new QPushButton(tr("Add Effect"), this);
|
|
||||||
connect(add_button, &QPushButton::clicked, this, [this]() {
|
|
||||||
for (const auto& effect : VideoCore::GetFxCatalog()) {
|
|
||||||
if (!effect.Valid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
VideoCore::FxChain::Instance().Append(effect.file, effect.techniques.front());
|
|
||||||
ApplyStructuralChange();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
actions->addWidget(add_button);
|
|
||||||
|
|
||||||
auto* reload_button = new QPushButton(tr("Reload From Disk"), this);
|
|
||||||
connect(reload_button, &QPushButton::clicked, this, [this]() {
|
|
||||||
VideoCore::ReloadFxCatalog();
|
|
||||||
VideoCore::FxChain::Instance().DropUnknownEntries();
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
actions->addWidget(reload_button);
|
|
||||||
|
|
||||||
auto* open_button = new QPushButton(tr("Open Folder"), this);
|
|
||||||
connect(open_button, &QPushButton::clicked, this, []() {
|
|
||||||
const auto path = VideoCore::GetFxRootDirectory();
|
|
||||||
void(Common::FS::CreateDirs(path));
|
|
||||||
QDesktopServices::openUrl(
|
|
||||||
QUrl::fromLocalFile(QString::fromStdString(Common::FS::PathToUTF8String(path))));
|
|
||||||
});
|
|
||||||
actions->addWidget(open_button);
|
|
||||||
|
|
||||||
actions->addStretch();
|
|
||||||
root->addLayout(actions);
|
|
||||||
|
|
||||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Close, this);
|
|
||||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::close);
|
|
||||||
root->addWidget(buttons);
|
|
||||||
|
|
||||||
RebuildRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigurePostProcessing::~ConfigurePostProcessing() = default;
|
|
||||||
|
|
||||||
void ConfigurePostProcessing::ApplyStructuralChange() {
|
|
||||||
VideoCore::FxChain::Instance().StoreToSettings();
|
|
||||||
RebuildRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurePostProcessing::PopulateEffectCombo(QComboBox* combo,
|
|
||||||
const VideoCore::FxChainEntry& entry) const {
|
|
||||||
combo->clear();
|
|
||||||
int selected = -1;
|
|
||||||
|
|
||||||
for (const auto& effect : VideoCore::GetFxCatalog()) {
|
|
||||||
if (!effect.Valid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const auto& technique : effect.techniques) {
|
|
||||||
const QString key = QString::fromStdString(effect.file + "|" + technique);
|
|
||||||
combo->addItem(SlotLabel(effect, technique), key);
|
|
||||||
if (effect.file == entry.file && technique == entry.technique) {
|
|
||||||
selected = combo->count() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected >= 0) {
|
|
||||||
combo->setCurrentIndex(selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurePostProcessing::BuildUniformWidget(QWidget* parent, QVBoxLayout* layout, int index,
|
|
||||||
const VideoCore::FxUniformDesc& uniform) {
|
|
||||||
const auto value = CurrentValue(index, uniform);
|
|
||||||
const QString label = QString::fromStdString(uniform.label);
|
|
||||||
|
|
||||||
if (uniform.ui_type == VideoCore::FxUiType::CheckBox) {
|
|
||||||
auto* box = new QCheckBox(label, parent);
|
|
||||||
box->setChecked(value[0] != 0.0f);
|
|
||||||
if (!uniform.tooltip.empty()) {
|
|
||||||
box->setToolTip(QString::fromStdString(uniform.tooltip));
|
|
||||||
}
|
|
||||||
const std::string name = uniform.name;
|
|
||||||
connect(box, &QCheckBox::toggled, this, [index, name](bool checked) {
|
|
||||||
std::array<float, 4> next{};
|
|
||||||
if (checked) {
|
|
||||||
next[0] = 1.0f;
|
|
||||||
}
|
|
||||||
VideoCore::FxChain::Instance().SetValue(static_cast<size_t>(index), name, next);
|
|
||||||
VideoCore::FxChain::Instance().StoreToSettings();
|
|
||||||
});
|
|
||||||
layout->addWidget(box);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uniform.ui_type == VideoCore::FxUiType::Combo ||
|
|
||||||
uniform.ui_type == VideoCore::FxUiType::Radio) {
|
|
||||||
auto* row = new QHBoxLayout();
|
|
||||||
row->addWidget(new QLabel(label, parent));
|
|
||||||
auto* combo = new QComboBox(parent);
|
|
||||||
for (size_t i = 0; i < uniform.items.size(); ++i) {
|
|
||||||
combo->addItem(QString::fromStdString(uniform.items[i]), static_cast<int>(i));
|
|
||||||
}
|
|
||||||
if (combo->count() == 0) {
|
|
||||||
combo->addItem(tr("Enabled"), 1);
|
|
||||||
combo->addItem(tr("Disabled"), 0);
|
|
||||||
}
|
|
||||||
const int current = static_cast<int>(std::lround(value[0]));
|
|
||||||
if (current >= 0 && current < combo->count()) {
|
|
||||||
combo->setCurrentIndex(current);
|
|
||||||
}
|
|
||||||
if (!uniform.tooltip.empty()) {
|
|
||||||
combo->setToolTip(QString::fromStdString(uniform.tooltip));
|
|
||||||
}
|
|
||||||
const std::string name = uniform.name;
|
|
||||||
connect(combo, &QComboBox::currentIndexChanged, this, [index, name](int selected) {
|
|
||||||
std::array<float, 4> next{};
|
|
||||||
next[0] = static_cast<float>(selected);
|
|
||||||
VideoCore::FxChain::Instance().SetValue(static_cast<size_t>(index), name, next);
|
|
||||||
VideoCore::FxChain::Instance().StoreToSettings();
|
|
||||||
});
|
|
||||||
row->addWidget(combo, 1);
|
|
||||||
layout->addLayout(row);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* grid = new QGridLayout();
|
|
||||||
for (unsigned component = 0; component < uniform.components; ++component) {
|
|
||||||
QString component_label = label;
|
|
||||||
if (uniform.components > 1) {
|
|
||||||
component_label = label + QStringLiteral(" [%1]").arg(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* name_label = new QLabel(component_label, parent);
|
|
||||||
auto* value_label = new QLabel(parent);
|
|
||||||
value_label->setMinimumWidth(64);
|
|
||||||
value_label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
||||||
value_label->setText(FormatValue(uniform, value[component]));
|
|
||||||
|
|
||||||
auto* slider = new QSlider(Qt::Horizontal, parent);
|
|
||||||
slider->setMinimum(0);
|
|
||||||
slider->setMaximum(SliderSteps(uniform));
|
|
||||||
slider->setValue(
|
|
||||||
static_cast<int>(std::lround((value[component] - uniform.ui_min) / uniform.ui_step)));
|
|
||||||
if (!uniform.tooltip.empty()) {
|
|
||||||
slider->setToolTip(QString::fromStdString(uniform.tooltip));
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string name = uniform.name;
|
|
||||||
const auto desc = uniform;
|
|
||||||
connect(slider, &QSlider::valueChanged, this,
|
|
||||||
[index, name, desc, component, value_label](int steps) {
|
|
||||||
auto next = CurrentValue(index, desc);
|
|
||||||
next[component] = desc.ui_min + static_cast<float>(steps) * desc.ui_step;
|
|
||||||
VideoCore::FxChain::Instance().SetValue(static_cast<size_t>(index), name, next);
|
|
||||||
value_label->setText(FormatValue(desc, next[component]));
|
|
||||||
});
|
|
||||||
connect(slider, &QSlider::sliderReleased, this,
|
|
||||||
[]() { VideoCore::FxChain::Instance().StoreToSettings(); });
|
|
||||||
|
|
||||||
grid->addWidget(name_label, static_cast<int>(component), 0);
|
|
||||||
grid->addWidget(slider, static_cast<int>(component), 1);
|
|
||||||
grid->addWidget(value_label, static_cast<int>(component), 2);
|
|
||||||
}
|
|
||||||
layout->addLayout(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* ConfigurePostProcessing::BuildSlot(int index, const VideoCore::FxChainEntry& entry) {
|
|
||||||
auto* group = new QGroupBox(slots_container);
|
|
||||||
auto* layout = new QVBoxLayout(group);
|
|
||||||
|
|
||||||
auto* header = new QHBoxLayout();
|
|
||||||
|
|
||||||
auto* combo = new QComboBox(group);
|
|
||||||
PopulateEffectCombo(combo, entry);
|
|
||||||
connect(combo, &QComboBox::currentIndexChanged, this, [this, index, combo](int) {
|
|
||||||
const QString key = combo->currentData().toString();
|
|
||||||
const qsizetype separator = key.indexOf(QLatin1Char('|'));
|
|
||||||
if (separator < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VideoCore::FxChain::Instance().Replace(static_cast<size_t>(index),
|
|
||||||
key.left(separator).toStdString(),
|
|
||||||
key.mid(separator + 1).toStdString());
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
header->addWidget(combo, 1);
|
|
||||||
|
|
||||||
auto* up_button = new QToolButton(group);
|
|
||||||
up_button->setText(QStringLiteral("▲"));
|
|
||||||
up_button->setEnabled(index > 0);
|
|
||||||
connect(up_button, &QToolButton::clicked, this, [this, index]() {
|
|
||||||
VideoCore::FxChain::Instance().Move(static_cast<size_t>(index), -1);
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
header->addWidget(up_button);
|
|
||||||
|
|
||||||
auto* down_button = new QToolButton(group);
|
|
||||||
down_button->setText(QStringLiteral("▼"));
|
|
||||||
down_button->setEnabled(static_cast<size_t>(index) + 1 < VideoCore::FxChain::Instance().Size());
|
|
||||||
connect(down_button, &QToolButton::clicked, this, [this, index]() {
|
|
||||||
VideoCore::FxChain::Instance().Move(static_cast<size_t>(index), 1);
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
header->addWidget(down_button);
|
|
||||||
|
|
||||||
auto* reset_button = new QToolButton(group);
|
|
||||||
reset_button->setText(QStringLiteral("⟲"));
|
|
||||||
reset_button->setToolTip(tr("Reset to defaults"));
|
|
||||||
connect(reset_button, &QToolButton::clicked, this, [this, index]() {
|
|
||||||
VideoCore::FxChain::Instance().ResetValues(static_cast<size_t>(index));
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
header->addWidget(reset_button);
|
|
||||||
|
|
||||||
auto* remove_button = new QToolButton(group);
|
|
||||||
remove_button->setText(QStringLiteral("✕"));
|
|
||||||
connect(remove_button, &QToolButton::clicked, this, [this, index]() {
|
|
||||||
VideoCore::FxChain::Instance().Remove(static_cast<size_t>(index));
|
|
||||||
ApplyStructuralChange();
|
|
||||||
});
|
|
||||||
header->addWidget(remove_button);
|
|
||||||
|
|
||||||
layout->addLayout(header);
|
|
||||||
|
|
||||||
const VideoCore::FxEffectDesc* effect = VideoCore::FindFxEffect(entry.file);
|
|
||||||
if (effect == nullptr) {
|
|
||||||
auto* missing =
|
|
||||||
new QLabel(tr("Effect '%1' was not found.").arg(QString::fromStdString(entry.file)),
|
|
||||||
group);
|
|
||||||
missing->setWordWrap(true);
|
|
||||||
layout->addWidget(missing);
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!effect->error.empty()) {
|
|
||||||
auto* failed = new QLabel(
|
|
||||||
tr("Effect failed to compile:\n%1").arg(QString::fromStdString(effect->error)), group);
|
|
||||||
failed->setWordWrap(true);
|
|
||||||
layout->addWidget(failed);
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string current_category;
|
|
||||||
for (const auto& uniform : effect->uniforms) {
|
|
||||||
if (uniform.category != current_category) {
|
|
||||||
current_category = uniform.category;
|
|
||||||
if (!current_category.empty()) {
|
|
||||||
auto* category = new QLabel(QString::fromStdString(current_category), group);
|
|
||||||
category->setStyleSheet(QStringLiteral("font-weight: bold;"));
|
|
||||||
layout->addWidget(category);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuildUniformWidget(group, layout, index, uniform);
|
|
||||||
}
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurePostProcessing::RebuildRows() {
|
|
||||||
QLayoutItem* item = nullptr;
|
|
||||||
while ((item = slots_layout->takeAt(0)) != nullptr) {
|
|
||||||
if (item->widget() != nullptr) {
|
|
||||||
item->widget()->deleteLater();
|
|
||||||
}
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_usable = false;
|
|
||||||
for (const auto& effect : VideoCore::GetFxCatalog()) {
|
|
||||||
if (effect.Valid()) {
|
|
||||||
has_usable = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_usable) {
|
|
||||||
auto* empty = new QLabel(
|
|
||||||
tr("No usable ReShade FX effects were found. Place .fx files in the post_shaders "
|
|
||||||
"folder."),
|
|
||||||
slots_container);
|
|
||||||
empty->setWordWrap(true);
|
|
||||||
slots_layout->addWidget(empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto entries = VideoCore::FxChain::Instance().Entries();
|
|
||||||
for (size_t i = 0; i < entries.size(); ++i) {
|
|
||||||
slots_layout->addWidget(BuildSlot(static_cast<int>(i), entries[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
class QComboBox;
|
|
||||||
class QVBoxLayout;
|
|
||||||
class QWidget;
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
struct FxChainEntry;
|
|
||||||
struct FxEffectDesc;
|
|
||||||
struct FxUniformDesc;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConfigurePostProcessing : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ConfigurePostProcessing(QWidget* parent = nullptr);
|
|
||||||
~ConfigurePostProcessing() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void RebuildRows();
|
|
||||||
QWidget* BuildSlot(int index, const VideoCore::FxChainEntry& entry);
|
|
||||||
void BuildUniformWidget(QWidget* parent, QVBoxLayout* layout, int index,
|
|
||||||
const VideoCore::FxUniformDesc& uniform);
|
|
||||||
void PopulateEffectCombo(QComboBox* combo, const VideoCore::FxChainEntry& entry) const;
|
|
||||||
void ApplyStructuralChange();
|
|
||||||
|
|
||||||
QVBoxLayout* slots_layout{};
|
|
||||||
QWidget* slots_container{};
|
|
||||||
};
|
|
||||||
@@ -148,7 +148,6 @@
|
|||||||
<addaction name="action_Show_Filter_Bar"/>
|
<addaction name="action_Show_Filter_Bar"/>
|
||||||
<addaction name="action_Show_Status_Bar"/>
|
<addaction name="action_Show_Status_Bar"/>
|
||||||
<addaction name="action_Show_Performance_Overlay"/>
|
<addaction name="action_Show_Performance_Overlay"/>
|
||||||
<addaction name="action_Post_Processing_Shaders"/>
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_Reset_Window_Size"/>
|
<addaction name="menu_Reset_Window_Size"/>
|
||||||
<addaction name="menu_View_Debugging"/>
|
<addaction name="menu_View_Debugging"/>
|
||||||
@@ -611,11 +610,6 @@
|
|||||||
<string>Show &Performance Overlay</string>
|
<string>Show &Performance Overlay</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="action_Post_Processing_Shaders">
|
|
||||||
<property name="text">
|
|
||||||
<string>Post-Processing &Shaders...</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="action_Carousel_View">
|
<action name="action_Carousel_View">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|||||||
@@ -13,9 +13,6 @@
|
|||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
#include "frontend_common/settings_generator.h"
|
#include "frontend_common/settings_generator.h"
|
||||||
#include "render/performance_overlay.h"
|
#include "render/performance_overlay.h"
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
#include "configuration/configure_post_processing.h"
|
|
||||||
#endif
|
|
||||||
#include "updater/update_dialog.h"
|
#include "updater/update_dialog.h"
|
||||||
|
|
||||||
#include "common/fs/ryujinx_compat.h"
|
#include "common/fs/ryujinx_compat.h"
|
||||||
@@ -1521,11 +1518,6 @@ void MainWindow::ConnectMenuEvents() {
|
|||||||
connect_menu(ui->action_Show_Filter_Bar, &MainWindow::OnToggleFilterBar);
|
connect_menu(ui->action_Show_Filter_Bar, &MainWindow::OnToggleFilterBar);
|
||||||
connect_menu(ui->action_Show_Status_Bar, &MainWindow::OnToggleStatusBar);
|
connect_menu(ui->action_Show_Status_Bar, &MainWindow::OnToggleStatusBar);
|
||||||
connect_menu(ui->action_Show_Performance_Overlay, &MainWindow::OnTogglePerfOverlay);
|
connect_menu(ui->action_Show_Performance_Overlay, &MainWindow::OnTogglePerfOverlay);
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
connect_menu(ui->action_Post_Processing_Shaders, &MainWindow::OnPostProcessingShaders);
|
|
||||||
#else
|
|
||||||
ui->action_Post_Processing_Shaders->setVisible(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connect_menu(ui->action_Reset_Window_Size_720, &MainWindow::ResetWindowSize720);
|
connect_menu(ui->action_Reset_Window_Size_720, &MainWindow::ResetWindowSize720);
|
||||||
connect_menu(ui->action_Reset_Window_Size_900, &MainWindow::ResetWindowSize900);
|
connect_menu(ui->action_Reset_Window_Size_900, &MainWindow::ResetWindowSize900);
|
||||||
@@ -3908,22 +3900,6 @@ void MainWindow::OnTogglePerfOverlay() {
|
|||||||
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
|
perf_overlay->setVisible(ui->action_Show_Performance_Overlay->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
void MainWindow::OnPostProcessingShaders() {
|
|
||||||
if (post_processing_dialog == nullptr) {
|
|
||||||
post_processing_dialog = new ConfigurePostProcessing(this);
|
|
||||||
connect(post_processing_dialog, &QDialog::finished, post_processing_dialog, [this]() {
|
|
||||||
post_processing_dialog->deleteLater();
|
|
||||||
post_processing_dialog = nullptr;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
post_processing_dialog->show();
|
|
||||||
post_processing_dialog->raise();
|
|
||||||
post_processing_dialog->activateWindow();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void MainWindow::OnGameListRefresh() {
|
void MainWindow::OnGameListRefresh() {
|
||||||
// Resets metadata cache and reloads
|
// Resets metadata cache and reloads
|
||||||
QtCommon::Game::ResetMetadata(false);
|
QtCommon::Game::ResetMetadata(false);
|
||||||
|
|||||||
@@ -56,9 +56,6 @@ class QSlider;
|
|||||||
class QHBoxLayout;
|
class QHBoxLayout;
|
||||||
class WaitTreeWidget;
|
class WaitTreeWidget;
|
||||||
class PerformanceOverlay;
|
class PerformanceOverlay;
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
class ConfigurePostProcessing;
|
|
||||||
#endif
|
|
||||||
enum class GameListOpenTarget;
|
enum class GameListOpenTarget;
|
||||||
enum class DumpRomFSTarget;
|
enum class DumpRomFSTarget;
|
||||||
class GameListPlaceholder;
|
class GameListPlaceholder;
|
||||||
@@ -395,9 +392,6 @@ private slots:
|
|||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnToggleStatusBar();
|
void OnToggleStatusBar();
|
||||||
void OnTogglePerfOverlay();
|
void OnTogglePerfOverlay();
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
void OnPostProcessingShaders();
|
|
||||||
#endif
|
|
||||||
void OnGameListRefresh();
|
void OnGameListRefresh();
|
||||||
void InitializeHotkeys();
|
void InitializeHotkeys();
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
@@ -502,9 +496,6 @@ private:
|
|||||||
QTimer shutdown_timer;
|
QTimer shutdown_timer;
|
||||||
OverlayDialog* shutdown_dialog{};
|
OverlayDialog* shutdown_dialog{};
|
||||||
PerformanceOverlay* perf_overlay = nullptr;
|
PerformanceOverlay* perf_overlay = nullptr;
|
||||||
#ifdef HAS_RESHADE
|
|
||||||
ConfigurePostProcessing* post_processing_dialog = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GameListPlaceholder* game_list_placeholder = nullptr;
|
GameListPlaceholder* game_list_placeholder = nullptr;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user