Camera code cleanup and fixes (#5211)

* Split the HLE camera library into multiple source files for readability

* copyright 2026

* calibration functions

* Pull out camera config type table to a separate header

* Do not assume the base format is always RGBA8888 for RAW8/RAW16 outputs
If a game registers YUV422 for eye 1 and RAW16 for eye 2, that made the HLE library register YUY2 as the SDL format before, but then tried to use it as RGBA88888, which lead to overidexing and reading from unmapped pages

* Cleanup Turtle's cleanup

* copyright 2024-2026

* the loathsome clang-formatter

* sir clang offnir, the all formatting
This commit is contained in:
kalaposfos13
2026-10-03 16:31:45 +03:00
committed by GitHub
parent 8e23388af8
commit 9a8ae4417b
12 changed files with 1322 additions and 1091 deletions
+7
View File
@@ -732,6 +732,13 @@ set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
src/core/libraries/camera/camera.h
src/core/libraries/camera/camera_helpers.h
src/core/libraries/camera/camera_helpers.cpp
src/core/libraries/camera/camera_audio.cpp
src/core/libraries/camera/camera_calibration.cpp
src/core/libraries/camera/camera_config.cpp
src/core/libraries/camera/camera_config_types.h
src/core/libraries/camera/camera_device.cpp
src/core/libraries/camera/camera_error.h
)
File diff suppressed because it is too large Load Diff
+8 -180
View File
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <core/libraries/system/userservice.h>
#include "camera_config_types.h"
#include "camera_error.h"
#include "common/types.h"
#include "core/libraries/system/userservice.h"
namespace Core::Loader {
class SymbolsResolver;
@@ -12,6 +14,9 @@ class SymbolsResolver;
namespace Libraries::Camera {
extern bool g_library_opened;
extern s32 g_firmware_version;
constexpr s32 ORBIS_CAMERA_MAX_DEVICE_NUM = 2;
constexpr s32 ORBIS_CAMERA_MAX_FORMAT_LEVEL_NUM = 4;
constexpr s32 ORBIS_CAMERA_CALIBRATION_DATA_MESH_NUMBER_OF_TOTAL_VERTICIES = 1276;
@@ -29,195 +34,18 @@ struct OrbisCameraOpenParameter {
u32 reserved3;
};
enum OrbisCameraConfigType {
ORBIS_CAMERA_CONFIG_TYPE1 = 0x01,
ORBIS_CAMERA_CONFIG_TYPE2 = 0x02,
ORBIS_CAMERA_CONFIG_TYPE3 = 0x03,
ORBIS_CAMERA_CONFIG_TYPE4 = 0x04,
ORBIS_CAMERA_CONFIG_TYPE5 = 0x05,
ORBIS_CAMERA_CONFIG_EXTENTION = 0x10,
};
enum OrbisCameraResolution {
ORBIS_CAMERA_RESOLUTION_1280X800 = 0x0,
ORBIS_CAMERA_RESOLUTION_640X400 = 0x1,
ORBIS_CAMERA_RESOLUTION_320X200 = 0x2,
ORBIS_CAMERA_RESOLUTION_160X100 = 0x3,
ORBIS_CAMERA_RESOLUTION_320X192 = 0x4,
ORBIS_CAMERA_RESOLUTION_SPECIFIED_WIDTH_HEIGHT,
ORBIS_CAMERA_RESOLUTION_UNKNOWN = 0xFF,
};
enum OrbisCameraFramerate {
ORBIS_CAMERA_FRAMERATE_UNKNOWN = 0,
ORBIS_CAMERA_FRAMERATE_7_5 = 7,
ORBIS_CAMERA_FRAMERATE_15 = 15,
ORBIS_CAMERA_FRAMERATE_30 = 30,
ORBIS_CAMERA_FRAMERATE_60 = 60,
ORBIS_CAMERA_FRAMERATE_120 = 120,
ORBIS_CAMERA_FRAMERATE_240 = 240,
};
enum OrbisCameraBaseFormat {
ORBIS_CAMERA_FORMAT_YUV422 = 0x0,
ORBIS_CAMERA_FORMAT_RAW16,
ORBIS_CAMERA_FORMAT_RAW8,
ORBIS_CAMERA_FORMAT_NO_USE = 0x10,
ORBIS_CAMERA_FORMAT_UNKNOWN = 0xFF,
};
enum OrbisCameraScaleFormat {
ORBIS_CAMERA_SCALE_FORMAT_YUV422 = 0x0,
ORBIS_CAMERA_SCALE_FORMAT_Y16 = 0x3,
ORBIS_CAMERA_SCALE_FORMAT_Y8,
ORBIS_CAMERA_SCALE_FORMAT_NO_USE = 0x10,
ORBIS_CAMERA_SCALE_FORMAT_UNKNOWN = 0xFF,
};
enum OrbisCameraCalibrationDataFunctionType {
ORBIS_CAMERA_CALIBRATION_DATA_FUNCTION_TYPE_IMAGE_RECTIFICATION = 0,
ORBIS_CAMERA_CALIBRATION_DATA_FUNCTION_TYPE_IMAGE_INVERSE_RECTIFICATION = 1,
};
struct OrbisCameraFormat {
OrbisCameraBaseFormat formatLevel0;
OrbisCameraScaleFormat formatLevel1;
OrbisCameraScaleFormat formatLevel2;
OrbisCameraScaleFormat formatLevel3;
};
struct OrbisCameraConfigExtention {
OrbisCameraFormat format;
OrbisCameraResolution resolution;
OrbisCameraFramerate framerate;
u32 width;
u32 height;
u32 reserved1;
void* pBaseOption;
};
struct OrbisCameraConfig {
u32 sizeThis;
OrbisCameraConfigType configType;
OrbisCameraConfigExtention configExtention[ORBIS_CAMERA_MAX_DEVICE_NUM];
};
constexpr OrbisCameraConfigExtention camera_config_types[5][ORBIS_CAMERA_MAX_DEVICE_NUM]{
{
// type 1
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 2
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 3
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 4
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 5
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
}};
extern OrbisCameraConfigExtention output_config0, output_config1;
enum OrbisCameraAecAgcTarget {
ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_DEF = 0x00,
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/camera/camera.h"
namespace Libraries::Camera {
s32 PS4_SYSV_ABI sceCameraAudioClose() {
LOG_DEBUG(Lib_Camera, "called");
// Returns ORBIS_OK while internally failing
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraAudioGetData(void* data) {
LOG_DEBUG(Lib_Camera, "called");
if (data == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
// Returns ORBIS_OK while internally failing
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraAudioGetData2(void* data) {
LOG_DEBUG(Lib_Camera, "called");
if (data == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
// Returns ORBIS_OK while internally failing
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraAudioOpen() {
LOG_DEBUG(Lib_Camera, "called");
// Returns error fatal as a side effect of not having a camera attached.
return ORBIS_CAMERA_ERROR_FATAL;
}
s32 PS4_SYSV_ABI sceCameraAudioReset() {
LOG_DEBUG(Lib_Camera, "called");
// Returns ORBIS_OK while internally failing
return ORBIS_OK;
}
} // namespace Libraries::Camera
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/camera/camera.h"
namespace Libraries::Camera {
s32 PS4_SYSV_ABI sceCameraGetCalibData(s32 id, void* calib_data, void* maybe_reserved) {
LOG_WARNING(Lib_Camera, "(DUMMY) called, id: {}", id);
if (calib_data == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (id != 1 && id != 2) {
return ORBIS_OK;
}
static constexpr u64 dumped_calib_data1[32]{
0x000000000b0b0200, 0x00000000bc128639, 0x000000003de79766, 0x00000000be6e2869,
0x3ca0ce1f3e30521c, 0x4452d55e3d0160b6, 0x3cf7fdaa4453abf1, 0x000000003ce3f40f,
0x000000003cdbcaa6, 0x000000003d635f4e, 0x00000000be299439, 0xbb98ddc23e1159b4,
0x44523b6c3c87d83c, 0x3c8d1c46445308a2, 0xbbb83fe03c9bcc40, 0xbab0ed65b8fe93b0,
0xba677004bb598091, 0x3c0eb61e3b49d173, 0xba1bac9f3c67af0b, 0x00000000b9d0e64d,
0x0000000000000000, 0x0000000000000000, 0xb97653e400000000, 0x00000000ba95460e,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0xbab5330000000000, 0x000001013b07a47f,
};
static constexpr u64 dumped_calib_data2[32]{
0x002d000000ff0009, 0x002d0ff70ffd0f14, 0x002d0efe00010008, 0x002f0ffe0f020008,
0x002f000800060105, 0x002e00fc0001000a, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
};
std::memcpy(calib_data, id == 1 ? dumped_calib_data1 : dumped_calib_data2,
sizeof(dumped_calib_data1));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetCalibDataFromDevice() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraGetCalibrationData(const OrbisCameraGetCalibrationDataParameter* param,
OrbisCameraCalibrationData* calibration_data) {
LOG_DEBUG(Lib_Camera, "called");
if (param == nullptr || calibration_data == nullptr ||
param->size != sizeof(OrbisCameraGetCalibrationDataParameter) || param->format_type != 0 ||
param->function_type <
OrbisCameraCalibrationDataFunctionType::
ORBIS_CAMERA_CALIBRATION_DATA_FUNCTION_TYPE_IMAGE_RECTIFICATION ||
param->function_type >
OrbisCameraCalibrationDataFunctionType::
ORBIS_CAMERA_CALIBRATION_DATA_FUNCTION_TYPE_IMAGE_INVERSE_RECTIFICATION) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (EmulatorSettings.GetCameraId() == -1) {
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraInitializeRegistryCalibData() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_INIT;
}
return ORBIS_OK;
}
} // namespace Libraries::Camera
+648
View File
@@ -0,0 +1,648 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/camera/camera.h"
namespace Libraries::Camera {
s32 PS4_SYSV_ABI sceCameraGetAttribute(s32 handle, OrbisCameraAttribute* attribute) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || attribute == nullptr || attribute->sizeThis != sizeof(OrbisCameraAttribute) ||
attribute->channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
attribute->channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
auto channel = attribute->channel;
// Set default attributes
memset(attribute, 0, sizeof(OrbisCameraAttribute));
attribute->sizeThis = sizeof(OrbisCameraAttribute);
attribute->channel = channel;
attribute->exposureGain.exposure = 83;
attribute->exposureGain.gain = 100;
attribute->whiteBalance.gainRed = 768;
attribute->whiteBalance.gainBlue = 768;
attribute->whiteBalance.gainGreen = 512;
attribute->gamma.value = 4;
attribute->saturation = 64;
attribute->contrast = 32;
attribute->sharpness = 1;
attribute->hue = 1;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32* enable,
OrbisCameraAutoExposureGainTarget* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (option != nullptr && (g_firmware_version < Common::ElfInfo::FW_300 ||
option->sizeThis != sizeof(OrbisCameraAutoExposureGainTarget))) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*enable = 0;
if (option != nullptr) {
option->sizeThis = 0;
option->target = OrbisCameraAecAgcTarget::ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_DEF;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32* enable,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*enable = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetConfig(s32 handle, OrbisCameraConfig* config) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || config == nullptr || config->sizeThis != sizeof(OrbisCameraConfig)) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
// Set default config
config->configType = ORBIS_CAMERA_CONFIG_TYPE1;
OrbisCameraConfigExtention default_extension;
default_extension.format.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422;
default_extension.format.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8;
default_extension.format.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8;
default_extension.format.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8;
default_extension.resolution = ORBIS_CAMERA_RESOLUTION_1280X800;
default_extension.framerate = ORBIS_CAMERA_FRAMERATE_60;
default_extension.width = 0;
default_extension.height = 0;
default_extension.reserved1 = 0;
memcpy(&config->configExtention[0], &default_extension, sizeof(OrbisCameraConfigExtention));
memcpy(&config->configExtention[1], &default_extension, sizeof(OrbisCameraConfigExtention));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetContrast(s32 handle, OrbisCameraChannel channel, u32* contrast,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || contrast == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*contrast = 32;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
u32* enable, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*enable = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetExposureGain(s32 handle, OrbisCameraChannel channel,
OrbisCameraExposureGain* exposure_gain, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || exposure_gain == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
// Return default parameters
exposure_gain->exposureControl = 0;
exposure_gain->exposure = 20;
exposure_gain->gain = 100;
exposure_gain->mode = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* gamma,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || gamma == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
// Return default parameters
memset(gamma, 0, sizeof(OrbisCameraGamma));
gamma->value = 4;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetHue(s32 handle, OrbisCameraChannel channel, s32* hue, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || hue == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*hue = 1;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetLensCorrection(s32 handle, OrbisCameraChannel channel, u32* enable,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*enable = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetMmapConnectedCount(u32* count) {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
if (count == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
*count = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetProductInfo(void* product_info) {
LOG_DEBUG(Lib_Camera, "(STUBBED) called");
if (product_info == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_INIT;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraGetRegister() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetRegistryInfo(void* registry_info) {
LOG_DEBUG(Lib_Camera, "(STUBBED) called");
if (registry_info == nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_INIT;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraGetSaturation(s32 handle, OrbisCameraChannel channel, u32* saturation,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || saturation == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*saturation = 64;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetSharpness(s32 handle, OrbisCameraChannel channel, u32* sharpness,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || sharpness == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
*sharpness = 1;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetVrCaptureInfo() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetWhiteBalance(s32 handle, OrbisCameraChannel channel,
OrbisCameraWhiteBalance* white_balance, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel >= OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || white_balance == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
// Set default parameters
white_balance->whiteBalanceControl = 0;
white_balance->gainRed = 768;
white_balance->gainBlue = 768;
white_balance->gainGreen = 512;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetAttribute(s32 handle, OrbisCameraAttribute* attribute) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || attribute == nullptr || attribute->sizeThis != sizeof(OrbisCameraAttribute) ||
attribute->channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
attribute->channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetAttributeInternal() {
// Stubbed on real hardware
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetAutoExposureGain(s32 handle, OrbisCameraChannel channel, u32 enable,
OrbisCameraAutoExposureGainTarget* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (option != nullptr) {
if (g_firmware_version < Common::ElfInfo::FW_300 ||
option->sizeThis != sizeof(OrbisCameraAutoExposureGainTarget)) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (option->target % 2 == 1 || option->target < ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_DEF ||
option->target > ORBIS_CAMERA_ATTRIBUTE_AECAGC_TARGET_2_0) {
return ORBIS_CAMERA_ERROR_PARAM;
}
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetAutoWhiteBalance(s32 handle, OrbisCameraChannel channel, u32 enable,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetCalibData() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetConfig(s32 handle, OrbisCameraConfig* config) {
LOG_INFO(Lib_Camera, "called");
if (handle < 1 || config == nullptr || config->sizeThis != sizeof(OrbisCameraConfig)) {
LOG_ERROR(Lib_Camera, "ORBIS_CAMERA_ERROR_PARAM");
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
LOG_ERROR(Lib_Camera, "ORBIS_CAMERA_ERROR_NOT_OPEN");
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
if (EmulatorSettings.GetCameraId() == -1) {
LOG_ERROR(Lib_Camera, "ORBIS_CAMERA_ERROR_NOT_CONNECTED");
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
switch (config->configType) {
case ORBIS_CAMERA_CONFIG_TYPE1:
case ORBIS_CAMERA_CONFIG_TYPE2:
case ORBIS_CAMERA_CONFIG_TYPE3:
case ORBIS_CAMERA_CONFIG_TYPE4:
output_config0 = camera_config_types[config->configType - 1][0];
output_config1 = camera_config_types[config->configType - 1][1];
break;
case ORBIS_CAMERA_CONFIG_TYPE5:
if (g_firmware_version < Common::ElfInfo::FW_450) {
LOG_ERROR(Lib_Camera, "ORBIS_CAMERA_ERROR_UNKNOWN_CONFIG");
return ORBIS_CAMERA_ERROR_UNKNOWN_CONFIG;
}
output_config0 = camera_config_types[config->configType - 1][0];
output_config1 = camera_config_types[config->configType - 1][1];
break;
case ORBIS_CAMERA_CONFIG_EXTENTION:
output_config0 = config->configExtention[0];
output_config1 = config->configExtention[1];
break;
default:
LOG_ERROR(Lib_Camera, "Invalid config type {}", std::to_underlying(config->configType));
return ORBIS_CAMERA_ERROR_PARAM;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetConfigInternal(s32 handle, OrbisCameraConfig* config) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || config == nullptr || config->sizeThis != sizeof(OrbisCameraConfig)) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetContrast(s32 handle, OrbisCameraChannel channel, u32 contrast,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetDebugStop(u32 debug_stop_enable) {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
if (debug_stop_enable > 1) {
return ORBIS_CAMERA_ERROR_PARAM;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellation(s32 handle, OrbisCameraChannel channel,
u32 enable, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable > 1 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetDefectivePixelCancellationInternal(s32 handle,
OrbisCameraChannel channel,
u32 enable, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable > 2 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetExposureGain(s32 handle, OrbisCameraChannel channel,
OrbisCameraExposureGain* exposure_gain, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || exposure_gain != nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
if (EmulatorSettings.GetCameraId() == -1) {
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetForceActivate() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetGamma(s32 handle, OrbisCameraChannel channel, OrbisCameraGamma* gamma,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || gamma != nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetHue(s32 handle, OrbisCameraChannel channel, s32 hue, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetLensCorrection(s32 handle, OrbisCameraChannel channel, u32 enable,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable > 1 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetLensCorrectionInternal(s32 handle, OrbisCameraChannel channel,
u32 enable, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || enable > 2 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetProcessFocus() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetProcessFocusByHandle() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetRegister() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetSaturation(s32 handle, OrbisCameraChannel channel, u32 saturation,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetSharpness(s32 handle, OrbisCameraChannel channel, u32 sharpness,
void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (g_firmware_version >= Common::ElfInfo::FW_350 && sharpness > 10) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
s32 PS4_SYSV_ABI sceCameraSetTrackerMode() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetUacModeInternal() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetVideoSync(s32 handle, OrbisCameraVideoSyncParameter* video_sync) {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
if (handle < 1 || video_sync == nullptr ||
video_sync->sizeThis != sizeof(OrbisCameraVideoSyncParameter) ||
video_sync->videoSyncMode > 1 || video_sync->pModeOption != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetVideoSyncInternal(s32 handle,
OrbisCameraVideoSyncParameter* video_sync) {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraSetWhiteBalance(s32 handle, OrbisCameraChannel channel,
OrbisCameraWhiteBalance* white_balance, void* option) {
LOG_DEBUG(Lib_Camera, "called");
if (handle < 1 || channel > OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_BOTH ||
channel < OrbisCameraChannel::ORBIS_CAMERA_CHANNEL_0 || white_balance == nullptr ||
option != nullptr) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
} // namespace Libraries::Camera
@@ -0,0 +1,187 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
constexpr s32 ORBIS_CAMERA_MAX_DEVICE_NUM = 2;
enum OrbisCameraResolution {
ORBIS_CAMERA_RESOLUTION_1280X800 = 0x0,
ORBIS_CAMERA_RESOLUTION_640X400 = 0x1,
ORBIS_CAMERA_RESOLUTION_320X200 = 0x2,
ORBIS_CAMERA_RESOLUTION_160X100 = 0x3,
ORBIS_CAMERA_RESOLUTION_320X192 = 0x4,
ORBIS_CAMERA_RESOLUTION_SPECIFIED_WIDTH_HEIGHT,
ORBIS_CAMERA_RESOLUTION_UNKNOWN = 0xFF,
};
enum OrbisCameraFramerate {
ORBIS_CAMERA_FRAMERATE_UNKNOWN = 0,
ORBIS_CAMERA_FRAMERATE_7_5 = 7,
ORBIS_CAMERA_FRAMERATE_15 = 15,
ORBIS_CAMERA_FRAMERATE_30 = 30,
ORBIS_CAMERA_FRAMERATE_60 = 60,
ORBIS_CAMERA_FRAMERATE_120 = 120,
ORBIS_CAMERA_FRAMERATE_240 = 240,
};
enum OrbisCameraBaseFormat {
ORBIS_CAMERA_FORMAT_YUV422 = 0x0,
ORBIS_CAMERA_FORMAT_RAW16,
ORBIS_CAMERA_FORMAT_RAW8,
ORBIS_CAMERA_FORMAT_NO_USE = 0x10,
ORBIS_CAMERA_FORMAT_UNKNOWN = 0xFF,
};
enum OrbisCameraScaleFormat {
ORBIS_CAMERA_SCALE_FORMAT_YUV422 = 0x0,
ORBIS_CAMERA_SCALE_FORMAT_Y16 = 0x3,
ORBIS_CAMERA_SCALE_FORMAT_Y8,
ORBIS_CAMERA_SCALE_FORMAT_NO_USE = 0x10,
ORBIS_CAMERA_SCALE_FORMAT_UNKNOWN = 0xFF,
};
struct OrbisCameraFormat {
OrbisCameraBaseFormat formatLevel0;
OrbisCameraScaleFormat formatLevel1;
OrbisCameraScaleFormat formatLevel2;
OrbisCameraScaleFormat formatLevel3;
};
struct OrbisCameraConfigExtention {
OrbisCameraFormat format;
OrbisCameraResolution resolution;
OrbisCameraFramerate framerate;
u32 width;
u32 height;
u32 reserved1;
void* pBaseOption;
};
constexpr OrbisCameraConfigExtention camera_config_types[5][ORBIS_CAMERA_MAX_DEVICE_NUM]{
{
// type 1
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 2
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 3
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_Y8,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 4
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
},
{
// type 5
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_YUV422,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
{
.format =
{
.formatLevel0 = ORBIS_CAMERA_FORMAT_RAW16,
.formatLevel1 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel2 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
.formatLevel3 = ORBIS_CAMERA_SCALE_FORMAT_YUV422,
},
.framerate = ORBIS_CAMERA_FRAMERATE_60,
},
}};
enum OrbisCameraConfigType {
ORBIS_CAMERA_CONFIG_TYPE1 = 0x01,
ORBIS_CAMERA_CONFIG_TYPE2 = 0x02,
ORBIS_CAMERA_CONFIG_TYPE3 = 0x03,
ORBIS_CAMERA_CONFIG_TYPE4 = 0x04,
ORBIS_CAMERA_CONFIG_TYPE5 = 0x05,
ORBIS_CAMERA_CONFIG_EXTENTION = 0x10,
};
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/camera/camera.h"
namespace Libraries::Camera {
s32 PS4_SYSV_ABI sceCameraDeviceOpen() {
// Stubbed on real hardware
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDeviceConfig(s32 handle, OrbisCameraConfig* config) {
if (handle < 1 || config == nullptr || config->sizeThis != sizeof(OrbisCameraConfig)) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
memset(config, 0, sizeof(OrbisCameraConfig));
config->sizeThis = sizeof(OrbisCameraConfig);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDeviceConfigWithoutHandle(OrbisCameraConfig* config) {
if (config == nullptr || config->sizeThis != sizeof(OrbisCameraConfig)) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_OPEN;
}
memset(config, 0, sizeof(OrbisCameraConfig));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDeviceID() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDeviceIDWithoutOpen() {
LOG_ERROR(Lib_Camera, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCameraGetDeviceInfo(s32 reserved, OrbisCameraDeviceInfo* device_info) {
LOG_DEBUG(Lib_Camera, "called");
if (reserved != 0 || device_info == nullptr ||
device_info->sizeThis != sizeof(OrbisCameraDeviceInfo) || device_info->infoRevision != 1) {
return ORBIS_CAMERA_ERROR_PARAM;
}
if (!g_library_opened) {
return ORBIS_CAMERA_ERROR_NOT_INIT;
}
return ORBIS_CAMERA_ERROR_NOT_CONNECTED;
}
} // namespace Libraries::Camera
+3 -1
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/libraries/error_codes.h"
#pragma once
constexpr int ORBIS_CAMERA_ERROR_PARAM = 0x802E0000;
@@ -0,0 +1,204 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "camera_helpers.h"
namespace Libraries::Camera {
static inline u8 ClampU8(int v) {
return static_cast<u8>(v < 0 ? 0 : (v > 255 ? 255 : v));
}
static inline u16 ClampU16(int v) {
return static_cast<u16>(v < 0 ? 0 : (v > 65535 ? 65535 : v));
}
static inline u8 RgbaToLuma(const u8* px) {
const u8 b = px[0];
const u8 g = px[1];
const u8 r = px[2];
return ClampU8((77 * r + 150 * g + 29 * b) >> 8);
}
void ConvertRGBA8888ToRAW16(const u8* src, u16* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 4;
u16* outRow = dst + static_cast<u64>(y) * width;
const bool evenRow = (y & 1) == 0;
for (int x = 0; x < width; ++x) {
const u8* px = row + x * 4;
const u16 b = u16(px[1]) << 4;
const u16 g = u16(px[2]) << 4;
const u16 r = u16(px[3]) << 4;
const bool evenCol = (x & 1) == 0;
if (evenRow && evenCol)
outRow[x] = b;
else if (evenRow && !evenCol)
outRow[x] = g;
else if (!evenRow && evenCol)
outRow[x] = g;
else
outRow[x] = r;
}
}
}
void ConvertRGBA8888ToRAW8(const u8* src, u8* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 4;
u8* outRow = dst + static_cast<u64>(y) * width;
const bool evenRow = (y & 1) == 0;
for (int x = 0; x < width; ++x) {
const u8* px = row + x * 4;
const u8 b = px[1];
const u8 g = px[2];
const u8 r = px[3];
const bool evenCol = (x & 1) == 0;
if (evenRow && evenCol)
outRow[x] = b;
else if (evenRow && !evenCol)
outRow[x] = g;
else if (!evenRow && evenCol)
outRow[x] = g;
else
outRow[x] = r;
}
}
}
void ConvertRGBA8888ToY8(const u8* src, u8* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 4;
u8* outRow = dst + static_cast<u64>(y) * width;
for (int x = 0; x < width; ++x) {
outRow[x] = RgbaToLuma(row + x * 4);
}
}
}
void ConvertRGBA8888ToY16(const u8* src, u16* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 4;
u16* outRow = dst + static_cast<u64>(y) * width;
for (int x = 0; x < width; ++x) {
outRow[x] = static_cast<u16>(RgbaToLuma(row + x * 4)) << 2;
}
}
}
void ConvertYUY2ToY8(const u8* src, u8* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 2;
u8* outRow = dst + static_cast<u64>(y) * width;
for (int x = 0; x < width; x += 2) {
outRow[x] = row[x * 2 + 0]; // Y0
outRow[x + 1] = row[x * 2 + 2]; // Y1
}
}
}
void ConvertYUY2ToY16(const u8* src, u16* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 2;
u16* outRow = dst + static_cast<u64>(y) * width;
for (int x = 0; x < width; x += 2) {
outRow[x] = static_cast<u16>(row[x * 2 + 0]) << 2;
outRow[x + 1] = static_cast<u16>(row[x * 2 + 2]) << 2;
}
}
}
static inline void YuvToRgb(u8 y, u8 u, u8 v, u8& r, u8& g, u8& b) {
const int c = static_cast<int>(y) - 16;
const int d = static_cast<int>(u) - 128;
const int e = static_cast<int>(v) - 128;
r = ClampU8((298 * c + 409 * e + 128) >> 8);
g = ClampU8((298 * c - 100 * d - 208 * e + 128) >> 8);
b = ClampU8((298 * c + 516 * d + 128) >> 8);
}
void ConvertYUY2ToRAW8(const u8* src, u8* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 2;
u8* outRow = dst + static_cast<u64>(y) * width;
const bool evenRow = (y & 1) == 0;
for (int x = 0; x < width; x += 2) {
const u8 y0 = row[x * 2 + 0];
const u8 u = row[x * 2 + 1];
const u8 y1 = row[x * 2 + 2];
const u8 v = row[x * 2 + 3];
u8 r0, g0, b0;
u8 r1, g1, b1;
YuvToRgb(y0, u, v, r0, g0, b0);
YuvToRgb(y1, u, v, r1, g1, b1);
if (evenRow) {
outRow[x] = b0;
outRow[x + 1] = g1;
} else {
outRow[x] = g0;
outRow[x + 1] = r1;
}
}
}
}
void ConvertYUY2ToRAW16(const u8* src, u16* dst, int width, int height) {
for (int y = 0; y < height; ++y) {
const u8* row = src + static_cast<u64>(y) * width * 2;
u16* outRow = dst + static_cast<u64>(y) * width;
const bool evenRow = (y & 1) == 0;
for (int x = 0; x < width; x += 2) {
const u8 y0 = row[x * 2 + 0];
const u8 u = row[x * 2 + 1];
const u8 y1 = row[x * 2 + 2];
const u8 v = row[x * 2 + 3];
u8 r0, g0, b0;
u8 r1, g1, b1;
YuvToRgb(y0, u, v, r0, g0, b0);
YuvToRgb(y1, u, v, r1, g1, b1);
if (evenRow) {
outRow[x] = static_cast<u16>(b0) << 4;
outRow[x + 1] = static_cast<u16>(g1) << 4;
} else {
outRow[x] = static_cast<u16>(g0) << 4;
outRow[x + 1] = static_cast<u16>(r1) << 4;
}
}
}
}
s32 SizeOfBaseFormat(OrbisCameraBaseFormat f) {
switch (f) {
case ORBIS_CAMERA_FORMAT_YUV422:
return 2;
case ORBIS_CAMERA_FORMAT_RAW16:
return 2;
case ORBIS_CAMERA_FORMAT_RAW8:
return 1;
case ORBIS_CAMERA_FORMAT_NO_USE:
default:
return 0;
}
}
s32 SizeOfScaleFormat(OrbisCameraScaleFormat f) {
switch (f) {
case ORBIS_CAMERA_SCALE_FORMAT_YUV422:
return 2;
case ORBIS_CAMERA_SCALE_FORMAT_Y16:
return 2;
case ORBIS_CAMERA_SCALE_FORMAT_Y8:
return 1;
case ORBIS_CAMERA_SCALE_FORMAT_NO_USE:
default:
return 0;
}
}
} // namespace Libraries::Camera
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "camera.h"
namespace Libraries::Camera {
void ConvertRGBA8888ToY8(const u8* src, u8* dst, int width, int height);
void ConvertRGBA8888ToY16(const u8* src, u16* dst, int width, int height);
void ConvertRGBA8888ToRAW8(const u8* src, u8* dst, int width, int height);
void ConvertRGBA8888ToRAW16(const u8* src, u16* dst, int width, int height);
void ConvertYUY2ToY8(const u8* src, u8* dst, int width, int height);
void ConvertYUY2ToY16(const u8* src, u16* dst, int width, int height);
void ConvertYUY2ToRAW8(const u8* src, u8* dst, int width, int height);
void ConvertYUY2ToRAW16(const u8* src, u16* dst, int width, int height);
s32 SizeOfBaseFormat(OrbisCameraBaseFormat f);
s32 SizeOfScaleFormat(OrbisCameraScaleFormat f);
} // namespace Libraries::Camera
+1 -1
View File
@@ -151,7 +151,7 @@ int main(int argc, char* argv[]) {
// Initialize main log with default config
Common::Log::Setup("shadps4.log");
LOG_INFO(Debug, "Run: {}", fmt::join(std::span(argv, argc), ""));
LOG_INFO(Debug, "Run: {}", fmt::join(std::span(argv, argc), " "));
IPC::Instance().Init();