Compare commits

..

5 Commits

Author SHA1 Message Date
lizzie d336341c8d 2026-09-27 17:50:19
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-27 17:50:19 +00:00
lizzie a4fd5c6b0b 2026-09-27 17:15:36
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-27 17:15:37 +00:00
lizzie ef7e9c00b0 2026-09-27 16:59:33
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-27 16:59:33 +00:00
lizzie 3be0db1266 2026-09-27 15:50:47
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-27 16:58:25 +00:00
PavelBARABANOV 815325ccec [loader] add ZBIC (zstd variant) NSO decompression support for Switch 22.0+ (#4482)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
A new NSO compression method was introduced in Switch 22.0.0. This is a
customized variant of zstd and is used when NSO flags have bit 7 set.

Key characteristics:
  - ZSTD_MAGICNUMBER is set to 0x4349425A (b'ZBIC') instead of 0xFD2FB528
  - ZSTD_LEGACY_SUPPORT is set to 0
  - ZSTD_TRACE is set to 1, zstd version used is 1.5.7 (10507)
  - FSE_readNCount is replaced with a BIC (Binary Interpolative Coding)
    version which improves compression of entropy tables significantly

Source: https://switchbrew.org/wiki/22.0.0

Implementation:
  - Detect ZBIC segments via NSO flag bit 7 (NsoFlags_UseZbicCompression)
    and/or ZBIC magic scan in nso.cpp
  - Fall back to LZ4 when ZBIC is not detected or returns unexpected size
  - Handle segment 0 alternate offset (0x100 vs 0x108) for both ZBIC and LZ4

References:
  - Atmosphère loader/strat zstd-zbic integration:
    https://github.com/Atmosphere-NX/Atmosphere/commit/082115187a0509cb6b8a757de639a4e4741b8712
  - nxdumptool ZBIC segment compression support:
    https://github.com/DarkMatterCore/nxdumptool/commit/441e5c0904f29427987433be4eb057a2843d222a
  - STORM_SWITCH ZBIC implementation:
    https://github.com/ReiKatari/STORM_SWITCH/commit/1e09eb82b760aaf340b810031400991c0740c091

Tested with firmware 23.0.0 and ZBIC-compressed NSOs.

Co-authored-by: xbzk <xbzk@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4482
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-27 12:48:45 +02:00
62 changed files with 2447 additions and 2459 deletions
+5
View File
@@ -333,6 +333,11 @@
"repo": "herumi/xbyak",
"version": "v7.40.1"
},
"zbic": {
"hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482",
"repo": "kinnay/zbic",
"version": "11b08f2712264bbed731545085cbd9702096ceb7"
},
"zlib": {
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
"min_version": "1.2",
+5
View File
@@ -48,6 +48,11 @@ if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
AddJsonPackage(NAME zbic DOWNLOAD_ONLY)
set(ZBIC_INCLUDE_DIR
"${zbic_SOURCE_DIR}/src"
PARENT_SCOPE)
# ItaniumDemangle (Windows only)
if (WIN32 AND NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
+7 -1
View File
@@ -62,7 +62,6 @@ add_library(
fs/fs_util.h
fs/path_util.cpp
fs/path_util.h
hash.h
heap_tracker.cpp
heap_tracker.h
hex_util.cpp
@@ -106,6 +105,7 @@ add_library(
settings_input.h
settings_setting.h
slot_vector.h
socket_types.h
sparse_large_vector.cpp
sparse_large_vector.h
stb.cpp
@@ -136,6 +136,8 @@ add_library(
uuid.cpp
uuid.h
vector_math.h
zbic_compression.cpp
zbic_compression.h
zstd_compression.cpp
zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
@@ -146,6 +148,10 @@ add_library(
net/net.h net/net.cpp
container/unordered_map.h container/unordered_set.h)
set_source_files_properties(zbic_compression.cpp PROPERTIES
INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}"
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:Clang,GNU>:-Wno-unused-function;-Wno-missing-declarations;-Wno-shadow>")
if(WIN32)
target_sources(common PRIVATE windows/timer_resolution.cpp
windows/timer_resolution.h)
+252 -251
View File
@@ -12,93 +12,94 @@
#include "common/android/multiplayer/multiplayer.h"
#include <network/network.h>
static struct {
JavaVM *java_vm;
jclass native_library_class;
jclass disk_cache_progress_class;
jclass load_callback_stage_class;
jclass game_dir_class;
jmethodID game_dir_constructor;
jmethodID exit_emulation_activity;
jmethodID disk_cache_load_progress;
jmethodID on_emulation_started;
jmethodID on_emulation_stopped;
jmethodID on_program_changed;
jmethodID copy_to_storage;
jmethodID file_exists;
jmethodID file_extension;
static JavaVM *s_java_vm;
static jclass s_native_library_class;
static jclass s_disk_cache_progress_class;
static jclass s_load_callback_stage_class;
static jclass s_game_dir_class;
static jmethodID s_game_dir_constructor;
static jmethodID s_exit_emulation_activity;
static jmethodID s_disk_cache_load_progress;
static jmethodID s_on_emulation_started;
static jmethodID s_on_emulation_stopped;
static jmethodID s_on_program_changed;
static jmethodID s_copy_to_storage;
static jmethodID s_file_exists;
static jmethodID s_file_extension;
jclass game_class;
jmethodID game_constructor;
jfieldID game_title_field;
jfieldID game_path_field;
jfieldID game_program_id_field;
jfieldID game_developer_field;
jfieldID game_version_field;
jfieldID game_is_homebrew_field;
static jclass s_game_class;
static jmethodID s_game_constructor;
static jfieldID s_game_title_field;
static jfieldID s_game_path_field;
static jfieldID s_game_program_id_field;
static jfieldID s_game_developer_field;
static jfieldID s_game_version_field;
static jfieldID s_game_is_homebrew_field;
jclass string_class;
jclass pair_class;
jmethodID pair_constructor;
jfieldID pair_first_field;
jfieldID pair_second_field;
static jclass s_string_class;
static jclass s_pair_class;
static jmethodID s_pair_constructor;
static jfieldID s_pair_first_field;
static jfieldID s_pair_second_field;
jclass overlay_control_data_class;
jmethodID overlay_control_data_constructor;
jfieldID overlay_control_data_id_field;
jfieldID overlay_control_data_enabled_field;
jfieldID overlay_control_data_individual_scale_field;
jfieldID overlay_control_data_landscape_position_field;
jfieldID overlay_control_data_portrait_position_field;
jfieldID overlay_control_data_foldable_position_field;
static jclass s_overlay_control_data_class;
static jmethodID s_overlay_control_data_constructor;
static jfieldID s_overlay_control_data_id_field;
static jfieldID s_overlay_control_data_enabled_field;
static jfieldID s_overlay_control_data_individual_scale_field;
static jfieldID s_overlay_control_data_landscape_position_field;
static jfieldID s_overlay_control_data_portrait_position_field;
static jfieldID s_overlay_control_data_foldable_position_field;
jclass patch_class;
jmethodID patch_constructor;
jfieldID patch_enabled_field;
jfieldID patch_name_field;
jfieldID patch_version_field;
jfieldID patch_type_field;
jfieldID patch_program_id_field;
jfieldID patch_title_id_field;
static jclass s_patch_class;
static jmethodID s_patch_constructor;
static jfieldID s_patch_enabled_field;
static jfieldID s_patch_name_field;
static jfieldID s_patch_version_field;
static jfieldID s_patch_type_field;
static jfieldID s_patch_program_id_field;
static jfieldID s_patch_title_id_field;
jclass double_class;
jmethodID double_constructor;
jmethodID double_value_method;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jmethodID s_double_value_method;
jclass integer_class;
jmethodID integer_constructor;
jmethodID integer_value_method;
static jclass s_integer_class;
static jmethodID s_integer_constructor;
static jmethodID s_integer_value_method;
jclass boolean_class;
jmethodID boolean_constructor;
jmethodID boolean_value_method;
static jclass s_boolean_class;
static jmethodID s_boolean_constructor;
static jmethodID s_boolean_value_method;
jclass player_input_class;
jmethodID player_input_constructor;
jfieldID player_input_connected_field;
jfieldID player_input_buttons_field;
jfieldID player_input_analogs_field;
jfieldID player_input_motions_field;
jfieldID player_input_vibration_enabled_field;
jfieldID player_input_vibration_strength_field;
jfieldID player_input_body_color_left_field;
jfieldID player_input_body_color_right_field;
jfieldID player_input_button_color_left_field;
jfieldID player_input_button_color_right_field;
jfieldID player_input_profile_name_field;
jfieldID player_input_use_system_vibrator_field;
static jclass s_player_input_class;
static jmethodID s_player_input_constructor;
static jfieldID s_player_input_connected_field;
static jfieldID s_player_input_buttons_field;
static jfieldID s_player_input_analogs_field;
static jfieldID s_player_input_motions_field;
static jfieldID s_player_input_vibration_enabled_field;
static jfieldID s_player_input_vibration_strength_field;
static jfieldID s_player_input_body_color_left_field;
static jfieldID s_player_input_body_color_right_field;
static jfieldID s_player_input_button_color_left_field;
static jfieldID s_player_input_button_color_right_field;
static jfieldID s_player_input_profile_name_field;
static jfieldID s_player_input_use_system_vibrator_field;
jclass yuzu_input_device_interface;
jmethodID yuzu_input_device_get_name;
jmethodID yuzu_input_device_get_guid;
jmethodID yuzu_input_device_get_port;
jmethodID yuzu_input_device_get_supports_vibration;
jmethodID yuzu_input_device_vibrate;
jmethodID yuzu_input_device_get_axes;
jmethodID yuzu_input_device_has_keys;
static jclass s_yuzu_input_device_interface;
static jmethodID s_yuzu_input_device_get_name;
static jmethodID s_yuzu_input_device_get_guid;
static jmethodID s_yuzu_input_device_get_port;
static jmethodID s_yuzu_input_device_get_supports_vibration;
static jmethodID s_yuzu_input_device_vibrate;
static jmethodID s_yuzu_input_device_get_axes;
static jmethodID s_yuzu_input_device_has_keys;
static jmethodID s_add_netplay_message;
static jmethodID s_clear_chat;
jmethodID add_netplay_message;
jmethodID clear_chat;
} state;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
@@ -106,14 +107,14 @@ namespace Common::Android {
JNIEnv *GetEnvForThread() {
thread_local static struct OwnedEnv {
OwnedEnv() {
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED)
s_java_vm->AttachCurrentThread(&env, nullptr);
state.java_vm->AttachCurrentThread(&env, nullptr);
}
~OwnedEnv() {
if (status == JNI_EDETACHED)
s_java_vm->DetachCurrentThread();
state.java_vm->DetachCurrentThread();
}
int status;
@@ -123,303 +124,303 @@ namespace Common::Android {
}
jclass GetNativeLibraryClass() {
return s_native_library_class;
return state.native_library_class;
}
jclass GetDiskCacheProgressClass() {
return s_disk_cache_progress_class;
return state.disk_cache_progress_class;
}
jclass GetDiskCacheLoadCallbackStageClass() {
return s_load_callback_stage_class;
return state.load_callback_stage_class;
}
jclass GetGameDirClass() {
return s_game_dir_class;
return state.game_dir_class;
}
jmethodID GetGameDirConstructor() {
return s_game_dir_constructor;
return state.game_dir_constructor;
}
jmethodID GetExitEmulationActivity() {
return s_exit_emulation_activity;
return state.exit_emulation_activity;
}
jmethodID GetDiskCacheLoadProgress() {
return s_disk_cache_load_progress;
return state.disk_cache_load_progress;
}
jmethodID GetCopyToStorage() {
return s_copy_to_storage;
return state.copy_to_storage;
}
jmethodID GetFileExists() {
return s_file_exists;
return state.file_exists;
}
jmethodID GetFileExtension() {
return s_file_extension;
return state.file_extension;
}
jmethodID GetOnEmulationStarted() {
return s_on_emulation_started;
return state.on_emulation_started;
}
jmethodID GetOnEmulationStopped() {
return s_on_emulation_stopped;
return state.on_emulation_stopped;
}
jmethodID GetOnProgramChanged() {
return s_on_program_changed;
return state.on_program_changed;
}
jclass GetGameClass() {
return s_game_class;
return state.game_class;
}
jmethodID GetGameConstructor() {
return s_game_constructor;
return state.game_constructor;
}
jfieldID GetGameTitleField() {
return s_game_title_field;
return state.game_title_field;
}
jfieldID GetGamePathField() {
return s_game_path_field;
return state.game_path_field;
}
jfieldID GetGameProgramIdField() {
return s_game_program_id_field;
return state.game_program_id_field;
}
jfieldID GetGameDeveloperField() {
return s_game_developer_field;
return state.game_developer_field;
}
jfieldID GetGameVersionField() {
return s_game_version_field;
return state.game_version_field;
}
jfieldID GetGameIsHomebrewField() {
return s_game_is_homebrew_field;
return state.game_is_homebrew_field;
}
jclass GetStringClass() {
return s_string_class;
return state.string_class;
}
jclass GetPairClass() {
return s_pair_class;
return state.pair_class;
}
jmethodID GetPairConstructor() {
return s_pair_constructor;
return state.pair_constructor;
}
jfieldID GetPairFirstField() {
return s_pair_first_field;
return state.pair_first_field;
}
jfieldID GetPairSecondField() {
return s_pair_second_field;
return state.pair_second_field;
}
jclass GetOverlayControlDataClass() {
return s_overlay_control_data_class;
return state.overlay_control_data_class;
}
jmethodID GetOverlayControlDataConstructor() {
return s_overlay_control_data_constructor;
return state.overlay_control_data_constructor;
}
jfieldID GetOverlayControlDataIdField() {
return s_overlay_control_data_id_field;
return state.overlay_control_data_id_field;
}
jfieldID GetOverlayControlDataEnabledField() {
return s_overlay_control_data_enabled_field;
return state.overlay_control_data_enabled_field;
}
jfieldID GetOverlayControlDataIndividualScaleField() {
return s_overlay_control_data_individual_scale_field;
return state.overlay_control_data_individual_scale_field;
}
jfieldID GetOverlayControlDataLandscapePositionField() {
return s_overlay_control_data_landscape_position_field;
return state.overlay_control_data_landscape_position_field;
}
jfieldID GetOverlayControlDataPortraitPositionField() {
return s_overlay_control_data_portrait_position_field;
return state.overlay_control_data_portrait_position_field;
}
jfieldID GetOverlayControlDataFoldablePositionField() {
return s_overlay_control_data_foldable_position_field;
return state.overlay_control_data_foldable_position_field;
}
jclass GetPatchClass() {
return s_patch_class;
return state.patch_class;
}
jmethodID GetPatchConstructor() {
return s_patch_constructor;
return state.patch_constructor;
}
jfieldID GetPatchEnabledField() {
return s_patch_enabled_field;
return state.patch_enabled_field;
}
jfieldID GetPatchNameField() {
return s_patch_name_field;
return state.patch_name_field;
}
jfieldID GetPatchVersionField() {
return s_patch_version_field;
return state.patch_version_field;
}
jfieldID GetPatchTypeField() {
return s_patch_type_field;
return state.patch_type_field;
}
jfieldID GetPatchProgramIdField() {
return s_patch_program_id_field;
return state.patch_program_id_field;
}
jfieldID GetPatchTitleIdField() {
return s_patch_title_id_field;
return state.patch_title_id_field;
}
jclass GetDoubleClass() {
return s_double_class;
return state.double_class;
}
jmethodID GetDoubleConstructor() {
return s_double_constructor;
return state.double_constructor;
}
jmethodID GetDoubleValueMethod() {
return s_double_value_method;
return state.double_value_method;
}
jclass GetIntegerClass() {
return s_integer_class;
return state.integer_class;
}
jmethodID GetIntegerConstructor() {
return s_integer_constructor;
return state.integer_constructor;
}
jmethodID GetIntegerValueMethod() {
return s_integer_value_method;
return state.integer_value_method;
}
jclass GetBooleanClass() {
return s_boolean_class;
return state.boolean_class;
}
jmethodID GetBooleanConstructor() {
return s_boolean_constructor;
return state.boolean_constructor;
}
jmethodID GetBooleanValueMethod() {
return s_boolean_value_method;
return state.boolean_value_method;
}
jclass GetPlayerInputClass() {
return s_player_input_class;
return state.player_input_class;
}
jmethodID GetPlayerInputConstructor() {
return s_player_input_constructor;
return state.player_input_constructor;
}
jfieldID GetPlayerInputConnectedField() {
return s_player_input_connected_field;
return state.player_input_connected_field;
}
jfieldID GetPlayerInputButtonsField() {
return s_player_input_buttons_field;
return state.player_input_buttons_field;
}
jfieldID GetPlayerInputAnalogsField() {
return s_player_input_analogs_field;
return state.player_input_analogs_field;
}
jfieldID GetPlayerInputMotionsField() {
return s_player_input_motions_field;
return state.player_input_motions_field;
}
jfieldID GetPlayerInputVibrationEnabledField() {
return s_player_input_vibration_enabled_field;
return state.player_input_vibration_enabled_field;
}
jfieldID GetPlayerInputVibrationStrengthField() {
return s_player_input_vibration_strength_field;
return state.player_input_vibration_strength_field;
}
jfieldID GetPlayerInputBodyColorLeftField() {
return s_player_input_body_color_left_field;
return state.player_input_body_color_left_field;
}
jfieldID GetPlayerInputBodyColorRightField() {
return s_player_input_body_color_right_field;
return state.player_input_body_color_right_field;
}
jfieldID GetPlayerInputButtonColorLeftField() {
return s_player_input_button_color_left_field;
return state.player_input_button_color_left_field;
}
jfieldID GetPlayerInputButtonColorRightField() {
return s_player_input_button_color_right_field;
return state.player_input_button_color_right_field;
}
jfieldID GetPlayerInputProfileNameField() {
return s_player_input_profile_name_field;
return state.player_input_profile_name_field;
}
jfieldID GetPlayerInputUseSystemVibratorField() {
return s_player_input_use_system_vibrator_field;
return state.player_input_use_system_vibrator_field;
}
jclass GetYuzuInputDeviceInterface() {
return s_yuzu_input_device_interface;
return state.yuzu_input_device_interface;
}
jmethodID GetYuzuDeviceGetName() {
return s_yuzu_input_device_get_name;
return state.yuzu_input_device_get_name;
}
jmethodID GetYuzuDeviceGetGUID() {
return s_yuzu_input_device_get_guid;
return state.yuzu_input_device_get_guid;
}
jmethodID GetYuzuDeviceGetPort() {
return s_yuzu_input_device_get_port;
return state.yuzu_input_device_get_port;
}
jmethodID GetYuzuDeviceGetSupportsVibration() {
return s_yuzu_input_device_get_supports_vibration;
return state.yuzu_input_device_get_supports_vibration;
}
jmethodID GetYuzuDeviceVibrate() {
return s_yuzu_input_device_vibrate;
return state.yuzu_input_device_vibrate;
}
jmethodID GetYuzuDeviceGetAxes() {
return s_yuzu_input_device_get_axes;
return state.yuzu_input_device_get_axes;
}
jmethodID GetYuzuDeviceHasKeys() {
return s_yuzu_input_device_has_keys;
return state.yuzu_input_device_has_keys;
}
jmethodID GetAddNetPlayMessage() {
return s_add_netplay_message;
return state.add_netplay_message;
}
jmethodID ClearChat() {
return s_clear_chat;
return state.clear_chat;
}
#ifdef __cplusplus
@@ -436,20 +437,20 @@ namespace Common::Android {
// UnInitialize Android Storage
Common::FS::Android::UnRegisterCallbacks();
env->DeleteGlobalRef(s_native_library_class);
env->DeleteGlobalRef(s_disk_cache_progress_class);
env->DeleteGlobalRef(s_load_callback_stage_class);
env->DeleteGlobalRef(s_game_dir_class);
env->DeleteGlobalRef(s_game_class);
env->DeleteGlobalRef(s_string_class);
env->DeleteGlobalRef(s_pair_class);
env->DeleteGlobalRef(s_overlay_control_data_class);
env->DeleteGlobalRef(s_patch_class);
env->DeleteGlobalRef(s_double_class);
env->DeleteGlobalRef(s_integer_class);
env->DeleteGlobalRef(s_boolean_class);
env->DeleteGlobalRef(s_player_input_class);
env->DeleteGlobalRef(s_yuzu_input_device_interface);
env->DeleteGlobalRef(state.native_library_class);
env->DeleteGlobalRef(state.disk_cache_progress_class);
env->DeleteGlobalRef(state.load_callback_stage_class);
env->DeleteGlobalRef(state.game_dir_class);
env->DeleteGlobalRef(state.game_class);
env->DeleteGlobalRef(state.string_class);
env->DeleteGlobalRef(state.pair_class);
env->DeleteGlobalRef(state.overlay_control_data_class);
env->DeleteGlobalRef(state.patch_class);
env->DeleteGlobalRef(state.double_class);
env->DeleteGlobalRef(state.integer_class);
env->DeleteGlobalRef(state.boolean_class);
env->DeleteGlobalRef(state.player_input_class);
env->DeleteGlobalRef(state.yuzu_input_device_interface);
// UnInitialize applets
SoftwareKeyboard::CleanupJNI(env);
@@ -463,7 +464,7 @@ namespace Common::Android {
#endif
void Initialize(JavaVM* vm, JNIEnv *env) {
s_java_vm = vm;
state.java_vm = vm;
InitFFmpegOnLoad(vm);
if (env->ExceptionCheck()) {
@@ -472,169 +473,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
state.exit_emulation_activity =
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V");
state.disk_cache_load_progress =
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V");
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z");
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
"(Ljava/lang/String;)Z");
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
state.on_emulation_started =
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V");
state.on_emulation_stopped =
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V");
state.on_program_changed =
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
state.game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
state.pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
state.overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
state.overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
s_overlay_control_data_id_field =
state.overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
state.overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
state.overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
state.overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
state.overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
state.overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
state.patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
state.patch_type_field = env->GetFieldID(patch_class, "type", "I");
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
state.player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
state.player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
s_player_input_analogs_field =
state.player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
s_player_input_motions_field =
state.player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
s_player_input_vibration_enabled_field =
state.player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
s_player_input_vibration_strength_field =
state.player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
s_player_input_body_color_left_field =
state.player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
s_player_input_body_color_right_field =
state.player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
s_player_input_button_color_left_field =
state.player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
s_player_input_button_color_right_field =
state.player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
s_player_input_profile_name_field =
state.player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
s_player_input_use_system_vibrator_field =
state.player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
s_yuzu_input_device_interface =
state.yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
s_yuzu_input_device_get_name =
state.yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
s_yuzu_input_device_get_guid =
state.yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
s_yuzu_input_device_get_supports_vibration =
state.yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
s_yuzu_input_device_get_axes =
state.yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
s_yuzu_input_device_has_keys =
state.yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom {
+4 -18
View File
@@ -22,36 +22,22 @@ template <typename T>
return std::size_t(sizeof(T) * CHAR_BIT);
}
template<typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
}
template<typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr T Log2Floor(const T value) {
return T(MostSignificantBit<T>(value));
return std::bit_width(value) - 1;
}
template<typename T>
requires std::is_integral_v<T>
[[nodiscard]] constexpr T Log2Ceil(const T value) {
const T log2_f = Log2Floor<T>(value);
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
}
template <typename T>
requires std::is_integral_v<T>
[[nodiscard]] T NextPow2(T value) {
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
return std::bit_width(value - 1);
}
template <size_t bit_index, typename T>
requires std::is_integral_v<T>
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
[[nodiscard]] constexpr bool Bit(const T value) {
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
return ((value >> bit_index) & T(1)) == T(1);
return (T(value >> bit_index) & T(1)) == T(1);
}
} // namespace Common
+5 -13
View File
@@ -25,26 +25,18 @@ template <typename T>
requires std::is_unsigned_v<T>
inline std::size_t HashValue(T val) {
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
const unsigned int length =
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
std::size_t seed = 0;
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
}
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
return seed;
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
}
template <size_t Bits>
struct HashCombineImpl {
template <typename T>
static inline T fn(T seed, T value) {
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
+5 -2
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -12,14 +15,14 @@ namespace Common {
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
return N((D(number) + divisor - 1) / divisor);
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
requires std::is_integral_v<N> && std::is_unsigned_v<D>
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
}
} // namespace Common
-28
View File
@@ -1,28 +0,0 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <utility>
#include <boost/functional/hash.hpp>
namespace Common {
struct PairHash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
std::size_t seed = std::hash<T1>()(pair.first);
boost::hash_combine(seed, std::hash<T2>()(pair.second));
return seed;
}
};
template <typename T>
struct IdentityHash {
[[nodiscard]] size_t operator()(T value) const noexcept {
return static_cast<size_t>(value);
}
};
} // namespace Common
+10 -16
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,14 +19,12 @@ namespace Common {
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
if (c >= 65 && c <= 70) {
return static_cast<u8>(c - 55);
return u8(c - 55);
}
if (c >= 97 && c <= 102) {
return static_cast<u8>(c - 87);
return u8(c - 87);
}
return static_cast<u8>(c - 48);
return u8(c - 48);
}
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
@@ -31,35 +32,28 @@ namespace Common {
template <std::size_t Size, bool le = false>
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
} else {
for (std::size_t i = 0; i < 2 * Size; i += 2) {
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
}
return out;
}
template <typename ContiguousContainer>
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
"Underlying type within the contiguous container must be u8.");
constexpr std::size_t pad_width = 2;
std::string out;
out.reserve(std::size(data) * pad_width);
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
for (const u8 c : data) {
for (const u8 c : data)
out += fmt::format(format_str, c);
}
return out;
}
+10 -6
View File
@@ -10,6 +10,7 @@
#include <cstdlib>
#include <regex>
#include <thread>
#include <boost/algorithm/string/predicate.hpp>
#if defined(__ANDROID__)
#include <android/log.h>
@@ -95,9 +96,10 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
template <typename It>
Level GetLevelByName(const It begin, const It end) {
std::string_view const sv{begin, end};
for (u32 i = 0; i < u32(Level::Count); ++i) {
const char* level_name = GetLevelName(Level(i));
if (Common::ComparePartialString(begin, end, level_name))
auto const level_name = GetLevelName(Level(i));
if (boost::iequals(sv, level_name))
return Level(i);
}
return Level::Count;
@@ -105,9 +107,10 @@ Level GetLevelByName(const It begin, const It end) {
template <typename It>
Class GetClassByName(const It begin, const It end) {
std::string_view const sv{begin, end};
for (u32 i = 0; i < u32(Class::Count); ++i) {
const char* level_name = GetLogClassName(Class(i));
if (Common::ComparePartialString(begin, end, level_name))
auto const level_name = GetLogClassName(Class(i));
if (boost::iequals(sv, level_name))
return Class(i);
}
return Class::Count;
@@ -120,12 +123,13 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
return false;
}
const Level level = GetLevelByName(level_separator + 1, end);
auto const sv = std::string_view{begin, level_separator};
auto const level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) {
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
return false;
}
if (Common::ComparePartialString(begin, level_separator, "*")) {
if (boost::iequals(sv, "*")) {
instance.class_levels.fill(level);
return true;
}
+20 -18
View File
@@ -8,6 +8,9 @@
#include <stdexcept>
#include <utility>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include "common/logging.h"
#include "common/param_package.h"
@@ -15,17 +18,16 @@
namespace Common {
constexpr char KEY_VALUE_SEPARATOR = ':';
constexpr char PARAM_SEPARATOR = ',';
constexpr char ESCAPE_CHARACTER = '$';
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
constexpr auto KEY_VALUE_SEPARATOR = ":";
constexpr auto PARAM_SEPARATOR = ",";
constexpr auto ESCAPE_CHARACTER = "$";
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0";
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1";
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2";
/// A placeholder for empty param packages to avoid empty strings
/// (they may be recognized as "not set" by some frontend libraries like qt)
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
ParamPackage::ParamPackage(const std::string& serialized) {
if (serialized == EMPTY_PLACEHOLDER) {
@@ -33,20 +35,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
}
std::vector<std::string> pairs;
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
for (const std::string& pair : pairs) {
std::vector<std::string> key_value;
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
if (key_value.size() != 2) {
LOG_ERROR(Common, "invalid key pair {}", pair);
continue;
}
for (std::string& part : key_value) {
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
for (auto& part : key_value) {
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR);
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR);
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER);
}
Set(key_value[0], std::move(key_value[1]));
@@ -63,10 +65,10 @@ std::string ParamPackage::Serialize() const {
for (const auto& pair : data) {
std::array<std::string, 2> key_value{{pair.first, pair.second}};
for (std::string& part : key_value) {
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
for (auto& part : key_value) {
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE);
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE);
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE);
}
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
}
+178
View File
@@ -0,0 +1,178 @@
// 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
-51
View File
@@ -24,22 +24,6 @@
namespace Common {
/// Make a string lowercase
std::string ToLower(const std::string_view sv) {
std::string str{sv};
std::transform(str.begin(), str.end(), str.begin(),
[](auto const c) { return char(std::tolower(c)); });
return str;
}
/// Make a string uppercase
std::string ToUpper(const std::string_view sv) {
std::string str{sv};
std::transform(str.begin(), str.end(), str.begin(),
[](auto const c) { return char(std::toupper(c)); });
return str;
}
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension) {
if (full_path.empty())
@@ -80,41 +64,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
return true;
}
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim)) {
output.emplace_back();
}
output.pop_back();
}
std::string TabsToSpaces(int tab_size, std::string in) {
std::size_t i = 0;
while ((i = in.find('\t')) != std::string::npos) {
in.replace(i, 1, tab_size, ' ');
}
return in;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
std::size_t pos = 0;
if (src == dest)
return result;
while ((pos = result.find(src, pos)) != std::string::npos) {
result.replace(pos, src.size(), dest);
pos += dest.length();
}
return result;
}
std::string UTF16ToUTF8(std::u16string_view input) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(input.data(), input.data() + input.size());
+4 -56
View File
@@ -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-FileCopyrightText: 2013 Dolphin Emulator Project
@@ -16,12 +16,6 @@
namespace Common {
/// Make a string lowercase
[[nodiscard]] std::string ToLower(const std::string_view sv);
/// Make a string uppercase
[[nodiscard]] std::string ToUpper(const std::string_view sv);
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
@@ -29,37 +23,8 @@ namespace Common {
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
}
/// Turns " hej " into "hej". Also handles tabs.
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
const std::size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
return {};
}
/// "\"hello\"" is turned to "hello"
/// This one assumes that the string has already been space stripped in both
/// ends, as done by StripSpaces above, for example.
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
return std::string{s.substr(1, s.size() - 2)};
return std::string{s};
}
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
return value ? "True" : "False";
}
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
std::string* _pExtension);
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
const std::string& dest);
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
@@ -73,30 +38,13 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
/**
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
* `other` for equality.
*/
template <typename InIt>
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
for (; begin != end && *other != '\0'; ++begin, ++other) {
if (*begin != *other) {
return false;
}
}
// Only return true if both strings finished at the same point
return (begin == end) == (*other == '\0');
}
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
/// NUL-terminated then the string ends at max_len characters.
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
std::size_t max_len);
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len);
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
/// max_len_bytes.
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
std::size_t max_len);
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len);
} // namespace Common
+46
View File
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstring>
#include "common/zbic_compression.h"
#define ZSTD_ZBIC_SUPPORT 1
#define ZSTDLIB_VISIBLE static
#define ZSTDLIB_HIDDEN static
#define ZSTDERRORLIB_VISIBLE static
#define ZSTDERRORLIB_HIDDEN static
#undef ZSTD_MULTITHREAD
#if defined(__ANDROID__)
#undef _GNU_SOURCE
#endif
#include "zstd.h"
#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol
#include "zstd.c"
#undef g_ZSTD_threading_useless_symbol
namespace Common::Compression {
bool IsZBIC(std::span<const u8> src) {
if (src.size() < sizeof(u32)) {
return false;
}
u32 magic = 0;
std::memcpy(&magic, src.data(), sizeof(u32));
return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC")
}
int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src) {
if (dst.empty() || src.empty()) {
return -1;
}
const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size());
if (ZSTD_isError(res)) {
return -1;
}
return static_cast<int>(res);
}
} // namespace Common::Compression
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <span>
#include "common/common_types.h"
namespace Common::Compression {
[[nodiscard]] bool IsZBIC(std::span<const u8> src);
[[nodiscard]] int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src);
} // namespace Common::Compression
-7
View File
@@ -1127,7 +1127,6 @@ add_library(core STATIC
internal_network/network_interface.h
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
internal_network/socket_types.h
internal_network/sockets.h
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
@@ -1166,12 +1165,6 @@ add_library(core STATIC
tools/renderdoc.cpp
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)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (LINUX)
-1
View File
@@ -13,7 +13,6 @@
#include <dynarmic/interface/A64/a64.h>
#include <dynarmic/interface/code_page.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
+12 -19
View File
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
UNREACHABLE();
}
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
/// Initialization
system.RegisterCoreThread(core);
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
@@ -178,26 +178,19 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
auto& data = core_data[core];
data.host_context = Common::Fiber::ThreadToFiber();
// Cleanup
SCOPE_EXIT {
data.host_context->Exit();
};
// Running
if (!gpu_barrier->Sync(token)) {
return;
gpu_barrier->arrive_and_wait();
if (!stop_token.stop_requested()) {
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
}
if (!is_async_gpu && !is_multicore) {
system.GPU().ObtainContext();
}
auto& kernel = system.Kernel();
auto& scheduler = *kernel.CurrentScheduler();
auto* thread = scheduler.GetSchedulerCurrentThread();
Kernel::SetCurrentThread(kernel, thread);
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
data.host_context->Exit();
}
} // namespace Core
+3 -2
View File
@@ -8,6 +8,7 @@
#include <array>
#include <atomic>
#include <barrier>
#include <functional>
#include <memory>
#include <thread>
@@ -52,7 +53,7 @@ public:
}
void OnGpuReady() {
gpu_barrier->Sync();
gpu_barrier->arrive_and_wait();
}
void Initialize();
@@ -95,7 +96,7 @@ private:
static constexpr std::size_t max_cycle_runs = 5;
std::optional<Common::Barrier> gpu_barrier{};
std::optional<std::barrier<>> gpu_barrier{};
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
std::jthread host_thread;
+2 -1
View File
@@ -14,6 +14,7 @@
#include <tuple>
#include <vector>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/evp.h>
#include "common/fs/file.h"
@@ -622,7 +623,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
Key128 key = Common::HexStringToArray<16>(out[1]);
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
} else {
out[0] = Common::ToLower(out[0]);
boost::algorithm::to_lower(out[0]);
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
const auto& index = iter128->second;
const Key128 key = Common::HexStringToArray<16>(out[1]);
+3 -5
View File
@@ -6,6 +6,7 @@
#include <array>
#include <cstring>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/hex_util.h"
@@ -41,16 +42,13 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
const std::string& name) {
const auto upper = Common::ToUpper(name);
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) {
const auto upper = boost::algorithm::to_upper_copy(name);
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
if (dir->GetFile(fname) != nullptr) {
return dir->GetFile(fname);
}
}
return nullptr;
}
+2 -3
View File
@@ -8,6 +8,7 @@
#include <array>
#include <cstddef>
#include <cstring>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/assert.h"
#include "common/hex_util.h"
@@ -73,12 +74,10 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
#else
const auto subdirs = dir->GetSubdirectories();
for (const auto& subdir : subdirs) {
std::string dir_name = Common::ToLower(subdir->GetName());
if (dir_name == name) {
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
return subdir;
}
}
return nullptr;
#endif
}
+4 -4
View File
@@ -8,6 +8,7 @@
#include <limits>
#include <random>
#include <regex>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/evp.h>
#include "common/assert.h"
#include "common/fs/path_util.h"
@@ -1414,11 +1415,10 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
continue;
}
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
if (extension == "nsp") {
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
if (ext == "nsp") {
ProcessNSP(file);
} else if (extension == "xci") {
} else if (ext == "xci") {
ProcessXCI(file);
}
}
+3 -2
View File
@@ -9,6 +9,7 @@
#include <regex>
#include <string>
#include <boost/algorithm/string/case_conv.hpp>
#include <openssl/err.h>
#include <openssl/evp.h>
@@ -63,8 +64,8 @@ NAX::NAX(VirtualFile file_)
return;
}
const std::string two_dir = Common::ToUpper(std::string{match[1]});
const std::string nca_id = Common::ToLower(std::string{match[2]});
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]});
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]});
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
}
+11 -2
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -19,8 +22,14 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
// Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
0, 1, 2, 3, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 3,
};
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
+1 -1
View File
@@ -18,7 +18,7 @@
#include "common/container/unordered_map.h"
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
+8 -7
View File
@@ -129,13 +129,6 @@ 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) {
server_manager->m_system.RunServer(std::move(server_manager));
}
@@ -252,6 +245,14 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
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() {
SCOPE_EXIT {
m_stopped.Set();
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -51,8 +48,8 @@ public:
Result ManageDeferral(Kernel::KEvent** out_event);
Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
+296 -258
View File
@@ -12,7 +12,7 @@
#include <fmt/ranges.h>
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/core.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
@@ -20,9 +20,6 @@
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.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 "network/network.h"
#include <common/settings.h>
@@ -31,18 +28,15 @@ namespace Service::Sockets {
namespace {
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
bool IsConnectionBased(Type type) {
switch (type) {
case Network::Type::STREAM:
case Network::Type::SEQPACKET:
case Type::STREAM:
return true;
case Network::Type::RAW:
case Network::Type::DGRAM:
case Network::Type::RDM:
case Network::Type::Unspecified:
case Type::DGRAM:
return false;
default:
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return false;
}
}
@@ -98,7 +92,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
@@ -176,9 +170,10 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -194,8 +189,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
if (bsd_errno == Network::Errno::E_SUCCESS) {
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
if (bsd_errno == Errno::SUCCESS) {
bsd_errno = ShutdownImpl(fd, 0);
}
@@ -269,13 +264,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -287,13 +282,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -301,19 +296,21 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const auto level = Network::SocketLevel(rp.Pop<u32>());
const auto optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const auto optname = static_cast<OptName>(rp.Pop<u32>());
std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
optval.size());
const Errno err = GetSockOptImpl(fd, level, optname, optval);
ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(err);
rb.Push<u32>(static_cast<u32>(optval.size()));
}
@@ -336,7 +333,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -348,11 +345,13 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
}
@@ -474,7 +473,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
struct OutputParameters {
s32 ret;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x8);
@@ -486,7 +485,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (is_user) {
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = Network::Errno::E_INVAL,
.bsd_errno = Errno::INVAL,
});
return;
}
@@ -495,10 +494,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{
.ret = *res,
.bsd_errno = Network::Errno::E_SUCCESS,
.bsd_errno = Errno::SUCCESS,
});
} else {
auto* err = std::get_if<Network::Errno>(&res_v);
auto* err = std::get_if<Errno>(&res_v);
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = *err,
@@ -513,7 +512,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
BuildErrnoResponse(ctx, Errno::SUCCESS);
}
template <typename Work>
@@ -522,143 +521,132 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Response(ctx);
}
std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) {
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
// fine, can use on bsd:s and bsd:u
} else {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
}
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = Network::Type(u32(type) & ~0x20000000);
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
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};
return {-1, Errno::MFILE};
}
file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
// 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?
LOG_INFO(Service, "New socket fd={}", fd);
auto room_member = Network::GetRoomMember().lock();
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
&& (room_member && room_member->IsConnected())) {
if (room_member && room_member->IsConnected()) {
descriptor.socket = std::make_shared<Network::ProxySocket>();
descriptor.socket->fd = fd;
} else {
descriptor.socket = std::make_shared<Network::Socket>();
}
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
#if defined(__unix__) && !defined(__APPLE__)
// ...only unix has this issue it seems, ICMP works otherwise fine on win
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
&& bsd_errno != Network::Errno::E_SUCCESS) {
LOG_WARNING(Network, "Using ICMP emulated socket");
descriptor.socket = std::make_shared<Network::IcmpSocket>();
descriptor.socket->fd = fd;
}
#endif
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
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};
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
return {-1, Errno::NOTCONN};
}
return {fd, Network::Errno::E_SUCCESS};
return {fd, Errno::SUCCESS};
}
std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) {
if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Network::Errno::E_SUCCESS};
return {-1, Errno::SUCCESS};
}
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::E_INVAL};
if (read_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::E_INVAL};
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
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());
std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
if (timeout >= 0) {
const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
if (seconds < 0) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
if (nanoseconds > 999'999'999) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
} else if (timeout != -1) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
for (size_t i = 0; i < in_fds.size(); ++i) {
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
if (!IsFileDescriptorValid(in_fds[i].fd)) {
out_fds[i].revents = {};
if (!file_descriptors[in_fds[i].fd])
out_fds[i].revents = Network::PollEvents::NVAL;
return {0, Network::Errno::E_SUCCESS};
for (PollFD& pollfd : fds) {
ASSERT(False(pollfd.revents));
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
pollfd.revents = PollEvents{};
return {0, Errno::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::HostPollFD> host_pollfds(in_fds.size());
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
Network::HostPollFD result{};
result.socket = file_descriptors[e.fd]->socket.get();
result.events = e.events;
result.revents = {};
std::vector<Network::PollFD> host_pollfds(fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
Network::PollFD result;
result.socket = file_descriptors[pollfd.fd]->socket.get();
result.events = Translate(pollfd.events);
result.revents = Network::PollEvents{};
return result;
});
auto const res = Network::Poll(host_pollfds, timeout);
for (size_t i = 0; i < in_fds.size(); ++i)
out_fds[i].revents = host_pollfds[i].revents;
return res;
const auto result = Network::Poll(host_pollfds, timeout);
const size_t num = host_pollfds.size();
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, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Network::Errno::E_MFILE};
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return {-1, bsd_errno};
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, Translate(bsd_errno)};
}
file_descriptors[new_fd] = FileDescriptor{};
@@ -666,222 +654,268 @@ std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& writ
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
PutValue(write_buffer, result.sockaddr_in);
return {new_fd, Network::Errno::E_SUCCESS};
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
PutValue(write_buffer, guest_addr_in);
return {new_fd, Errno::SUCCESS};
}
Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
return file_descriptors[fd]->socket->Bind(addr_in);
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
if (result == Network::Errno::E_ISCONN) {
auto addr_in = GetValue<SockAddrIn>(addr);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result == Errno::ISCONN) {
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
return result;
}
Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return bsd_errno;
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return bsd_errno;
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Listen(backlog);
return Translate(file_descriptors[fd]->socket->Listen(backlog));
}
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);
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) {
case Network::FcntlCmd::GETFL:
case FcntlCmd::GETFL:
ASSERT(arg == 0);
return {descriptor.flags, Network::Errno::E_SUCCESS};
case Network::FcntlCmd::SETFL: {
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
if (bsd_errno != Network::Errno::E_SUCCESS) {
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
}
descriptor.flags = arg;
return {0, Network::Errno::E_SUCCESS};
return {0, Errno::SUCCESS};
}
default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
return {-1, Network::Errno::E_SUCCESS};
return {-1, Errno::SUCCESS};
}
}
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));
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (level != Network::SocketLevel::SOCKET) {
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
UNIMPLEMENTED_MSG("Unknown getsockopt level");
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
switch (optname) {
case Network::OptName::ERROR_: {
case OptName::ERROR_: {
auto [pending_err, getsockopt_err] = socket->GetPendingError();
if (getsockopt_err == Network::Errno::E_SUCCESS) {
if (getsockopt_err == Network::Errno::SUCCESS) {
Errno translated_pending_err = Translate(pending_err);
ASSERT_OR_EXECUTE_MSG(
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
optval.size() == sizeof(Errno), { return Errno::INVAL; },
"Incorrect getsockopt option size");
optval.resize(sizeof(Network::Errno));
PutValue(optval, pending_err);
optval.resize(sizeof(Errno));
PutValue(optval, translated_pending_err);
}
return getsockopt_err;
return Translate(getsockopt_err);
}
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
}
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);
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::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();
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;
}
}
Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
LOG_DEBUG(Network, "fd={},how={}", fd, how);
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
}
std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
// Restore original state
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
if (!IsFileDescriptorValid(fd))
return {-1, Network::Errno::E_BADF};
if (message.size() == 0)
return {0, Network::Errno::E_SUCCESS};
if (!std::in_range<u32>(message.size()))
return {-1, Network::Errno::E_FAULT};
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
@@ -895,75 +929,79 @@ std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vec
}
// Apply flags
auto const is_nonblock = descriptor.socket->GetNonBlock();
auto const f_dontwait = (flags & u32(Network::MsgOpt::DONTWAIT)) != 0;
auto const f_waitall = (flags & u32(Network::MsgOpt::WAITALL)) != 0;
// DONTWAIT set clears WAITALL, if socket is non-blocking it also clears WAITALL
if (f_dontwait || (f_waitall && is_nonblock))
flags &= ~u32(Network::MsgOpt::WAITALL);
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
if (f_dontwait) descriptor.socket->SetNonBlock(true); //set non-block
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
if (f_dontwait) descriptor.socket->SetNonBlock(is_nonblock); //restore
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
// Restore original state
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
if (p_addr_in) {
if (ret < 0) {
addr.clear();
} else {
ASSERT(addr.size() >= 16);
PutValue(addr, addr_in);
const SockAddrIn result = Translate(addr_in);
PutValue(addr, result);
}
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
return file_descriptors[fd]->socket->Send(message, flags);
return Translate(file_descriptors[fd]->socket->Send(message, flags));
}
std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
Network::SockAddrIn addr_in{};
Network::SockAddrIn addr_in;
Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) {
ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
addr_in = guest_addr_in;
auto guest_addr_in = GetValue<SockAddrIn>(addr);
addr_in = Translate(guest_addr_in);
p_addr_in = &addr_in;
}
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
}
Network::Errno BSD_USA::CloseImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::CloseImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto const bsd_errno = file_descriptors[fd]->socket->Close();
if (bsd_errno != Network::Errno::E_SUCCESS) {
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
if (bsd_errno != Errno::SUCCESS) {
return bsd_errno;
}
@@ -973,27 +1011,26 @@ Network::Errno BSD_USA::CloseImpl(s32 fd) {
return bsd_errno;
}
std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
// Don't use IsFileDescriptorValid as new_fd isn't a proper handle yet
if (s32 const new_fd = FindFreeFileDescriptorHandle(); new_fd >= 0) {
file_descriptors[new_fd] = FileDescriptor{
.socket = file_descriptors[fd]->socket,
.flags = file_descriptors[fd]->flags,
.is_connection_based = file_descriptors[fd]->is_connection_based,
};
return new_fd;
} else {
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return Network::Errno::E_MFILE;
return Errno::MFILE;
}
file_descriptors[new_fd] = FileDescriptor{
.socket = file_descriptors[fd]->socket,
.flags = file_descriptors[fd]->flags,
.is_connection_based = file_descriptors[fd]->is_connection_based,
};
return new_fd;
}
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return std::nullopt;
}
@@ -1005,40 +1042,41 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
}
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
// first three file descriptors are reserved for:
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
if (!file_descriptors[fd])
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) {
return fd;
}
}
return -1;
}
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd < 0 || fd >= s32(file_descriptors.size())) {
LOG_ERROR(Service, "Invalid handle={}", fd);
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
return false;
}
if (!file_descriptors[fd]) {
LOG_ERROR(Service, "handle={} is not allocated", fd);
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
return false;
}
return true;
}
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) {
if (optional_descriptor.has_value()) {
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
if (!optional_descriptor.has_value()) {
continue;
}
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
}
}
+27 -27
View File
@@ -11,7 +11,7 @@
#include <variant>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
#include "network/network.h"
@@ -35,8 +35,8 @@ public:
// These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service.
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
Network::Errno CloseImpl(s32 fd);
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
private:
@@ -58,7 +58,7 @@ private:
std::span<const u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct AcceptWork {
@@ -68,7 +68,7 @@ private:
s32 fd;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct ConnectWork {
@@ -77,7 +77,7 @@ private:
s32 fd;
std::span<const u8> addr;
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvWork {
@@ -88,7 +88,7 @@ private:
u32 flags;
std::vector<u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvFromWork {
@@ -100,7 +100,7 @@ private:
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendWork {
@@ -111,7 +111,7 @@ private:
u32 flags;
std::span<const u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendToWork {
@@ -123,7 +123,7 @@ private:
std::span<const u8> message;
std::span<const u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
void RegisterClient(HLERequestContext& ctx);
@@ -155,29 +155,29 @@ private:
template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work);
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
Network::Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, 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, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, std::span<const u8> addr);
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
std::pair<s32, Network::Errno> SendImpl(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::pair<s32, 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::span<const u8> addr);
s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+44 -40
View File
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
}
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
return NetDbError::Success;
case Network::GetAddrInfoError::AGAIN:
case GetAddrInfoError::AGAIN:
return NetDbError::TryAgain;
case Network::GetAddrInfoError::NODATA:
case GetAddrInfoError::NODATA:
return NetDbError::HostNotFound;
case Network::GetAddrInfoError::SERVICE:
case GetAddrInfoError::SERVICE:
return NetDbError::Success;
default:
return NetDbError::HostNotFound;
}
}
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
// some reason, but that doesn't seem useful to implement.
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::AGAIN:
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::NODATA:
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::SERVICE:
return Network::Errno::E_INVAL;
return Errno::SUCCESS;
case GetAddrInfoError::AGAIN:
return Errno::SUCCESS;
case GetAddrInfoError::NODATA:
return Errno::SUCCESS;
case GetAddrInfoError::SERVICE:
return Errno::INVAL;
default:
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
}
@@ -155,7 +155,9 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
// doesn't implement h_errno.
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
std::vector<u8> data;
// h_name: use the input hostname (append nul-terminated)
AppendNulTerminated(data, host);
@@ -163,12 +165,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
Append<u32_be>(data, 0); // count of h_aliases
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
// h_addr_list:
size_t count = vec.size();
ASSERT(count <= UINT32_MAX);
Append<u32_be>(data, u32(count));
Append<u32_be>(data, static_cast<uint32_t>(count));
for (const Network::AddrInfo& addrinfo : vec) {
// On the Switch, this is passed through htonl despite already being
// big-endian, so it ends up as little-endian.
@@ -180,7 +182,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -203,7 +205,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
@@ -211,10 +213,10 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -222,7 +224,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
struct OutputParameters {
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -242,7 +244,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -255,23 +257,24 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
});
}
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
// Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data;
for (const Network::AddrInfo& addrinfo : vec) {
// serialized addrinfo:
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr:
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
// On the Switch, the following fields are passed through htonl despite
// already being big-endian, so they end up as little-endian.
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
@@ -293,7 +296,7 @@ static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec,
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -318,7 +321,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
std::optional<std::string> service = std::nullopt;
@@ -328,23 +331,24 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
}
// Serialized hints are also passed in a buffer, but are ignored for now.
auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
struct OutputParameters {
Network::Errno bsd_errno;
Network::GetAddrInfoError gai_error;
Errno bsd_errno;
GetAddrInfoError gai_error;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -360,7 +364,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
struct InputParameters {
Network::GetAddrInfoError gai_errno;
GetAddrInfoError gai_errno;
};
IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>();
@@ -378,9 +382,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
Network::GetAddrInfoError gai_error;
GetAddrInfoError gai_error;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x10);
+236 -1
View File
@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
namespace Core {
class System;
@@ -16,6 +15,242 @@ class System;
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);
} // namespace Service::Sockets
@@ -15,42 +15,388 @@
namespace Service::Sockets {
const char* Translate(Network::GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
Errno Translate(Network::Errno value) {
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 "Success";
return GetAddrInfoError::SUCCESS;
case Network::GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
return GetAddrInfoError::ADDRFAMILY;
case Network::GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
return GetAddrInfoError::AGAIN;
case Network::GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
return GetAddrInfoError::BADFLAGS;
case Network::GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
return GetAddrInfoError::FAIL;
case Network::GetAddrInfoError::FAMILY:
return "ai_family not supported";
return GetAddrInfoError::FAMILY;
case Network::GetAddrInfoError::MEMORY:
return "Memory allocation failure";
return GetAddrInfoError::MEMORY;
case Network::GetAddrInfoError::NODATA:
return "No address associated with hostname";
return GetAddrInfoError::NODATA;
case Network::GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
return GetAddrInfoError::NONAME;
case Network::GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
return GetAddrInfoError::SERVICE;
case Network::GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
return GetAddrInfoError::SOCKTYPE;
case Network::GetAddrInfoError::SYSTEM:
return "System error returned in errno";
return GetAddrInfoError::SYSTEM;
case Network::GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
return GetAddrInfoError::BADHINTS;
case Network::GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
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
switch (error) {
case GetAddrInfoError::SUCCESS:
return "Success";
case GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
case GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
case GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
case GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
case GetAddrInfoError::FAMILY:
return "ai_family not supported";
case GetAddrInfoError::MEMORY:
return "Memory allocation failure";
case GetAddrInfoError::NODATA:
return "No address associated with hostname";
case GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
case GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
case GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
case GetAddrInfoError::SYSTEM:
return "System error returned in errno";
case GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
case GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
case GetAddrInfoError::OVERFLOW_:
return "Argument buffer overflow";
default:
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
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,49 @@
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
const char* Translate(Network::GetAddrInfoError value);
const char* Translate(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
+2 -2
View File
@@ -138,7 +138,7 @@ public:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
if (bsd) {
auto err = bsd->CloseImpl(fd);
if (err != Network::Errno::E_SUCCESS) {
if (err != Service::Sockets::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
}
}
@@ -230,7 +230,7 @@ private:
R_UNLESS(socket, ResultNoSocket);
const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::E_SUCCESS) {
if (error != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
R_SUCCEED();
@@ -327,10 +327,10 @@ public:
BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
*actual_p = actual;
return 1;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
return 0;
default:
@@ -346,13 +346,13 @@ public:
BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
*actual_p = actual;
if (actual == 0) {
self->got_read_eof = true;
}
return actual ? 1 : 0;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
return 0;
default:
+2 -3
View File
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <common/socket_types.h>
#include <mutex>
#include "core/internal_network/socket_types.h"
namespace Network {
File diff suppressed because it is too large Load Diff
+63 -9
View File
@@ -13,7 +13,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -31,10 +31,68 @@ namespace Network {
class SocketBase;
class Socket;
struct HostPollFD {
SocketBase* socket = nullptr;
Network::PollEvents events = {};
Network::PollEvents revents = {};
/// Error code for network functions
enum class Errno {
SUCCESS,
BADF,
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 {
@@ -43,10 +101,6 @@ public:
~NetworkInstance();
};
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
s32 TranslateMsgOptToNative(s32 flags);
void CancelPendingSocketOperations();
void RestartSocketOperations();
@@ -46,7 +46,7 @@ namespace Network {
#ifdef _WIN32
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
ULONG buf_size = 0;
if (GetAdaptersAddresses(
@@ -66,7 +66,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
return {};
}
std::vector<NetworkInterface> result;
std::vector<Network::NetworkInterface> result;
for (auto* a = addrs; a; a = a->Next) {
@@ -89,7 +89,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
->sin_addr;
result.emplace_back(NetworkInterface{
result.emplace_back(Network::NetworkInterface{
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
.ip_address = ip,
.subnet_mask = mask,
@@ -103,7 +103,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#else
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__)
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
@@ -135,7 +135,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
}
#endif
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
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 */
@@ -149,7 +149,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
});
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;
ifaces.emplace_back(NetworkInterface{
ifaces.emplace_back(Network::NetworkInterface{
.name = ifa->ifa_name,
.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,
@@ -159,7 +159,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
freeifaddrs(ifaddr);
return ifaces;
#elif defined(__FreeBSD__)
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) {
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
@@ -191,7 +191,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
char const* p = (char const*)(ifm + 1);
NetworkInterface iface{};
Network::NetworkInterface iface{};
for (size_t i = 0; i < RTAX_MAX; i++)
if ((ifm->ifm_addrs & (1 << i)) != 0) {
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
@@ -220,7 +220,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif // _WIN32
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
auto const& sel_if = Settings::values.network_interface.GetValue();
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
if (sel_if.empty())
-281
View File
@@ -1,281 +0,0 @@
// 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");
}
bool IcmpSocket::GetNonBlock() {
return !blocking;
}
Errno IcmpSocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_SUCCESS;
}
} // namespace Network
-57
View File
@@ -1,57 +0,0 @@
// 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;
bool GetNonBlock() 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
+151 -83
View File
@@ -47,147 +47,169 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
received_packets.push(decompressed);
}
bool ProxySocket::GetNonBlock() {
return blocking;
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_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;
template <typename T>
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
LOG_DEBUG(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
protocol = socket_protocol;
return Errno::E_SUCCESS;
SetSockOpt(fd, SO_TYPE, type);
return Errno::SUCCESS;
}
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::E_SUCCESS};
LOG_WARNING(Network, "(STUBBED) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
Errno ProxySocket::Connect(SockAddrIn addr_in) {
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
Errno ProxySocket::Bind(SockAddrIn addr) {
if (is_bound) {
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
return Errno::E_SUCCESS;
return Errno::SUCCESS;
}
local_endpoint = addr;
is_bound = true;
return Errno::E_SUCCESS;
return Errno::SUCCESS;
}
Errno ProxySocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
LOG_WARNING(Network, "(stubbed) called");
ASSERT(flags == 0 && message.size() < std::size_t((std::numeric_limits<int>::max)()));
return {s32(0), Errno::E_SUCCESS};
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
return {static_cast<s32>(0), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "called");
ASSERT(flags == 0 && message.size() < std::size_t((std::numeric_limits<int>::max)()));
do {
std::unique_lock lk{packets_mutex};
if (received_packets.size() > 0)
return ReceivePacket(flags, message, addr, message.size());
} while (blocking);
return {-1, Errno::E_AGAIN};
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
const auto timestamp = std::chrono::steady_clock::now();
// When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
// packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
// the timeout to 5s instead
const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
while (true) {
{
std::lock_guard guard(packets_mutex);
if (received_packets.size() > 0) {
return ReceivePacket(flags, message, addr, message.size());
}
}
if (!blocking) {
return {-1, Errno::AGAIN};
}
std::this_thread::yield();
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 > timeout) {
return {-1, Errno::TIMEDOUT};
}
}
}
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length) {
ProxyPacket& packet = received_packets.front();
if (addr) {
addr->len = 16;
addr->family = u8(Network::Domain::INET);
addr->family = Domain::INET;
addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number
addr->zeroes = {};
}
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
std::size_t read_bytes = (std::min)(max_length, packet.data.size());
std::memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) {
packet.data.erase(packet.data.begin(), packet.data.begin() + read_bytes);
if (packet.data.empty())
bool peek = (flags & FLAG_MSG_PEEK) != 0;
std::size_t read_bytes;
if (packet.data.size() > max_length) {
read_bytes = max_length;
memcpy(message.data(), packet.data.data(), max_length);
if (protocol == Protocol::UDP) {
if (!peek) {
received_packets.pop();
}
return {-1, Errno::MSGSIZE};
} else if (protocol == Protocol::TCP) {
std::vector<u8> numArray(packet.data.size() - max_length);
std::copy(packet.data.begin() + max_length, packet.data.end(),
std::back_inserter(numArray));
packet.data = numArray;
}
} else {
read_bytes = packet.data.size();
memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) {
received_packets.pop();
}
}
if (packet.data.size() > max_length && protocol == Protocol::UDP) {
LOG_ERROR(Network, "Packet size");
return {-1, Errno::E_MSGSIZE};
}
return {u32(read_bytes), Errno::E_SUCCESS};
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
ASSERT(flags == 0);
return {s32(0), Errno::E_SUCCESS};
return {static_cast<s32>(0), Errno::SUCCESS};
}
void ProxySocket::SendPacket(ProxyPacket& packet) {
if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
packet.data.size());
room_member->SendProxyPacket(packet);
}
}
}
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
ASSERT(flags == 0);
if (!is_bound) {
LOG_ERROR(Network, "ProxySocket is not bound!");
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
}
@@ -212,20 +234,66 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
SendPacket(packet);
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
Errno ProxySocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
closed = true;
return Errno::E_SUCCESS;
return Errno::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() {
LOG_DEBUG(Network, "called");
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
LOG_DEBUG(Network, "(STUBBED) called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
bool ProxySocket::IsOpened() const {
+48 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -22,25 +22,62 @@ public:
~ProxySocket() override;
void HandleProxyPacket(const ProxyPacket& packet) 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 Connect(SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(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> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length);
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length);
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
void SendPacket(ProxyPacket& packet);
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
bool GetNonBlock() override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
const 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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
bool IsOpened() const override;
private:
@@ -49,7 +86,7 @@ private:
u32 send_timeout = 0;
u32 receive_timeout = 0;
bool is_bound = false;
Network::SockAddrIn local_endpoint{};
SockAddrIn local_endpoint{};
bool blocking = true;
std::queue<ProxyPacket> received_packets;
Protocol protocol;
-470
View File
@@ -1,470 +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"
#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
+88 -20
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -30,7 +30,7 @@ public:
struct AcceptResult {
std::unique_ptr<SocketBase> socket;
Network::SockAddrIn sockaddr_in;
SockAddrIn sockaddr_in;
};
SocketBase() = default;
@@ -41,29 +41,61 @@ public:
YUZU_NON_MOVEABLE(SocketBase);
virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
virtual Errno Close() = 0;
virtual std::pair<AcceptResult, Errno> Accept() = 0;
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(Network::SockAddrIn addr) = 0;
virtual Errno Connect(SockAddrIn addr_in) = 0;
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(SockAddrIn addr) = 0;
virtual Errno Listen(s32 backlog) = 0;
virtual Errno Shutdown(ShutdownHow how) = 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, Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 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, const Network::SockAddrIn* addr) = 0;
virtual bool GetNonBlock() = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
virtual std::pair<Errno, Errno> GetPendingError() = 0;
virtual bool IsOpened() const = 0;
virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
[[nodiscard]] SOCKET GetFD() const {
return fd;
}
protected:
SOCKET fd = INVALID_SOCKET;
};
@@ -77,30 +109,66 @@ public:
Socket(Socket&& rhs) noexcept;
Errno Initialize(Domain domain, Type type, Protocol 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 Connect(SockAddrIn addr_in) override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(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> RecvFrom(int flags, std::span<u8> message, 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;
bool GetNonBlock() override;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
template <typename T>
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
bool IsOpened() const override;
void HandleProxyPacket(const ProxyPacket& packet) override;
private:
bool is_non_blocking = false;
};
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
} // namespace Network
+2 -2
View File
@@ -10,6 +10,7 @@
#include <string>
#include <concepts>
#include <algorithm>
#include <boost/algorithm/string/case_conv.hpp>
#include "common/concepts.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
@@ -170,8 +171,7 @@ FileType GuessFromFilename(const std::string& name) {
else if (name == "00")
return FileType::NCA;
auto const extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
if (extension == "nro")
return FileType::NRO;
else if (extension == "nso")
+31 -4
View File
@@ -7,12 +7,14 @@
#include <algorithm>
#include <cinttypes>
#include <cstring>
#include <span>
#include <vector>
#include "common/common_funcs.h"
#include "common/hex_util.h"
#include "common/logging.h"
#include "common/lz4_compression.h"
#include "common/zbic_compression.h"
#include "common/settings.h"
#include "common/swap.h"
#include "core/core.h"
@@ -104,11 +106,36 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
if (nso_header.IsSegmentCompressed(i)) {
int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]);
ASSERT(r == int(nso_header.segments[i].size));
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size);
if (nso_header.IsZBICCompressed()) {
// ZBIC compression
const int r = Common::Compression::DecompressDataZBIC(
std::span<u8>{decompressed_size}.first(nso_header.segments[i].size),
std::span<const u8>{compressed_data}.first(nso_header.segments_compressed_size[i])
);
ASSERT(r > 0);
} else {
// LZ4 compression
int r = Common::Compression::DecompressDataLZ4(
decompressed_size.data(),
nso_header.segments[i].size,
compressed_data.data(),
nso_header.segments_compressed_size[i]
);
ASSERT(r == int(nso_header.segments[i].size));
}
std::memcpy(
codeset.memory.data() + module_start + nso_header.segments[i].location,
decompressed_size.data(),
nso_header.segments[i].size
);
} else {
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size);
// Not compressed
std::memcpy(
codeset.memory.data() + module_start + nso_header.segments[i].location,
compressed_data.data(),
nso_header.segments[i].size
);
}
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
+6
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -58,6 +61,9 @@ struct NSOHeader {
std::array<SHA256Hash, 3> segment_hashes;
bool IsSegmentCompressed(size_t segment_num) const;
bool IsZBICCompressed() const {
return ((flags >> 7) & 1) != 0;
}
};
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
+2 -1
View File
@@ -14,6 +14,7 @@
#include <regex>
#include <string>
#include <thread>
#include <boost/algorithm/string/trim.hpp>
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
@@ -132,7 +133,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
std::string line;
std::getline(file, line);
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
line = Common::StripSpaces(line);
boost::trim(line);
if (line.empty()) {
// An empty line marks start of the IP ban list
ban_list_type = true;
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/verify_user.h"
namespace Network {
+4 -8
View File
@@ -10,7 +10,7 @@
#include <thread>
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "enet/enet.h"
#include "network/packet.h"
#include "network/room_member.h"
@@ -358,23 +358,19 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
// Parse the ProxyPacket from the packet
u8 local_family;
packet.Read(local_family);
proxy_packet.local_endpoint.len = 16;
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
packet.Read(proxy_packet.local_endpoint.ip);
packet.Read(proxy_packet.local_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 remote_family;
packet.Read(remote_family);
proxy_packet.remote_endpoint.len = 16;
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
packet.Read(proxy_packet.remote_endpoint.ip);
packet.Read(proxy_packet.remote_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 protocol_type;
packet.Read(protocol_type);
proxy_packet.protocol = Network::Protocol(protocol_type);
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
packet.Read(proxy_packet.broadcast);
packet.Read(proxy_packet.data);
+4 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/room.h"
namespace Network {
@@ -39,8 +39,8 @@ struct LDNPacket {
/// Information about the received proxy packets.
struct ProxyPacket {
Network::SockAddrIn local_endpoint;
Network::SockAddrIn remote_endpoint;
SockAddrIn local_endpoint;
SockAddrIn remote_endpoint;
Protocol protocol;
bool broadcast;
std::vector<u8> data;
+2 -1
View File
@@ -8,6 +8,7 @@
#include <string>
#include <QEventLoop>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/replace.hpp>
#include "common/httplib.h"
@@ -45,7 +46,7 @@ void DiscordImpl::Pause() {
std::string DiscordImpl::GetGameString(const std::string& title) {
// Convert to lowercase
std::string icon_name = Common::ToLower(title);
std::string icon_name = boost::algorithm::to_lower_copy(title);
// Replace spaces with dashes
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
+5 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,18 +12,16 @@ TEST_CASE("Network::Errors", "[core]") {
Network::Socket socks[2];
for (Network::Socket& sock : socks) {
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
Network::Protocol::TCP) == Network::Errno::SUCCESS);
}
Network::SockAddrIn addr{
16,
u8(Network::Domain::INET),
1, // hopefully nobody running this test has something listening on port 1
Network::Domain::INET,
{127, 0, 0, 1},
{},
1, // hopefully nobody running this test has something listening on port 1
};
REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
std::vector<u8> message{1, 2, 3, 4};
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
}
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -55,7 +55,7 @@ size_t StagingBuffers::RequestBuffer(size_t requested_size) {
}
StagingBufferAlloc alloc;
alloc.buffer.Create();
const auto next_pow2_size = Common::NextPow2(requested_size);
const auto next_pow2_size = std::bit_ceil(requested_size);
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
storage_flags | GL_MAP_PERSISTENT_BIT);
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
@@ -1404,7 +1404,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
if (pbo_size < copy_size) {
intermediate_pbo.Create();
pbo_size = Common::NextPow2(copy_size);
pbo_size = std::bit_ceil(copy_size);
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
}
// Copy from source to PBO
@@ -1011,7 +1011,7 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
if (buffers[level]) {
return *buffers[level];
}
const auto new_size = Common::NextPow2(needed_size);
const auto new_size = std::bit_ceil(needed_size);
static constexpr VkBufferUsageFlags flags =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+1 -1
View File
@@ -2190,7 +2190,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
image.flags &= ~ImageFlagBits::BadOverlap;
lru_cache.Free(image.lru_index);
const auto& clear_page_table =
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& selected_page_table) {
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>>& selected_page_table) {
const auto page_it = selected_page_table.find(page);
if (page_it == selected_page_table.end()) {
ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS);
@@ -22,7 +22,6 @@
#include <queue>
#include "common/common_types.h"
#include "common/hash.h"
#include "common/literals.h"
#include "common/lru_cache.h"
#include <ranges>
@@ -69,7 +68,7 @@ struct AsyncDecodeContext {
std::atomic_bool complete;
};
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>>;
class TextureCacheChannelInfo : public ChannelInfo {
public:
@@ -443,7 +442,7 @@ private:
u64 last_framebuffer_serial = 0;
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
::Common::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
::Common::unordered_map<u64, std::vector<ImageMapId>> page_table;
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
DAddr virtual_invalid_space{};