From fa76a3cb608cec43f43bfd868d664ad4b892c334 Mon Sep 17 00:00:00 2001 From: misson20000 Date: Sat, 15 Sep 2018 03:27:05 -0700 Subject: [PATCH] CRLF -> LF --- audio/drivers/switch_nx_thread_audio.c | 716 +++++----- frontend/drivers/platform_switch.c | 1474 ++++++++++---------- gfx/common/switch_common.h | 118 +- gfx/drivers/switch_nx_gfx.c | 1544 ++++++++++----------- libretro-common/rthreads/switch_pthread.h | 414 +++--- 5 files changed, 2133 insertions(+), 2133 deletions(-) diff --git a/audio/drivers/switch_nx_thread_audio.c b/audio/drivers/switch_nx_thread_audio.c index d6acaa73ce..72a8fc4869 100644 --- a/audio/drivers/switch_nx_thread_audio.c +++ b/audio/drivers/switch_nx_thread_audio.c @@ -1,358 +1,358 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2018 - misson2000 - * Copyright (C) 2018 - m4xw - * Copyright (C) 2018 - lifajucejo - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include "../audio_driver.h" -#include "../../verbosity.h" - -#include "../../tasks/tasks_internal.h" - -#define THREAD_STACK_SIZE (1024 * 8) - -#define AUDIO_THREAD_CPU 2 - -#define CHANNELCOUNT 2 -#define BYTESPERSAMPLE sizeof(uint16_t) -#define SAMPLE_SIZE (CHANNELCOUNT * BYTESPERSAMPLE) - -#define AUDIO_BUFFER_COUNT 2 - -static inline void lockMutex(Mutex* mtx) -{ - mutexLock(mtx); -} - -typedef struct -{ - fifo_buffer_t* fifo; - Mutex fifoLock; - CondVar cond; - Mutex condLock; - - size_t fifoSize; - - volatile bool running; - bool nonblocking; - bool is_paused; - - AudioOutBuffer buffer[AUDIO_BUFFER_COUNT]; - Thread thread; - - unsigned latency; - uint32_t sampleRate; -} switch_thread_audio_t; - -static void mainLoop(void* data) -{ - switch_thread_audio_t* swa = (switch_thread_audio_t*)data; - - if (!swa) - return; - - RARCH_LOG("[Audio]: start mainLoop cpu %u tid %u\n", svcGetCurrentProcessorNumber(), swa->thread.handle); - - for (int i = 0; i < AUDIO_BUFFER_COUNT; i++) - { - swa->buffer[i].next = NULL; /* Unused */ - swa->buffer[i].buffer_size = swa->fifoSize; - swa->buffer[i].buffer = memalign(0x1000, swa->buffer[i].buffer_size); - swa->buffer[i].data_size = swa->buffer[i].buffer_size; - swa->buffer[i].data_offset = 0; - - memset(swa->buffer[i].buffer, 0, swa->buffer[i].buffer_size); - audoutAppendAudioOutBuffer(&swa->buffer[i]); - } - - AudioOutBuffer* released_out_buffer = NULL; - u32 released_out_count; - Result rc; - - while (swa->running) - { - if (!released_out_buffer) - { - rc = audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX); - if (R_FAILED(rc)) - { - swa->running = false; - RARCH_LOG("[Audio]: audoutGetReleasedAudioOutBuffer failed: %d\n", (int)rc); - break; - } - released_out_buffer->data_size = 0; - } - - size_t bufAvail = released_out_buffer->buffer_size - released_out_buffer->data_size; - - lockMutex(&swa->fifoLock); - - size_t avail = fifo_read_avail(swa->fifo); - size_t to_write = MIN(avail, bufAvail); - if (to_write > 0) - fifo_read(swa->fifo, ((u8*)released_out_buffer->buffer) + released_out_buffer->data_size, to_write); - - mutexUnlock(&swa->fifoLock); - condvarWakeAll(&swa->cond); - - released_out_buffer->data_size += to_write; - if (released_out_buffer->data_size >= released_out_buffer->buffer_size / 2) - { - rc = audoutAppendAudioOutBuffer(released_out_buffer); - if (R_FAILED(rc)) - { - RARCH_LOG("[Audio]: audoutAppendAudioOutBuffer failed: %d\n", (int)rc); - } - released_out_buffer = NULL; - } - else - svcSleepThread(16000000); /* 16ms */ - } -} - -static void *switch_thread_audio_init(const char *device, unsigned rate, unsigned latency, unsigned block_frames, unsigned *new_rate) -{ - (void)device; - - switch_thread_audio_t *swa = (switch_thread_audio_t *)calloc(1, sizeof(switch_thread_audio_t)); - - if (!swa) - return NULL; - - swa->running = true; - swa->nonblocking = true; - swa->is_paused = true; - swa->latency = MAX(latency, 8); - - Result rc = audoutInitialize(); - if (R_FAILED(rc)) - { - RARCH_LOG("[Audio]: audio init failed %d\n", (int)rc); - return NULL; - } - - rc = audoutStartAudioOut(); - if (R_FAILED(rc)) - { - RARCH_LOG("[Audio]: audio start init failed: %d\n", (int)rc); - return NULL; - } - - swa->sampleRate = audoutGetSampleRate(); - *new_rate = swa->sampleRate; - - mutexInit(&swa->fifoLock); - swa->fifoSize = (swa->sampleRate * SAMPLE_SIZE * swa->latency) / 1000; - swa->fifo = fifo_new(swa->fifoSize); - - condvarInit(&swa->cond); - - RARCH_LOG("[Audio]: switch_thread_audio_init device %s requested rate %hu rate %hu latency %hu block_frames %hu fifoSize %lu\n", - device, rate, swa->sampleRate, swa->latency, block_frames, swa->fifoSize); - - u32 prio; - svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); - rc = threadCreate(&swa->thread, &mainLoop, (void*)swa, THREAD_STACK_SIZE, prio + 1, AUDIO_THREAD_CPU); - - if (R_FAILED(rc)) - { - RARCH_LOG("[Audio]: thread creation failed create %u\n", swa->thread.handle); - swa->running = false; - return NULL; - } - - if (R_FAILED(threadStart(&swa->thread))) - { - RARCH_LOG("[Audio]: thread creation failed start %u\n", swa->thread.handle); - threadClose(&swa->thread); - swa->running = false; - return NULL; - } - - return swa; -} - -static bool switch_thread_audio_start(void *data, bool is_shutdown) -{ - /* RARCH_LOG("[Audio]: switch_thread_audio_start\n"); */ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (!swa) - return false; - - swa->is_paused = false; - return true; -} - -static bool switch_thread_audio_stop(void *data) -{ - switch_thread_audio_t* swa = (switch_thread_audio_t*)data; - - if (!swa) - return false; - - swa->is_paused = true; - return true; -} - -static void switch_thread_audio_free(void *data) -{ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (!swa) - return; - - if (swa->running) - { - swa->running = false; - threadWaitForExit(&swa->thread); - threadClose(&swa->thread); - } - - audoutStopAudioOut(); - audoutExit(); - - if (swa->fifo) - { - fifo_free(swa->fifo); - swa->fifo = NULL; - } - - for (int i = 0; i < AUDIO_BUFFER_COUNT; i++) - free(swa->buffer[i].buffer); - - free(swa); - swa = NULL; -} - -static ssize_t switch_thread_audio_write(void *data, const void *buf, size_t size) -{ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (!swa || !swa->running) - return 0; - - size_t avail; - size_t written; - - if (swa->nonblocking) - { - lockMutex(&swa->fifoLock); - avail = fifo_write_avail(swa->fifo); - written = MIN(avail, size); - if (written > 0) - { - fifo_write(swa->fifo, buf, written); - } - mutexUnlock(&swa->fifoLock); - } - else - { - written = 0; - while (written < size && swa->running) - { - lockMutex(&swa->fifoLock); - avail = fifo_write_avail(swa->fifo); - if (avail == 0) - { - mutexUnlock(&swa->fifoLock); - lockMutex(&swa->condLock); - if (swa->running) - condvarWait(&swa->cond, &swa->condLock); - mutexUnlock(&swa->condLock); - } - else - { - size_t write_amt = MIN(size - written, avail); - fifo_write(swa->fifo, (const char*)buf + written, write_amt); - mutexUnlock(&swa->fifoLock); - written += write_amt; - } - } - } - - return written; -} - -static bool switch_thread_audio_alive(void *data) -{ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (!swa) - return false; - - return !swa->is_paused; -} - -static void switch_thread_audio_set_nonblock_state(void *data, bool state) -{ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (swa) - swa->nonblocking = state; -} - -static bool switch_thread_audio_use_float(void *data) -{ - (void)data; - return false; -} - -static size_t switch_thread_audio_write_avail(void *data) -{ - switch_thread_audio_t* swa = (switch_thread_audio_t*)data; - - lockMutex(&swa->fifoLock); - size_t val = fifo_write_avail(swa->fifo); - mutexUnlock(&swa->fifoLock); - - return val; -} - -size_t switch_thread_audio_buffer_size(void *data) -{ - switch_thread_audio_t *swa = (switch_thread_audio_t *)data; - - if (!swa) - return 0; - - return swa->fifoSize; -} - -audio_driver_t audio_switch_thread = { - switch_thread_audio_init, - switch_thread_audio_write, - switch_thread_audio_stop, - switch_thread_audio_start, - switch_thread_audio_alive, - switch_thread_audio_set_nonblock_state, - switch_thread_audio_free, - switch_thread_audio_use_float, - "switch_thread", - NULL, /* device_list_new */ - NULL, /* device_list_free */ - switch_thread_audio_write_avail, - switch_thread_audio_buffer_size -}; - -/* vim: set ts=6 sw=6 sts=6: */ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - misson2000 + * Copyright (C) 2018 - m4xw + * Copyright (C) 2018 - lifajucejo + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include "../audio_driver.h" +#include "../../verbosity.h" + +#include "../../tasks/tasks_internal.h" + +#define THREAD_STACK_SIZE (1024 * 8) + +#define AUDIO_THREAD_CPU 2 + +#define CHANNELCOUNT 2 +#define BYTESPERSAMPLE sizeof(uint16_t) +#define SAMPLE_SIZE (CHANNELCOUNT * BYTESPERSAMPLE) + +#define AUDIO_BUFFER_COUNT 2 + +static inline void lockMutex(Mutex* mtx) +{ + mutexLock(mtx); +} + +typedef struct +{ + fifo_buffer_t* fifo; + Mutex fifoLock; + CondVar cond; + Mutex condLock; + + size_t fifoSize; + + volatile bool running; + bool nonblocking; + bool is_paused; + + AudioOutBuffer buffer[AUDIO_BUFFER_COUNT]; + Thread thread; + + unsigned latency; + uint32_t sampleRate; +} switch_thread_audio_t; + +static void mainLoop(void* data) +{ + switch_thread_audio_t* swa = (switch_thread_audio_t*)data; + + if (!swa) + return; + + RARCH_LOG("[Audio]: start mainLoop cpu %u tid %u\n", svcGetCurrentProcessorNumber(), swa->thread.handle); + + for (int i = 0; i < AUDIO_BUFFER_COUNT; i++) + { + swa->buffer[i].next = NULL; /* Unused */ + swa->buffer[i].buffer_size = swa->fifoSize; + swa->buffer[i].buffer = memalign(0x1000, swa->buffer[i].buffer_size); + swa->buffer[i].data_size = swa->buffer[i].buffer_size; + swa->buffer[i].data_offset = 0; + + memset(swa->buffer[i].buffer, 0, swa->buffer[i].buffer_size); + audoutAppendAudioOutBuffer(&swa->buffer[i]); + } + + AudioOutBuffer* released_out_buffer = NULL; + u32 released_out_count; + Result rc; + + while (swa->running) + { + if (!released_out_buffer) + { + rc = audoutWaitPlayFinish(&released_out_buffer, &released_out_count, U64_MAX); + if (R_FAILED(rc)) + { + swa->running = false; + RARCH_LOG("[Audio]: audoutGetReleasedAudioOutBuffer failed: %d\n", (int)rc); + break; + } + released_out_buffer->data_size = 0; + } + + size_t bufAvail = released_out_buffer->buffer_size - released_out_buffer->data_size; + + lockMutex(&swa->fifoLock); + + size_t avail = fifo_read_avail(swa->fifo); + size_t to_write = MIN(avail, bufAvail); + if (to_write > 0) + fifo_read(swa->fifo, ((u8*)released_out_buffer->buffer) + released_out_buffer->data_size, to_write); + + mutexUnlock(&swa->fifoLock); + condvarWakeAll(&swa->cond); + + released_out_buffer->data_size += to_write; + if (released_out_buffer->data_size >= released_out_buffer->buffer_size / 2) + { + rc = audoutAppendAudioOutBuffer(released_out_buffer); + if (R_FAILED(rc)) + { + RARCH_LOG("[Audio]: audoutAppendAudioOutBuffer failed: %d\n", (int)rc); + } + released_out_buffer = NULL; + } + else + svcSleepThread(16000000); /* 16ms */ + } +} + +static void *switch_thread_audio_init(const char *device, unsigned rate, unsigned latency, unsigned block_frames, unsigned *new_rate) +{ + (void)device; + + switch_thread_audio_t *swa = (switch_thread_audio_t *)calloc(1, sizeof(switch_thread_audio_t)); + + if (!swa) + return NULL; + + swa->running = true; + swa->nonblocking = true; + swa->is_paused = true; + swa->latency = MAX(latency, 8); + + Result rc = audoutInitialize(); + if (R_FAILED(rc)) + { + RARCH_LOG("[Audio]: audio init failed %d\n", (int)rc); + return NULL; + } + + rc = audoutStartAudioOut(); + if (R_FAILED(rc)) + { + RARCH_LOG("[Audio]: audio start init failed: %d\n", (int)rc); + return NULL; + } + + swa->sampleRate = audoutGetSampleRate(); + *new_rate = swa->sampleRate; + + mutexInit(&swa->fifoLock); + swa->fifoSize = (swa->sampleRate * SAMPLE_SIZE * swa->latency) / 1000; + swa->fifo = fifo_new(swa->fifoSize); + + condvarInit(&swa->cond); + + RARCH_LOG("[Audio]: switch_thread_audio_init device %s requested rate %hu rate %hu latency %hu block_frames %hu fifoSize %lu\n", + device, rate, swa->sampleRate, swa->latency, block_frames, swa->fifoSize); + + u32 prio; + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + rc = threadCreate(&swa->thread, &mainLoop, (void*)swa, THREAD_STACK_SIZE, prio + 1, AUDIO_THREAD_CPU); + + if (R_FAILED(rc)) + { + RARCH_LOG("[Audio]: thread creation failed create %u\n", swa->thread.handle); + swa->running = false; + return NULL; + } + + if (R_FAILED(threadStart(&swa->thread))) + { + RARCH_LOG("[Audio]: thread creation failed start %u\n", swa->thread.handle); + threadClose(&swa->thread); + swa->running = false; + return NULL; + } + + return swa; +} + +static bool switch_thread_audio_start(void *data, bool is_shutdown) +{ + /* RARCH_LOG("[Audio]: switch_thread_audio_start\n"); */ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (!swa) + return false; + + swa->is_paused = false; + return true; +} + +static bool switch_thread_audio_stop(void *data) +{ + switch_thread_audio_t* swa = (switch_thread_audio_t*)data; + + if (!swa) + return false; + + swa->is_paused = true; + return true; +} + +static void switch_thread_audio_free(void *data) +{ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (!swa) + return; + + if (swa->running) + { + swa->running = false; + threadWaitForExit(&swa->thread); + threadClose(&swa->thread); + } + + audoutStopAudioOut(); + audoutExit(); + + if (swa->fifo) + { + fifo_free(swa->fifo); + swa->fifo = NULL; + } + + for (int i = 0; i < AUDIO_BUFFER_COUNT; i++) + free(swa->buffer[i].buffer); + + free(swa); + swa = NULL; +} + +static ssize_t switch_thread_audio_write(void *data, const void *buf, size_t size) +{ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (!swa || !swa->running) + return 0; + + size_t avail; + size_t written; + + if (swa->nonblocking) + { + lockMutex(&swa->fifoLock); + avail = fifo_write_avail(swa->fifo); + written = MIN(avail, size); + if (written > 0) + { + fifo_write(swa->fifo, buf, written); + } + mutexUnlock(&swa->fifoLock); + } + else + { + written = 0; + while (written < size && swa->running) + { + lockMutex(&swa->fifoLock); + avail = fifo_write_avail(swa->fifo); + if (avail == 0) + { + mutexUnlock(&swa->fifoLock); + lockMutex(&swa->condLock); + if (swa->running) + condvarWait(&swa->cond, &swa->condLock); + mutexUnlock(&swa->condLock); + } + else + { + size_t write_amt = MIN(size - written, avail); + fifo_write(swa->fifo, (const char*)buf + written, write_amt); + mutexUnlock(&swa->fifoLock); + written += write_amt; + } + } + } + + return written; +} + +static bool switch_thread_audio_alive(void *data) +{ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (!swa) + return false; + + return !swa->is_paused; +} + +static void switch_thread_audio_set_nonblock_state(void *data, bool state) +{ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (swa) + swa->nonblocking = state; +} + +static bool switch_thread_audio_use_float(void *data) +{ + (void)data; + return false; +} + +static size_t switch_thread_audio_write_avail(void *data) +{ + switch_thread_audio_t* swa = (switch_thread_audio_t*)data; + + lockMutex(&swa->fifoLock); + size_t val = fifo_write_avail(swa->fifo); + mutexUnlock(&swa->fifoLock); + + return val; +} + +size_t switch_thread_audio_buffer_size(void *data) +{ + switch_thread_audio_t *swa = (switch_thread_audio_t *)data; + + if (!swa) + return 0; + + return swa->fifoSize; +} + +audio_driver_t audio_switch_thread = { + switch_thread_audio_init, + switch_thread_audio_write, + switch_thread_audio_stop, + switch_thread_audio_start, + switch_thread_audio_alive, + switch_thread_audio_set_nonblock_state, + switch_thread_audio_free, + switch_thread_audio_use_float, + "switch_thread", + NULL, /* device_list_new */ + NULL, /* device_list_free */ + switch_thread_audio_write_avail, + switch_thread_audio_buffer_size +}; + +/* vim: set ts=6 sw=6 sts=6: */ diff --git a/frontend/drivers/platform_switch.c b/frontend/drivers/platform_switch.c index e1250481f9..6c77b6d1d2 100644 --- a/frontend/drivers/platform_switch.c +++ b/frontend/drivers/platform_switch.c @@ -1,737 +1,737 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif - -#ifndef IS_SALAMANDER -#include -#endif - -#include "../frontend_driver.h" -#include "../../verbosity.h" -#include "../../defaults.h" -#include "../../paths.h" -#include "../../retroarch.h" -#include "../../file_path_special.h" -#include "../../audio/audio_driver.h" - -#ifndef IS_SALAMANDER -#ifdef HAVE_MENU -#include "../../menu/menu_driver.h" -#endif -#endif - -static enum frontend_fork switch_fork_mode = FRONTEND_FORK_NONE; -static const char *elf_path_cst = "/switch/retroarch_switch.nro"; - -static uint64_t frontend_switch_get_mem_used(void); - -// Splash -static uint32_t *splashData = NULL; - -// switch_gfx.c protypes, we really need a header -extern void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend); - -static void get_first_valid_core(char *path_return) -{ - DIR *dir; - struct dirent *ent; - const char *extension = ".nro"; - - path_return[0] = '\0'; - - dir = opendir("/retroarch/cores"); - if (dir != NULL) - { - while (ent = readdir(dir)) - { - if (ent == NULL) - break; - if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension)) - { - strcpy(path_return, "/retroarch/cores"); - strcat(path_return, "/"); - strcat(path_return, ent->d_name); - break; - } - } - closedir(dir); - } -} - -static void frontend_switch_get_environment_settings(int *argc, char *argv[], void *args, void *params_data) -{ - (void)args; - -#ifndef IS_SALAMANDER -#if defined(HAVE_LOGGER) - logger_init(); -#elif defined(HAVE_FILE_LOGGER) - retro_main_log_file_init("/retroarch-log.txt"); -#endif -#endif - - fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], "/retroarch/retroarch_switch.nro", sizeof(g_defaults.dirs[DEFAULT_DIR_PORT])); - RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], - "downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], - "media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT], - "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE], - "info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE], - "savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE], - "savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE], - "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE], - "playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT], - "config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT], - "config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT], - "filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT], - "database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); - - fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT], - "database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); - - fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT], - file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config)); -} - -static void frontend_switch_deinit(void *data) -{ - (void)data; - -#if defined(HAVE_LIBNX) && defined(NXLINK) // Else freeze on exit - socketExit(); -#endif - - // Splash - if (splashData) - { - free(splashData); - splashData = NULL; - } - - gfxExit(); -} - -static void frontend_switch_exec(const char *path, bool should_load_game) -{ - char game_path[PATH_MAX]; - const char *arg_data[3]; - char error_string[200 + PATH_MAX]; - int args = 0; - int error = 0; - - game_path[0] = NULL; - arg_data[0] = NULL; - - arg_data[args] = elf_path_cst; - arg_data[args + 1] = NULL; - args++; - - RARCH_LOG("Attempt to load core: [%s].\n", path); -#ifndef IS_SALAMANDER - if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT)) - { - strcpy(game_path, path_get(RARCH_PATH_CONTENT)); - arg_data[args] = game_path; - arg_data[args + 1] = NULL; - args++; - RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT)); - } -#endif - - if (path && path[0]) - { -#ifdef IS_SALAMANDER - struct stat sbuff; - bool file_exists; - - file_exists = stat(path, &sbuff) == 0; - if (!file_exists) - { - char core_path[PATH_MAX]; - - /* find first valid core and load it if the target core doesnt exist */ - get_first_valid_core(&core_path[0]); - - if (core_path[0] == '\0') - { - /*errorInit(&error_dialog, ERROR_TEXT, CFG_LANGUAGE_EN); - errorText(&error_dialog, "There are no cores installed, install a core to continue."); - errorDisp(&error_dialog);*/ - svcExitProcess(); - } - } -#endif - char *argBuffer = (char *)malloc(PATH_MAX); - if (should_load_game) - { - snprintf(argBuffer, PATH_MAX, "%s \"%s\"", path, game_path); - } - else - { - snprintf(argBuffer, PATH_MAX, "%s", path); - } - - envSetNextLoad(path, argBuffer); - } -} - -#ifndef IS_SALAMANDER -static bool frontend_switch_set_fork(enum frontend_fork fork_mode) -{ - switch (fork_mode) - { - case FRONTEND_FORK_CORE: - RARCH_LOG("FRONTEND_FORK_CORE\n"); - switch_fork_mode = fork_mode; - break; - case FRONTEND_FORK_CORE_WITH_ARGS: - RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n"); - switch_fork_mode = fork_mode; - break; - case FRONTEND_FORK_RESTART: - RARCH_LOG("FRONTEND_FORK_RESTART\n"); - /* NOTE: We don't implement Salamander, so just turn - this into FRONTEND_FORK_CORE. */ - switch_fork_mode = FRONTEND_FORK_CORE; - break; - case FRONTEND_FORK_NONE: - default: - return false; - } - - return true; -} -#endif - -static void frontend_switch_exitspawn(char *s, size_t len) -{ - bool should_load_game = false; -#ifndef IS_SALAMANDER - if (switch_fork_mode == FRONTEND_FORK_NONE) - return; - - switch (switch_fork_mode) - { - case FRONTEND_FORK_CORE_WITH_ARGS: - should_load_game = true; - break; - default: - break; - } -#endif - frontend_switch_exec(s, should_load_game); -} - -static void frontend_switch_shutdown(bool unused) -{ - (void)unused; -} - -void argb_to_rgba8(uint32_t *buff, uint32_t height, uint32_t width) -{ - // Convert - for (uint32_t h = 0; h < height; h++) - { - for (uint32_t w = 0; w < width; w++) - { - uint32_t offset = (h * width) + w; - uint32_t c = buff[offset]; - - uint32_t a = (uint32_t)((c & 0xff000000) >> 24); - uint32_t r = (uint32_t)((c & 0x00ff0000) >> 16); - uint32_t g = (uint32_t)((c & 0x0000ff00) >> 8); - uint32_t b = (uint32_t)(c & 0x000000ff); - - buff[offset] = RGBA8(r, g, b, a); - } - } -} - -void frontend_switch_showsplash() -{ - printf("[Splash] Showing splashScreen\n"); - - if (splashData) - { - uint32_t width, height; - width = height = 0; - - uint32_t *frambuffer = (uint32_t *)gfxGetFramebuffer(&width, &height); - - gfx_slow_swizzling_blit(frambuffer, splashData, width, height, 0, 0, false); - - gfxFlushBuffers(); - gfxSwapBuffers(); - gfxWaitForVsync(); - } -} - -// From rpng_test.c -bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height) -{ - int retval; - size_t file_len; - bool ret = true; - rpng_t *rpng = NULL; - void *ptr = NULL; - - struct nbio_t *handle = (struct nbio_t *)nbio_open(path, NBIO_READ); - - if (!handle) - goto end; - - nbio_begin_read(handle); - - while (!nbio_iterate(handle)) - svcSleepThread(3); - - ptr = nbio_get_ptr(handle, &file_len); - - if (!ptr) - { - ret = false; - goto end; - } - - rpng = rpng_alloc(); - - if (!rpng) - { - ret = false; - goto end; - } - - if (!rpng_set_buf_ptr(rpng, (uint8_t *)ptr)) - { - ret = false; - goto end; - } - - if (!rpng_start(rpng)) - { - ret = false; - goto end; - } - - while (rpng_iterate_image(rpng)) - svcSleepThread(3); - - if (!rpng_is_valid(rpng)) - { - ret = false; - goto end; - } - - do - { - retval = rpng_process_image(rpng, (void **)data, file_len, width, height); - svcSleepThread(3); - } while (retval == IMAGE_PROCESS_NEXT); - - if (retval == IMAGE_PROCESS_ERROR || retval == IMAGE_PROCESS_ERROR_END) - ret = false; - -end: - if (handle) - nbio_free(handle); - - if (rpng) - rpng_free(rpng); - - rpng = NULL; - - if (!ret) - free(*data); - - return ret; -} - -int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) -{ - svcSleepThread(rqtp->tv_nsec + (rqtp->tv_sec * 1000000000)); - return 0; -} - -long sysconf(int name) -{ - switch (name) - { - case 8: - return 0x1000; - } - return -1; -} - -ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize) -{ - return -1; -} - -// Taken from glibc -char *realpath(const char *name, char *resolved) -{ - char *rpath, *dest, *extra_buf = NULL; - const char *start, *end, *rpath_limit; - long int path_max; - int num_links = 0; - - if (name == NULL) - { - /* As per Single Unix Specification V2 we must return an error if - either parameter is a null pointer. We extend this to allow - the RESOLVED parameter to be NULL in case the we are expected to - allocate the room for the return value. */ - return NULL; - } - - if (name[0] == '\0') - { - /* As per Single Unix Specification V2 we must return an error if - the name argument points to an empty string. */ - return NULL; - } - -#ifdef PATH_MAX - path_max = PATH_MAX; -#else - path_max = pathconf(name, _PC_PATH_MAX); - if (path_max <= 0) - path_max = 1024; -#endif - - if (resolved == NULL) - { - rpath = malloc(path_max); - if (rpath == NULL) - return NULL; - } - else - rpath = resolved; - rpath_limit = rpath + path_max; - - if (name[0] != '/') - { - if (!getcwd(rpath, path_max)) - { - rpath[0] = '\0'; - goto error; - } - dest = memchr(rpath, '\0', path_max); - } - else - { - rpath[0] = '/'; - dest = rpath + 1; - } - - for (start = end = name; *start; start = end) - { - int n; - - /* Skip sequence of multiple path-separators. */ - while (*start == '/') - ++start; - - /* Find end of path component. */ - for (end = start; *end && *end != '/'; ++end) - /* Nothing. */; - - if (end - start == 0) - break; - else if (end - start == 1 && start[0] == '.') - /* nothing */; - else if (end - start == 2 && start[0] == '.' && start[1] == '.') - { - /* Back up to previous component, ignore if at root already. */ - if (dest > rpath + 1) - while ((--dest)[-1] != '/') - ; - } - else - { - size_t new_size; - - if (dest[-1] != '/') - *dest++ = '/'; - - if (dest + (end - start) >= rpath_limit) - { - ptrdiff_t dest_offset = dest - rpath; - char *new_rpath; - - if (resolved) - { - if (dest > rpath + 1) - dest--; - *dest = '\0'; - goto error; - } - new_size = rpath_limit - rpath; - if (end - start + 1 > path_max) - new_size += end - start + 1; - else - new_size += path_max; - new_rpath = (char *)realloc(rpath, new_size); - if (new_rpath == NULL) - goto error; - rpath = new_rpath; - rpath_limit = rpath + new_size; - - dest = rpath + dest_offset; - } - - dest = memcpy(dest, start, end - start); - *dest = '\0'; - } - } - if (dest > rpath + 1 && dest[-1] == '/') - --dest; - *dest = '\0'; - - return rpath; - -error: - if (resolved == NULL) - free(rpath); - return NULL; -} - -// runloop_get_system_info isnt initialized that early.. -extern void retro_get_system_info(struct retro_system_info *info); - -static void frontend_switch_init(void *data) -{ - (void)data; - - // Init Resolution before initDefault - gfxInitResolution(1280, 720); - - gfxInitDefault(); - gfxSetMode(GfxMode_TiledDouble); - - gfxConfigureTransform(0); - -#if defined(HAVE_LIBNX) && defined(NXLINK) - socketInitializeDefault(); - nxlinkStdio(); -#ifndef IS_SALAMANDER - verbosity_enable(); -#endif -#endif - - rarch_system_info_t *sys_info = runloop_get_system_info(); - retro_get_system_info(sys_info); - - const char *core_name = NULL; - - printf("[Video]: Video initialized\n"); - - uint32_t width, height; - width = height = 0; - - // Load splash - if (!splashData) - { - if (sys_info) - { - core_name = sys_info->info.library_name; - char *full_core_splash_path = (char *)malloc(PATH_MAX); - snprintf(full_core_splash_path, PATH_MAX, "/retroarch/rgui/splash/%s.png", core_name); - - rpng_load_image_argb((const char *)full_core_splash_path, &splashData, &width, &height); - if (splashData) - { - argb_to_rgba8(splashData, height, width); - frontend_switch_showsplash(); - } - else - { - rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height); - if (splashData) - { - argb_to_rgba8(splashData, height, width); - frontend_switch_showsplash(); - } - } - - free(full_core_splash_path); - } - else - { - rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height); - if (splashData) - { - argb_to_rgba8(splashData, height, width); - frontend_switch_showsplash(); - } - } - } - else - { - frontend_switch_showsplash(); - } -} - -static int frontend_switch_get_rating(void) -{ - return 1000; -} - -enum frontend_architecture frontend_switch_get_architecture(void) -{ - return FRONTEND_ARCH_ARMV8; -} - -static int frontend_switch_parse_drive_list(void *data, bool load_content) -{ -#ifndef IS_SALAMANDER - file_list_t *list = (file_list_t *)data; - enum msg_hash_enums enum_idx = load_content ? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR : MSG_UNKNOWN; - - if (!list) - return -1; - - menu_entries_append_enum(list, "/", msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), - enum_idx, - FILE_TYPE_DIRECTORY, 0, 0); -#endif - - return 0; -} - -static uint64_t frontend_switch_get_mem_total(void) -{ - uint64_t memoryTotal = 0; - svcGetInfo(&memoryTotal, 6, CUR_PROCESS_HANDLE, 0); // avaiable - memoryTotal += frontend_switch_get_mem_used(); - - return memoryTotal; -} - -static uint64_t frontend_switch_get_mem_used(void) -{ - uint64_t memoryUsed = 0; - svcGetInfo(&memoryUsed, 7, CUR_PROCESS_HANDLE, 0); // used - - return memoryUsed; -} - -static enum frontend_powerstate frontend_switch_get_powerstate(int *seconds, int *percent) -{ - // This is fine monkaS - return FRONTEND_POWERSTATE_CHARGED; -} - -static void frontend_switch_get_os(char *s, size_t len, int *major, int *minor) -{ - strlcpy(s, "Horizon OS", len); - - // There is pretty sure a better way, but this will do just fine - if (kernelAbove500()) - { - *major = 5; - *minor = 0; - } - else if (kernelAbove400()) - { - *major = 4; - *minor = 0; - } - else if (kernelAbove300()) - { - *major = 3; - *minor = 0; - } - else if (kernelAbove200()) - { - *major = 2; - *minor = 0; - } - else - { - // either 1.0 or > 5.x - *major = 1; - *minor = 0; - } -} - -static void frontend_switch_get_name(char *s, size_t len) -{ - // TODO: Add Mariko at some point - strlcpy(s, "Nintendo Switch", len); -} - -frontend_ctx_driver_t frontend_ctx_switch = - { - frontend_switch_get_environment_settings, - frontend_switch_init, - frontend_switch_deinit, - frontend_switch_exitspawn, - NULL, /* process_args */ - frontend_switch_exec, -#ifdef IS_SALAMANDER - NULL, -#else - frontend_switch_set_fork, -#endif - frontend_switch_shutdown, - frontend_switch_get_name, - frontend_switch_get_os, - frontend_switch_get_rating, - NULL, /* load_content */ - frontend_switch_get_architecture, - frontend_switch_get_powerstate, - frontend_switch_parse_drive_list, - frontend_switch_get_mem_total, - frontend_switch_get_mem_used, - NULL, /* install_signal_handler */ - NULL, /* get_signal_handler_state */ - NULL, /* set_signal_handler_state */ - NULL, /* destroy_signal_handler_state */ - NULL, /* attach_console */ - NULL, /* detach_console */ - NULL, /* watch_path_for_changes */ - NULL, /* check_for_path_changes */ - NULL, /* set_sustained_performance_mode */ - "switch", -}; +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifndef IS_SALAMANDER +#include +#endif + +#include "../frontend_driver.h" +#include "../../verbosity.h" +#include "../../defaults.h" +#include "../../paths.h" +#include "../../retroarch.h" +#include "../../file_path_special.h" +#include "../../audio/audio_driver.h" + +#ifndef IS_SALAMANDER +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif +#endif + +static enum frontend_fork switch_fork_mode = FRONTEND_FORK_NONE; +static const char *elf_path_cst = "/switch/retroarch_switch.nro"; + +static uint64_t frontend_switch_get_mem_used(void); + +// Splash +static uint32_t *splashData = NULL; + +// switch_gfx.c protypes, we really need a header +extern void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend); + +static void get_first_valid_core(char *path_return) +{ + DIR *dir; + struct dirent *ent; + const char *extension = ".nro"; + + path_return[0] = '\0'; + + dir = opendir("/retroarch/cores"); + if (dir != NULL) + { + while (ent = readdir(dir)) + { + if (ent == NULL) + break; + if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension)) + { + strcpy(path_return, "/retroarch/cores"); + strcat(path_return, "/"); + strcat(path_return, ent->d_name); + break; + } + } + closedir(dir); + } +} + +static void frontend_switch_get_environment_settings(int *argc, char *argv[], void *args, void *params_data) +{ + (void)args; + +#ifndef IS_SALAMANDER +#if defined(HAVE_LOGGER) + logger_init(); +#elif defined(HAVE_FILE_LOGGER) + retro_main_log_file_init("/retroarch-log.txt"); +#endif +#endif + + fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], "/retroarch/retroarch_switch.nro", sizeof(g_defaults.dirs[DEFAULT_DIR_PORT])); + RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], + "downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT], + "media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT], + "cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE], + "info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE], + "savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE], + "savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE], + "system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE], + "playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT], + "config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT], + "config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT], + "filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT], + "database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE])); + + fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT], + "database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR])); + + fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT], + file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config)); +} + +static void frontend_switch_deinit(void *data) +{ + (void)data; + +#if defined(HAVE_LIBNX) && defined(NXLINK) // Else freeze on exit + socketExit(); +#endif + + // Splash + if (splashData) + { + free(splashData); + splashData = NULL; + } + + gfxExit(); +} + +static void frontend_switch_exec(const char *path, bool should_load_game) +{ + char game_path[PATH_MAX]; + const char *arg_data[3]; + char error_string[200 + PATH_MAX]; + int args = 0; + int error = 0; + + game_path[0] = NULL; + arg_data[0] = NULL; + + arg_data[args] = elf_path_cst; + arg_data[args + 1] = NULL; + args++; + + RARCH_LOG("Attempt to load core: [%s].\n", path); +#ifndef IS_SALAMANDER + if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT)) + { + strcpy(game_path, path_get(RARCH_PATH_CONTENT)); + arg_data[args] = game_path; + arg_data[args + 1] = NULL; + args++; + RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT)); + } +#endif + + if (path && path[0]) + { +#ifdef IS_SALAMANDER + struct stat sbuff; + bool file_exists; + + file_exists = stat(path, &sbuff) == 0; + if (!file_exists) + { + char core_path[PATH_MAX]; + + /* find first valid core and load it if the target core doesnt exist */ + get_first_valid_core(&core_path[0]); + + if (core_path[0] == '\0') + { + /*errorInit(&error_dialog, ERROR_TEXT, CFG_LANGUAGE_EN); + errorText(&error_dialog, "There are no cores installed, install a core to continue."); + errorDisp(&error_dialog);*/ + svcExitProcess(); + } + } +#endif + char *argBuffer = (char *)malloc(PATH_MAX); + if (should_load_game) + { + snprintf(argBuffer, PATH_MAX, "%s \"%s\"", path, game_path); + } + else + { + snprintf(argBuffer, PATH_MAX, "%s", path); + } + + envSetNextLoad(path, argBuffer); + } +} + +#ifndef IS_SALAMANDER +static bool frontend_switch_set_fork(enum frontend_fork fork_mode) +{ + switch (fork_mode) + { + case FRONTEND_FORK_CORE: + RARCH_LOG("FRONTEND_FORK_CORE\n"); + switch_fork_mode = fork_mode; + break; + case FRONTEND_FORK_CORE_WITH_ARGS: + RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n"); + switch_fork_mode = fork_mode; + break; + case FRONTEND_FORK_RESTART: + RARCH_LOG("FRONTEND_FORK_RESTART\n"); + /* NOTE: We don't implement Salamander, so just turn + this into FRONTEND_FORK_CORE. */ + switch_fork_mode = FRONTEND_FORK_CORE; + break; + case FRONTEND_FORK_NONE: + default: + return false; + } + + return true; +} +#endif + +static void frontend_switch_exitspawn(char *s, size_t len) +{ + bool should_load_game = false; +#ifndef IS_SALAMANDER + if (switch_fork_mode == FRONTEND_FORK_NONE) + return; + + switch (switch_fork_mode) + { + case FRONTEND_FORK_CORE_WITH_ARGS: + should_load_game = true; + break; + default: + break; + } +#endif + frontend_switch_exec(s, should_load_game); +} + +static void frontend_switch_shutdown(bool unused) +{ + (void)unused; +} + +void argb_to_rgba8(uint32_t *buff, uint32_t height, uint32_t width) +{ + // Convert + for (uint32_t h = 0; h < height; h++) + { + for (uint32_t w = 0; w < width; w++) + { + uint32_t offset = (h * width) + w; + uint32_t c = buff[offset]; + + uint32_t a = (uint32_t)((c & 0xff000000) >> 24); + uint32_t r = (uint32_t)((c & 0x00ff0000) >> 16); + uint32_t g = (uint32_t)((c & 0x0000ff00) >> 8); + uint32_t b = (uint32_t)(c & 0x000000ff); + + buff[offset] = RGBA8(r, g, b, a); + } + } +} + +void frontend_switch_showsplash() +{ + printf("[Splash] Showing splashScreen\n"); + + if (splashData) + { + uint32_t width, height; + width = height = 0; + + uint32_t *frambuffer = (uint32_t *)gfxGetFramebuffer(&width, &height); + + gfx_slow_swizzling_blit(frambuffer, splashData, width, height, 0, 0, false); + + gfxFlushBuffers(); + gfxSwapBuffers(); + gfxWaitForVsync(); + } +} + +// From rpng_test.c +bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height) +{ + int retval; + size_t file_len; + bool ret = true; + rpng_t *rpng = NULL; + void *ptr = NULL; + + struct nbio_t *handle = (struct nbio_t *)nbio_open(path, NBIO_READ); + + if (!handle) + goto end; + + nbio_begin_read(handle); + + while (!nbio_iterate(handle)) + svcSleepThread(3); + + ptr = nbio_get_ptr(handle, &file_len); + + if (!ptr) + { + ret = false; + goto end; + } + + rpng = rpng_alloc(); + + if (!rpng) + { + ret = false; + goto end; + } + + if (!rpng_set_buf_ptr(rpng, (uint8_t *)ptr)) + { + ret = false; + goto end; + } + + if (!rpng_start(rpng)) + { + ret = false; + goto end; + } + + while (rpng_iterate_image(rpng)) + svcSleepThread(3); + + if (!rpng_is_valid(rpng)) + { + ret = false; + goto end; + } + + do + { + retval = rpng_process_image(rpng, (void **)data, file_len, width, height); + svcSleepThread(3); + } while (retval == IMAGE_PROCESS_NEXT); + + if (retval == IMAGE_PROCESS_ERROR || retval == IMAGE_PROCESS_ERROR_END) + ret = false; + +end: + if (handle) + nbio_free(handle); + + if (rpng) + rpng_free(rpng); + + rpng = NULL; + + if (!ret) + free(*data); + + return ret; +} + +int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) +{ + svcSleepThread(rqtp->tv_nsec + (rqtp->tv_sec * 1000000000)); + return 0; +} + +long sysconf(int name) +{ + switch (name) + { + case 8: + return 0x1000; + } + return -1; +} + +ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize) +{ + return -1; +} + +// Taken from glibc +char *realpath(const char *name, char *resolved) +{ + char *rpath, *dest, *extra_buf = NULL; + const char *start, *end, *rpath_limit; + long int path_max; + int num_links = 0; + + if (name == NULL) + { + /* As per Single Unix Specification V2 we must return an error if + either parameter is a null pointer. We extend this to allow + the RESOLVED parameter to be NULL in case the we are expected to + allocate the room for the return value. */ + return NULL; + } + + if (name[0] == '\0') + { + /* As per Single Unix Specification V2 we must return an error if + the name argument points to an empty string. */ + return NULL; + } + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf(name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + if (resolved == NULL) + { + rpath = malloc(path_max); + if (rpath == NULL) + return NULL; + } + else + rpath = resolved; + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!getcwd(rpath, path_max)) + { + rpath[0] = '\0'; + goto error; + } + dest = memchr(rpath, '\0', path_max); + } + else + { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) + { + int n; + + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/') + ; + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + char *new_rpath; + + if (resolved) + { + if (dest > rpath + 1) + dest--; + *dest = '\0'; + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + new_rpath = (char *)realloc(rpath, new_size); + if (new_rpath == NULL) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + + dest = rpath + dest_offset; + } + + dest = memcpy(dest, start, end - start); + *dest = '\0'; + } + } + if (dest > rpath + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return rpath; + +error: + if (resolved == NULL) + free(rpath); + return NULL; +} + +// runloop_get_system_info isnt initialized that early.. +extern void retro_get_system_info(struct retro_system_info *info); + +static void frontend_switch_init(void *data) +{ + (void)data; + + // Init Resolution before initDefault + gfxInitResolution(1280, 720); + + gfxInitDefault(); + gfxSetMode(GfxMode_TiledDouble); + + gfxConfigureTransform(0); + +#if defined(HAVE_LIBNX) && defined(NXLINK) + socketInitializeDefault(); + nxlinkStdio(); +#ifndef IS_SALAMANDER + verbosity_enable(); +#endif +#endif + + rarch_system_info_t *sys_info = runloop_get_system_info(); + retro_get_system_info(sys_info); + + const char *core_name = NULL; + + printf("[Video]: Video initialized\n"); + + uint32_t width, height; + width = height = 0; + + // Load splash + if (!splashData) + { + if (sys_info) + { + core_name = sys_info->info.library_name; + char *full_core_splash_path = (char *)malloc(PATH_MAX); + snprintf(full_core_splash_path, PATH_MAX, "/retroarch/rgui/splash/%s.png", core_name); + + rpng_load_image_argb((const char *)full_core_splash_path, &splashData, &width, &height); + if (splashData) + { + argb_to_rgba8(splashData, height, width); + frontend_switch_showsplash(); + } + else + { + rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height); + if (splashData) + { + argb_to_rgba8(splashData, height, width); + frontend_switch_showsplash(); + } + } + + free(full_core_splash_path); + } + else + { + rpng_load_image_argb("/retroarch/rgui/splash/RetroArch.png", &splashData, &width, &height); + if (splashData) + { + argb_to_rgba8(splashData, height, width); + frontend_switch_showsplash(); + } + } + } + else + { + frontend_switch_showsplash(); + } +} + +static int frontend_switch_get_rating(void) +{ + return 1000; +} + +enum frontend_architecture frontend_switch_get_architecture(void) +{ + return FRONTEND_ARCH_ARMV8; +} + +static int frontend_switch_parse_drive_list(void *data, bool load_content) +{ +#ifndef IS_SALAMANDER + file_list_t *list = (file_list_t *)data; + enum msg_hash_enums enum_idx = load_content ? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR : MSG_UNKNOWN; + + if (!list) + return -1; + + menu_entries_append_enum(list, "/", msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR), + enum_idx, + FILE_TYPE_DIRECTORY, 0, 0); +#endif + + return 0; +} + +static uint64_t frontend_switch_get_mem_total(void) +{ + uint64_t memoryTotal = 0; + svcGetInfo(&memoryTotal, 6, CUR_PROCESS_HANDLE, 0); // avaiable + memoryTotal += frontend_switch_get_mem_used(); + + return memoryTotal; +} + +static uint64_t frontend_switch_get_mem_used(void) +{ + uint64_t memoryUsed = 0; + svcGetInfo(&memoryUsed, 7, CUR_PROCESS_HANDLE, 0); // used + + return memoryUsed; +} + +static enum frontend_powerstate frontend_switch_get_powerstate(int *seconds, int *percent) +{ + // This is fine monkaS + return FRONTEND_POWERSTATE_CHARGED; +} + +static void frontend_switch_get_os(char *s, size_t len, int *major, int *minor) +{ + strlcpy(s, "Horizon OS", len); + + // There is pretty sure a better way, but this will do just fine + if (kernelAbove500()) + { + *major = 5; + *minor = 0; + } + else if (kernelAbove400()) + { + *major = 4; + *minor = 0; + } + else if (kernelAbove300()) + { + *major = 3; + *minor = 0; + } + else if (kernelAbove200()) + { + *major = 2; + *minor = 0; + } + else + { + // either 1.0 or > 5.x + *major = 1; + *minor = 0; + } +} + +static void frontend_switch_get_name(char *s, size_t len) +{ + // TODO: Add Mariko at some point + strlcpy(s, "Nintendo Switch", len); +} + +frontend_ctx_driver_t frontend_ctx_switch = + { + frontend_switch_get_environment_settings, + frontend_switch_init, + frontend_switch_deinit, + frontend_switch_exitspawn, + NULL, /* process_args */ + frontend_switch_exec, +#ifdef IS_SALAMANDER + NULL, +#else + frontend_switch_set_fork, +#endif + frontend_switch_shutdown, + frontend_switch_get_name, + frontend_switch_get_os, + frontend_switch_get_rating, + NULL, /* load_content */ + frontend_switch_get_architecture, + frontend_switch_get_powerstate, + frontend_switch_parse_drive_list, + frontend_switch_get_mem_total, + frontend_switch_get_mem_used, + NULL, /* install_signal_handler */ + NULL, /* get_signal_handler_state */ + NULL, /* set_signal_handler_state */ + NULL, /* destroy_signal_handler_state */ + NULL, /* attach_console */ + NULL, /* detach_console */ + NULL, /* watch_path_for_changes */ + NULL, /* check_for_path_changes */ + NULL, /* set_sustained_performance_mode */ + "switch", +}; diff --git a/gfx/common/switch_common.h b/gfx/common/switch_common.h index d3858cb975..205f8ab68d 100644 --- a/gfx/common/switch_common.h +++ b/gfx/common/switch_common.h @@ -1,59 +1,59 @@ -#ifndef SWITCH_COMMON_H__ -#define SWITCH_COMMON_H__ - -#include -#include - -typedef struct -{ - bool vsync; - bool rgb32; - bool smooth; // bilinear - unsigned width, height; - unsigned rotation; - struct video_viewport vp; - struct texture_image *overlay; - bool overlay_enabled; - bool in_menu; - struct - { - bool enable; - bool fullscreen; - - uint32_t *pixels; - - uint32_t width; - uint32_t height; - - unsigned tgtw; - unsigned tgth; - - struct scaler_ctx scaler; - } menu_texture; - - struct - { - uint32_t width; - uint32_t height; - uint32_t x_offset; - } hw_scale; - - uint32_t image[1280 * 720]; - uint32_t tmp_image[1280 * 720]; - u32 cnt; - struct scaler_ctx scaler; - uint32_t last_width; - uint32_t last_height; - bool keep_aspect; - bool should_resize; - bool need_clear; - bool is_threaded; - - bool o_size; - uint32_t o_height; - uint32_t o_width; -} switch_video_t; - -void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend); - -#endif +#ifndef SWITCH_COMMON_H__ +#define SWITCH_COMMON_H__ + +#include +#include + +typedef struct +{ + bool vsync; + bool rgb32; + bool smooth; // bilinear + unsigned width, height; + unsigned rotation; + struct video_viewport vp; + struct texture_image *overlay; + bool overlay_enabled; + bool in_menu; + struct + { + bool enable; + bool fullscreen; + + uint32_t *pixels; + + uint32_t width; + uint32_t height; + + unsigned tgtw; + unsigned tgth; + + struct scaler_ctx scaler; + } menu_texture; + + struct + { + uint32_t width; + uint32_t height; + uint32_t x_offset; + } hw_scale; + + uint32_t image[1280 * 720]; + uint32_t tmp_image[1280 * 720]; + u32 cnt; + struct scaler_ctx scaler; + uint32_t last_width; + uint32_t last_height; + bool keep_aspect; + bool should_resize; + bool need_clear; + bool is_threaded; + + bool o_size; + uint32_t o_height; + uint32_t o_width; +} switch_video_t; + +void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend); + +#endif diff --git a/gfx/drivers/switch_nx_gfx.c b/gfx/drivers/switch_nx_gfx.c index 3674852544..f207db442f 100644 --- a/gfx/drivers/switch_nx_gfx.c +++ b/gfx/drivers/switch_nx_gfx.c @@ -1,772 +1,772 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2018 - misson2000 - * Copyright (C) 2018 - m4xw - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif - -#ifdef HAVE_MENU -#include "../../menu/menu_driver.h" -#endif - -#include "../font_driver.h" - -#include "../../configuration.h" -#include "../../command.h" -#include "../../driver.h" - -#include "../../retroarch.h" -#include "../../verbosity.h" - -#include "../common/switch_common.h" - -#ifndef HAVE_THREADS -#include "../../tasks/tasks_internal.h" -#endif - -#ifdef HAVE_NXRGUI -extern uint32_t *nx_backgroundImage; -// Temp Overlay // KILL IT WITH FIRE -extern uint32_t *tmp_overlay; -#endif - -// (C) libtransistor -static int pdep(uint32_t mask, uint32_t value) -{ - uint32_t out = 0; - for (int shift = 0; shift < 32; shift++) - { - uint32_t bit = 1u << shift; - if (mask & bit) - { - if (value & 1) - out |= bit; - value >>= 1; - } - } - return out; -} - -static uint32_t swizzle_x(uint32_t v) { return pdep(~0x7B4u, v); } -static uint32_t swizzle_y(uint32_t v) { return pdep(0x7B4, v); } - -void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend) -{ - uint32_t *dest = buffer; - uint32_t *src = image; - int x0 = tx; - int y0 = ty; - int x1 = x0 + w; - int y1 = y0 + h; - const uint32_t tile_height = 128; - const uint32_t padded_width = 128 * 10; - - // we're doing this in pixels - should just shift the swizzles instead - uint32_t offs_x0 = swizzle_x(x0); - uint32_t offs_y = swizzle_y(y0); - uint32_t x_mask = swizzle_x(~0u); - uint32_t y_mask = swizzle_y(~0u); - uint32_t incr_y = swizzle_x(padded_width); - - // step offs_x0 to the right row of tiles - offs_x0 += incr_y * (y0 / tile_height); - - uint32_t x, y; - for (y = y0; y < y1; y++) - { - uint32_t *dest_line = dest + offs_y; - uint32_t offs_x = offs_x0; - - for (x = x0; x < x1; x++) - { - uint32_t pixel = *src++; - if (blend) // supercheap masking - { - uint32_t dst = dest_line[offs_x]; - uint8_t src_a = ((pixel & 0xFF000000) >> 24); - - if (src_a > 0) - pixel &= 0x00FFFFFF; - else - pixel = dst; - } - - dest_line[offs_x] = pixel; - - offs_x = (offs_x - x_mask) & x_mask; - } - - offs_y = (offs_y - y_mask) & y_mask; - if (!offs_y) - offs_x0 += incr_y; // wrap into next tile row - } -} - -// needed to clear surface completely as hw scaling doesn't always scale to full resoution perflectly -static void clear_screen(switch_video_t *sw) -{ - gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height); - - uint32_t *out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL); - - memset(out_buffer, 0, gfxGetFramebufferSize()); - - gfxFlushBuffers(); - gfxSwapBuffers(); - gfxWaitForVsync(); -} - -static void *switch_init(const video_info_t *video, - const input_driver_t **input, void **input_data) -{ - void *switchinput = NULL; - switch_video_t *sw = (switch_video_t *)calloc(1, sizeof(*sw)); - if (!sw) - return NULL; - - printf("loading switch gfx driver, width: %d, height: %d threaded: %d smooth %d\n", video->width, video->height, video->is_threaded, video->smooth); - sw->vp.x = 0; - sw->vp.y = 0; - sw->vp.width = sw->o_width = video->width; - sw->vp.height = sw->o_height = video->height; - sw->overlay_enabled = false; - sw->overlay = NULL; - sw->in_menu = false; - - sw->vp.full_width = 1280; - sw->vp.full_height = 720; - - // Sanity check - sw->vp.width = MIN(sw->vp.width, sw->vp.full_width); - sw->vp.height = MIN(sw->vp.height, sw->vp.full_height); - - sw->vsync = video->vsync; - sw->rgb32 = video->rgb32; - sw->keep_aspect = true; - sw->should_resize = true; - sw->o_size = true; - sw->is_threaded = video->is_threaded; - sw->smooth = video->smooth; - sw->menu_texture.enable = false; - - // Autoselect driver - if (input && input_data) - { - settings_t *settings = config_get_ptr(); - switchinput = input_switch.init(settings->arrays.input_joypad_driver); - *input = switchinput ? &input_switch : NULL; - *input_data = switchinput; - } - - font_driver_init_osd(sw, false, - video->is_threaded, - FONT_DRIVER_RENDER_SWITCH); - - clear_screen(sw); - - return sw; -} - -static void switch_update_viewport(switch_video_t *sw, - video_frame_info_t *video_info) -{ - settings_t *settings = config_get_ptr(); - int x = 0; - int y = 0; - float desired_aspect = 0.0f; - float width = sw->vp.full_width; - float height = sw->vp.full_height; - - if (sw->o_size) - { - width = sw->o_width; - height = sw->o_height; - sw->vp.x = (int)(((float)sw->vp.full_width - width)) / 2; - sw->vp.y = (int)(((float)sw->vp.full_height - height)) / 2; - - sw->vp.width = width; - sw->vp.height = height; - - return; - } - - desired_aspect = video_driver_get_aspect_ratio(); - - /* We crash if >1.0f */ - printf("[Video] Aspect: %f\n", desired_aspect); - /*if (desired_aspect > 1.8f) - desired_aspect = 1.7778f; - - if (desired_aspect < 1.2f && desired_aspect != 0.0f) - desired_aspect = 1.0f;*/ - - if (settings->bools.video_scale_integer) - { - video_viewport_get_scaled_integer(&sw->vp, sw->vp.full_width, sw->vp.full_height, desired_aspect, sw->keep_aspect); - } - else if (sw->keep_aspect) - { -#if defined(HAVE_MENU) - if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM) - { - sw->vp.x = sw->vp.y = 0; - sw->vp.width = width; - sw->vp.height = height; - } - else -#endif - { - float delta; - float device_aspect = ((float)sw->vp.full_width) / sw->vp.full_height; - - if (fabsf(device_aspect - desired_aspect) < 0.0001f) - { - /* - * If the aspect ratios of screen and desired aspect - * ratio are sufficiently equal (floating point stuff), - * assume they are actually equal. - */ - } - else if (device_aspect > desired_aspect) - { - delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; - x = (int)roundf(width * (0.5f - delta)); - width = (unsigned)roundf(2.0f * width * delta); - } - else - { - delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; - y = (int)roundf(height * (0.5f - delta)); - height = (unsigned)roundf(2.0f * height * delta); - } - } - - sw->vp.x = x; - sw->vp.y = y; - - sw->vp.width = width; - sw->vp.height = height; - } - else - { - sw->vp.x = sw->vp.y = 0; - sw->vp.width = width; - sw->vp.height = height; - } -} - -static void switch_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) -{ - switch_video_t *sw = (switch_video_t *)data; - - if (!sw) - return; - - sw->keep_aspect = true; - sw->o_size = false; - - settings_t *settings = config_get_ptr(); - - switch (aspect_ratio_idx) - { - case ASPECT_RATIO_SQUARE: - video_driver_set_viewport_square_pixel(); - break; - - case ASPECT_RATIO_CORE: - video_driver_set_viewport_core(); - sw->o_size = true; - sw->keep_aspect = false; - break; - - case ASPECT_RATIO_CONFIG: - video_driver_set_viewport_config(); - break; - - case ASPECT_RATIO_CUSTOM: - if (settings->bools.video_scale_integer) - { - video_driver_set_viewport_core(); - sw->o_size = true; - sw->keep_aspect = false; - } - break; - - default: - break; - } - - video_driver_set_aspect_ratio_value(aspectratio_lut[aspect_ratio_idx].value); - - sw->should_resize = true; -} - -static bool switch_frame(void *data, const void *frame, - unsigned width, unsigned height, - uint64_t frame_count, unsigned pitch, - const char *msg, video_frame_info_t *video_info) -{ - switch_video_t *sw = data; - uint32_t *out_buffer = NULL; - bool ffwd_mode = video_info->input_driver_nonblock_state; - - if (!frame) - return true; - - if (ffwd_mode && !sw->is_threaded) - { - // render every 4th frame when in ffwd mode and not threaded - if ((frame_count % 4) != 0) - return true; - } - - if (sw->should_resize || width != sw->last_width || height != sw->last_height) - { - printf("[Video] Requesting new size: width %i height %i\n", width, height); - printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y); - switch_update_viewport(sw, video_info); - printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y); - - // Sanity check - sw->vp.width = MIN(sw->vp.width, sw->vp.full_width); - sw->vp.height = MIN(sw->vp.height, sw->vp.full_height); - - scaler_ctx_gen_reset(&sw->scaler); - - sw->scaler.in_width = width; - sw->scaler.in_height = height; - sw->scaler.in_stride = pitch; - sw->scaler.in_fmt = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; - - if (!sw->smooth) - { - sw->scaler.out_width = sw->vp.width; - sw->scaler.out_height = sw->vp.height; - sw->scaler.out_stride = sw->vp.full_width * sizeof(uint32_t); - } - else - { - sw->scaler.out_width = width; - sw->scaler.out_height = height; - sw->scaler.out_stride = width * sizeof(uint32_t); - - float screen_ratio = (float)sw->vp.full_width / sw->vp.full_height; - float tgt_ratio = (float)sw->vp.width / sw->vp.height; - - sw->hw_scale.width = ceil(screen_ratio / tgt_ratio * sw->scaler.out_width); - sw->hw_scale.height = sw->scaler.out_height; - sw->hw_scale.x_offset = ceil((sw->hw_scale.width - sw->scaler.out_width) / 2.0); - if (!video_info->menu_is_alive) - { - clear_screen(sw); - gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); - } - } - sw->scaler.out_fmt = SCALER_FMT_ABGR8888; - - sw->scaler.scaler_type = SCALER_TYPE_POINT; - - if (!scaler_ctx_gen_filter(&sw->scaler)) - { - printf("failed to generate scaler for main image\n"); - return false; - } - - sw->last_width = width; - sw->last_height = height; - - sw->should_resize = false; - } - - out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL); - - if (sw->in_menu && !video_info->menu_is_alive && sw->smooth) - { - memset(out_buffer, 0, sw->vp.full_width * sw->vp.full_height * 4); - gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); - } - sw->in_menu = video_info->menu_is_alive; - - if (sw->menu_texture.enable) - { - menu_driver_frame(video_info); - - if (sw->menu_texture.pixels) - { -#ifdef HAVE_NXRGUI - gfx_slow_swizzling_blit(out_buffer, nx_backgroundImage, sw->vp.full_width, sw->vp.full_height, 0, 0, false); -#else - memset(out_buffer, 0, gfxGetFramebufferSize()); -#endif - scaler_ctx_scale(&sw->menu_texture.scaler, sw->tmp_image + ((sw->vp.full_height - sw->menu_texture.tgth) / 2) * sw->vp.full_width + ((sw->vp.full_width - sw->menu_texture.tgtw) / 2), sw->menu_texture.pixels); - gfx_slow_swizzling_blit(out_buffer, sw->tmp_image, sw->vp.full_width, sw->vp.full_height, 0, 0, true); - } - } - else if (sw->smooth) // bilinear - { - struct scaler_ctx *ctx = &sw->scaler; - scaler_ctx_scale_direct(ctx, sw->image, frame); - int w = sw->scaler.out_width; - int h = sw->scaler.out_height; - for (int y = 0; y < h; y++) - for (int x = 0; x < w; x++) - out_buffer[gfxGetFramebufferDisplayOffset(x + sw->hw_scale.x_offset, y)] = sw->image[y * w + x]; - } - else - { - struct scaler_ctx *ctx = &sw->scaler; - scaler_ctx_scale(ctx, sw->image + (sw->vp.y * sw->vp.full_width) + sw->vp.x, frame); - gfx_slow_swizzling_blit(out_buffer, sw->image, sw->vp.full_width, sw->vp.full_height, 0, 0, false); -#ifdef HAVE_NXRGUI - if (tmp_overlay) - { - gfx_slow_swizzling_blit(out_buffer, tmp_overlay, sw->vp.full_width, sw->vp.full_height, 0, 0, true); - } -#endif - } - - if (video_info->statistics_show && !sw->smooth) - { - struct font_params *osd_params = (struct font_params *)&video_info->osd_stat_params; - - if (osd_params) - font_driver_render_msg(video_info, NULL, video_info->stat_text, - (const struct font_params *)&video_info->osd_stat_params); - } - - if (msg) - font_driver_render_msg(video_info, NULL, msg, NULL); - - gfxFlushBuffers(); - gfxSwapBuffers(); - if (sw->vsync || video_info->menu_is_alive) - gfxWaitForVsync(); - - return true; -} - -static void switch_set_nonblock_state(void *data, bool toggle) -{ - switch_video_t *sw = data; - sw->vsync = !toggle; -} - -static bool switch_alive(void *data) -{ - (void)data; - return true; -} - -static bool switch_focus(void *data) -{ - (void)data; - return true; -} - -static bool switch_suppress_screensaver(void *data, bool enable) -{ - (void)data; - (void)enable; - return false; -} - -static bool switch_has_windowed(void *data) -{ - (void)data; - return false; -} - -static void switch_free(void *data) -{ - switch_video_t *sw = data; - if (sw->menu_texture.pixels) - free(sw->menu_texture.pixels); - - free(sw); -} - -static bool switch_set_shader(void *data, - enum rarch_shader_type type, const char *path) -{ - (void)data; - (void)type; - (void)path; - - return false; -} - -static void switch_set_rotation(void *data, unsigned rotation) -{ - switch_video_t *sw = data; - if (!sw) - return; - sw->rotation = rotation; -} - -static void switch_viewport_info(void *data, struct video_viewport *vp) -{ - switch_video_t *sw = data; - *vp = sw->vp; -} - -static bool switch_read_viewport(void *data, uint8_t *buffer, bool is_idle) -{ - (void)data; - (void)buffer; - - return true; -} - -static void switch_set_texture_frame( - void *data, const void *frame, bool rgb32, - unsigned width, unsigned height, float alpha) -{ - switch_video_t *sw = data; - size_t sz = width * height * (rgb32 ? 4 : 2); - - if (!sw->menu_texture.pixels || - sw->menu_texture.width != width || - sw->menu_texture.height != height) - { - if (sw->menu_texture.pixels) - realloc(sw->menu_texture.pixels, sz); - else - sw->menu_texture.pixels = malloc(sz); - - if (!sw->menu_texture.pixels) - { - printf("failed to allocate buffer for menu texture\n"); - return; - } - - int xsf = 1280 / width; - int ysf = 720 / height; - int sf = xsf; - - if (ysf < sf) - sf = ysf; - - sw->menu_texture.width = width; - sw->menu_texture.height = height; - sw->menu_texture.tgtw = width * sf; - sw->menu_texture.tgth = height * sf; - - struct scaler_ctx *sctx = &sw->menu_texture.scaler; - scaler_ctx_gen_reset(sctx); - - sctx->in_width = width; - sctx->in_height = height; - sctx->in_stride = width * (rgb32 ? 4 : 2); - sctx->in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; - sctx->out_width = sw->menu_texture.tgtw; - sctx->out_height = sw->menu_texture.tgth; - sctx->out_stride = 1280 * 4; - sctx->out_fmt = SCALER_FMT_ABGR8888; - - sctx->scaler_type = SCALER_TYPE_POINT; - - if (!scaler_ctx_gen_filter(sctx)) - { - printf("failed to generate scaler for menu texture\n"); - return; - } - } - - memcpy(sw->menu_texture.pixels, frame, sz); -} - -static void switch_apply_state_changes(void *data) -{ - (void)data; -} - -static void switch_set_texture_enable(void *data, bool enable, bool full_screen) -{ - switch_video_t *sw = data; - - if (!sw->menu_texture.enable && enable) - gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height); - else if (!enable && sw->menu_texture.enable && sw->smooth) - { - clear_screen(sw); - gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); - } - - sw->menu_texture.enable = enable; - sw->menu_texture.fullscreen = full_screen; -} - -static void switch_set_osd_msg(void *data, - video_frame_info_t *video_info, - const char *msg, - const void *params, void *font) -{ - switch_video_t *sw = (switch_video_t *)data; - - if (sw) - font_driver_render_msg(video_info, font, msg, params); -} - -#ifdef HAVE_OVERLAY -static void switch_overlay_enable(void *data, bool state) -{ - printf("[Video] Enabled Overlay\n"); - - switch_video_t *swa = (switch_video_t *)data; - - if (!swa) - return; - - swa->overlay_enabled = state; -} - -static bool switch_overlay_load(void *data, - const void *image_data, unsigned num_images) -{ - switch_video_t *swa = (switch_video_t *)data; - - struct texture_image *images = (struct texture_image *)image_data; - - if (!swa) - return false; - - swa->overlay = images; - swa->overlay_enabled = true; - - return true; -} - -static void switch_overlay_tex_geom(void *data, - unsigned idx, float x, float y, float w, float h) -{ - switch_video_t *swa = (switch_video_t *)data; - - if (!swa) - return; -} - -static void switch_overlay_vertex_geom(void *data, - unsigned idx, float x, float y, float w, float h) -{ - switch_video_t *swa = (switch_video_t *)data; - - if (!swa) - return; -} - -static void switch_overlay_full_screen(void *data, bool enable) -{ - (void)data; - (void)enable; -} - -static void switch_overlay_set_alpha(void *data, unsigned idx, float mod) -{ - (void)data; - (void)idx; - (void)mod; -} - -static const video_overlay_interface_t switch_overlay = { - switch_overlay_enable, - switch_overlay_load, - switch_overlay_tex_geom, - switch_overlay_vertex_geom, - switch_overlay_full_screen, - switch_overlay_set_alpha, -}; - -void switch_overlay_interface(void *data, const video_overlay_interface_t **iface) -{ - switch_video_t *swa = (switch_video_t *)data; - if (!swa) - return; - *iface = &switch_overlay; -} - -#endif - -static const video_poke_interface_t switch_poke_interface = { - NULL, /* get_flags */ - NULL, /* set_coords */ - NULL, /* set_mvp */ - NULL, /* load_texture */ - NULL, /* unload_texture */ - NULL, /* set_video_mode */ - NULL, /* get_refresh_rate */ - NULL, /* set_filtering */ - NULL, /* get_video_output_size */ - NULL, /* get_video_output_prev */ - NULL, /* get_video_output_next */ - NULL, /* get_current_framebuffer */ - NULL, /* get_proc_address */ - switch_set_aspect_ratio, /* set_aspect_ratio */ - switch_apply_state_changes, /* apply_state_changes */ - switch_set_texture_frame, - switch_set_texture_enable, - switch_set_osd_msg, - NULL, /* show_mouse */ - NULL, /* grab_mouse_toggle */ - NULL, /* get_current_shader */ - NULL, /* get_current_software_framebuffer */ - NULL, /* get_hw_render_interface */ -}; - -static void switch_get_poke_interface(void *data, - const video_poke_interface_t **iface) -{ - (void)data; - *iface = &switch_poke_interface; -} - -video_driver_t video_switch = { - switch_init, - switch_frame, - switch_set_nonblock_state, - switch_alive, - switch_focus, - switch_suppress_screensaver, - switch_has_windowed, - switch_set_shader, - switch_free, - "switch", - NULL, /* set_viewport */ - switch_set_rotation, - switch_viewport_info, - switch_read_viewport, - NULL, /* read_frame_raw */ -#ifdef HAVE_OVERLAY - switch_overlay_interface, /* switch_overlay_interface */ -#endif - switch_get_poke_interface, -}; - -/* vim: set ts=6 sw=6 sts=6: */ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - misson2000 + * Copyright (C) 2018 - m4xw + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#include "../font_driver.h" + +#include "../../configuration.h" +#include "../../command.h" +#include "../../driver.h" + +#include "../../retroarch.h" +#include "../../verbosity.h" + +#include "../common/switch_common.h" + +#ifndef HAVE_THREADS +#include "../../tasks/tasks_internal.h" +#endif + +#ifdef HAVE_NXRGUI +extern uint32_t *nx_backgroundImage; +// Temp Overlay // KILL IT WITH FIRE +extern uint32_t *tmp_overlay; +#endif + +// (C) libtransistor +static int pdep(uint32_t mask, uint32_t value) +{ + uint32_t out = 0; + for (int shift = 0; shift < 32; shift++) + { + uint32_t bit = 1u << shift; + if (mask & bit) + { + if (value & 1) + out |= bit; + value >>= 1; + } + } + return out; +} + +static uint32_t swizzle_x(uint32_t v) { return pdep(~0x7B4u, v); } +static uint32_t swizzle_y(uint32_t v) { return pdep(0x7B4, v); } + +void gfx_slow_swizzling_blit(uint32_t *buffer, uint32_t *image, int w, int h, int tx, int ty, bool blend) +{ + uint32_t *dest = buffer; + uint32_t *src = image; + int x0 = tx; + int y0 = ty; + int x1 = x0 + w; + int y1 = y0 + h; + const uint32_t tile_height = 128; + const uint32_t padded_width = 128 * 10; + + // we're doing this in pixels - should just shift the swizzles instead + uint32_t offs_x0 = swizzle_x(x0); + uint32_t offs_y = swizzle_y(y0); + uint32_t x_mask = swizzle_x(~0u); + uint32_t y_mask = swizzle_y(~0u); + uint32_t incr_y = swizzle_x(padded_width); + + // step offs_x0 to the right row of tiles + offs_x0 += incr_y * (y0 / tile_height); + + uint32_t x, y; + for (y = y0; y < y1; y++) + { + uint32_t *dest_line = dest + offs_y; + uint32_t offs_x = offs_x0; + + for (x = x0; x < x1; x++) + { + uint32_t pixel = *src++; + if (blend) // supercheap masking + { + uint32_t dst = dest_line[offs_x]; + uint8_t src_a = ((pixel & 0xFF000000) >> 24); + + if (src_a > 0) + pixel &= 0x00FFFFFF; + else + pixel = dst; + } + + dest_line[offs_x] = pixel; + + offs_x = (offs_x - x_mask) & x_mask; + } + + offs_y = (offs_y - y_mask) & y_mask; + if (!offs_y) + offs_x0 += incr_y; // wrap into next tile row + } +} + +// needed to clear surface completely as hw scaling doesn't always scale to full resoution perflectly +static void clear_screen(switch_video_t *sw) +{ + gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height); + + uint32_t *out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL); + + memset(out_buffer, 0, gfxGetFramebufferSize()); + + gfxFlushBuffers(); + gfxSwapBuffers(); + gfxWaitForVsync(); +} + +static void *switch_init(const video_info_t *video, + const input_driver_t **input, void **input_data) +{ + void *switchinput = NULL; + switch_video_t *sw = (switch_video_t *)calloc(1, sizeof(*sw)); + if (!sw) + return NULL; + + printf("loading switch gfx driver, width: %d, height: %d threaded: %d smooth %d\n", video->width, video->height, video->is_threaded, video->smooth); + sw->vp.x = 0; + sw->vp.y = 0; + sw->vp.width = sw->o_width = video->width; + sw->vp.height = sw->o_height = video->height; + sw->overlay_enabled = false; + sw->overlay = NULL; + sw->in_menu = false; + + sw->vp.full_width = 1280; + sw->vp.full_height = 720; + + // Sanity check + sw->vp.width = MIN(sw->vp.width, sw->vp.full_width); + sw->vp.height = MIN(sw->vp.height, sw->vp.full_height); + + sw->vsync = video->vsync; + sw->rgb32 = video->rgb32; + sw->keep_aspect = true; + sw->should_resize = true; + sw->o_size = true; + sw->is_threaded = video->is_threaded; + sw->smooth = video->smooth; + sw->menu_texture.enable = false; + + // Autoselect driver + if (input && input_data) + { + settings_t *settings = config_get_ptr(); + switchinput = input_switch.init(settings->arrays.input_joypad_driver); + *input = switchinput ? &input_switch : NULL; + *input_data = switchinput; + } + + font_driver_init_osd(sw, false, + video->is_threaded, + FONT_DRIVER_RENDER_SWITCH); + + clear_screen(sw); + + return sw; +} + +static void switch_update_viewport(switch_video_t *sw, + video_frame_info_t *video_info) +{ + settings_t *settings = config_get_ptr(); + int x = 0; + int y = 0; + float desired_aspect = 0.0f; + float width = sw->vp.full_width; + float height = sw->vp.full_height; + + if (sw->o_size) + { + width = sw->o_width; + height = sw->o_height; + sw->vp.x = (int)(((float)sw->vp.full_width - width)) / 2; + sw->vp.y = (int)(((float)sw->vp.full_height - height)) / 2; + + sw->vp.width = width; + sw->vp.height = height; + + return; + } + + desired_aspect = video_driver_get_aspect_ratio(); + + /* We crash if >1.0f */ + printf("[Video] Aspect: %f\n", desired_aspect); + /*if (desired_aspect > 1.8f) + desired_aspect = 1.7778f; + + if (desired_aspect < 1.2f && desired_aspect != 0.0f) + desired_aspect = 1.0f;*/ + + if (settings->bools.video_scale_integer) + { + video_viewport_get_scaled_integer(&sw->vp, sw->vp.full_width, sw->vp.full_height, desired_aspect, sw->keep_aspect); + } + else if (sw->keep_aspect) + { +#if defined(HAVE_MENU) + if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM) + { + sw->vp.x = sw->vp.y = 0; + sw->vp.width = width; + sw->vp.height = height; + } + else +#endif + { + float delta; + float device_aspect = ((float)sw->vp.full_width) / sw->vp.full_height; + + if (fabsf(device_aspect - desired_aspect) < 0.0001f) + { + /* + * If the aspect ratios of screen and desired aspect + * ratio are sufficiently equal (floating point stuff), + * assume they are actually equal. + */ + } + else if (device_aspect > desired_aspect) + { + delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; + x = (int)roundf(width * (0.5f - delta)); + width = (unsigned)roundf(2.0f * width * delta); + } + else + { + delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; + y = (int)roundf(height * (0.5f - delta)); + height = (unsigned)roundf(2.0f * height * delta); + } + } + + sw->vp.x = x; + sw->vp.y = y; + + sw->vp.width = width; + sw->vp.height = height; + } + else + { + sw->vp.x = sw->vp.y = 0; + sw->vp.width = width; + sw->vp.height = height; + } +} + +static void switch_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) +{ + switch_video_t *sw = (switch_video_t *)data; + + if (!sw) + return; + + sw->keep_aspect = true; + sw->o_size = false; + + settings_t *settings = config_get_ptr(); + + switch (aspect_ratio_idx) + { + case ASPECT_RATIO_SQUARE: + video_driver_set_viewport_square_pixel(); + break; + + case ASPECT_RATIO_CORE: + video_driver_set_viewport_core(); + sw->o_size = true; + sw->keep_aspect = false; + break; + + case ASPECT_RATIO_CONFIG: + video_driver_set_viewport_config(); + break; + + case ASPECT_RATIO_CUSTOM: + if (settings->bools.video_scale_integer) + { + video_driver_set_viewport_core(); + sw->o_size = true; + sw->keep_aspect = false; + } + break; + + default: + break; + } + + video_driver_set_aspect_ratio_value(aspectratio_lut[aspect_ratio_idx].value); + + sw->should_resize = true; +} + +static bool switch_frame(void *data, const void *frame, + unsigned width, unsigned height, + uint64_t frame_count, unsigned pitch, + const char *msg, video_frame_info_t *video_info) +{ + switch_video_t *sw = data; + uint32_t *out_buffer = NULL; + bool ffwd_mode = video_info->input_driver_nonblock_state; + + if (!frame) + return true; + + if (ffwd_mode && !sw->is_threaded) + { + // render every 4th frame when in ffwd mode and not threaded + if ((frame_count % 4) != 0) + return true; + } + + if (sw->should_resize || width != sw->last_width || height != sw->last_height) + { + printf("[Video] Requesting new size: width %i height %i\n", width, height); + printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y); + switch_update_viewport(sw, video_info); + printf("[Video] fw: %i fh: %i w: %i h: %i x: %i y: %i\n", sw->vp.full_width, sw->vp.full_height, sw->vp.width, sw->vp.height, sw->vp.x, sw->vp.y); + + // Sanity check + sw->vp.width = MIN(sw->vp.width, sw->vp.full_width); + sw->vp.height = MIN(sw->vp.height, sw->vp.full_height); + + scaler_ctx_gen_reset(&sw->scaler); + + sw->scaler.in_width = width; + sw->scaler.in_height = height; + sw->scaler.in_stride = pitch; + sw->scaler.in_fmt = sw->rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; + + if (!sw->smooth) + { + sw->scaler.out_width = sw->vp.width; + sw->scaler.out_height = sw->vp.height; + sw->scaler.out_stride = sw->vp.full_width * sizeof(uint32_t); + } + else + { + sw->scaler.out_width = width; + sw->scaler.out_height = height; + sw->scaler.out_stride = width * sizeof(uint32_t); + + float screen_ratio = (float)sw->vp.full_width / sw->vp.full_height; + float tgt_ratio = (float)sw->vp.width / sw->vp.height; + + sw->hw_scale.width = ceil(screen_ratio / tgt_ratio * sw->scaler.out_width); + sw->hw_scale.height = sw->scaler.out_height; + sw->hw_scale.x_offset = ceil((sw->hw_scale.width - sw->scaler.out_width) / 2.0); + if (!video_info->menu_is_alive) + { + clear_screen(sw); + gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); + } + } + sw->scaler.out_fmt = SCALER_FMT_ABGR8888; + + sw->scaler.scaler_type = SCALER_TYPE_POINT; + + if (!scaler_ctx_gen_filter(&sw->scaler)) + { + printf("failed to generate scaler for main image\n"); + return false; + } + + sw->last_width = width; + sw->last_height = height; + + sw->should_resize = false; + } + + out_buffer = (uint32_t *)gfxGetFramebuffer(NULL, NULL); + + if (sw->in_menu && !video_info->menu_is_alive && sw->smooth) + { + memset(out_buffer, 0, sw->vp.full_width * sw->vp.full_height * 4); + gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); + } + sw->in_menu = video_info->menu_is_alive; + + if (sw->menu_texture.enable) + { + menu_driver_frame(video_info); + + if (sw->menu_texture.pixels) + { +#ifdef HAVE_NXRGUI + gfx_slow_swizzling_blit(out_buffer, nx_backgroundImage, sw->vp.full_width, sw->vp.full_height, 0, 0, false); +#else + memset(out_buffer, 0, gfxGetFramebufferSize()); +#endif + scaler_ctx_scale(&sw->menu_texture.scaler, sw->tmp_image + ((sw->vp.full_height - sw->menu_texture.tgth) / 2) * sw->vp.full_width + ((sw->vp.full_width - sw->menu_texture.tgtw) / 2), sw->menu_texture.pixels); + gfx_slow_swizzling_blit(out_buffer, sw->tmp_image, sw->vp.full_width, sw->vp.full_height, 0, 0, true); + } + } + else if (sw->smooth) // bilinear + { + struct scaler_ctx *ctx = &sw->scaler; + scaler_ctx_scale_direct(ctx, sw->image, frame); + int w = sw->scaler.out_width; + int h = sw->scaler.out_height; + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + out_buffer[gfxGetFramebufferDisplayOffset(x + sw->hw_scale.x_offset, y)] = sw->image[y * w + x]; + } + else + { + struct scaler_ctx *ctx = &sw->scaler; + scaler_ctx_scale(ctx, sw->image + (sw->vp.y * sw->vp.full_width) + sw->vp.x, frame); + gfx_slow_swizzling_blit(out_buffer, sw->image, sw->vp.full_width, sw->vp.full_height, 0, 0, false); +#ifdef HAVE_NXRGUI + if (tmp_overlay) + { + gfx_slow_swizzling_blit(out_buffer, tmp_overlay, sw->vp.full_width, sw->vp.full_height, 0, 0, true); + } +#endif + } + + if (video_info->statistics_show && !sw->smooth) + { + struct font_params *osd_params = (struct font_params *)&video_info->osd_stat_params; + + if (osd_params) + font_driver_render_msg(video_info, NULL, video_info->stat_text, + (const struct font_params *)&video_info->osd_stat_params); + } + + if (msg) + font_driver_render_msg(video_info, NULL, msg, NULL); + + gfxFlushBuffers(); + gfxSwapBuffers(); + if (sw->vsync || video_info->menu_is_alive) + gfxWaitForVsync(); + + return true; +} + +static void switch_set_nonblock_state(void *data, bool toggle) +{ + switch_video_t *sw = data; + sw->vsync = !toggle; +} + +static bool switch_alive(void *data) +{ + (void)data; + return true; +} + +static bool switch_focus(void *data) +{ + (void)data; + return true; +} + +static bool switch_suppress_screensaver(void *data, bool enable) +{ + (void)data; + (void)enable; + return false; +} + +static bool switch_has_windowed(void *data) +{ + (void)data; + return false; +} + +static void switch_free(void *data) +{ + switch_video_t *sw = data; + if (sw->menu_texture.pixels) + free(sw->menu_texture.pixels); + + free(sw); +} + +static bool switch_set_shader(void *data, + enum rarch_shader_type type, const char *path) +{ + (void)data; + (void)type; + (void)path; + + return false; +} + +static void switch_set_rotation(void *data, unsigned rotation) +{ + switch_video_t *sw = data; + if (!sw) + return; + sw->rotation = rotation; +} + +static void switch_viewport_info(void *data, struct video_viewport *vp) +{ + switch_video_t *sw = data; + *vp = sw->vp; +} + +static bool switch_read_viewport(void *data, uint8_t *buffer, bool is_idle) +{ + (void)data; + (void)buffer; + + return true; +} + +static void switch_set_texture_frame( + void *data, const void *frame, bool rgb32, + unsigned width, unsigned height, float alpha) +{ + switch_video_t *sw = data; + size_t sz = width * height * (rgb32 ? 4 : 2); + + if (!sw->menu_texture.pixels || + sw->menu_texture.width != width || + sw->menu_texture.height != height) + { + if (sw->menu_texture.pixels) + realloc(sw->menu_texture.pixels, sz); + else + sw->menu_texture.pixels = malloc(sz); + + if (!sw->menu_texture.pixels) + { + printf("failed to allocate buffer for menu texture\n"); + return; + } + + int xsf = 1280 / width; + int ysf = 720 / height; + int sf = xsf; + + if (ysf < sf) + sf = ysf; + + sw->menu_texture.width = width; + sw->menu_texture.height = height; + sw->menu_texture.tgtw = width * sf; + sw->menu_texture.tgth = height * sf; + + struct scaler_ctx *sctx = &sw->menu_texture.scaler; + scaler_ctx_gen_reset(sctx); + + sctx->in_width = width; + sctx->in_height = height; + sctx->in_stride = width * (rgb32 ? 4 : 2); + sctx->in_fmt = rgb32 ? SCALER_FMT_ARGB8888 : SCALER_FMT_RGB565; + sctx->out_width = sw->menu_texture.tgtw; + sctx->out_height = sw->menu_texture.tgth; + sctx->out_stride = 1280 * 4; + sctx->out_fmt = SCALER_FMT_ABGR8888; + + sctx->scaler_type = SCALER_TYPE_POINT; + + if (!scaler_ctx_gen_filter(sctx)) + { + printf("failed to generate scaler for menu texture\n"); + return; + } + } + + memcpy(sw->menu_texture.pixels, frame, sz); +} + +static void switch_apply_state_changes(void *data) +{ + (void)data; +} + +static void switch_set_texture_enable(void *data, bool enable, bool full_screen) +{ + switch_video_t *sw = data; + + if (!sw->menu_texture.enable && enable) + gfxConfigureResolution(sw->vp.full_width, sw->vp.full_height); + else if (!enable && sw->menu_texture.enable && sw->smooth) + { + clear_screen(sw); + gfxConfigureResolution(sw->hw_scale.width, sw->hw_scale.height); + } + + sw->menu_texture.enable = enable; + sw->menu_texture.fullscreen = full_screen; +} + +static void switch_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + switch_video_t *sw = (switch_video_t *)data; + + if (sw) + font_driver_render_msg(video_info, font, msg, params); +} + +#ifdef HAVE_OVERLAY +static void switch_overlay_enable(void *data, bool state) +{ + printf("[Video] Enabled Overlay\n"); + + switch_video_t *swa = (switch_video_t *)data; + + if (!swa) + return; + + swa->overlay_enabled = state; +} + +static bool switch_overlay_load(void *data, + const void *image_data, unsigned num_images) +{ + switch_video_t *swa = (switch_video_t *)data; + + struct texture_image *images = (struct texture_image *)image_data; + + if (!swa) + return false; + + swa->overlay = images; + swa->overlay_enabled = true; + + return true; +} + +static void switch_overlay_tex_geom(void *data, + unsigned idx, float x, float y, float w, float h) +{ + switch_video_t *swa = (switch_video_t *)data; + + if (!swa) + return; +} + +static void switch_overlay_vertex_geom(void *data, + unsigned idx, float x, float y, float w, float h) +{ + switch_video_t *swa = (switch_video_t *)data; + + if (!swa) + return; +} + +static void switch_overlay_full_screen(void *data, bool enable) +{ + (void)data; + (void)enable; +} + +static void switch_overlay_set_alpha(void *data, unsigned idx, float mod) +{ + (void)data; + (void)idx; + (void)mod; +} + +static const video_overlay_interface_t switch_overlay = { + switch_overlay_enable, + switch_overlay_load, + switch_overlay_tex_geom, + switch_overlay_vertex_geom, + switch_overlay_full_screen, + switch_overlay_set_alpha, +}; + +void switch_overlay_interface(void *data, const video_overlay_interface_t **iface) +{ + switch_video_t *swa = (switch_video_t *)data; + if (!swa) + return; + *iface = &switch_overlay; +} + +#endif + +static const video_poke_interface_t switch_poke_interface = { + NULL, /* get_flags */ + NULL, /* set_coords */ + NULL, /* set_mvp */ + NULL, /* load_texture */ + NULL, /* unload_texture */ + NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ + NULL, /* set_filtering */ + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + NULL, /* get_current_framebuffer */ + NULL, /* get_proc_address */ + switch_set_aspect_ratio, /* set_aspect_ratio */ + switch_apply_state_changes, /* apply_state_changes */ + switch_set_texture_frame, + switch_set_texture_enable, + switch_set_osd_msg, + NULL, /* show_mouse */ + NULL, /* grab_mouse_toggle */ + NULL, /* get_current_shader */ + NULL, /* get_current_software_framebuffer */ + NULL, /* get_hw_render_interface */ +}; + +static void switch_get_poke_interface(void *data, + const video_poke_interface_t **iface) +{ + (void)data; + *iface = &switch_poke_interface; +} + +video_driver_t video_switch = { + switch_init, + switch_frame, + switch_set_nonblock_state, + switch_alive, + switch_focus, + switch_suppress_screensaver, + switch_has_windowed, + switch_set_shader, + switch_free, + "switch", + NULL, /* set_viewport */ + switch_set_rotation, + switch_viewport_info, + switch_read_viewport, + NULL, /* read_frame_raw */ +#ifdef HAVE_OVERLAY + switch_overlay_interface, /* switch_overlay_interface */ +#endif + switch_get_poke_interface, +}; + +/* vim: set ts=6 sw=6 sts=6: */ diff --git a/libretro-common/rthreads/switch_pthread.h b/libretro-common/rthreads/switch_pthread.h index 5d51194666..7dc32adfca 100644 --- a/libretro-common/rthreads/switch_pthread.h +++ b/libretro-common/rthreads/switch_pthread.h @@ -1,207 +1,207 @@ -/* Copyright (C) 2018 - M4xw , RetroArch Team - * - * --------------------------------------------------------------------------------------- - * The following license statement only applies to this file (switch_pthread.h). - * --------------------------------------------------------------------------------------- - * - * Permission is hereby granted, free of charge, - * to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _SWITCH_PTHREAD_WRAP_ -#define _SWITCH_PTHREAD_WRAP_ - -#include -#include -#include -#include - -#include "../include/retro_inline.h" -#include "../../verbosity.h" - -#define THREADVARS_MAGIC 0x21545624 // !TV$ - -// This structure is exactly 0x20 bytes, if more is needed modify getThreadVars() below -typedef struct -{ - // Magic value used to check if the struct is initialized - u32 magic; - - // Thread handle, for mutexes - Handle handle; - - // Pointer to the current thread (if exists) - Thread *thread_ptr; - - // Pointer to this thread's newlib state - struct _reent *reent; - - // Pointer to this thread's thread-local segment - void *tls_tp; // !! Offset needs to be TLS+0x1F8 for __aarch64_read_tp !! -} ThreadVars; - -static INLINE ThreadVars *getThreadVars(void) -{ - return (ThreadVars *)((u8 *)armGetTls() + 0x1E0); -} - -#define STACKSIZE (8 * 1024) - -/* libnx threads return void but pthreads return void pointer */ - -static uint32_t threadCounter = 1; - -int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) -{ - u32 prio = 0; - Thread new_switch_thread; - - svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); - - // Launch threads on Core 1 - int rc = threadCreate(&new_switch_thread, (void (*)(void *))start_routine, arg, STACKSIZE, prio - 1, 1); - - if (R_FAILED(rc)) - { - return EAGAIN; - } - - printf("[Threading]: Starting Thread(#%i)\n", threadCounter); - if (R_FAILED(threadStart(&new_switch_thread))) - { - threadClose(&new_switch_thread); - return -1; - } - - *thread = new_switch_thread; - - return 0; -} - -void pthread_exit(void *retval) -{ - (void)retval; - printf("[Threading]: Exiting Thread\n"); - svcExitThread(); -} - -static INLINE Thread threadGetCurrent(void) -{ - ThreadVars *tv = getThreadVars(); - return *tv->thread_ptr; -} - -static INLINE pthread_t pthread_self(void) -{ - return threadGetCurrent(); -} - -static INLINE int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) -{ - mutexInit(mutex); - - return 0; -} - -INLINE int pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - // Nothing - *mutex = 0; - - return 0; -} - -static INLINE int pthread_mutex_lock(pthread_mutex_t *mutex) -{ - mutexLock(mutex); - return 0; -} - -static INLINE int pthread_mutex_unlock(pthread_mutex_t *mutex) -{ - mutexUnlock(mutex); - - return 0; -} - -INLINE int pthread_detach(pthread_t thread) -{ - (void)thread; - // Nothing for now - return 0; -} - -static INLINE int pthread_join(pthread_t thread, void **retval) -{ - printf("[Threading]: Waiting for Thread Exit\n"); - threadWaitForExit(&thread); - threadClose(&thread); - - return 0; -} - -static INLINE int pthread_mutex_trylock(pthread_mutex_t *mutex) -{ - return mutexTryLock(mutex) ? 0 : 1; -} - -static INLINE int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - condvarWait(cond, mutex); - - return 0; -} - -static INLINE int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) -{ - condvarWaitTimeout(cond, mutex, abstime->tv_nsec); - - return 0; -} - -static INLINE int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) -{ - condvarInit(cond); - - return 0; -} - -static INLINE int pthread_cond_signal(pthread_cond_t *cond) -{ - condvarWakeOne(cond); - return 0; -} - -static INLINE int pthread_cond_broadcast(pthread_cond_t *cond) -{ - condvarWakeAll(cond); - return 0; -} - -INLINE int pthread_cond_destroy(pthread_cond_t *cond) -{ - // Nothing - return 0; -} - -INLINE int pthread_equal(pthread_t t1, pthread_t t2) -{ - if (t1.handle == t2.handle) - return 1; - - return 0; -} - -#endif +/* Copyright (C) 2018 - M4xw , RetroArch Team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (switch_pthread.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _SWITCH_PTHREAD_WRAP_ +#define _SWITCH_PTHREAD_WRAP_ + +#include +#include +#include +#include + +#include "../include/retro_inline.h" +#include "../../verbosity.h" + +#define THREADVARS_MAGIC 0x21545624 // !TV$ + +// This structure is exactly 0x20 bytes, if more is needed modify getThreadVars() below +typedef struct +{ + // Magic value used to check if the struct is initialized + u32 magic; + + // Thread handle, for mutexes + Handle handle; + + // Pointer to the current thread (if exists) + Thread *thread_ptr; + + // Pointer to this thread's newlib state + struct _reent *reent; + + // Pointer to this thread's thread-local segment + void *tls_tp; // !! Offset needs to be TLS+0x1F8 for __aarch64_read_tp !! +} ThreadVars; + +static INLINE ThreadVars *getThreadVars(void) +{ + return (ThreadVars *)((u8 *)armGetTls() + 0x1E0); +} + +#define STACKSIZE (8 * 1024) + +/* libnx threads return void but pthreads return void pointer */ + +static uint32_t threadCounter = 1; + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + u32 prio = 0; + Thread new_switch_thread; + + svcGetThreadPriority(&prio, CUR_THREAD_HANDLE); + + // Launch threads on Core 1 + int rc = threadCreate(&new_switch_thread, (void (*)(void *))start_routine, arg, STACKSIZE, prio - 1, 1); + + if (R_FAILED(rc)) + { + return EAGAIN; + } + + printf("[Threading]: Starting Thread(#%i)\n", threadCounter); + if (R_FAILED(threadStart(&new_switch_thread))) + { + threadClose(&new_switch_thread); + return -1; + } + + *thread = new_switch_thread; + + return 0; +} + +void pthread_exit(void *retval) +{ + (void)retval; + printf("[Threading]: Exiting Thread\n"); + svcExitThread(); +} + +static INLINE Thread threadGetCurrent(void) +{ + ThreadVars *tv = getThreadVars(); + return *tv->thread_ptr; +} + +static INLINE pthread_t pthread_self(void) +{ + return threadGetCurrent(); +} + +static INLINE int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) +{ + mutexInit(mutex); + + return 0; +} + +INLINE int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + // Nothing + *mutex = 0; + + return 0; +} + +static INLINE int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + mutexLock(mutex); + return 0; +} + +static INLINE int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + mutexUnlock(mutex); + + return 0; +} + +INLINE int pthread_detach(pthread_t thread) +{ + (void)thread; + // Nothing for now + return 0; +} + +static INLINE int pthread_join(pthread_t thread, void **retval) +{ + printf("[Threading]: Waiting for Thread Exit\n"); + threadWaitForExit(&thread); + threadClose(&thread); + + return 0; +} + +static INLINE int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + return mutexTryLock(mutex) ? 0 : 1; +} + +static INLINE int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + condvarWait(cond, mutex); + + return 0; +} + +static INLINE int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + condvarWaitTimeout(cond, mutex, abstime->tv_nsec); + + return 0; +} + +static INLINE int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) +{ + condvarInit(cond); + + return 0; +} + +static INLINE int pthread_cond_signal(pthread_cond_t *cond) +{ + condvarWakeOne(cond); + return 0; +} + +static INLINE int pthread_cond_broadcast(pthread_cond_t *cond) +{ + condvarWakeAll(cond); + return 0; +} + +INLINE int pthread_cond_destroy(pthread_cond_t *cond) +{ + // Nothing + return 0; +} + +INLINE int pthread_equal(pthread_t t1, pthread_t t2) +{ + if (t1.handle == t2.handle) + return 1; + + return 0; +} + +#endif