diff --git a/Makefile.common b/Makefile.common index 291741557a16..a3b34d36900d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -2693,7 +2693,10 @@ ifeq ($(HAVE_STATIC_VIDEO_FILTERS), 1) gfx/video_filters/upscale_240x160_320x240.o \ gfx/video_filters/upscale_mix_240x160_320x240.o \ gfx/video_filters/dedither.o \ - gfx/video_filters/pixel_art_aa.o + gfx/video_filters/pixel_art_aa.o \ + gfx/video_filters/crop_borders.o \ + gfx/video_filters/ntsc.o \ + gfx/video_filters/ntsc_crt.o endif ifeq ($(WANT_IOSUHAX), 1) diff --git a/audio/drivers/openal.c b/audio/drivers/openal.c index 69f8993c7633..ede8c40efc9e 100644 --- a/audio/drivers/openal.c +++ b/audio/drivers/openal.c @@ -134,7 +134,7 @@ static void *al_init(const char *device, unsigned rate, unsigned latency, if (string_is_equal(device, list->elems[i].data)) { RARCH_DBG("[OpenAL] Found device #%d: \"%s\".\n", i, list->elems[i].data); - idx_found = i; + idx_found = (int32_t)i; dev_id = strdup(list->elems[i].data); break; } @@ -144,7 +144,7 @@ static void *al_init(const char *device, unsigned rate, unsigned latency, if (idx_found == -1 && isdigit(device[0])) { - idx_found = strtoul(device, NULL, 0); + idx_found = (int32_t)strtoul(device, NULL, 0); RARCH_LOG("[OpenAL] Fallback, device index is a single number index instead: %d.\n", idx_found); if (idx_found != -1) @@ -190,7 +190,7 @@ static void *al_init(const char *device, unsigned rate, unsigned latency, _latency = latency * rate * 2 * sizeof(int16_t); } - al->num_buffers = _latency / (1000 * OPENAL_BUFSIZE); + al->num_buffers = (ALsizei)(_latency / (1000 * OPENAL_BUFSIZE)); if (al->num_buffers < 2) al->num_buffers = 2; @@ -263,7 +263,7 @@ static ssize_t al_write(void *data, const void *s, size_t len) if (!al_get_buffer(al, &buffer)) break; - alBufferData(buffer, al->format, buf, rc, al->rate); + alBufferData(buffer, al->format, buf, (ALsizei)rc, al->rate); alSourceQueueBuffers(al->source, 1, &buffer); _len += rc; diff --git a/cheevos/cheevos_rvz.c b/cheevos/cheevos_rvz.c index 4da8a77a9f11..7b867a6b5d74 100644 --- a/cheevos/cheevos_rvz.c +++ b/cheevos/cheevos_rvz.c @@ -855,7 +855,7 @@ static uint32_t rvz_get_group_decompression_size(rcheevos_rvz_file_t* rvz, /* This is a partition data group */ /* Calculate blocks per group based on chunk_size * chunk_size is in ISO space (with headers), convert to decrypted space */ - uint32_t adjusted_chunk_size = ((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE; + uint32_t adjusted_chunk_size = (uint32_t)(((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE); uint32_t blocks_per_group = adjusted_chunk_size / RVZ_WII_SECTOR_DATA_SIZE; uint32_t group_offset_in_partition = group_index - pdata->group_index; uint32_t total_blocks = pdata->number_of_sectors; @@ -2262,7 +2262,7 @@ static int rvz_calculate_partition_offsets_stateless( offsets->file_offset = (offsets->block_index * RVZ_WII_SECTOR_DATA_SIZE) + offsets->offset_in_block; /* Calculate effective chunk size (decrypted group size) */ - effective_chunk_size = ((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE; + effective_chunk_size = (uint32_t)(((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE); /* Calculate group index */ file_block = offsets->file_offset / effective_chunk_size; @@ -2307,7 +2307,7 @@ static size_t rvz_read_partition_data_stateless( uint64_t bytes_left_in_block; /* Calculate effective chunk size (decrypted group size) */ - effective_chunk_size = ((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE; + effective_chunk_size = (uint32_t)(((uint64_t)chunk_size * RVZ_WII_SECTOR_DATA_SIZE) / RVZ_WII_SECTOR_SIZE); /* Get the decompressed chunk data */ chunk_data = rvz_get_chunk(handle, offsets->group_index, &chunk_data_size); diff --git a/configuration.c b/configuration.c index 11128b1abaae..ac032214e89d 100644 --- a/configuration.c +++ b/configuration.c @@ -4653,14 +4653,16 @@ static bool config_load_file(global_t *global, if (!(bool)RHMAP_HAS_STR(conf->entries_map, "user_language")) msg_hash_set_uint(MSG_HASH_USER_LANGUAGE, frontend_driver_get_user_language()); - if (frontend_driver_has_gamemode() && - !frontend_driver_set_gamemode(settings->bools.gamemode_enable) && - settings->bools.gamemode_enable) - { - RARCH_WARN("[Config] GameMode unsupported - disabling...\n"); - configuration_set_bool(settings, - settings->bools.gamemode_enable, false); - } + /* If GameMode is enabled in the config but libgamemode is not + * available, warn once. Do NOT clear the setting: that would + * silently overwrite the user's preference in retroarch.cfg, so + * a transient failure (sandbox path issue, library not yet + * installed, etc.) would force them to re-enable it manually on + * every subsequent launch. The probe is latched inside the + * frontend driver, so it will not be retried this session. */ + if ( settings->bools.gamemode_enable + && frontend_driver_has_gamemode()) + frontend_driver_set_gamemode(true); /* If this is the first run of an existing installation * after the independent favourites playlist size limit was @@ -5436,7 +5438,7 @@ void input_config_get_prefix(char *s, char len, char user, bool meta) */ static void input_config_save_keybinds_user(config_file_t *conf, unsigned user) { - size_t i = 0; + unsigned i; for (i = 0; input_config_bind_map_get_valid(i); i++) { char key[64]; @@ -5482,7 +5484,7 @@ static void input_config_save_keybinds_user_override(config_file_t *conf, unsigned user, unsigned bind_id, const struct retro_keybind *override_bind) { - size_t i = bind_id; + unsigned i = bind_id; if (input_config_bind_map_get_valid(i)) { @@ -5533,7 +5535,7 @@ static void input_config_save_keybinds_user_override(config_file_t *conf, static void input_config_save_keybinds_user_minimal(config_file_t *conf, unsigned user, const retro_keybind_set default_binds) { - size_t i = 0; + unsigned i; for (i = 0; input_config_bind_map_get_valid(i); i++) { char key[64]; diff --git a/cores/libretro-ffmpeg/ffmpeg_core.c b/cores/libretro-ffmpeg/ffmpeg_core.c index 25aa559143b3..eb46f116355d 100644 --- a/cores/libretro-ffmpeg/ffmpeg_core.c +++ b/cores/libretro-ffmpeg/ffmpeg_core.c @@ -1918,7 +1918,7 @@ static void check_variables(bool firststart) fft_ms_var.key = "ffmpeg_fft_multisample"; if (CORE_PREFIX(environ_cb)(RETRO_ENVIRONMENT_GET_VARIABLE, &fft_ms_var) && fft_ms_var.value) - FFT_MULTISAMPLE_STR = strtoul(fft_ms_var.value, NULL, 0); + FFT_MULTISAMPLE_STR = (unsigned)strtoul(fft_ms_var.value, NULL, 0); #endif color_var.key = "ffmpeg_color_space"; @@ -2502,7 +2502,7 @@ void CORE_PREFIX(retro_run)(void) #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES3) else if (FFT_STR) { - unsigned fft_frames = to_read_frames; + unsigned fft_frames = (unsigned)to_read_frames; const int16_t *buffer = audio_buffer; while (fft_frames) @@ -2518,7 +2518,7 @@ void CORE_PREFIX(retro_run)(void) buffer += to_read * 2; fft_frames -= to_read; } - hwfft_render(FFT_STR, HW_RENDER_STR.get_current_framebuffer(), FFT_WIDTH_STR, FFT_HEIGHT_STR); + hwfft_render(FFT_STR, (GLuint)HW_RENDER_STR.get_current_framebuffer(), FFT_WIDTH_STR, FFT_HEIGHT_STR); CORE_PREFIX(video_cb)(RETRO_HW_FRAME_BUFFER_VALID, FFT_WIDTH_STR, FFT_HEIGHT_STR, FFT_WIDTH_STR * sizeof(uint32_t)); } diff --git a/deps/SPIRV-Cross/spirv_cross_parsed_ir.cpp b/deps/SPIRV-Cross/spirv_cross_parsed_ir.cpp index 8d1acf69f973..32059098a75a 100644 --- a/deps/SPIRV-Cross/spirv_cross_parsed_ir.cpp +++ b/deps/SPIRV-Cross/spirv_cross_parsed_ir.cpp @@ -1005,8 +1005,9 @@ ParsedIR::LoopLock::LoopLock(uint32_t *lock_) } ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT + : lock(other.lock) { - *this = std::move(other); + other.lock = nullptr; } ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT diff --git a/deps/glslang/glslang/glslang/MachineIndependent/SymbolTable.cpp b/deps/glslang/glslang/glslang/MachineIndependent/SymbolTable.cpp index db46e1075d7b..23e2bd7d1709 100644 --- a/deps/glslang/glslang/glslang/MachineIndependent/SymbolTable.cpp +++ b/deps/glslang/glslang/glslang/MachineIndependent/SymbolTable.cpp @@ -308,7 +308,7 @@ TVariable* TVariable::clone() const TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf) { for (unsigned int i = 0; i < copyOf.parameters.size(); ++i) { - TParameter param; + TParameter param = { nullptr, nullptr, nullptr }; parameters.push_back(param); parameters.back().copyParam(copyOf.parameters[i]); } diff --git a/deps/mbedtls/net_sockets.c b/deps/mbedtls/net_sockets.c index a05b0c26dfd6..2c9964e42881 100644 --- a/deps/mbedtls/net_sockets.c +++ b/deps/mbedtls/net_sockets.c @@ -306,7 +306,8 @@ int mbedtls_net_accept( mbedtls_net_context *bind_ctx, struct sockaddr_storage client_addr; #if defined(__socklen_t_defined) || defined(_SOCKLEN_T) || \ - defined(_SOCKLEN_T_DECLARED) || defined(__DEFINED_socklen_t) + defined(_SOCKLEN_T_DECLARED) || defined(__DEFINED_socklen_t) || \ + defined(socklen_t) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) socklen_t n = (socklen_t) sizeof( client_addr ); socklen_t type_len = (socklen_t) sizeof( type ); #else diff --git a/deps/rcheevos/src/rapi/rc_api_info.c b/deps/rcheevos/src/rapi/rc_api_info.c index 5ab9709287bd..a1fa7e5d5323 100644 --- a/deps/rcheevos/src/rapi/rc_api_info.c +++ b/deps/rcheevos/src/rapi/rc_api_info.c @@ -383,7 +383,7 @@ int rc_api_process_fetch_games_list_server_response(rc_api_fetch_games_list_resp entry = response->entries; while (rc_json_get_next_object_field(&iterator, &field)) { - entry->id = strtol(field.name, &end, 10); + entry->id = (uint32_t)strtol(field.name, &end, 10); field.name = ""; if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, "")) diff --git a/deps/rcheevos/src/rapi/rc_api_user.c b/deps/rcheevos/src/rapi/rc_api_user.c index 323756cfd155..6b61611773f0 100644 --- a/deps/rcheevos/src/rapi/rc_api_user.c +++ b/deps/rcheevos/src/rapi/rc_api_user.c @@ -457,7 +457,7 @@ int rc_api_process_fetch_all_user_progress_server_response(rc_api_fetch_all_user entry = response->entries; while (rc_json_get_next_object_field(&iterator, &field)) { - entry->game_id = strtol(field.name, &end, 10); + entry->game_id = (uint32_t)strtol(field.name, &end, 10); field.name = ""; if (!rc_json_get_required_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), diff --git a/deps/rcheevos/src/rcheevos/condition.c b/deps/rcheevos/src/rcheevos/condition.c index ff6da02bf835..fcb7788a817b 100644 --- a/deps/rcheevos/src/rcheevos/condition.c +++ b/deps/rcheevos/src/rcheevos/condition.c @@ -166,7 +166,7 @@ static int rc_parse_operator(const char** memaddr) { void rc_condition_convert_to_operand(const rc_condition_t* condition, rc_operand_t* operand, rc_parse_state_t* parse) { if (condition->oper == RC_OPERATOR_NONE) { if (operand != &condition->operand1) - memcpy(operand, &condition->operand1, sizeof(*operand)); + *operand = condition->operand1; } else { uint8_t new_size = RC_MEMSIZE_32_BITS; diff --git a/deps/stb/stb_vorbis.h b/deps/stb/stb_vorbis.h index f53f1dd47b01..bce979efbb3f 100644 --- a/deps/stb/stb_vorbis.h +++ b/deps/stb/stb_vorbis.h @@ -3702,12 +3702,12 @@ void stb_vorbis_seek_start(stb_vorbis *f) unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { - unsigned int restore_offset, previous_safe; - unsigned int end, last_page_loc; + uint32_t restore_offset, previous_safe; + uint32_t end, last_page_loc; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { - unsigned int last; + uint32_t last; uint32_t lo,hi; char header[6]; diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c index 0d51cecda5af..9c640b93150d 100644 --- a/frontend/drivers/platform_unix.c +++ b/frontend/drivers/platform_unix.c @@ -2186,8 +2186,20 @@ static void android_app_destroy(struct android_app *android_app) static bool frontend_unix_set_gamemode(bool on) { #ifdef FERAL_GAMEMODE - int gamemode_status = gamemode_query_status(); - bool gamemode_active = (gamemode_status == 2); + /* Once gamemode_query_status() reports failure (typically because + * libgamemode.so is not installed), there is no point repeatedly + * re-probing on every config load or menu toggle - the result will + * not change for the lifetime of the process, and each probe emits + * a warning. Latch the unavailable state and short-circuit. */ + static bool gamemode_unavailable = false; + int gamemode_status; + bool gamemode_active; + + if (gamemode_unavailable) + return false; + + gamemode_status = gamemode_query_status(); + gamemode_active = (gamemode_status == 2); if (gamemode_status < 0) { @@ -2196,6 +2208,7 @@ static bool frontend_unix_set_gamemode(bool on) "https://github.com/FeralInteractive/gamemode needs to be installed.\n", gamemode_error_string()); + gamemode_unavailable = true; return false; } diff --git a/gfx/display_servers/dispserv_apple.m b/gfx/display_servers/dispserv_apple.m index 8a6511c84981..8d0f239708da 100644 --- a/gfx/display_servers/dispserv_apple.m +++ b/gfx/display_servers/dispserv_apple.m @@ -556,9 +556,9 @@ static enum rotation apple_display_server_get_screen_orientation(void *data) && settings->floats.video_refresh_rate >= 10.0f && settings->floats.video_refresh_rate <= 250.0f) { - float hz = settings->floats.video_refresh_rate; - CocoaView *view = [CocoaView get]; #if defined(IOS) + float hz = settings->floats.video_refresh_rate; + CocoaView *view = [CocoaView get]; if (view && view.displayLink) { RARCH_DBG("[Video] Setting initial refresh rate to %.3f Hz\n", hz); @@ -571,6 +571,8 @@ static enum rotation apple_display_server_get_screen_orientation(void *data) view.displayLink.preferredFramesPerSecond = hz; } #elif defined(OSX) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 + float hz = settings->floats.video_refresh_rate; + CocoaView *view = [CocoaView get]; if (view) { if (@available(macOS 14, *)) diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index b2c2d348446c..2257535a2ea6 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -6117,7 +6117,6 @@ static bool d3d12_gpu_hdr_readback_to_bgr24( d3d12_texture_t sdr_rt = { 0 }; D3D12Resource readback = NULL; D3D12Resource back_buffer; - D3D12_RESOURCE_DESC sdr_desc; D3D12_HEAP_PROPERTIES heap_props; D3D12_RESOURCE_DESC buf_desc; D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 7ba05c1ba25e..24f795960dc1 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -437,7 +437,7 @@ - (void)setRotation:(unsigned)rotation; { \ NSObject * __y = y; \ if (x != nil) { \ - NSObject * __foo = (__bridge_transfer NSObject *)(__bridge void *)(x); \ + __attribute__((unused)) NSObject * __foo = (__bridge_transfer NSObject *)(__bridge void *)(x); \ __foo = nil; \ x = (__bridge __typeof__(x))nil; \ } \ @@ -561,19 +561,17 @@ static bool metal_display_supports_edr(void) if (@available(macOS 10.15, *)) { NSScreen *screen = [NSScreen mainScreen]; - if (!screen) - return false; /* Potential, not current: the value reflects what the display *could* * produce if EDR were enabled, not what's being used right now. * That's the right signal for "is HDR an available mode". SDR-only * displays return exactly 1.0. */ - return screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0; + if (screen) + return screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0; } - return false; #elif defined(HAVE_COCOATOUCH) /* TARGET_OS_TV / TARGET_OS_IOS are always defined to 0 or 1, not * just present — have to test the value, not defined-ness. */ -# if TARGET_OS_TV +#if TARGET_OS_TV /* tvOS: no NSScreen/UIScreen EDR API parallel to macOS. The device * itself (Apple TV 4K) advertises HDR capability via AVDisplayCriteria * but that's AVFoundation, not a layer we can plumb into here without @@ -582,20 +580,16 @@ static bool metal_display_supports_edr(void) * is only shipping on HDR-capable hardware (Apple TV 4K). */ if (@available(tvOS 16.0, *)) return true; - return false; -# else +#else if (@available(iOS 16.0, *)) { UIScreen *screen = [UIScreen mainScreen]; - if (!screen) - return false; - return screen.potentialEDRHeadroom > 1.0; + if (screen) + return screen.potentialEDRHeadroom > 1.0; } - return false; -# endif -#else - return false; #endif +#endif + return false; } #endif /* METAL_HDR_AVAILABLE */ @@ -1369,7 +1363,7 @@ - (bool)_initHDRPipelines ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; } - id s = [_device newRenderPipelineStateWithDescriptor:psd error:&e]; + id s = [self->_device newRenderPipelineStateWithDescriptor:psd error:&e]; if (e) { RARCH_ERR("[Metal] HDR %s pipeline: %s.\n", @@ -4958,7 +4952,7 @@ - (BOOL)setShaderFromPath:(NSString *)path @try { - size_t i; + unsigned i; texture_t *source = NULL; if (!video_shader_load_preset_into_shader(path.UTF8String, shader)) return NO; @@ -5602,7 +5596,7 @@ static bool metal_suppress_screensaver(void *data, bool disable) } static bool metal_set_shader(void *data, - enum rarch_shader_type type, const char *path) + enum rarch_shader_type type, const char *path) { #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) MetalDriver *md = (__bridge MetalDriver *)data; @@ -5630,7 +5624,7 @@ static bool metal_set_shader(void *data, static void metal_free(void *data) { - MetalDriver *md = (__bridge_transfer MetalDriver *)data; + __attribute__((unused)) MetalDriver *md = (__bridge_transfer MetalDriver *)data; metal_ctx_data = NULL; md = nil; } @@ -5748,7 +5742,7 @@ static void metal_unload_texture(void *data, * buffer is still using it -- the Metal runtime refcounts * resources across CPU and GPU. No cross-thread * serialisation needed for unload. */ - Texture *t = (__bridge_transfer Texture *)(void *)handle; + __attribute__((unused)) Texture *t = (__bridge_transfer Texture *)(void *)handle; t = nil; } diff --git a/gfx/drivers/sdl2_gfx.c b/gfx/drivers/sdl2_gfx.c index 52f89c89d7a6..89521c8c3ed7 100644 --- a/gfx/drivers/sdl2_gfx.c +++ b/gfx/drivers/sdl2_gfx.c @@ -984,7 +984,7 @@ static bool sdl2_gfx_set_shader(void *data, return false; } -#ifdef HAVE_GFX_WIDGETS +#if defined(HAVE_GFX_WIDGETS) && SDL_VERSION_ATLEAST(2, 0, 18) static bool sdl2_gfx_widgets_enabled(void *data) { (void)data; return true; } #endif diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 03ecc6eea162..70bd211d6d45 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -6234,7 +6234,7 @@ static bool vulkan_frame(void *data, const void *frame, uint64_t frame_count, unsigned pitch, const char *msg, video_frame_info_t *video_info) { - int i, j, k; + int j, k; VkSubmitInfo submit_info; VkClearValue clear_color; VkRenderPassBeginInfo rp_info; diff --git a/gfx/drivers_shader/slang_cache.cpp b/gfx/drivers_shader/slang_cache.cpp index 79aa5c257f42..f6112f0ee321 100644 --- a/gfx/drivers_shader/slang_cache.cpp +++ b/gfx/drivers_shader/slang_cache.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -83,12 +84,15 @@ static bool spirv_cache_get_filename(const char *hash, */ static bool spirv_cache_write_string(RFILE *file, const std::string &str) { - uint32_t len = str.length(); + uint32_t _len; + if (str.length() > UINT32_MAX) + return false; + _len = (uint32_t)str.length(); - if (filestream_write(file, &len, sizeof(uint32_t)) != sizeof(uint32_t)) + if (filestream_write(file, &_len, sizeof(uint32_t)) != sizeof(uint32_t)) return false; - if (len > 0 && filestream_write(file, str.c_str(), len) != len) + if (_len > 0 && filestream_write(file, str.c_str(), _len) != _len) return false; return true; @@ -103,29 +107,30 @@ static bool spirv_cache_write_string(RFILE *file, const std::string &str) */ static bool spirv_cache_read_string(RFILE *file, std::string &str_out) { - uint32_t len; + uint32_t _len; + char *buf; - if (filestream_read(file, &len, sizeof(uint32_t)) != sizeof(uint32_t)) + if (filestream_read(file, &_len, sizeof(uint32_t)) != sizeof(uint32_t)) return false; - if (len == 0) + if (_len == 0) { str_out.clear(); return true; } /* Allocate and read string */ - char *buf = new char[len + 1]; + buf = new char[_len + 1]; if (!buf) return false; - if (filestream_read(file, buf, len) != len) + if (filestream_read(file, buf, _len) != _len) { delete[] buf; return false; } - buf[len] = '\0'; + buf[_len] = '\0'; str_out = buf; delete[] buf; @@ -134,18 +139,18 @@ static bool spirv_cache_read_string(RFILE *file, std::string &str_out) extern "C" { -bool spirv_cache_compute_hash(const char *vertex_source, const char *fragment_source, - char *hash_out) +bool spirv_cache_compute_hash(const char *vertex_source, const char *fragment_source, char *hash_out) { + uint8_t *combined; + size_t vertex_len, fragment_len, total_len; if (!vertex_source || !fragment_source || !hash_out) return false; /* Build combined hash input: vertex + "|" + fragment */ - size_t vertex_len = strlen(vertex_source); - size_t fragment_len = strlen(fragment_source); - size_t total_len = vertex_len + 1 + fragment_len; /* 1 for "|" separator */ - - uint8_t *combined = new uint8_t[total_len]; + vertex_len = strlen(vertex_source); + fragment_len = strlen(fragment_source); + total_len = vertex_len + 1 + fragment_len; /* 1 for "|" separator */ + combined = new uint8_t[total_len]; if (!combined) return false; @@ -163,8 +168,8 @@ bool spirv_cache_compute_hash(const char *vertex_source, const char *fragment_so bool spirv_cache_load(const char *hash, struct glslang_output *output) { RFILE *file; - char cache_file[PATH_MAX_LENGTH]; uint8_t version; + char cache_file[PATH_MAX_LENGTH]; uint32_t vertex_size, fragment_size, param_count, i; uint16_t rt_format; @@ -259,10 +264,10 @@ bool spirv_cache_load(const char *hash, struct glslang_output *output) bool spirv_cache_save(const char *hash, const struct glslang_output *output) { RFILE *file; + uint16_t rt_format; char cache_file[PATH_MAX_LENGTH]; uint8_t version = SPIRV_CACHE_VERSION; uint32_t vertex_size, fragment_size, param_count, i; - uint16_t rt_format; if (!hash || !output) return false; @@ -284,7 +289,9 @@ bool spirv_cache_save(const char *hash, const struct glslang_output *output) goto error; /* Write vertex SPIR-V */ - vertex_size = output->vertex.size(); + if (output->vertex.size() > UINT32_MAX) + goto error; + vertex_size = (uint32_t)output->vertex.size(); if (filestream_write(file, &vertex_size, sizeof(uint32_t)) != sizeof(uint32_t)) goto error; if (vertex_size > 0) @@ -294,7 +301,9 @@ bool spirv_cache_save(const char *hash, const struct glslang_output *output) } /* Write fragment SPIR-V */ - fragment_size = output->fragment.size(); + if (output->fragment.size() > UINT32_MAX) + goto error; + fragment_size = (uint32_t)output->fragment.size(); if (filestream_write(file, &fragment_size, sizeof(uint32_t)) != sizeof(uint32_t)) goto error; if (fragment_size > 0) @@ -304,7 +313,9 @@ bool spirv_cache_save(const char *hash, const struct glslang_output *output) } /* Write parameters */ - param_count = output->meta.parameters.size(); + if (output->meta.parameters.size() > UINT32_MAX) + goto error; + param_count = (uint32_t)output->meta.parameters.size(); if (filestream_write(file, ¶m_count, sizeof(uint32_t)) != sizeof(uint32_t)) goto error; diff --git a/gfx/include/dxsdk/dxsdk_sal_compat.h b/gfx/include/dxsdk/dxsdk_sal_compat.h index d919a0cb6ef6..72e01f80cd8b 100644 --- a/gfx/include/dxsdk/dxsdk_sal_compat.h +++ b/gfx/include/dxsdk/dxsdk_sal_compat.h @@ -374,8 +374,24 @@ #endif /* --- SAL 2 misc ------------------------------------------------------ */ +/* _Inexpressible_(s) marks an annotation size expression that the + * static analyser cannot easily evaluate; on a real SAL implementation + * the SDK consumes `s` inside its annotation metadata. When the SDK's + * is present and active (modern MSVC), _Inexpressible_ is + * defined elsewhere only under _PREFAST_ -- so for normal compilation + * the SDK leaves it undefined and falls back to whatever the + * environment provides. If we stub it to empty, the argument is + * discarded, and any enclosing function-like macro receives an empty + * argument list -- triggering C4003 on MSVC for sites like + * _In_reads_opt_(_Inexpressible_(p->q != 0)) + * because the SDK's _In_reads_opt_(size) ultimately invokes + * _Pre_opt_count_(size), which does not accept zero arguments. + * + * Defining the stub as `(s)` (the parenthesised expression) keeps the + * token stream non-empty for any enclosing macro and parses as a valid + * size expression. */ #ifndef _Inexpressible_ -#define _Inexpressible_(s) +#define _Inexpressible_(s) (s) #endif #ifndef _Use_decl_annotations_ #define _Use_decl_annotations_ diff --git a/gfx/video_filters/Makefile b/gfx/video_filters/Makefile index 48dd802eb5f4..f8cc455c3157 100644 --- a/gfx/video_filters/Makefile +++ b/gfx/video_filters/Makefile @@ -132,7 +132,10 @@ objects += blargg_ntsc_snes.$(DYLIB) \ upscale_256x_320x240.$(DYLIB) \ picoscale_256x_320x240.$(DYLIB) \ upscale_240x160_320x240.$(DYLIB) \ - upscale_mix_240x160_320x240.$(DYLIB) + upscale_mix_240x160_320x240.$(DYLIB) \ + crop_borders.$(DYLIB) \ + ntsc.$(DYLIB) \ + ntsc_crt.$(DYLIB) all: build; diff --git a/gfx/video_filters/crop_borders.c b/gfx/video_filters/crop_borders.c new file mode 100644 index 000000000000..7c7e5292c391 --- /dev/null +++ b/gfx/video_filters/crop_borders.c @@ -0,0 +1,193 @@ +#include "softfilter.h" +#include +#include + +#ifdef RARCH_INTERNAL +#define softfilter_get_implementation crop_borders_get_implementation +#define softfilter_thread_data crop_borders_softfilter_thread_data +#define filter_data crop_borders_filter_data +#endif + +struct softfilter_thread_data +{ + void *out_data; + const void *in_data; + size_t out_pitch; + size_t in_pitch; + unsigned width; + unsigned height; +}; + +struct filter_data +{ + struct softfilter_thread_data *workers; + unsigned in_fmt; + float crop_x; + float crop_y; +}; + +static unsigned crop_borders_generic_input_fmts(void) +{ + return SOFTFILTER_FMT_RGB565 | SOFTFILTER_FMT_XRGB8888; +} + +static unsigned crop_borders_generic_output_fmts(unsigned input_fmts) +{ + return input_fmts; +} + +static unsigned crop_borders_generic_threads(void *data) +{ + return 1; +} + +static void crop_borders_initialize(struct filter_data *filt, + const struct softfilter_config *config, + void *userdata) +{ + /* RetroArch wull look at .filt for: crop_borders_crop_x */ + config->get_float(userdata, "crop_x", &filt->crop_x, 0.0f); + config->get_float(userdata, "crop_y", &filt->crop_y, 0.0f); +} + +static void *crop_borders_generic_create(const struct softfilter_config *config, + unsigned in_fmt, unsigned out_fmt, + unsigned max_width, unsigned max_height, + unsigned threads, softfilter_simd_mask_t simd, void *userdata) +{ + struct filter_data *filt = (struct filter_data*)calloc(1, sizeof(*filt)); + if (!filt) return NULL; + + if (!(filt->workers = (struct softfilter_thread_data*)calloc(1, sizeof(struct softfilter_thread_data)))) + { + free(filt); + return NULL; + } + + filt->in_fmt = in_fmt; + + crop_borders_initialize(filt, config, userdata); + + return filt; +} + +static void crop_borders_generic_output(void *data, + unsigned *out_width, unsigned *out_height, + unsigned width, unsigned height) +{ + *out_width = width; + *out_height = height; +} + +static void crop_borders_generic_destroy(void *data) +{ + struct filter_data *filt = (struct filter_data*)data; + if (!filt) return; + free(filt->workers); + free(filt); +} + +/* Rendering Logic with Float Coordinates */ +static void crop_borders_work_cb_xrgb8888(void *data, void *thread_data) +{ + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data; + const uint32_t *input = (const uint32_t*)thr->in_data; + uint32_t *output = (uint32_t*)thr->out_data; + unsigned in_stride = (unsigned)(thr->in_pitch >> 2); + unsigned out_stride = (unsigned)(thr->out_pitch >> 2); + + float visible_w = (float)thr->width - (filt->crop_x * 2.0f); + float visible_h = (float)thr->height - (filt->crop_y * 2.0f); + + if (visible_w <= 0.0f || visible_h <= 0.0f) return; + + float step_x = visible_w / (float)thr->width; + float step_y = visible_h / (float)thr->height; + + for (unsigned y = 0; y < thr->height; y++) + { + unsigned i_y = (unsigned)(filt->crop_y + (y * step_y)); + for (unsigned x = 0; x < thr->width; x++) + { + unsigned i_x = (unsigned)(filt->crop_x + (x * step_x)); + output[y * out_stride + x] = input[i_y * in_stride + i_x]; + } + } +} + +static void crop_borders_work_cb_rgb565(void *data, void *thread_data) +{ + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data; + const uint16_t *input = (const uint16_t*)thr->in_data; + uint16_t *output = (uint16_t*)thr->out_data; + unsigned in_stride = (unsigned)(thr->in_pitch >> 1); + unsigned out_stride = (unsigned)(thr->out_pitch >> 1); + + float visible_w = (float)thr->width - (filt->crop_x * 2.0f); + float visible_h = (float)thr->height - (filt->crop_y * 2.0f); + + if (visible_w <= 0.0f || visible_h <= 0.0f) return; + + float step_x = visible_w / (float)thr->width; + float step_y = visible_h / (float)thr->height; + + for (unsigned y = 0; y < thr->height; y++) + { + unsigned i_y = (unsigned)(filt->crop_y + (y * step_y)); + for (unsigned x = 0; x < thr->width; x++) + { + unsigned i_x = (unsigned)(filt->crop_x + (x * step_x)); + output[y * out_stride + x] = input[i_y * in_stride + i_x]; + } + } +} + +static void crop_borders_generic_packets(void *data, + struct softfilter_work_packet *packets, + void *output, size_t output_stride, + const void *input, unsigned width, unsigned height, size_t input_stride) +{ + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = &filt->workers[0]; + + thr->out_data = output; + thr->in_data = input; + thr->out_pitch = output_stride; + thr->in_pitch = input_stride; + thr->width = width; + thr->height = height; + + if (filt->in_fmt == SOFTFILTER_FMT_RGB565) + packets[0].work = crop_borders_work_cb_rgb565; + else + packets[0].work = crop_borders_work_cb_xrgb8888; + + packets[0].thread_data = thr; +} + +static const struct softfilter_implementation crop_borders_generic = { + crop_borders_generic_input_fmts, + crop_borders_generic_output_fmts, + crop_borders_generic_create, + crop_borders_generic_destroy, + crop_borders_generic_threads, + crop_borders_generic_output, + crop_borders_generic_packets, + SOFTFILTER_API_VERSION, + "Crop Borders", + "crop_borders", +}; + +const struct softfilter_implementation *softfilter_get_implementation(softfilter_simd_mask_t simd) +{ + (void)simd; + return &crop_borders_generic; +} + +#ifdef RARCH_INTERNAL +#undef softfilter_get_implementation +#undef softfilter_thread_data +#undef filter_data +#endif diff --git a/gfx/video_filters/crop_borders.filt b/gfx/video_filters/crop_borders.filt new file mode 100644 index 000000000000..6322c33fa31e --- /dev/null +++ b/gfx/video_filters/crop_borders.filt @@ -0,0 +1,4 @@ +filter = crop_borders + +crop_borders_crop_x = 8.0 +crop_borders_crop_y = 8.0 \ No newline at end of file diff --git a/gfx/video_filters/ntsc.c b/gfx/video_filters/ntsc.c new file mode 100644 index 000000000000..5a90c2d6911b --- /dev/null +++ b/gfx/video_filters/ntsc.c @@ -0,0 +1,224 @@ +#include "softfilter.h" +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef RARCH_INTERNAL +#define softfilter_get_implementation ntsc_get_implementation +#define softfilter_thread_data ntsc_softfilter_thread_data +#define filter_data ntsc_filter_data +#endif + +#define NTSC_SCALE_X 2 +#define NTSC_SCALE_Y 1 +#define NTSC_MAX_WIDTH 1024 +#define PHASE_MAX 16 // Max phases for Atari 2600 + +typedef struct { int Y, I, Q; } yiq_t; + +struct softfilter_thread_data { + void *out_data; + const void *in_data; + size_t out_pitch; + size_t in_pitch; + unsigned width; + unsigned height; + int first; +}; + +struct filter_data { + unsigned threads; + struct softfilter_thread_data *workers; + unsigned in_fmt; + int hue_cos, hue_sin; + int saturation, sharpness, artifacts; + int huesat_identity, pal_mode, atari_mode, c64_mode; + int phase_count, nes_frame_count; + int lut_sin[PHASE_MAX], lut_cos[PHASE_MAX]; +}; + +static inline void rgb_to_yiq(int r, int g, int b, int *Y, int *I, int *Q) { + *Y = ( 77*r + 150*g + 29*b) >> 8; + *I = ( 157*r - 132*g - 26*b) >> 8; + *Q = ( -38*r - 74*g + 112*b) >> 8; +} + +static inline void yiq_to_rgb(int Y, int I, int Q, int *r, int *g, int *b) { + int rv = Y + ((292*I) >> 8); + int gv = Y - ((149*I) >> 8) - ((101*Q) >> 8); + int bv = Y + ((520*Q) >> 8); + *r = (rv < 0) ? 0 : (rv > 255 ? 255 : rv); + *g = (gv < 0) ? 0 : (gv > 255 ? 255 : gv); + *b = (bv < 0) ? 0 : (bv > 255 ? 255 : bv); +} + +static void ntsc_process_line(const struct filter_data *filt, + const struct softfilter_thread_data *thr, unsigned y, + int *cbuf, int *lineI, int *lineQ, int *lineY, yiq_t *yiq_cache, + void *dst_ptr) { + unsigned width = thr->width; + unsigned ow = width * 2; + int phases = filt->phase_count; + + // C64 uses 2-phase steps per output pixel if sampled at 8 phases + int phase_step = (filt->c64_mode) ? 2 : 4; + int frame_offset = (filt->nes_frame_count % 2) * (phases / 2); + int line_mult = (filt->pal_mode) ? (phase_step + 1) : phase_step; + int line_phase = (frame_offset + ((thr->first + (int)y) * line_mult)) % phases; + + for (unsigned x = 0; x < width; x++) { + int r, g, b, Y, I, Q; + if (filt->in_fmt == SOFTFILTER_FMT_RGB565) { + uint16_t p = ((uint16_t*)thr->in_data)[y * (thr->in_pitch/2) + x]; + r = ((p >> 11) & 0x1f) << 3; g = ((p >> 5) & 0x3f) << 2; b = (p & 0x1f) << 3; + } else { + uint32_t p = ((uint32_t*)thr->in_data)[y * (thr->in_pitch/4) + x]; + r = (p >> 16) & 0xFF; g = (p >> 8) & 0xFF; b = p & 0xFF; + } + rgb_to_yiq(r, g, b, &Y, &I, &Q); + yiq_cache[x].Y = Y; yiq_cache[x].I = I; yiq_cache[x].Q = Q; + + for (int p_idx = 0; p_idx < 2; p_idx++) { + int ph = (line_phase + (x * phase_step * 2) + (p_idx * phase_step)) % phases; + cbuf[x * 2 + p_idx] = Y + ((I * filt->lut_cos[ph] + Q * filt->lut_sin[ph]) >> 8); + } + } + + for (unsigned x = 0; x < ow; x++) { + int accI = 0, accQ = 0; + int taps = (filt->atari_mode) ? 8 : (filt->c64_mode ? 4 : 6); + + for (int t = -taps; t < taps; t++) { + int idx = (x + t < 0) ? 0 : (x + t >= (int)ow ? (int)ow - 1 : x + t); + int ph = (line_phase + (x * phase_step) + (t * phase_step)) % phases; + accI += cbuf[idx] * filt->lut_cos[ph]; + accQ += cbuf[idx] * filt->lut_sin[ph]; + } + lineI[x] = accI / (taps * 256); lineQ[x] = accQ / (taps * 256); + + int i_m2 = (x > 1) ? (int)x - 2 : 0, i_m1 = (x > 0) ? (int)x - 1 : 0; + int i_p1 = (x < ow - 1) ? (int)x + 1 : (int)ow - 1, i_p2 = (x < ow - 2) ? (int)x + 2 : (int)ow - 1; + + // Notch filter optimized for system-specific bandwidth + int notchedY = (filt->c64_mode) ? + (cbuf[i_m1] + (cbuf[x] << 1) + cbuf[i_p1]) >> 2 : + (cbuf[i_m2] + (cbuf[i_m1] << 2) + (cbuf[x] * 6) + (cbuf[i_p1] << 2) + cbuf[i_p2]) >> 4; + + lineY[x] = ((notchedY * (256 - filt->artifacts)) + (cbuf[x] * filt->artifacts)) >> 8; + } + + for (unsigned x = 0; x < ow; x++) { + int Y = lineY[x], I = lineI[x], Q = lineQ[x]; + if (filt->sharpness > 0) { + int x_m1 = (x > 0) ? (int)x - 1 : 0, x_p1 = (x < ow - 1) ? (int)x + 1 : (int)ow - 1; + int edge = (Y << 1) - (lineY[x_m1] + lineY[x_p1]); + Y += (edge * filt->sharpness) >> 9; + Y = (Y < 0) ? 0 : (Y > 255 ? 255 : Y); + } + if (!filt->huesat_identity) { + int Ir = (I * filt->hue_cos - Q * filt->hue_sin) >> 8; + int Qr = (I * filt->hue_sin + Q * filt->hue_cos) >> 8; + I = (Ir * filt->saturation) >> 8; Q = (Qr * filt->saturation) >> 8; + } + int r, g, b; + yiq_to_rgb(Y, I, Q, &r, &g, &b); + if (filt->in_fmt == SOFTFILTER_FMT_RGB565) + ((uint16_t*)dst_ptr)[x] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + else + ((uint32_t*)dst_ptr)[x] = 0xFF000000u | (r << 16) | (g << 8) | b; + } +} + +static void ntsc_work_cb(void *data, void *thread_data) { + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data; + int cbuf[NTSC_MAX_WIDTH * 2], lineI[NTSC_MAX_WIDTH * 2], lineQ[NTSC_MAX_WIDTH * 2], lineY[NTSC_MAX_WIDTH * 2]; + yiq_t yiq_cache[NTSC_MAX_WIDTH]; + for (unsigned y = 0; y < thr->height; y++) { + void *dst = (uint8_t*)thr->out_data + (y * thr->out_pitch); + ntsc_process_line(filt, thr, y, cbuf, lineI, lineQ, lineY, yiq_cache, dst); + } +} + +static void *ntsc_create(const struct softfilter_config *config, + unsigned in_fmt, unsigned out_fmt, unsigned max_width, unsigned max_height, + unsigned threads, softfilter_simd_mask_t simd, void *userdata) { + struct filter_data *filt = (struct filter_data*)calloc(1, sizeof(*filt)); + if (!filt) return NULL; + float h = 0.0f, s = 1.0f, sh = 0.0f, art = 0.5f, pal = 0.0f, atari = 0.0f, c64 = 0.0f; + if (config) { + config->get_float(userdata, "hue", &h, 0.0f); + config->get_float(userdata, "saturation", &s, 1.0f); + config->get_float(userdata, "sharpness", &sh, 0.0f); + config->get_float(userdata, "artifacts", &art, 0.5f); + config->get_float(userdata, "pal_mode", &pal, 0.0f); + config->get_float(userdata, "atari_mode", &atari, 0.0f); + config->get_float(userdata, "c64_mode", &c64, 0.0f); + } + filt->in_fmt = in_fmt; + filt->pal_mode = (pal != 0.0f); + filt->atari_mode = (atari != 0.0f); + filt->c64_mode = (c64 != 0.0f); + + // Determine phase count: Atari (16), NES (12), C64/Generic (8) + if (filt->atari_mode) filt->phase_count = 16; + else if (filt->c64_mode) filt->phase_count = 8; + else filt->phase_count = 12; + + filt->hue_cos = (int)(cos(h * M_PI / 180.0) * 256.0); + filt->hue_sin = (int)(sin(h * M_PI / 180.0) * 256.0); + filt->saturation = (int)(s * 256.0); + filt->sharpness = (int)(sh * 256.0f); + filt->artifacts = (int)(art * 256.0f); + filt->huesat_identity = (h == 0.0f && s == 1.0f); + for (int i = 0; i < filt->phase_count; i++) { + float rad = (float)(2.0 * M_PI * i / filt->phase_count); + filt->lut_sin[i] = (int)(sin(rad) * 256.0f); + filt->lut_cos[i] = (int)(cos(rad) * 256.0f); + } + filt->threads = threads; + filt->workers = (struct softfilter_thread_data*)calloc(threads, sizeof(struct softfilter_thread_data)); + return filt; +} + +static void ntsc_packets(void *data, struct softfilter_work_packet *packets, + void *output, size_t output_stride, const void *input, unsigned width, + unsigned height, size_t input_stride) { + struct filter_data *filt = (struct filter_data*)data; + filt->nes_frame_count++; + for (unsigned i = 0; i < filt->threads; i++) { + struct softfilter_thread_data *thr = &filt->workers[i]; + unsigned y_start = (height * i) / filt->threads, y_end = (height * (i + 1)) / filt->threads; + thr->in_data = (const uint8_t*)input + y_start * input_stride; + thr->out_data = (uint8_t*)output + y_start * output_stride; + thr->in_pitch = input_stride; thr->out_pitch = output_stride; + thr->width = width; thr->height = y_end - y_start; + thr->first = (int)y_start; + packets[i].work = ntsc_work_cb; + packets[i].thread_data = thr; + } +} + +static void ntsc_destroy(void *data) { + struct filter_data *f = (struct filter_data*)data; + if (f) { free(f->workers); free(f); } +} + +static void ntsc_output(void *data, unsigned *ow, unsigned *oh, unsigned w, unsigned h) { *ow = w*2; *oh = h; } +static unsigned ntsc_query_num_threads(void *data) { return ((struct filter_data*)data)->threads; } +static unsigned ntsc_input_fmts(void) { return SOFTFILTER_FMT_XRGB8888 | SOFTFILTER_FMT_RGB565; } +static unsigned ntsc_output_fmts(unsigned fmt) { return fmt; } + +static const struct softfilter_implementation ntsc_impl = { + ntsc_input_fmts, ntsc_output_fmts, ntsc_create, ntsc_destroy, + ntsc_query_num_threads, ntsc_output, ntsc_packets, SOFTFILTER_API_VERSION, "NTSC-Multi-System", "ntsc", +}; + +const struct softfilter_implementation *softfilter_get_implementation(softfilter_simd_mask_t simd) { + (void)simd; return &ntsc_impl; +} \ No newline at end of file diff --git a/gfx/video_filters/ntsc.filt b/gfx/video_filters/ntsc.filt new file mode 100644 index 000000000000..9ef5c4c7a3cc --- /dev/null +++ b/gfx/video_filters/ntsc.filt @@ -0,0 +1,20 @@ +filter = ntsc + +# --- System Selection --- +# Set only one to 1.0 +# If both are 0.0, defaults to 12-phase NES logic + +ntsc_atari_mode = 0.0 # 16-phase Atari 2600 logic +ntsc_c64_mode = 0.0 # 8-phase Commodore 64 logic + + +# --- Global Settings --- +# 1.0 for PAL/50Hz stability + +ntsc_pal_mode = 0.0 +ntsc_hue = 0.0 +ntsc_saturation = 1.0 + +# Increase for stronger C64 color bleeding +ntsc_artifacts = 0.0 +ntsc_sharpness = 0.3 \ No newline at end of file diff --git a/gfx/video_filters/ntsc_crt.c b/gfx/video_filters/ntsc_crt.c new file mode 100644 index 000000000000..26b01fccf1e8 --- /dev/null +++ b/gfx/video_filters/ntsc_crt.c @@ -0,0 +1,1512 @@ +/* RetroArch CPU Filter - NTSC/CRT + * + * Original NTSC/CRT library by EMMIR 2018-2023 + * https://www.youtube.com/@EMMIR_KC/videos + * https://discord.com/invite/hdYctSmyQJ + * + * RetroArch softfilter wrapper 2024 + * + * This is a consolidated single-file build of: + * crt_core.h crt_core.c + * crt_ntsc.h crt_ntsc.c + * ntsc_crt.c + */ + +#include "softfilter.h" + +#include +#include +#include + +#ifdef RARCH_INTERNAL +#define softfilter_get_implementation ntsc_crt_get_implementation +#define softfilter_thread_data ntsc_crt_softfilter_thread_data +#define filter_data ntsc_crt_filter_data +#endif + +/*****************************************************************************/ +/*****************************************************************************/ +/**** ****/ +/**** NTSC/CRT integer-only NTSC video signal ****/ +/**** encoding / decoding emulation (EMMIR 2018-2023) ****/ +/**** ****/ +/*****************************************************************************/ +/*****************************************************************************/ + +/* library version */ +#define CRT_MAJOR 2 +#define CRT_MINOR 3 +#define CRT_PATCH 2 + +/* NOTE: this library does not use the alpha channel at all */ +#define CRT_PIX_FORMAT_RGB 0 /* 3 bytes per pixel [R,G,B,R,G,B,R,G,B...] */ +#define CRT_PIX_FORMAT_BGR 1 /* 3 bytes per pixel [B,G,R,B,G,R,B,G,R...] */ +#define CRT_PIX_FORMAT_ARGB 2 /* 4 bytes per pixel [A,R,G,B,A,R,G,B...] */ +#define CRT_PIX_FORMAT_RGBA 3 /* 4 bytes per pixel [R,G,B,A,R,G,B,A...] */ +#define CRT_PIX_FORMAT_ABGR 4 /* 4 bytes per pixel [A,B,G,R,A,B,G,R...] */ +#define CRT_PIX_FORMAT_BGRA 5 /* 4 bytes per pixel [B,G,R,A,B,G,R,A...] */ + +/* do bloom emulation (side effect: makes screen have black borders) */ +#define CRT_DO_BLOOM 0 /* does not work for NES */ +#define CRT_DO_VSYNC 1 /* look for VSYNC */ +#define CRT_DO_HSYNC 1 /* look for HSYNC */ + +/*****************************************************************************/ +/******************** NTSC system parameters (from crt_ntsc.h) ***************/ +/*****************************************************************************/ + +/* 0 = vertical chroma (228 chroma clocks per line) */ +/* 1 = checkered chroma (227.5 chroma clocks per line) */ +#define CRT_CHROMA_PATTERN 1 + +/* chroma clocks (subcarrier cycles) per line */ +#if (CRT_CHROMA_PATTERN == 1) +#define CRT_CC_LINE 2275 +#else +/* this will give the 'rainbow' effect in the famous waterfall scene */ +#define CRT_CC_LINE 2280 +#endif + +/* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ +#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ +#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ +#define CRT_VRES 262 /* vertical resolution */ +#define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) + +#define CRT_TOP 21 /* first line with active video */ +#define CRT_BOT 261 /* final line with active video */ +#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ + +#define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */ +#define CRT_CC_VPER 1 /* vertical period in which the artifacts repeat */ + +/* search windows, in samples */ +#define CRT_HSYNC_WINDOW 8 +#define CRT_VSYNC_WINDOW 8 + +/* accumulated signal threshold required for sync detection. + * Larger = more stable, until it's so large that it is never reached in which + * case the CRT won't be able to sync + */ +#define CRT_HSYNC_THRESH 4 +#define CRT_VSYNC_THRESH 94 + +/* + * FULL HORIZONTAL LINE SIGNAL (~63500 ns) + * |---------------------------------------------------------------------------| + * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) + * |-------------------||------------------------------------------------------| + * + * + * WITHIN HBLANK PERIOD: + * + * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) + * |--------------||---------------||------------||-------------||-------------| + * BLANK SYNC BLANK BLANK BLANK + * + */ +#define LINE_BEG 0 +#define FP_ns 1500 /* front porch */ +#define SYNC_ns 4700 /* sync tip */ +#define BW_ns 600 /* breezeway */ +#define CB_ns 2500 /* color burst */ +#define BP_ns 1600 /* back porch */ +#define AV_ns 52600 /* active video */ +#define HB_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns) /* h blank */ +/* line duration should be ~63500 ns */ +#define LINE_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns + AV_ns) + +/* convert nanosecond offset to its corresponding point on the sampled line */ +#define ns2pos(ns) ((ns) * CRT_HRES / LINE_ns) +/* starting points for all the different pulses */ +#define FP_BEG ns2pos(0) +#define SYNC_BEG ns2pos(FP_ns) +#define BW_BEG ns2pos(FP_ns + SYNC_ns) +#define CB_BEG ns2pos(FP_ns + SYNC_ns + BW_ns) +#define BP_BEG ns2pos(FP_ns + SYNC_ns + BW_ns + CB_ns) +#define AV_BEG ns2pos(HB_ns) +#define AV_LEN ns2pos(AV_ns) + +/* somewhere between 7 and 12 cycles */ +#define CB_CYCLES 10 + +/* frequencies for bandlimiting */ +#define L_FREQ 1431818 /* full line */ +#define Y_FREQ 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ +#define I_FREQ 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ +#define Q_FREQ 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ + +/* IRE units (100 = 1.0V, -40 = 0.0V) */ +#define WHITE_LEVEL 100 +#define BURST_LEVEL 20 +#define BLACK_LEVEL 7 +#define BLANK_LEVEL 0 +#define SYNC_LEVEL -40 + +struct NTSC_SETTINGS { + const unsigned char *data; /* image data */ + int format; /* pix format (one of the CRT_PIX_FORMATs) */ + int w, h; /* width and height of image */ + int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ + int as_color; /* 0 = monochrome, 1 = full color */ + int field; /* 0 = even, 1 = odd */ + int frame; /* 0 = even, 1 = odd */ + int hue; /* 0-359 */ + int xoffset; /* x offset in sample space. 0 is minimum value */ + int yoffset; /* y offset in # of lines. 0 is minimum value */ + /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ + int iirs_initialized; /* internal state */ +}; + +/*****************************************************************************/ +/********************** CRT struct (from crt_core.h) *************************/ +/*****************************************************************************/ + +struct CRT { + signed char analog[CRT_INPUT_SIZE]; + signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ + + int outw, outh; /* output width/height */ + int out_format; /* output pixel format (one of the CRT_PIX_FORMATs) */ + unsigned char *out; /* output image */ + + int hue, brightness, contrast, saturation; /* common monitor settings */ + int black_point, white_point; /* user-adjustable */ + int scanlines; /* leave gaps between lines if necessary */ + int blend; /* blend new field onto previous image */ + unsigned v_fac; /* factor to stretch img vertically onto the output img */ + + /* internal data */ + int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */ + int hsync, vsync; /* keep track of sync over frames */ + int rn; /* seed for the 'random' noise */ +}; + +/*****************************************************************************/ +/*************************** FIXED POINT SIN/COS *****************************/ +/*****************************************************************************/ + +#define T14_2PI 16384 +#define T14_MASK (T14_2PI - 1) +#define T14_PI (T14_2PI / 2) + +/*****************************************************************************/ +/*****************************************************************************/ +/**** crt_core.c - the demodulator implementation ****/ +/*****************************************************************************/ +/*****************************************************************************/ + +/* ensure negative values for x get properly modulo'd */ +#define POSMOD(x, n) (((x) % (n) + (n)) % (n)) + +static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ + 0x0000, + 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, + 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, + 0x7f60 +}; + +static int +sintabil8(int n) +{ + int f, i, a, b; + + /* looks scary but if you don't change T14_2PI + * it won't cause out of bounds memory reads + */ + f = n >> 0 & 0xff; + i = n >> 8 & 0xff; + a = sigpsin15[i]; + b = sigpsin15[i + 1]; + return (a + ((b - a) * f >> 8)); +} + +/* 14-bit interpolated sine/cosine */ +static void +crt_sincos14(int *s, int *c, int n) +{ + int h; + + n &= T14_MASK; + h = n & ((T14_2PI >> 1) - 1); + + if (h > ((T14_2PI >> 2) - 1)) { + *c = -sintabil8(h - (T14_2PI >> 2)); + *s = sintabil8((T14_2PI >> 1) - h); + } else { + *c = sintabil8((T14_2PI >> 2) - h); + *s = sintabil8(h); + } + if (n > ((T14_2PI >> 1) - 1)) { + *c = -*c; + *s = -*s; + } +} + +static int +crt_bpp4fmt(int format) +{ + switch (format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_BGR: + return 3; + case CRT_PIX_FORMAT_ARGB: + case CRT_PIX_FORMAT_RGBA: + case CRT_PIX_FORMAT_ABGR: + case CRT_PIX_FORMAT_BGRA: + return 4; + default: + return 0; + } +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +/* convolution is much faster but the EQ looks softer, more authentic, and more analog */ +#define USE_CONVOLUTION 0 +#define USE_7_SAMPLE_KERNEL 1 +#define USE_6_SAMPLE_KERNEL 0 +#define USE_5_SAMPLE_KERNEL 0 + +#if (CRT_CC_SAMPLES != 4) +/* the current convolutions do not filter properly at > 4 samples */ +#undef USE_CONVOLUTION +#define USE_CONVOLUTION 0 +#endif + +#if USE_CONVOLUTION + +/* NOT 3 band equalizer, faster convolution instead. + * eq function names preserved to keep code clean + */ +static struct EQF { + int h[7]; +} eqY, eqI, eqQ; + +/* params unused to keep the function the same */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + memset(f, 0, sizeof(struct EQF)); +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i; + int *h = f->h; + + for (i = 6; i > 0; i--) { + h[i] = h[i - 1]; + } + h[0] = s; +#if USE_7_SAMPLE_KERNEL + /* index : 0 1 2 3 4 5 6 */ + /* weight: 1 4 7 8 7 4 1 */ + return (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; +#elif USE_6_SAMPLE_KERNEL + /* index : 0 1 2 3 4 5 */ + /* weight: 1 3 4 4 3 1 */ + return (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; +#elif USE_5_SAMPLE_KERNEL + /* index : 0 1 2 3 4 */ + /* weight: 1 2 2 2 1 */ + return (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; +#else + /* index : 0 1 2 3 */ + /* weight: 1 1 1 1*/ + return (s + h[3] + h[1] + h[2]) >> 2; +#endif +} + +#else + +#define HISTLEN 3 +#define HISTOLD (HISTLEN - 1) /* oldest entry */ +#define HISTNEW 0 /* newest entry */ + +#define EQ_P 16 /* if changed, the gains will need to be adjusted */ +#define EQ_R (1 << (EQ_P - 1)) /* rounding */ +/* three band equalizer */ +static struct EQF { + int lf, hf; /* fractions */ + int g[3]; /* gains */ + int fL[4]; + int fH[4]; + int h[HISTLEN]; /* history */ +} eqY, eqI, eqQ; + +/* f_lo - low cutoff frequency + * f_hi - high cutoff frequency + * rate - sampling rate + * g_lo, g_mid, g_hi - gains + */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + int sn, cs; + + memset(f, 0, sizeof(struct EQF)); + + f->g[0] = g_lo; + f->g[1] = g_mid; + f->g[2] = g_hi; + + crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); +#if (EQ_P >= 15) + f->lf = 2 * (sn << (EQ_P - 15)); +#else + f->lf = 2 * (sn >> (15 - EQ_P)); +#endif + crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); +#if (EQ_P >= 15) + f->hf = 2 * (sn << (EQ_P - 15)); +#else + f->hf = 2 * (sn >> (15 - EQ_P)); +#endif +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->fL, 0, sizeof(f->fL)); + memset(f->fH, 0, sizeof(f->fH)); + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i, r[3]; + + f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; + f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; + + for (i = 1; i < 4; i++) { + f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; + f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; + } + + r[0] = f->fL[3]; + r[1] = f->fH[3] - f->fL[3]; + r[2] = f->h[HISTOLD] - f->fH[3]; + + for (i = 0; i < 3; i++) { + r[i] = (r[i] * f->g[i]) >> EQ_P; + } + + for (i = HISTOLD; i > 0; i--) { + f->h[i] = f->h[i - 1]; + } + f->h[HISTNEW] = s; + + return (r[0] + r[1] + r[2]); +} + +#endif + +/*****************************************************************************/ +/***************************** PUBLIC FUNCTIONS ******************************/ +/*****************************************************************************/ + +static void +crt_resize(struct CRT *v, int w, int h, int f, unsigned char *out) +{ + v->outw = w; + v->outh = h; + v->out_format = f; + v->out = out; +} + +static void +crt_reset(struct CRT *v) +{ + v->hue = 0; + v->saturation = 10; + v->brightness = 0; + v->contrast = 180; + v->black_point = 0; + v->white_point = 100; + v->hsync = 0; + v->vsync = 0; +} + +static void +crt_init(struct CRT *v, int w, int h, int f, unsigned char *out) +{ + memset(v, 0, sizeof(struct CRT)); + crt_resize(v, w, h, f, out); + crt_reset(v); + v->rn = 194; + + /* kilohertz to line sample conversion */ +#define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) + + /* band gains are pre-scaled as 16-bit fixed point + * if you change the EQ_P define, you'll need to update these gains too + */ +#if (CRT_CC_SAMPLES == 4) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#elif (CRT_CC_SAMPLES == 5) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 12192, 7775); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#else +#error "NTSC-CRT currently only supports 4 or 5 samples per chroma period." +#endif + +} + +static void +crt_demodulate(struct CRT *v, int noise) +{ + /* made static so all this data does not go on the stack */ + static struct { + int y, i, q; + } out[AV_LEN + 1], *yiqA, *yiqB; + int i, j, line, rn; + signed char *sig; + int s = 0; + int field, ratio; + int *ccr; /* color carrier signal */ + int huesn, huecs; + int xnudge = -3, ynudge = 3; + int bright = v->brightness - (BLACK_LEVEL + v->black_point); + int bpp, pitch; +#if CRT_DO_BLOOM + int prev_e; /* filtered beam energy per scan line */ + int max_e; /* approx maximum energy in a scan line */ +#endif + + bpp = crt_bpp4fmt(v->out_format); + if (bpp == 0) { + return; + } + pitch = v->outw * bpp; + + crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); + huesn >>= 11; /* make 4-bit */ + huecs >>= 11; + + rn = v->rn; +#if !CRT_DO_VSYNC + /* determine field before we add noise, + * otherwise it's not reliably recoverable + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->analog + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto found_field; + } + } + } +found_field: + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); + v->vsync = -3; +#endif + for (i = 0; i < CRT_INPUT_SIZE; i++) { + int nn = noise; + rn = (214019 * rn + 140327895); + /* signal + noise */ + s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8); + if (s > 127) { s = 127; } + if (s < -127) { s = -127; } + v->inp[i] = s; + } + v->rn = rn; + +#if CRT_DO_VSYNC + /* Look for vertical sync. + * + * This is done by integrating the signal and + * seeing if it exceeds a threshold. The threshold of + * the vertical sync pulse is much higher because the + * vsync pulse is a lot longer than the hsync pulse. + * The signal needs to be integrated to lessen + * the noise in the signal. + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->inp + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + /* increase the multiplier to make the vsync + * more stable when there is a lot of noise + */ + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto vsync_found; + } + } + } +vsync_found: + v->vsync = line; /* vsync found (or gave up) at this line */ + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); +#endif + +#if CRT_DO_BLOOM + max_e = (128 + (noise / 2)) * AV_LEN; + prev_e = (16384 / 8); +#endif + /* ratio of output height to active video lines in the signal */ + ratio = (v->outh << 16) / CRT_LINES; + ratio = (ratio + 32768) >> 16; + + field = (field * (ratio / 2)); + + for (line = CRT_TOP; line < CRT_BOT; line++) { + unsigned pos, ln, scanR; + int scanL, dx; + int L, R; + unsigned char *cL, *cR; +#if (CRT_CC_SAMPLES == 4) + int wave[CRT_CC_SAMPLES]; +#else + int waveI[CRT_CC_SAMPLES]; + int waveQ[CRT_CC_SAMPLES]; +#endif + int dci, dcq; /* decoded I, Q */ + int xpos, ypos; + int beg, end; + int phasealign; +#if CRT_DO_BLOOM + int line_w; +#endif + + beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field; + end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field; + + if (beg >= v->outh) { continue; } + if (end > v->outh) { end = v->outh; } + + /* Look for horizontal sync. + * See comment above regarding vertical sync. + */ + ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; + sig = v->inp + ln + v->hsync; + s = 0; + for (i = -CRT_HSYNC_WINDOW; i < CRT_HSYNC_WINDOW; i++) { + s += sig[SYNC_BEG + i]; + if (s <= (CRT_HSYNC_THRESH * SYNC_LEVEL)) { + break; + } + } +#if CRT_DO_HSYNC + v->hsync = POSMOD(i + v->hsync, CRT_HRES); +#else + v->hsync = 0; +#endif + + xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); + ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); + pos = xpos + ypos * CRT_HRES; + + ccr = v->ccf[ypos % CRT_CC_VPER]; +#if (CRT_CC_SAMPLES == 4) + sig = v->inp + ln + (v->hsync & ~3); /* faster */ +#else + sig = v->inp + ln + (v->hsync - (v->hsync % CRT_CC_SAMPLES)); +#endif + for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { + int p, n; + p = ccr[i % CRT_CC_SAMPLES] * 127 / 128; /* fraction of the previous */ + n = sig[i]; /* mixed with the new sample */ + ccr[i % CRT_CC_SAMPLES] = p + n; + } + + phasealign = POSMOD(v->hsync, CRT_CC_SAMPLES); + +#if (CRT_CC_SAMPLES == 4) + /* amplitude of carrier = saturation, phase difference = hue */ + dci = ccr[(phasealign + 1) & 3] - ccr[(phasealign + 3) & 3]; + dcq = ccr[(phasealign + 2) & 3] - ccr[(phasealign + 0) & 3]; + + wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; + wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; + wave[2] = -wave[0]; + wave[3] = -wave[1]; +#elif (CRT_CC_SAMPLES == 5) + { + int dciA, dciB; + int dcqA, dcqB; + int ang = (v->hue % 360); + int off180 = CRT_CC_SAMPLES / 2; + int off90 = CRT_CC_SAMPLES / 4; + int peakA = phasealign + off90; + int peakB = phasealign + 0; + dciA = dciB = dcqA = dcqB = 0; + /* amplitude of carrier = saturation, phase difference = hue */ + dciA = ccr[(peakA) % CRT_CC_SAMPLES]; + /* average */ + dciB = (ccr[(peakA + off180) % CRT_CC_SAMPLES] + + ccr[(peakA + off180 + 1) % CRT_CC_SAMPLES]) / 2; + dcqA = ccr[(peakB + off180) % CRT_CC_SAMPLES]; + dcqB = ccr[(peakB) % CRT_CC_SAMPLES]; + dci = dciA - dciB; + dcq = dcqA - dcqB; + /* create wave tables and rotate them by the hue adjustment angle */ + for (i = 0; i < CRT_CC_SAMPLES; i++) { + int sn, cs; + crt_sincos14(&sn, &cs, ang * 8192 / 180); + waveI[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + /* Q is offset by 90 */ + crt_sincos14(&sn, &cs, (ang + 90) * 8192 / 180); + waveQ[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + ang += (360 / CRT_CC_SAMPLES); + } + } +#endif + sig = v->inp + pos; +#if CRT_DO_BLOOM + s = 0; + for (i = 0; i < AV_LEN; i++) { + s += sig[i]; /* sum up the scan line */ + } + /* bloom emulation */ + prev_e = (prev_e * 123 / 128) + ((((max_e >> 1) - s) << 10) / max_e); + line_w = (AV_LEN * 112 / 128) + (prev_e >> 9); + + dx = (line_w << 12) / v->outw; + scanL = ((AV_LEN / 2) - (line_w >> 1) + 8) << 12; + scanR = (AV_LEN - 1) << 12; + + L = (scanL >> 12); + R = (scanR >> 12); +#else + dx = ((AV_LEN - 1) << 12) / v->outw; + scanL = 0; + scanR = (AV_LEN - 1) << 12; + L = 0; + R = AV_LEN; +#endif + reset_eq(&eqY); + reset_eq(&eqI); + reset_eq(&eqQ); + +#if (CRT_CC_SAMPLES == 4) + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; + } +#else + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * waveI[i % CRT_CC_SAMPLES] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * waveQ[i % CRT_CC_SAMPLES] >> 9) >> 3; + } +#endif + + cL = v->out + (beg * pitch); + cR = cL + pitch; + + for (pos = scanL; pos < scanR && cL < cR; pos += dx) { + int y, i, q; + int r, g, b; + int aa, bb; + + R = pos & 0xfff; + L = 0xfff - R; + s = pos >> 12; + + yiqA = out + s; + yiqB = out + s + 1; + + /* interpolate between samples if needed */ + y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); + i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); + q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); + + /* YIQ to RGB */ + r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; + g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; + b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + if (v->blend) { + aa = (r << 16 | g << 8 | b); + + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + bb = cL[0] << 16 | cL[1] << 8 | cL[2]; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + bb = cL[2] << 16 | cL[1] << 8 | cL[0]; + break; + case CRT_PIX_FORMAT_ARGB: + bb = cL[1] << 16 | cL[2] << 8 | cL[3]; + break; + case CRT_PIX_FORMAT_ABGR: + bb = cL[3] << 16 | cL[2] << 8 | cL[1]; + break; + default: + bb = 0; + break; + } + + /* blend with previous color there */ + bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + } else { + bb = (r << 16 | g << 8 | b); + } + + switch (v->out_format) { + case CRT_PIX_FORMAT_RGB: + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + break; + + case CRT_PIX_FORMAT_RGBA: + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + cL[3] = 0xff; + break; + + case CRT_PIX_FORMAT_BGR: + cL[0] = bb >> 0 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 16 & 0xff; + break; + + case CRT_PIX_FORMAT_BGRA: + cL[0] = bb >> 0 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 16 & 0xff; + cL[3] = 0xff; + break; + + case CRT_PIX_FORMAT_ARGB: + cL[0] = 0xff; + cL[1] = bb >> 16 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 0 & 0xff; + break; + + case CRT_PIX_FORMAT_ABGR: + cL[0] = 0xff; + cL[1] = bb >> 0 & 0xff; + cL[2] = bb >> 8 & 0xff; + cL[3] = bb >> 16 & 0xff; + break; + + default: + break; + } + + cL += bpp; + } + + /* duplicate extra lines */ + for (s = beg + 1; s < (end - v->scanlines); s++) { + memcpy(v->out + s * pitch, v->out + (s - 1) * pitch, pitch); + } + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/**** crt_ntsc.c - the modulator implementation ****/ +/*****************************************************************************/ +/*****************************************************************************/ + +#if (CRT_CHROMA_PATTERN == 1) +/* 227.5 subcarrier cycles per line means every other line has reversed phase */ +#define CC_PHASE(ln) (((ln) & 1) ? -1 : 1) +#else +#define CC_PHASE(ln) (1) +#endif + +#define EXP_P 11 +#define EXP_ONE (1 << EXP_P) +#define EXP_MASK (EXP_ONE - 1) +#define EXP_PI 6434 +#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P) +#define EXP_DIV(x, y) (((x) << EXP_P) / (y)) + +static int e11[] = { + EXP_ONE, + 5567, /* e */ + 15133, /* e^2 */ + 41135, /* e^3 */ + 111817 /* e^4 */ +}; + +/* fixed point e^x */ +static int +expx(int n) +{ + int neg, idx, res; + int nxt, acc, del; + int i; + + if (n == 0) { + return EXP_ONE; + } + neg = n < 0; + if (neg) { + n = -n; + } + idx = n >> EXP_P; + res = EXP_ONE; + for (i = 0; i < idx / 4; i++) { + res = EXP_MUL(res, e11[4]); + } + idx &= 3; + if (idx > 0) { + res = EXP_MUL(res, e11[idx]); + } + + n &= EXP_MASK; + nxt = EXP_ONE; + acc = 0; + del = 1; + for (i = 1; i < 17; i++) { + acc += nxt / del; + nxt = EXP_MUL(nxt, n); + del *= i; + if (del > nxt || nxt <= 0 || del <= 0) { + break; + } + } + res = EXP_MUL(res, acc); + + if (neg) { + res = EXP_DIV(EXP_ONE, res); + } + return res; +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +/* infinite impulse response low pass filter for bandlimiting YIQ */ +static struct IIRLP { + int c; + int h; /* history */ +} iirY, iirI, iirQ; + +/* freq - total bandwidth + * limit - max frequency + */ +static void +init_iir(struct IIRLP *f, int freq, int limit) +{ + int rate; /* cycles/pixel rate */ + + memset(f, 0, sizeof(struct IIRLP)); + rate = (freq << 9) / limit; + f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); +} + +static void +reset_iir(struct IIRLP *f) +{ + f->h = 0; +} + +/* hi-pass for debugging */ +#define HIPASS 0 + +static int +iirf(struct IIRLP *f, int s) +{ + f->h += EXP_MUL(s - f->h, f->c); +#if HIPASS + return s - f->h; +#else + return f->h; +#endif +} + +static void +crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = ((CRT_LINES * 64500) >> 16); + int iccf[CRT_CC_SAMPLES]; + int ccmodI[CRT_CC_SAMPLES]; /* color phase for mod */ + int ccmodQ[CRT_CC_SAMPLES]; /* color phase for mod */ + int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */ + int sn, cs, n, ph; + int inv_phase = 0; + int bpp; + + if (!s->iirs_initialized) { + init_iir(&iirY, L_FREQ, Y_FREQ); + init_iir(&iirI, L_FREQ, I_FREQ); + init_iir(&iirQ, L_FREQ, Q_FREQ); + s->iirs_initialized = 1; + } +#if CRT_DO_BLOOM + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > ((AV_LEN * 55500) >> 16)) { + destw = ((AV_LEN * 55500) >> 16); + } + if (desth > ((CRT_LINES * 63500) >> 16)) { + desth = ((CRT_LINES * 63500) >> 16); + } + } else { + destw = (AV_LEN * 55500) >> 16; + desth = (CRT_LINES * 63500) >> 16; + } +#else + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > AV_LEN) { + destw = AV_LEN; + } + if (desth > ((CRT_LINES * 64500) >> 16)) { + desth = ((CRT_LINES * 64500) >> 16); + } + } +#endif + if (s->as_color) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + n = s->hue + x * (360 / CRT_CC_SAMPLES); + crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); + ccburst[x] = sn >> 10; + crt_sincos14(&sn, &cs, n * 8192 / 180); + ccmodI[x] = sn >> 10; + crt_sincos14(&sn, &cs, (n - 90) * 8192 / 180); + ccmodQ[x] = sn >> 10; + } + } else { + memset(ccburst, 0, sizeof(ccburst)); + memset(ccmodI, 0, sizeof(ccmodI)); + memset(ccmodQ, 0, sizeof(ccmodQ)); + } + + bpp = crt_bpp4fmt(s->format); + if (bpp == 0) { + return; /* just to be safe */ + } + xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2; + yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2; + + s->field &= 1; + s->frame &= 1; + inv_phase = (s->field == s->frame); + ph = CC_PHASE(inv_phase); + + /* align signal */ + xo = (xo & ~3); + + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + if (n <= 3 || (n >= 7 && n <= 9)) { + /* equalizing pulses - small blips of sync, mostly blank */ + while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else if (n >= 4 && n <= 6) { + int even[4] = { 46, 50, 96, 100 }; + int odd[4] = { 4, 50, 96, 100 }; + int *offs = even; + if (s->field == 1) { + offs = odd; + } + /* vertical sync pulse - small blips of blank, mostly sync */ + while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else { + int cb; + + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + if (n < CRT_TOP) { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { +#if (CRT_CHROMA_PATTERN == 1) + int off180 = CRT_CC_SAMPLES / 2; + cb = ccburst[(t + inv_phase * off180) % CRT_CC_SAMPLES]; +#else + cb = ccburst[t % CRT_CC_SAMPLES]; +#endif + line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + iccf[t % CRT_CC_SAMPLES] = line[t]; + } + } + } + + for (y = 0; y < desth; y++) { + int field_offset; + int sy; + + field_offset = (s->field * s->h + desth) / desth / 2; + sy = (y * s->h) / desth; + + sy += field_offset; + + if (sy >= s->h) sy = s->h; + + sy *= s->w; + + reset_iir(&iirY); + reset_iir(&iirI); + reset_iir(&iirQ); + + for (x = 0; x < destw; x++) { + int fy, fi, fq; + int rA, gA, bA; + const unsigned char *pix; + int ire; /* composite signal */ + int xoff; + + pix = s->data + ((((x * s->w) / destw) + sy) * bpp); + switch (s->format) { + case CRT_PIX_FORMAT_RGB: + case CRT_PIX_FORMAT_RGBA: + rA = pix[0]; + gA = pix[1]; + bA = pix[2]; + break; + case CRT_PIX_FORMAT_BGR: + case CRT_PIX_FORMAT_BGRA: + rA = pix[2]; + gA = pix[1]; + bA = pix[0]; + break; + case CRT_PIX_FORMAT_ARGB: + rA = pix[1]; + gA = pix[2]; + bA = pix[3]; + break; + case CRT_PIX_FORMAT_ABGR: + rA = pix[3]; + gA = pix[2]; + bA = pix[1]; + break; + default: + rA = gA = bA = 0; + break; + } + + /* RGB to YIQ */ + fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; + fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; + fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; + ire = BLACK_LEVEL + v->black_point; + + xoff = (x + xo) % CRT_CC_SAMPLES; + /* bandlimit Y,I,Q */ + fy = iirf(&iirY, fy); + fi = iirf(&iirI, fi) * ph * ccmodI[xoff] >> 4; + fq = iirf(&iirQ, fq) * ph * ccmodQ[xoff] >> 4; + ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; + if (ire < 0) ire = 0; + if (ire > 110) ire = 110; + + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + } + } + for (n = 0; n < CRT_CC_VPER; n++) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + v->ccf[n][x] = iccf[x] << 7; + } + } +} + +/*****************************************************************************/ +/*****************************************************************************/ +/**** ntsc_crt.c - RetroArch softfilter wrapper ****/ +/*****************************************************************************/ +/*****************************************************************************/ + +/* ----------------------------------------------------------------------- + * Default tuning — these become the starting values shown in the .filt + * ----------------------------------------------------------------------- */ +#define DEFAULT_NOISE 2 /* 0 = clean signal, ~10 = noisy */ +#define DEFAULT_HUE 0 /* 0-359 */ +#define DEFAULT_BRIGHTNESS 0 /* monitor brightness offset */ +#define DEFAULT_CONTRAST 180 /* monitor contrast */ +#define DEFAULT_SATURATION 10 /* color saturation */ +#define DEFAULT_SCANLINES 0 /* 1 = dark gap between scanlines */ +#define DEFAULT_BLEND 1 /* 1 = blend fields (less flicker) */ +#define DEFAULT_AS_COLOR 1 /* 0 = monochrome, 1 = color */ +#define DEFAULT_BLACK_PT 0 /* black point adjustment */ +#define DEFAULT_WHITE_PT 100 /* white point adjustment */ + +/* ----------------------------------------------------------------------- + * Per-thread data (same pattern as scanline2x) + * ----------------------------------------------------------------------- */ +struct softfilter_thread_data +{ + void *out_data; + const void *in_data; + size_t out_pitch; + size_t in_pitch; + unsigned colfmt; + unsigned width; + unsigned height; + int first; + int last; +}; + +/* ----------------------------------------------------------------------- + * Main filter state + * ----------------------------------------------------------------------- */ +struct filter_data +{ + unsigned threads; + struct softfilter_thread_data *workers; + unsigned in_fmt; + + /* CRT library state */ + struct CRT crt; + struct NTSC_SETTINGS ntsc; + + /* output buffer (XRGB8888) */ + unsigned char *out_buf; + unsigned out_buf_w; + unsigned out_buf_h; + + /* user-settable params */ + int noise; + int hue; + int scanlines; + int blend; + int as_color; + + /* frame/field counter */ + int frame; + int field; +}; + +/* ----------------------------------------------------------------------- + * Helpers: XRGB8888 ↔ RGB packing + * ----------------------------------------------------------------------- */ + +/* Convert XRGB8888 source row to a packed RGB byte array for crt_modulate */ +static void xrgb8888_to_rgb(const uint32_t *src, unsigned char *dst, + unsigned w) +{ + unsigned x; + for (x = 0; x < w; x++) { + uint32_t c = src[x]; + dst[x * 3 + 0] = (c >> 16) & 0xff; /* R */ + dst[x * 3 + 1] = (c >> 8) & 0xff; /* G */ + dst[x * 3 + 2] = (c >> 0) & 0xff; /* B */ + } +} + +static void rgb565_to_rgb(const uint16_t *src, unsigned char *dst, + unsigned w) +{ + unsigned x; + for (x = 0; x < w; x++) { + uint16_t c = src[x]; + /* expand to 8-bit */ + dst[x * 3 + 0] = ((c >> 11) & 0x1f) << 3; + dst[x * 3 + 1] = ((c >> 5) & 0x3f) << 2; + dst[x * 3 + 2] = ((c >> 0) & 0x1f) << 3; + } +} + +/* Convert CRT XRGB output back to RetroArch XRGB8888 scanline */ +static void rgb_to_xrgb8888(const unsigned char *src, uint32_t *dst, + unsigned w) +{ + unsigned x; + for (x = 0; x < w; x++) { + dst[x] = 0xff000000u + | ((uint32_t)src[x * 3 + 0] << 16) + | ((uint32_t)src[x * 3 + 1] << 8) + | ((uint32_t)src[x * 3 + 2] << 0); + } +} + +/* ----------------------------------------------------------------------- + * softfilter API + * ----------------------------------------------------------------------- */ + +static unsigned ntsc_crt_input_fmts(void) +{ + return SOFTFILTER_FMT_XRGB8888 | SOFTFILTER_FMT_RGB565; +} + +static unsigned ntsc_crt_output_fmts(unsigned input_fmts) +{ + /* We always output XRGB8888 (CRT lib works in RGB) */ + (void)input_fmts; + return SOFTFILTER_FMT_XRGB8888; +} + +static unsigned ntsc_crt_threads(void *data) +{ + struct filter_data *filt = (struct filter_data*)data; + return filt->threads; +} + +/* Output is the same size as input — the CRT effect is spatial, not scaling */ +static void ntsc_crt_output_size(void *data, + unsigned *out_width, unsigned *out_height, + unsigned width, unsigned height) +{ + (void)data; + *out_width = width; + *out_height = height; +} + +static void *ntsc_crt_create(const struct softfilter_config *config, + unsigned in_fmt, unsigned out_fmt, + unsigned max_width, unsigned max_height, + unsigned threads, softfilter_simd_mask_t simd, void *userdata) +{ + struct filter_data *filt; + unsigned char *out_buf; + (void)out_fmt; + (void)simd; + (void)userdata; + + filt = (struct filter_data*)calloc(1, sizeof(*filt)); + if (!filt) return NULL; + + filt->workers = (struct softfilter_thread_data*) + calloc(1, sizeof(struct softfilter_thread_data)); + if (!filt->workers) { free(filt); return NULL; } + + /* single-threaded — CRT state is not thread-safe */ + filt->threads = 1; + filt->in_fmt = in_fmt; + + /* Allocate CRT output buffer: RGB (3 bpp) */ + out_buf = (unsigned char*)malloc(max_width * max_height * 3); + if (!out_buf) { free(filt->workers); free(filt); return NULL; } + + filt->out_buf = out_buf; + filt->out_buf_w = max_width; + filt->out_buf_h = max_height; + + /* Init CRT library */ + crt_init(&filt->crt, max_width, max_height, + CRT_PIX_FORMAT_RGB, out_buf); + + /* Read tunable params from .filt config (falls back to defaults) */ + if (config) { + config->get_int(userdata, "noise", &filt->noise, DEFAULT_NOISE); + config->get_int(userdata, "hue", &filt->hue, DEFAULT_HUE); + config->get_int(userdata, "as_color", &filt->as_color, DEFAULT_AS_COLOR); + config->get_int(userdata, "scanlines", &filt->scanlines, DEFAULT_SCANLINES); + config->get_int(userdata, "blend", &filt->blend, DEFAULT_BLEND); + config->get_int(userdata, "brightness", &filt->crt.brightness, DEFAULT_BRIGHTNESS); + config->get_int(userdata, "contrast", &filt->crt.contrast, DEFAULT_CONTRAST); + config->get_int(userdata, "saturation", &filt->crt.saturation, DEFAULT_SATURATION); + config->get_int(userdata, "black_point",&filt->crt.black_point, DEFAULT_BLACK_PT); + config->get_int(userdata, "white_point",&filt->crt.white_point, DEFAULT_WHITE_PT); + } else { + filt->noise = DEFAULT_NOISE; + filt->hue = DEFAULT_HUE; + filt->as_color = DEFAULT_AS_COLOR; + filt->scanlines = DEFAULT_SCANLINES; + filt->blend = DEFAULT_BLEND; + filt->crt.brightness = DEFAULT_BRIGHTNESS; + filt->crt.contrast = DEFAULT_CONTRAST; + filt->crt.saturation = DEFAULT_SATURATION; + filt->crt.black_point = DEFAULT_BLACK_PT; + filt->crt.white_point = DEFAULT_WHITE_PT; + } + + filt->crt.scanlines = filt->scanlines; + filt->crt.blend = filt->blend; + filt->crt.hue = filt->hue; + + /* NTSC_SETTINGS — zeroed by calloc, just set fields */ + memset(&filt->ntsc, 0, sizeof(filt->ntsc)); + filt->ntsc.as_color = filt->as_color; + filt->ntsc.hue = filt->hue; + filt->ntsc.format = CRT_PIX_FORMAT_RGB; + + filt->frame = 0; + filt->field = 0; + + return filt; +} + +static void ntsc_crt_destroy(void *data) +{ + struct filter_data *filt = (struct filter_data*)data; + if (!filt) return; + if (filt->out_buf) free(filt->out_buf); + free(filt->workers); + free(filt); +} + +/* ----------------------------------------------------------------------- + * The actual work callback — called from RetroArch's worker thread + * ----------------------------------------------------------------------- */ +static void ntsc_crt_work_cb(void *data, void *thread_data) +{ + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data; + + unsigned width = thr->width; + unsigned height = thr->height; + size_t in_pitch = thr->in_pitch; + size_t out_pitch = thr->out_pitch; + + /* ---- Convert input to RGB byte-array for crt_modulate ---- */ + unsigned char *rgb_in = (unsigned char*)malloc(width * height * 3); + if (!rgb_in) return; + + if (thr->colfmt == SOFTFILTER_FMT_XRGB8888) { + unsigned y; + for (y = 0; y < height; y++) { + const uint32_t *row = (const uint32_t*) + ((const uint8_t*)thr->in_data + y * in_pitch); + xrgb8888_to_rgb(row, rgb_in + y * width * 3, width); + } + } else { + unsigned y; + for (y = 0; y < height; y++) { + const uint16_t *row = (const uint16_t*) + ((const uint8_t*)thr->in_data + y * in_pitch); + rgb565_to_rgb(row, rgb_in + y * width * 3, width); + } + } + + /* ---- Resize CRT output buffer if needed ---- */ + if (filt->out_buf_w != width || filt->out_buf_h != height) { + unsigned char *new_buf = (unsigned char*)realloc(filt->out_buf, + width * height * 3); + if (!new_buf) { free(rgb_in); return; } + filt->out_buf = new_buf; + filt->out_buf_w = width; + filt->out_buf_h = height; + crt_resize(&filt->crt, width, height, CRT_PIX_FORMAT_RGB, filt->out_buf); + } + filt->crt.out = filt->out_buf; + + /* ---- Set up NTSC_SETTINGS for this frame ---- */ + filt->ntsc.data = rgb_in; + filt->ntsc.format = CRT_PIX_FORMAT_RGB; + filt->ntsc.w = (int)width; + filt->ntsc.h = (int)height; + filt->ntsc.raw = 0; /* scale to fit */ + filt->ntsc.field = filt->field; + filt->ntsc.frame = filt->frame & 1; + filt->ntsc.hue = filt->hue; + + /* ---- Encode → signal → decode ---- */ + crt_modulate(&filt->crt, &filt->ntsc); + crt_demodulate(&filt->crt, filt->noise); + + /* ---- Advance field/frame counters ---- */ + filt->field ^= 1; + if (filt->field == 0) filt->frame++; + + /* ---- Copy RGB output → XRGB8888 destination ---- */ + { + unsigned y; + for (y = 0; y < height; y++) { + uint32_t *dst_row = (uint32_t*) + ((uint8_t*)thr->out_data + y * out_pitch); + const unsigned char *src_row = filt->out_buf + y * width * 3; + rgb_to_xrgb8888(src_row, dst_row, width); + } + } + + free(rgb_in); +} + +/* ----------------------------------------------------------------------- + * Packet submission (same structure as scanline2x) + * ----------------------------------------------------------------------- */ +static void ntsc_crt_packets(void *data, + struct softfilter_work_packet *packets, + void *output, size_t output_stride, + const void *input, unsigned width, unsigned height, + size_t input_stride) +{ + struct filter_data *filt = (struct filter_data*)data; + struct softfilter_thread_data *thr = &filt->workers[0]; + + thr->out_data = (uint8_t*)output; + thr->in_data = (const uint8_t*)input; + thr->out_pitch = output_stride; + thr->in_pitch = input_stride; + thr->width = width; + thr->height = height; + thr->colfmt = filt->in_fmt; + + packets[0].work = ntsc_crt_work_cb; + packets[0].thread_data = thr; +} + +/* ----------------------------------------------------------------------- + * Implementation descriptor — matches struct softfilter_implementation + * ----------------------------------------------------------------------- */ +static const struct softfilter_implementation ntsc_crt_impl = { + ntsc_crt_input_fmts, + ntsc_crt_output_fmts, + + ntsc_crt_create, + ntsc_crt_destroy, + + ntsc_crt_threads, + ntsc_crt_output_size, + ntsc_crt_packets, + + SOFTFILTER_API_VERSION, + "NTSC/CRT", + "ntsc_crt", +}; + +const struct softfilter_implementation *softfilter_get_implementation( + softfilter_simd_mask_t simd) +{ + (void)simd; + return &ntsc_crt_impl; +} + +#ifdef RARCH_INTERNAL +#undef softfilter_get_implementation +#undef softfilter_thread_data +#undef filter_data +#endif diff --git a/gfx/video_filters/ntsc_crt.filt b/gfx/video_filters/ntsc_crt.filt new file mode 100644 index 000000000000..fdb71df1fb01 --- /dev/null +++ b/gfx/video_filters/ntsc_crt.filt @@ -0,0 +1,25 @@ +filter = ntsc_crt + +# --- Signal --- +# Amount of noise added to the analog signal (0 = clean, 10 = very noisy) +ntsc_crt_noise = 2 + +# Hue rotation in degrees (0-359) +ntsc_crt_hue = 0 + +# 0 = monochrome / 1 = full color +ntsc_crt_as_color = 1 + +# --- Monitor --- +ntsc_crt_brightness = 0 +ntsc_crt_contrast = 180 +ntsc_crt_saturation = 10 +ntsc_crt_black_point = 0 +ntsc_crt_white_point = 100 + +# --- Display --- +# 1 = dark gap between scanlines (classic CRT look) +ntsc_crt_scanlines = 0 + +# 1 = blend even/odd fields together (reduces flicker, softer look) +ntsc_crt_blend = 1 diff --git a/griffin/griffin.c b/griffin/griffin.c index 18d8dccc3d7e..11badc6b9346 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1034,6 +1034,7 @@ FILTERS #include "../gfx/video_filters/picoscale_256x_320x240.c" #include "../gfx/video_filters/upscale_240x160_320x240.c" #include "../gfx/video_filters/upscale_mix_240x160_320x240.c" +#include "../gfx/video_filters/ntsc_crt.c" #endif #ifdef HAVE_DSP_FILTER diff --git a/input/drivers_joypad/sdl_joypad.c b/input/drivers_joypad/sdl_joypad.c index 9a5155d97429..6151a808c33d 100644 --- a/input/drivers_joypad/sdl_joypad.c +++ b/input/drivers_joypad/sdl_joypad.c @@ -274,7 +274,7 @@ static void sdl_joypad_destroy(void) static void *sdl_joypad_init(void *data) { - size_t i; + unsigned i; unsigned num_sticks; #ifdef HAVE_SDL2 uint32_t subsystem = SDL_INIT_GAMECONTROLLER; diff --git a/libretro-common/audio/dsp_filters/reverb.c b/libretro-common/audio/dsp_filters/reverb.c index 62bdab2f7ed7..0981047b4aba 100644 --- a/libretro-common/audio/dsp_filters/reverb.c +++ b/libretro-common/audio/dsp_filters/reverb.c @@ -109,7 +109,7 @@ struct revmodel static float revmodel_process(struct revmodel *rev, float in) { - int i; + unsigned i; float mono_out = 0.0f; float mono_in = in; float input = mono_in * rev->gain; @@ -125,7 +125,7 @@ static float revmodel_process(struct revmodel *rev, float in) static void revmodel_update(struct revmodel *rev) { - int i; + unsigned i; rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f); if (rev->mode >= freezemode) @@ -187,27 +187,27 @@ static void revmodel_setmode(struct revmodel *rev, float value) static void revmodel_init(struct revmodel *rev,int srate) { - unsigned c; - static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 }; - static const int allpass_lengths[4] = { 225,341,441,556 }; - double r = srate * (1 / 44100.0); + unsigned c; + static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 }; + static const int allpass_lengths[4] = { 225,341,441,556 }; + double r = srate * (1 / 44100.0); for (c = 0; c < numcombs; ++c) { - rev->bufcomb[c] = (float*)malloc(r * comb_lengths[c] * sizeof(float)); - rev->combL[c].buffer = rev->bufcomb[c]; - memset(rev->combL[c].buffer, 0, r * comb_lengths[c] * sizeof(float)); - rev->combL[c].bufsize=r*comb_lengths[c]; - } + unsigned bufsize = (unsigned)(r * comb_lengths[c]); + rev->bufcomb[c] = (float*)calloc(bufsize, sizeof(float)); + rev->combL[c].buffer = rev->bufcomb[c]; + rev->combL[c].bufsize = bufsize; + } for (c = 0; c < numallpasses; ++c) { - rev->bufallpass[c] = (float*)malloc(r * allpass_lengths[c] * sizeof(float)); - rev->allpassL[c].buffer = rev->bufallpass[c]; - memset(rev->allpassL[c].buffer, 0, r * allpass_lengths[c] * sizeof(float)); - rev->allpassL[c].bufsize=r*allpass_lengths[c]; + unsigned bufsize = (unsigned)(r * allpass_lengths[c]); + rev->bufallpass[c] = (float*)calloc(bufsize, sizeof(float)); + rev->allpassL[c].buffer = rev->bufallpass[c]; + rev->allpassL[c].bufsize = bufsize; rev->allpassL[c].feedback = 0.5f; - } + } revmodel_setwet(rev, initialwet); revmodel_setroomsize(rev, initialroom); @@ -224,7 +224,7 @@ struct reverb_data static void reverb_free(void *data) { - int i; + unsigned i; struct reverb_data *rev = (struct reverb_data*)data; for (i = 0; i < numcombs; i++) @@ -244,7 +244,7 @@ static void reverb_free(void *data) static void reverb_process(void *data, struct dspfilter_output *output, const struct dspfilter_input *input) { - int i; + unsigned i; float *out; struct reverb_data *rev = (struct reverb_data*)data; diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index dde9771aa8c6..c1eb0bd91f6d 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -1632,7 +1632,7 @@ static uint32_t header_guess_unitbytes(chd_file *chd) { /* look for hard disk metadata; if found, then the unit size == sector size */ char metadata[512]; - int i0, i1, i2, i3; + unsigned int i0, i1, i2, i3; if (chd_get_metadata(chd, HARD_DISK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE && sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) return i3; diff --git a/libretro-common/gfx/scaler/pixconv.c b/libretro-common/gfx/scaler/pixconv.c index 909cdbf153dc..2563c046027b 100644 --- a/libretro-common/gfx/scaler/pixconv.c +++ b/libretro-common/gfx/scaler/pixconv.c @@ -442,10 +442,11 @@ void conv_rgba4444_argb8888(void *output_, const void *input_, const __m64 pix_mask_r = _mm_set1_pi16(0xf << 10); const __m64 pix_mask_g = _mm_set1_pi16(0xf << 8); const __m64 pix_mask_b = _mm_set1_pi16(0xf << 8); + const __m64 pix_mask_a = _mm_set1_pi16(0x000f); const __m64 mul16_r = _mm_set1_pi16(0x0440); const __m64 mul16_g = _mm_set1_pi16(0x1100); const __m64 mul16_b = _mm_set1_pi16(0x1100); - const __m64 a = _mm_set1_pi16(0x00ff); + const __m64 mul_a = _mm_set1_pi16(0x0011); int max_width = width - 3; #endif @@ -463,10 +464,15 @@ void conv_rgba4444_argb8888(void *output_, const void *input_, __m64 r = _mm_and_si64(_mm_srli_pi16(in, 2), pix_mask_r); __m64 g = _mm_and_si64(in, pix_mask_g); __m64 b = _mm_and_si64(_mm_slli_pi16(in, 4), pix_mask_b); + /* Source is rgba4444 -- alpha is the low nibble of each 16-bit + * input word. Expand 4-bit -> 8-bit via a*0x11 (== a<<4 | a), + * matching the scalar fallback. */ + __m64 a = _mm_and_si64(in, pix_mask_a); r = _mm_mulhi_pi16(r, mul16_r); g = _mm_mulhi_pi16(g, mul16_g); b = _mm_mulhi_pi16(b, mul16_b); + a = _mm_mullo_pi16(a, mul_a); res_lo_bg = _mm_unpacklo_pi8(b, g); res_hi_bg = _mm_unpackhi_pi8(b, g); diff --git a/libretro-common/include/libchdr/chd.h b/libretro-common/include/libchdr/chd.h index c8fcc13a35a5..e47036efede5 100644 --- a/libretro-common/include/libchdr/chd.h +++ b/libretro-common/include/libchdr/chd.h @@ -227,7 +227,7 @@ extern "C" { /* standard hard disk metadata */ #define HARD_DISK_METADATA_TAG CHD_MAKE_TAG('G','D','D','D') -#define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d" +#define HARD_DISK_METADATA_FORMAT "CYLS:%u,HEADS:%u,SECS:%u,BPS:%u" /* hard disk identify information */ #define HARD_DISK_IDENT_METADATA_TAG CHD_MAKE_TAG('I','D','N','T') diff --git a/libretro-common/include/retro_spsc.h b/libretro-common/include/retro_spsc.h index 9191d1f0e656..edb86d4a3001 100644 --- a/libretro-common/include/retro_spsc.h +++ b/libretro-common/include/retro_spsc.h @@ -97,7 +97,7 @@ #include #include -#include +#include #include #include diff --git a/libretro-common/vfs/vfs_implementation_smb.c b/libretro-common/vfs/vfs_implementation_smb.c index 65f4bc2a549e..1dffffd79d72 100644 --- a/libretro-common/vfs/vfs_implementation_smb.c +++ b/libretro-common/vfs/vfs_implementation_smb.c @@ -36,7 +36,7 @@ static bool smb_initialized = false; static int max_context_configured = 0; static const struct smb_settings *smb_cfg = NULL; -static struct smb2_context *get_smb_context() +static struct smb2_context *get_smb_context(void) { int idx; @@ -81,7 +81,7 @@ bool smb_init_cfg(const struct smb_settings *new_cfg) } /* Initialize SMB context */ -static bool smb_init() +static bool smb_init(void) { char server[256]; char share[256]; @@ -220,7 +220,7 @@ void smb_close_context(int index) } /* Shutdown SMB context - called on exit */ -void smb_shutdown() +void smb_shutdown(void) { int i; diff --git a/libretro-common/vfs/vfs_implementation_smb.h b/libretro-common/vfs/vfs_implementation_smb.h index aeefc333ebe9..15c2f2cbff68 100644 --- a/libretro-common/vfs/vfs_implementation_smb.h +++ b/libretro-common/vfs/vfs_implementation_smb.h @@ -66,7 +66,7 @@ int retro_vfs_stat_smb(const char *path, int64_t *size); int retro_vfs_file_error_smb(libretro_vfs_implementation_file *stream); /* Context management */ -void smb_shutdown(); +void smb_shutdown(void); #ifdef __cplusplus } diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c index 8cb133f30194..3621b497ead0 100644 --- a/menu/drivers/materialui.c +++ b/menu/drivers/materialui.c @@ -10784,7 +10784,6 @@ static int materialui_pointer_down(void *userdata, int drag_margin_vert; gfx_display_t *p_disp = disp_get_ptr(); unsigned header_height = p_disp->header_height; - unsigned width = mui->last_width; unsigned height = mui->last_height; /* Check whether pointer down event is within diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index 1e9c9ae1ae01..4bbf0f692915 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -7458,7 +7458,7 @@ static void ozone_draw_messagebox( unsigned slice_new_w = longest_width + (slice_margin * 2); unsigned slice_new_h = line_height * (line_count + 2) + (slice_margin / 2); unsigned slice_w = 256; - int slice_x = x - (longest_width / 2) - slice_margin; + int slice_x = (int)(x - (longest_width / 2) - slice_margin); int slice_y = y - line_height - (slice_margin / 4) + ((slice_new_h >= slice_w) ? (16.0f * scale_factor) diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 9e784c271f1f..d478109b6da7 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -2972,7 +2972,7 @@ static void rgui_render_mini_thumbnail( else fb_y_offset = (rgui->term_layout.start_y + term_height) - thumbnail->max_height; - text_x = (thumbnail->max_width / 2) + fb_x_offset - (strlen(msg) * (rgui->font_width_stride / 2)); + text_x = (thumbnail->max_width / 2) + fb_x_offset - ((int)strlen(msg) * (rgui->font_width_stride / 2)); text_y = (thumbnail->max_height / 2) + fb_y_offset - (rgui->font_height_stride / 3); /* Draw background */ @@ -5543,9 +5543,9 @@ static void rgui_render(void *data, unsigned width, unsigned height, rgui_blit_line(rgui, fb_width, - term_end_x + (int)(term_end_x - (powerstate_len * rgui->font_width_stride) - - (timedate_len * rgui->font_width_stride), + - (timedate_len * rgui->font_width_stride)), title_y, timedate, rgui->colors.hover_color, diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index c39491a593aa..aa007eb73f9a 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -2549,7 +2549,7 @@ static void xmb_set_title(xmb_handle_t *xmb) if (xmb->categories_selection_ptr > xmb->system_tab_end) { xmb_node_t *sidebar_node = NULL; - int i = xmb->categories_selection_ptr - xmb->system_tab_end - 1; + size_t i = xmb->categories_selection_ptr - xmb->system_tab_end - 1; /* Explore views */ if (string_ends_with_size(xmb->horizontal_list.list[i].label, ".lvw", diff --git a/menu/menu_explore.c b/menu/menu_explore.c index 178598a163db..57518aa92797 100644 --- a/menu/menu_explore.c +++ b/menu/menu_explore.c @@ -1489,8 +1489,9 @@ unsigned menu_displaylist_explore(file_list_t *list, settings_t *settings) && !explore_by_info[cat].is_boolean && RBUF_LEN(state->by[cat]) > 1)) { - size_t _len = strlcpy(tmp, - msg_hash_to_str(explore_by_info[cat].by_enum), sizeof(tmp)); + size_t _len = 0; + strlcpy_append(tmp, sizeof(tmp), &_len, + msg_hash_to_str(explore_by_info[cat].by_enum)); if (is_top) { @@ -1502,20 +1503,21 @@ unsigned menu_displaylist_explore(file_list_t *list, settings_t *settings) entries[RBUF_LEN(entries) - 1]->str); else if (!explore_by_info[cat].is_boolean) { - _len += strlcpy (tmp + _len, " (", sizeof(tmp) - _len); - _len += snprintf(tmp + _len, sizeof(tmp) - _len, + strlcpy_append(tmp, sizeof(tmp), &_len, " ("); + _len += snprintf(tmp + _len, sizeof(tmp) - _len, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ITEMS_COUNT), (unsigned)RBUF_LEN(entries)); - strlcpy(tmp + _len, ")", sizeof(tmp) - _len); + if (_len >= sizeof(tmp)) + _len = sizeof(tmp) - 1; + strlcpy_append(tmp, sizeof(tmp), &_len, ")"); } } else if (i != state->view_levels) { - _len += strlcpy(tmp + _len, " (", sizeof(tmp) - _len); - _len += strlcpy(tmp + _len, - msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER), - sizeof(tmp) - _len); - strlcpy(tmp + _len, ")", sizeof(tmp) - _len); + strlcpy_append(tmp, sizeof(tmp), &_len, " ("); + strlcpy_append(tmp, sizeof(tmp), &_len, + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER)); + strlcpy_append(tmp, sizeof(tmp), &_len, ")"); } explore_menu_entry(list, state, diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 604576241f3d..e5864731ccc6 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -22,6 +22,7 @@ #include #include +#include #ifdef HAVE_CONFIG_H #include "../../config.h" @@ -37,6 +38,8 @@ #include "../natt.h" +RETRO_BEGIN_DECLS + typedef struct netplay netplay_t; typedef struct netplay_client_info @@ -213,4 +216,7 @@ bool netplay_discovery_driver_ctl(enum rarch_netplay_discovery_ctl_state state, #endif extern const mitm_server_t netplay_mitm_server_list[NETPLAY_MITM_SERVERS]; + +RETRO_END_DECLS + #endif diff --git a/retroarch.c b/retroarch.c index 6c029f0c4d92..febf11167ab1 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1261,7 +1261,7 @@ static size_t find_driver_nonempty( int driver_find_index(const char *label, const char *drv) { - size_t i; + int i; char str[NAME_MAX_LENGTH]; str[0] = '\0'; @@ -1288,7 +1288,7 @@ int driver_find_index(const char *label, const char *drv) **/ static void driver_find_last(const char *label, char *s, size_t len) { - size_t i; + int i; for (i = 0; find_driver_nonempty(label, i, s, len) > 0; i++) { } if (i) @@ -1805,7 +1805,7 @@ void drivers_init( #endif #ifdef HAVE_MENU - srand(time(NULL)); + srand((unsigned)time(NULL)); #endif } @@ -5408,7 +5408,7 @@ bool command_event(enum event_command cmd, void *data) break; case CMD_EVENT_RUMBLE_STOP: { - size_t i; + unsigned i; for (i = 0; i < MAX_USERS; i++) { unsigned joy_idx = settings->uints.input_joypad_index[i]; @@ -6749,7 +6749,7 @@ static void retroarch_print_help(const char *arg0) fprintf(stdout, "Usage: %s [OPTIONS]... [FILE]\n\n", arg0); - _len = strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -h, --help " "Show this help message.\n" " -v, --verbose " @@ -6759,30 +6759,29 @@ static void retroarch_print_help(const char *arg0) " -V, --version " "Show version.\n" " --features " - "Print available features compiled into program.\n" - , sizeof(buf) - _len); + "Print available features compiled into program.\n"); #ifdef HAVE_MENU - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --menu " "Do not require content or libretro core to be loaded,\n" " " " starts directly in menu. If no arguments are passed to\n" " " - " the program, it is equivalent to using --menu as only argument.\n" - , sizeof(buf) - _len); + " the program, it is equivalent to using --menu as only argument.\n"); #endif #ifdef HAVE_CONFIGFILE - _len += strlcpy(buf + _len, " -c, --config=FILE " - "Path for config file.\n", sizeof(buf) - _len); + strlcpy_append(buf, sizeof(buf), &_len, + " -c, --config=FILE " + "Path for config file.\n"); #ifdef _WIN32 - _len += strlcpy(buf + _len, " " + strlcpy_append(buf, sizeof(buf), &_len, + " " " Defaults to retroarch.cfg in same directory as retroarch.exe.\n" " " - " If a default config is not found, the program will attempt to create one.\n" - , sizeof(buf) - _len); + " If a default config is not found, the program will attempt to create one.\n"); #else - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " " " By default looks for config in\n" " " @@ -6794,33 +6793,31 @@ static void retroarch_print_help(const char *arg0) " " " If a default config is not found, the program will attempt to create one\n" " " - " based on the skeleton config (" GLOBAL_CONFIG_DIR "/retroarch.cfg).\n" - , sizeof(buf) - _len); + " based on the skeleton config (" GLOBAL_CONFIG_DIR "/retroarch.cfg).\n"); #endif - _len += strlcpy(buf + _len, " --appendconfig=FILE " + strlcpy_append(buf, sizeof(buf), &_len, + " --appendconfig=FILE " "Extra config files are loaded in, and take priority over\n" " " " config selected in -c (or default). Multiple configs are\n" " " - " delimited by '|'.\n" - , sizeof(buf) - _len); + " delimited by '|'.\n"); #endif fputs(buf, stdout); buf[0] = '\0'; _len = 0; - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --subsystem=NAME " "Use a subsystem of the libretro core. Multiple content\n" " " " files are loaded as multiple arguments. If a content\n" " " - " file is skipped, use a blank (\"\") command line argument.\n" - , sizeof(buf) - _len); + " file is skipped, use a blank (\"\") command line argument.\n"); #ifdef HAVE_DYNAMIC - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -L, --libretro=FILE " "Path to libretro implementation. Overrides any config setting.\n" " " @@ -6828,35 +6825,31 @@ static void retroarch_print_help(const char *arg0) " " " 1. The full path to a core shared object library: path/to/_libretro.\n" " " - " 2. A core shared object library 'file name' (*): _libretro.\n" - , sizeof(buf) - _len); - _len += strlcpy(buf + _len, + " 2. A core shared object library 'file name' (*): _libretro.\n"); + strlcpy_append(buf, sizeof(buf), &_len, " " " 3. A core 'short name' (*): _libretro OR \n" " " " (*) If 'file name' or 'short name' do not correspond to an existing full file path,\n" " " - " the configured frontend 'cores' directory will be searched for a match.\n" - , sizeof(buf) - _len); + " the configured frontend 'cores' directory will be searched for a match.\n"); #endif - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " " " Content must be loaded in an order which depends on the\n" " " " particular subsystem used. See verbose log output to learn\n" " " - " how a particular subsystem wants content to be loaded.\n" - , sizeof(buf) - _len); + " how a particular subsystem wants content to be loaded.\n"); #ifdef HAVE_LIBRETRODB - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --scan=PATH|FILE " - "Import content from path.\n" - , sizeof(buf) - _len); + "Import content from path.\n"); #endif - strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -f, --fullscreen " "Start the program in fullscreen regardless of config setting.\n" " --set-shader=PATH " @@ -6864,8 +6857,7 @@ static void retroarch_print_help(const char *arg0) " " " Effectively overrides automatic shader presets.\n" " " - " An empty argument \"\" will disable automatic shader presets.\n" - , sizeof(buf) - _len); + " An empty argument \"\" will disable automatic shader presets.\n"); fputs(buf, stdout); buf[0] = '\0'; @@ -6873,12 +6865,18 @@ static void retroarch_print_help(const char *arg0) _len += snprintf(buf + _len, sizeof(buf) - _len," -N, --nodevice=PORT " "Disconnects controller device connected to PORT (1 to %d).\n", MAX_USERS); + if (_len >= sizeof(buf)) + _len = sizeof(buf) - 1; _len += snprintf(buf + _len, sizeof(buf) - _len," -A, --dualanalog=PORT " "Connect a DualAnalog controller to PORT (1 to %d).\n", MAX_USERS); + if (_len >= sizeof(buf)) + _len = sizeof(buf) - 1; _len += snprintf(buf + _len, sizeof(buf) - _len," -d, --device=PORT:ID " "Connect a generic device into PORT of the device (1 to %d).\n", MAX_USERS); + if (_len >= sizeof(buf)) + _len = sizeof(buf) - 1; - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " " " Format is PORT:ID, where ID is a number corresponding to the particular device.\n" " -M, --sram-mode=MODE " @@ -6886,11 +6884,10 @@ static void retroarch_print_help(const char *arg0) " " " 'noload-nosave', 'noload-save', 'load-nosave' or 'load-save'.\n" " " - " Note: 'noload-save' implies that save files *WILL BE OVERWRITTEN*.\n" - , sizeof(buf) - _len); + " Note: 'noload-save' implies that save files *WILL BE OVERWRITTEN*.\n"); #ifdef HAVE_NETWORKING - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -H, --host " "Host netplay as user 1.\n" " -C, --connect=HOST " @@ -6902,87 +6899,77 @@ static void retroarch_print_help(const char *arg0) " --nick=NICK " "Picks a username (for use with netplay). Not mandatory.\n" " --check-frames=NUMBER " - "Check frames when using netplay.\n" - , sizeof(buf) - _len); + "Check frames when using netplay.\n"); #ifdef HAVE_NETWORK_CMD - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --command " "Sends a command over UDP to an already running program process.\n" " " - " Available commands are listed if command is invalid.\n" - , sizeof(buf) - _len); + " Available commands are listed if command is invalid.\n"); #endif #endif #ifdef HAVE_BSV_MOVIE - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -P, --play-replay=FILE " "Playback a replay file.\n" " -R, --record-replay=FILE " "Start recording a replay file from the beginning.\n" " --eof-exit " - "Exit upon reaching the end of the replay file.\n" - , sizeof(buf) - _len); + "Exit upon reaching the end of the replay file.\n"); #endif - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -r, --record=FILE " "Path to record video file. Using mkv extension is recommended.\n" " --recordconfig " "Path to settings used during recording.\n" " --size=WIDTHxHEIGHT " - "Overrides output video size when recording.\n" - , sizeof(buf) - _len); + "Overrides output video size when recording.\n"); fputs(buf, stdout); buf[0] = '\0'; _len = 0; - _len = strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -D, --detach " "Detach program from the running console. Not relevant for all platforms.\n" " --max-frames=NUMBER " - "Runs for the specified number of frames, then exits.\n" - , sizeof(buf) - _len); + "Runs for the specified number of frames, then exits.\n"); #ifdef HAVE_PATCH - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " -U, --ups=FILE " "Specifies path for UPS patch that will be applied to content.\n" " --bps=FILE " "Specifies path for BPS patch that will be applied to content.\n" " --ips=FILE " - "Specifies path for IPS patch that will be applied to content.\n" - , sizeof(buf) - _len); + "Specifies path for IPS patch that will be applied to content.\n"); #ifdef HAVE_XDELTA - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --xdelta=FILE " - "Specifies path for Xdelta patch that will be applied to content.\n" - , sizeof(buf) - _len); + "Specifies path for Xdelta patch that will be applied to content.\n"); #endif /* HAVE_XDELTA */ - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --no-patch " - "Disables all forms of content patching.\n" - , sizeof(buf) - _len); + "Disables all forms of content patching.\n"); #endif /* HAVE_PATCH */ #ifdef HAVE_SCREENSHOTS - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --max-frames-ss " "Takes a screenshot at the end of max-frames.\n" " --max-frames-ss-path=FILE " - "Path to save the screenshot to at the end of max-frames.\n" - , sizeof(buf) - _len); + "Path to save the screenshot to at the end of max-frames.\n"); #endif #ifdef HAVE_ACCESSIBILITY - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --accessibility " - "Enables accessibility for blind users using text-to-speech.\n" - , sizeof(buf) - _len); + "Enables accessibility for blind users using text-to-speech.\n"); #endif - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, " --load-menu-on-error " "Open menu instead of quitting if specified core or content fails to load.\n" " -e, --entryslot=NUMBER " @@ -6990,8 +6977,7 @@ static void retroarch_print_help(const char *arg0) " -s, --save=PATH " "Path for save files (*.srm). (DEPRECATED, use --appendconfig and savefile_directory)\n" " -S, --savestate=PATH " - "Path for the save state files (*.state). (DEPRECATED, use --appendconfig and savestate_directory)\n" - , sizeof(buf) - _len); + "Path for the save state files (*.state). (DEPRECATED, use --appendconfig and savestate_directory)\n"); /* Flush buffer here to avoid the error "error: string length ‘752’ * is greater than the length ‘509’ ISO C90 compilers are required @@ -7001,7 +6987,7 @@ static void retroarch_print_help(const char *arg0) #if defined(__linux__) || defined(__GNU__) || (defined(BSD) && !defined(__MACH__)) buf[0] = '\0'; _len = 0; - _len += strlcpy(buf + _len, + strlcpy_append(buf, sizeof(buf), &_len, "\nThe following environment variables are supported:\n\n" " LIBRETRO_ASSETS_DIRECTORY\n" " LIBRETRO_AUTOCONFIG_DIRECTORY\n" @@ -7011,8 +6997,7 @@ static void retroarch_print_help(const char *arg0) " LIBRETRO_SYSTEM_DIRECTORY\n" " LIBRETRO_VIDEO_FILTER_DIRECTORY\n" " LIBRETRO_VIDEO_SHADER_DIRECTORY\n\n" - "Refer to `man 6 retroarch' for a description of what they do.\n" - , sizeof(buf) - _len); + "Refer to `man 6 retroarch' for a description of what they do.\n"); fputs(buf, stdout); #endif } diff --git a/runahead.c b/runahead.c index 3dbb6557914f..68b87dfba052 100644 --- a/runahead.c +++ b/runahead.c @@ -1612,7 +1612,7 @@ static INLINE void preempt_input_poll(preempt_t *preempt, for (p = 0; p < max_users; p++) { /* Check full digital joypad */ - int16_t joypad_state = (int16_t)(state_cb(p, RETRO_DEVICE_JOYPAD, + int16_t joypad_state = (int16_t)(state_cb((unsigned)p, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK)); if (joypad_state != preempt->joypad_state[p]) { diff --git a/runloop.c b/runloop.c index c8078db282fe..3f83d36a6dca 100644 --- a/runloop.c +++ b/runloop.c @@ -2752,7 +2752,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) memcpy(sys_info->subsystem.data, info, i * sizeof(*sys_info->subsystem.data)); - sys_info->subsystem.size = i; + sys_info->subsystem.size = (unsigned)i; runloop_st->current_core.flags |= RETRO_CORE_FLAG_HAS_SET_SUBSYSTEMS; } @@ -2797,7 +2797,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) sys_info->ports.data = info_ptr; memcpy(sys_info->ports.data, info, i * sizeof(*sys_info->ports.data)); - sys_info->ports.size = i; + sys_info->ports.size = (unsigned)i; } break; } diff --git a/tasks/task_content.c b/tasks/task_content.c index 66adf78c4c97..7f7130bf7335 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -2614,7 +2614,7 @@ void content_set_subsystem(unsigned idx) /* Sets the subsystem by name */ bool content_set_subsystem_by_name(const char* subsystem_name) { - size_t i; + unsigned i; runloop_state_t *runloop_st = runloop_state_get_ptr(); rarch_system_info_t *sys_info = &runloop_st->system; /* Core not loaded completely, use the data we peeked on load core */ diff --git a/tasks/task_movie.c b/tasks/task_movie.c index 6e6861459ef2..cad3ff00505e 100644 --- a/tasks/task_movie.c +++ b/tasks/task_movie.c @@ -415,7 +415,9 @@ bool movie_stop_record(input_driver_state_t *input_st) uint32s_index_print_count_data(movie->blocks); #endif #endif - frame_count = swap_if_big32(movie->frame_counter); + if (movie->frame_counter > UINT32_MAX) + RARCH_ERR("[Replay] Frame counter too big to fit in 32 bits\n"); + frame_count = swap_if_big32((uint32_t)movie->frame_counter); intfstream_seek(movie->file, REPLAY_HEADER_FRAME_COUNT_INDEX*sizeof(uint32_t), SEEK_SET); intfstream_write(movie->file, &frame_count, sizeof(uint32_t)); bsv_movie_deinit_full(input_st); diff --git a/ui/drivers/ui_qt_widgets.cpp b/ui/drivers/ui_qt_widgets.cpp index 7c4513ef883e..fb26805e0c2a 100644 --- a/ui/drivers/ui_qt_widgets.cpp +++ b/ui/drivers/ui_qt_widgets.cpp @@ -32,19 +32,12 @@ #include "ui_qt_widgets.h" #include "ui_qt.h" -#ifndef CXX_BUILD -extern "C" { -#endif - -#include - -#include -#include -#include -#include -#include -#include - +/* RetroArch-internal headers are included here, OUTSIDE the extern "C" + * block below. They self-guard their C declarations with + * RETRO_BEGIN_DECLS / RETRO_END_DECLS, and several of them transitively + * include libretro-common/include/retro_atomic.h, which in C++ mode + * pulls in the C++ header. Templates inside extern "C" are a + * hard error, so these must live outside the extern "C" block. */ #include "../../config.def.h" #include "../../command.h" #include "../../core_info.h" @@ -70,6 +63,19 @@ extern "C" { #endif #endif +#ifndef CXX_BUILD +extern "C" { +#endif + +#include + +#include +#include +#include +#include +#include +#include + #ifndef CXX_BUILD } #endif