[Scummvm-git-logs] scummvm master -> 36446fa6eb33e67cc798f56ce1a31070260e2ada
sev-
noreply at scummvm.org
Sat Mar 4 15:35:56 UTC 2023
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
36446fa6eb LIBRETRO: add libretro platform
Commit: 36446fa6eb33e67cc798f56ce1a31070260e2ada
https://github.com/scummvm/scummvm/commit/36446fa6eb33e67cc798f56ce1a31070260e2ada
Author: Giovanni Cascione (ing.cascione at gmail.com)
Date: 2023-03-04T16:35:51+01:00
Commit Message:
LIBRETRO: add libretro platform
Changed paths:
A .gitlab-ci.yml
A backends/platform/libretro/.gitignore
A backends/platform/libretro/.gitlab-ci.yml
A backends/platform/libretro/Makefile
A backends/platform/libretro/Makefile.common
A backends/platform/libretro/README.md
A backends/platform/libretro/dependencies.mk
A backends/platform/libretro/dist/README.md
A backends/platform/libretro/dist/Roland_SC-55.sf2
A backends/platform/libretro/include/config.h
A backends/platform/libretro/include/libretro-core-options-intl.h
A backends/platform/libretro/include/libretro-core-options.h
A backends/platform/libretro/include/libretro-fs-factory.h
A backends/platform/libretro/include/libretro-fs.h
A backends/platform/libretro/include/libretro-threads.h
A backends/platform/libretro/include/os.h
A backends/platform/libretro/include/portdefs.h
A backends/platform/libretro/jni/Android.mk
A backends/platform/libretro/jni/Application.mk
A backends/platform/libretro/link.T
A backends/platform/libretro/lite_engines.list
A backends/platform/libretro/scripts/bundle_datafiles.sh
A backends/platform/libretro/scripts/configure_engines.sh
A backends/platform/libretro/scripts/configure_submodules.sh
A backends/platform/libretro/scripts/retrieve_modules.sh
A backends/platform/libretro/sharedlib_test.mk
A backends/platform/libretro/src/android-fs-factory.cpp
A backends/platform/libretro/src/libretro-fs-factory.cpp
A backends/platform/libretro/src/libretro-fs.cpp
A backends/platform/libretro/src/libretro-os.cpp
A backends/platform/libretro/src/libretro-threads.cpp
A backends/platform/libretro/src/libretro.cpp
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000000..73ef449468f
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,4 @@
+# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
+
+include:
+ - 'backends/platform/libretro/.gitlab-ci.yml'
diff --git a/backends/platform/libretro/.gitignore b/backends/platform/libretro/.gitignore
new file mode 100644
index 00000000000..8f46d582208
--- /dev/null
+++ b/backends/platform/libretro/.gitignore
@@ -0,0 +1,11 @@
+build/libs/
+build/obj/
+deps/
+*.o
+*.a
+scummvm.zip
+scummvm_libretro.*
+config.mk.engines.lite
+shared_modules.mk
+script.mri
+ScummVM.dat
diff --git a/backends/platform/libretro/.gitlab-ci.yml b/backends/platform/libretro/.gitlab-ci.yml
new file mode 100644
index 00000000000..9a6c7b323f1
--- /dev/null
+++ b/backends/platform/libretro/.gitlab-ci.yml
@@ -0,0 +1,223 @@
+# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
+
+##############################################################################
+################################# BOILERPLATE ################################
+##############################################################################
+
+# Core definitions
+.core-defs:
+ variables:
+ JNI_PATH: backends/platform/libretro/build
+ MAKEFILE_PATH: backends/platform/libretro
+ CORENAME: scummvm
+
+.win-defs:
+ variables:
+ platform: win
+
+# Inclusion templates, required for the build to work
+include:
+ ################################## DESKTOPS ################################
+ # Windows 64-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/windows-x64-mingw.yml'
+
+ # Windows 32-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/windows-i686-mingw.yml'
+
+ # Linux 64-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/linux-x64.yml'
+
+ # Linux 32-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/linux-i686.yml'
+
+ # MacOS 64-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/osx-x64.yml'
+
+ # MacOS ARM 64-bit
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/osx-arm64.yml'
+
+ ################################## CELLULAR ################################
+ # Android
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/android-jni.yml'
+
+ # iOS
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/ios-arm64.yml'
+
+ # iOS (armv7)
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/ios9.yml'
+
+ ################################## CONSOLES ################################
+ # Nintendo WiiU
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/wiiu-static.yml'
+
+ # Nintendo 3DS
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/ctr-legacy-static.yml'
+
+ # Nintendo Switch
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/libnx-static.yml'
+
+ # OpenDingux
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/dingux-mips32.yml'
+
+ # OpenDingux (ARM)
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/dingux-arm32.yml'
+
+ # tvOS (AppleTV)
+ - project: 'libretro-infrastructure/ci-templates'
+ file: '/tvos-arm64.yml'
+
+ #################################### MISC ##################################
+
+# Stages for building
+stages:
+ - build-prepare
+ - build-shared
+ - build-static
+
+##############################################################################
+#################################### STAGES ##################################
+##############################################################################
+#
+################################### DESKTOPS #################################
+
+# Windows 64-bit
+libretro-build-windows-x64:
+ extends:
+ - .libretro-windows-x64-mingw-make-default
+ - .core-defs
+ - .win-defs
+ variables:
+ TOOLSET: x86_64-w64-mingw32.static-
+
+# Windows 64-bit
+libretro-build-windows-i686:
+ extends:
+ - .libretro-windows-i686-mingw-make-default
+ - .core-defs
+ - .win-defs
+ variables:
+ TOOLSET: i686-w64-mingw32.static-
+
+# Linux 64-bit
+libretro-build-linux-x64:
+ extends:
+ - .libretro-linux-x64-make-default
+ - .core-defs
+
+# Linux 32-bit
+libretro-build-linux-i686:
+ extends:
+ - .libretro-linux-i686-make-default
+ - .core-defs
+ variables:
+ BUILD_64BIT: 0
+
+# MacOS 64-bit
+libretro-build-osx-x64:
+ tags:
+ - macosx-packaging
+ extends:
+ - .libretro-osx-x64-make-default
+ - .core-defs
+
+# MacOS ARM 64-bit
+libretro-build-osx-arm64:
+ tags:
+ - macosx-packaging
+ extends:
+ - .libretro-osx-arm64-make-default
+ - .core-defs
+
+################################### CELLULAR #################################
+# Android ARMv7a
+android-armeabi-v7a:
+ extends:
+ - .libretro-android-jni-armeabi-v7a
+ - .core-defs
+
+# Android ARMv8a
+android-arm64-v8a:
+ extends:
+ - .libretro-android-jni-arm64-v8a
+ - .core-defs
+
+# Android 64-bit x86
+android-x86_64:
+ extends:
+ - .libretro-android-jni-x86_64
+ - .core-defs
+
+# Android 32-bit x86
+android-x86:
+ extends:
+ - .libretro-android-jni-x86
+ - .core-defs
+
+# iOS
+libretro-build-ios-arm64:
+ extends:
+ - .libretro-ios-arm64-make-default
+ - .core-defs
+
+# iOS (armv7) [iOS 9 and up]
+libretro-build-ios9:
+ extends:
+ - .libretro-ios9-make-default
+ - .core-defs
+
+# tvOS
+libretro-build-tvos-arm64:
+ extends:
+ - .libretro-tvos-arm64-make-default
+ - .core-defs
+
+################################### CONSOLES #################################
+# Nintendo WiiU
+libretro-build-wiiu:
+ extends:
+ - .libretro-wiiu-static-retroarch-master
+ - .core-defs
+
+# Nintendo 3DS
+libretro-build-ctr:
+ extends:
+ - .libretro-ctr-legacy-static-retroarch-master
+ - .core-defs
+
+# Nintendo Switch
+libretro-build-libnx-aarch64:
+ extends:
+ - .libretro-libnx-static-retroarch-master
+ - .core-defs
+
+# OpenDingux
+libretro-build-dingux-mips32:
+ extends:
+ - .libretro-dingux-mips32-make-default
+ - .core-defs
+
+# OpenDingux Beta
+libretro-build-dingux-odbeta-mips32:
+ extends:
+ - .libretro-dingux-odbeta-mips32-make-default
+ - .core-defs
+
+# Miyoo
+libretro-build-miyoo-arm32:
+ extends:
+ - .libretro-miyoo-arm32-make-default
+ - .core-defs
diff --git a/backends/platform/libretro/Makefile b/backends/platform/libretro/Makefile
new file mode 100644
index 00000000000..7b760d27ec1
--- /dev/null
+++ b/backends/platform/libretro/Makefile
@@ -0,0 +1,631 @@
+ROOT_PATH := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+
+# Output files prefix
+TARGET_NAME = scummvm
+
+HIDE := @
+SPACE :=
+SPACE := $(SPACE) $(SPACE)
+BACKSLASH :=
+BACKSLASH := \$(BACKSLASH)
+filter_out1 = $(filter-out $(firstword $1),$1)
+filter_out2 = $(call filter_out1,$(call filter_out1,$1))
+unixpath = $(subst \,/,$1)
+unixcygpath = /$(subst :,,$(call unixpath,$1))
+
+ifeq ($(shell uname -a),)
+ EXE_EXT = .exe
+endif
+
+ifeq ($(BUILD_64BIT),)
+ifeq (,$(findstring 64,$(platform)))
+ BUILD_64BIT := 0
+else
+ BUILD_64BIT := 1
+endif
+endif
+TARGET_64BIT := $(BUILD_64BIT)
+
+LD = $(CXX)
+AR = ar cru
+RANLIB = ranlib
+LS = ls
+MKDIR = mkdir -p
+RM = rm -f
+RM_REC = rm -rf
+
+# Raspberry Pi 3 (64 bit)
+ifeq ($(platform), rpi3_64)
+ TARGET = $(TARGET_NAME)_libretro.so
+ DEFINES += -fPIC -D_ARM_ASSEM_ -DUSE_CXX11 -DARM
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CFLAGS += -fPIC -mcpu=cortex-a53 -mtune=cortex-a53 -fomit-frame-pointer -ffast-math
+ CXXFLAGS = $(CFLAGS) -frtti -std=c++11
+
+# Raspberry Pi 4 (64 bit)
+else ifeq ($(platform), rpi4_64)
+ TARGET = $(TARGET_NAME)_libretro.so
+ DEFINES += -fPIC -D_ARM_ASSEM_ -DUSE_CXX11 -DARM
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CFLAGS += -fPIC -mcpu=cortex-a72 -mtune=cortex-a72 -fomit-frame-pointer -ffast-math
+ CXXFLAGS = $(CFLAGS) -frtti -std=c++11
+
+# iOS
+else ifneq (,$(findstring ios,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro_ios.dylib
+ DEFINES += -fPIC -DHAVE_POSIX_MEMALIGN=1 -DIOS
+ LDFLAGS += -dynamiclib -fPIC
+ MINVERSION :=
+
+ifeq ($(IOSSDK),)
+ IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path)
+endif
+ifeq ($(platform),ios-arm64)
+ CC = cc -arch arm64 -isysroot $(IOSSDK)
+ CXX = c++ -arch arm64 -isysroot $(IOSSDK)
+else
+ CC = cc -arch armv7 -isysroot $(IOSSDK) -marm
+ CXX = c++ -arch armv7 -isysroot $(IOSSDK)
+endif
+
+ifeq ($(platform),$(filter $(platform),ios9 ios-arm64))
+ MINVERSION += -miphoneos-version-min=8.0
+else
+ MINVERSION += -miphoneos-version-min=5.0
+endif
+ CFLAGS += $(MINVERSION)
+ CXXFLAGS += $(MINVERSION) -std=c++11
+
+else ifeq ($(platform), tvos-arm64)
+ EXT?=dylib
+ TARGET := $(TARGET_NAME)_libretro_tvos.$(EXT)
+ DEFINES += -fPIC -DHAVE_POSIX_MEMALIGN=1 -DIOS
+ LDFLAGS += -dynamiclib -fPIC
+ifeq ($(IOSSDK),)
+ IOSSDK := $(shell xcodebuild -version -sdk appletvos Path)
+endif
+ CC = cc -arch arm64 -isysroot $(IOSSDK)
+ CXX = c++ -arch arm64 -isysroot $(IOSSDK) -std=c++11
+
+# QNX
+else ifeq ($(platform), qnx)
+ TARGET := $(TARGET_NAME)_libretro_$(platform).so
+ DEFINES += -fPIC -DSYSTEM_NOT_SUPPORTING_D_TYPE
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CC = qcc -Vgcc_ntoarmv7le
+ CXX = QCC -Vgcc_ntoarmv7le
+ LD = QCC -Vgcc_ntoarmv7le
+ AR = qcc -Vgcc_ntoarmv7le -A
+ RANLIB="${QNX_HOST}/usr/bin/ntoarmv7-ranlib"
+
+# Genode
+else ifeq ($(platform), genode)
+ TARGET := libretro.so
+ DEFINES += -fPIC -DSYSTEM_NOT_SUPPORTING_D_TYPE -DFRONTEND_SUPPORTS_RGB565
+ C_PKGS = libc
+ CXX_PKGS = stdcxx genode-base
+ CFLAGS += -D__GENODE__ $(shell pkg-config --cflags $(C_PKGS))
+ CXXFLAGS += -D__GENODE__ $(shell pkg-config --cflags $(CXX_PKGS))
+
+ LIBS += $(shell pkg-config --libs $(C_PKGS) $(CXX_PKGS) genode-lib)
+
+ CC = $(shell pkg-config genode-base --variable=cc)
+ CXX = $(shell pkg-config genode-base --variable=cxx)
+ LD = $(shell pkg-config genode-base --variable=ld)
+ AR = $(shell pkg-config genode-base --variable=ar) rcs
+ RANLIB = genode-x86-ranlib
+
+# PS3
+else ifeq ($(platform), ps3)
+ TARGET := $(TARGET_NAME)_libretro_$(platform).a
+ CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
+ CXX = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-g++.exe
+ AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe rcs
+ DEFINES += -DPLAYSTATION3
+ STATIC_LINKING=1
+
+# Nintendo Wii
+else ifeq ($(platform), wii)
+ TARGET := $(TARGET_NAME)_libretro_wii.a
+ CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
+ CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
+ AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) rcs
+ DEFINES += -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float -D__ppc__ -I$(DEVKITPRO)/libogc/include
+ STATIC_LINKING=1
+
+# Nintendo Switch (libnx)
+else ifeq ($(platform), libnx)
+ export DEPSDIR := $(CURDIR)
+ include $(DEVKITPRO)/libnx/switch_rules
+ EXT=a
+ TARGET := $(TARGET_NAME)_libretro_$(platform).$(EXT)
+ AR = $(DEVKITPRO)/devkitA64/aarch64-none-elf/bin/ar$(EXE_EXT) rcs
+ DEFINES := -DSWITCH=1 -U__linux__ -U__linux
+ DEFINES += -g -O3 -fPIE -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec
+ DEFINES += $(INCDIRS)
+ DEFINES += -D__SWITCH__ -DHAVE_LIBNX -march=armv8-a -mtune=cortex-a57 -mtp=soft
+ CXXFLAGS := $(ASFLAGS) -std=gnu++11 -fpermissive
+ STATIC_LINKING = 1
+
+# Nintendo Wii U
+else ifeq ($(platform), wiiu)
+ TARGET := $(TARGET_NAME)_libretro_wiiu.a
+ CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
+ CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT)
+ AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) rcs
+ AR_ALONE = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
+ DEFINES += -DGEKKO -mwup -mcpu=750 -meabi -mhard-float -D__POWERPC__ -D__ppc__ -DRETRO_IS_BIG_ENDIAN=1 -DRETRO_IS_LITTLE_ENDIAN=0
+ DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
+ DEFINES += -DHAVE_STRTOUL -DWIIU
+ CXXFLAGS := -fpermissive
+ LITE := 1
+ CP := cp
+ STATIC_LINKING = 1
+
+# Nintendo 3DS
+else ifeq ($(platform), ctr)
+ TARGET := $(TARGET_NAME)_libretro_$(platform).a
+ CC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT)
+ CXX = $(DEVKITARM)/bin/arm-none-eabi-g++$(EXE_EXT)
+ AR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT) rcs
+ RANLIB = $(DEVKITARM)/bin/arm-none-eabi-ranlib$(EXE_EXT)
+ ifeq ($(strip $(CTRULIB)),)
+ $(error "Please set CTRULIB in your environment. export CTRULIB=<path to>libctru")
+ endif
+ DEFINES += -DARM11 -D_3DS -I$(CTRULIB)/include
+ DEFINES += -march=armv6k -mtune=mpcore -mfloat-abi=hard
+ DEFINES += -Wall -mword-relocations
+ DEFINES += -fomit-frame-pointer -ffast-math
+ CXXFLAGS += -std=gnu++11 -fpermissive
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_MT32EMU = 0
+ STATIC_LINKING = 1
+
+# Vita
+else ifeq ($(platform), vita)
+ TARGET := $(TARGET_NAME)_libretro_$(platform).a
+ CC = arm-vita-eabi-gcc$(EXE_EXT)
+ CXX = arm-vita-eabi-g++$(EXE_EXT)
+ AR = arm-vita-eabi-ar$(EXE_EXT) rcs
+ DEFINES += -DVITA
+ STATIC_LINKING = 1
+
+# GCW0
+else ifeq ($(platform), gcw0)
+ TARGET := $(TARGET_NAME)_libretro.so
+ CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
+ CXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
+ LD = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
+ AR = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar cru
+ RANLIB = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ranlib
+ DEFINES += -DDINGUX -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float -fPIC
+ DEFINES += -ffunction-sections -fdata-sections
+ LDFLAGS += -shared -Wl,--gc-sections -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CFLAGS += -std=gnu99
+ CXXFLAGS += -std=c++11
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_LIBCO = 0
+ USE_CURL = 0
+ USE_MT32EMU = 0
+ NO_HIGH_DEF := 1
+
+# MIYOO
+else ifeq ($(platform), miyoo)
+ TARGET := $(TARGET_NAME)_libretro.so
+ CC = /opt/miyoo/usr/bin/arm-linux-gcc
+ CXX = /opt/miyoo/usr/bin/arm-linux-g++
+ LD = /opt/miyoo/usr/bin/arm-linux-g++
+ AR = /opt/miyoo/usr/bin/arm-linux-ar cru
+ RANLIB = /opt/miyoo/usr/bin/arm-linux-ranlib
+ DEFINES += -DDINGUX -fomit-frame-pointer -ffast-math -march=armv5te -mtune=arm926ej-s -fPIC
+ DEFINES += -ffunction-sections -fdata-sections
+ LDFLAGS += -shared -Wl,--gc-sections -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_LIBCO = 0
+ USE_CURL = 0
+ USE_MT32EMU = 0
+ NO_HIGH_DEF := 1
+
+# MIYOOMINI
+else ifeq ($(platform), miyoomini)
+ TARGET := $(TARGET_NAME)_libretro.so
+ CC = /usr/bin/arm-linux-gnueabihf-gcc
+ CXX = /usr/bin/arm-linux-gnueabihf-g++
+ LD = /usr/bin/arm-linux-gnueabihf-g++
+ AR = /usr/bin/arm-linux-gnueabihf-ar cru
+ RANLIB = /usr/bin/arm-linux-gnueabihf-ranlib
+ DEFINES += -fomit-frame-pointer -ffast-math -marm -march=armv7ve+simd -mtune=cortex-a7 -fPIC
+ DEFINES += -ffunction-sections -fdata-sections
+ LDFLAGS += -shared -Wl,--gc-sections -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_LIBCO = 0
+ USE_MT32EMU = 0
+ NO_HIGH_DEF = 0
+
+# ARM v7
+else ifneq (,$(findstring armv7,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro.so
+ DEFINES += -fPIC -D_ARM_ASSEM_ -DUSE_CXX11 -marm -DARM
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_MT32EMU = 0
+ CXXFLAGS := -std=c++11
+ifneq (,$(findstring cortexa8,$(platform)))
+ DEFINES += -marm -mcpu=cortex-a8
+else ifneq (,$(findstring cortexa9,$(platform)))
+ DEFINES += -marm -mcpu=cortex-a9
+endif
+ifneq (,$(findstring neon,$(platform)))
+ DEFINES += -mfpu=neon
+ HAVE_NEON = 1
+endif
+ifneq (,$(findstring softfloat,$(platform)))
+ DEFINES += -mfloat-abi=softfp
+else ifneq (,$(findstring hardfloat,$(platform)))
+ DEFINES += -mfloat-abi=hard
+endif
+
+# ARM v8
+else ifneq (,$(findstring armv8,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro.so
+ DEFINES += -fPIC -D_ARM_ASSEM_ -DARM -marm -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard -march=armv8-a+crc
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CFLAGS += -fPIC
+ HAVE_NEON = 1
+
+# Odroid Go Advance
+else ifneq (,$(findstring oga_a35_neon_hardfloat,$(platform)))
+ TARGET := $(TARGET_NAME)_libretro.so
+ DEFINES += -fPIC -D_ARM_ASSEM_ -DARM -marm -mtune=cortex-a35 -mfpu=neon-fp-armv8 -mfloat-abi=hard -march=armv8-a+crc
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ USE_VORBIS = 0
+ USE_THEORADEC = 0
+ USE_TREMOR = 1
+ USE_MT32EMU = 0
+ HAVE_NEON = 1
+
+# Emscripten
+else ifeq ($(platform), emscripten)
+ TARGET := $(TARGET_NAME)_libretro_$(platform).bc
+ STATIC_LINKING = 1
+ USE_CLOUD = 0
+
+# Windows MSVC 2017 all architectures
+else ifneq (,$(findstring windows_msvc2017,$(platform)))
+
+ NO_GCC := 1
+ CFLAGS += -DNOMINMAX
+ CXXFLAGS += -DNOMINMAX
+ WINDOWS_VERSION = 1
+
+ PlatformSuffix = $(subst windows_msvc2017_,,$(platform))
+ ifneq (,$(findstring desktop,$(PlatformSuffix)))
+ WinPartition = desktop
+ MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -FS
+ LDFLAGS += -MANIFEST -LTCG:incremental -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1
+ LIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib
+ else ifneq (,$(findstring uwp,$(PlatformSuffix)))
+ WinPartition = uwp
+ MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -D_WINDLL -D_UNICODE -DUNICODE -D__WRL_NO_DEFAULT_LIB__ -EHsc -FS
+ LDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -LTCG -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO
+ LIBS += WindowsApp.lib
+ endif
+
+ CFLAGS += $(MSVC2017CompileFlags)
+ CXXFLAGS += $(MSVC2017CompileFlags)
+
+ TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix))
+
+ CC = cl.exe
+ CXX = cl.exe
+ LD = link.exe
+
+ reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul)))
+ fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1))
+
+ ProgramFiles86w := $(shell cmd /c "echo %PROGRAMFILES(x86)%")
+ ProgramFiles86 := $(shell cygpath "$(ProgramFiles86w)")
+
+ WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
+ WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0)
+ WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
+ WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0)
+ WindowsSdkDir := $(WindowsSdkDir)
+
+ WindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH)
+ WindowsSDKVersion := $(WindowsSDKVersion)
+
+ VsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools
+ VsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise
+ VsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional
+ VsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community
+
+ VsInstallRoot ?= $(shell if [ -d "$(VsInstallBuildTools)" ]; then echo "$(VsInstallBuildTools)"; fi)
+ ifeq ($(VsInstallRoot), )
+ VsInstallRoot = $(shell if [ -d "$(VsInstallEnterprise)" ]; then echo "$(VsInstallEnterprise)"; fi)
+ endif
+ ifeq ($(VsInstallRoot), )
+ VsInstallRoot = $(shell if [ -d "$(VsInstallProfessional)" ]; then echo "$(VsInstallProfessional)"; fi)
+ endif
+ ifeq ($(VsInstallRoot), )
+ VsInstallRoot = $(shell if [ -d "$(VsInstallCommunity)" ]; then echo "$(VsInstallCommunity)"; fi)
+ endif
+ VsInstallRoot := $(VsInstallRoot)
+
+ VcCompilerToolsVer := $(shell cat "$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" | grep -o '[0-9\.]*')
+ VcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer)
+
+ WindowsSDKSharedIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\shared")
+ WindowsSDKUCRTIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\ucrt")
+ WindowsSDKUMIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\um")
+ WindowsSDKUCRTLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\ucrt\$(TargetArchMoniker)")
+ WindowsSDKUMLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\um\$(TargetArchMoniker)")
+
+ # For some reason the HostX86 compiler doesn't like compiling for x64
+ # ("no such file" opening a shared library), and vice-versa.
+ # Work around it for now by using the strictly x86 compiler for x86, and x64 for x64.
+ # NOTE: What about ARM?
+ ifneq (,$(findstring x64,$(TargetArchMoniker)))
+ VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX64
+ else
+ VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX86
+ endif
+
+ PATH := $(shell IFS=$$'\n'; cygpath "$(VCCompilerToolsBinDir)/$(TargetArchMoniker)"):$(PATH)
+ PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VsInstallRoot)/Common7/IDE")
+ INCLUDE := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/include")
+ LIB := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)")
+ ifneq (,$(findstring uwp,$(PlatformSuffix)))
+ LIB := $(LIB);$(shell IFS=$$'\n'; cygpath -w "$(LIB)/store")
+ endif
+
+ export INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir)
+ export LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir)
+ TARGET := $(TARGET_NAME)_libretro.dll
+ PSS_STYLE :=2
+ LDFLAGS += -DLL
+
+else ifeq ($(platform), win)
+ # let out for next test block
+
+else ifeq ($(platform), osx)
+ # let out for next test block
+
+else ifeq ($(platform), unix)
+ # let out for next test block
+
+else
+ # Nothing found for specified platform or none set
+
+ override platform = unix
+ ifeq ($(shell uname -a),)
+ override platform = win
+ else ifneq ($(findstring MINGW,$(shell uname -a)),)
+ override platform = win
+ else ifneq ($(findstring Darwin,$(shell uname -a)),)
+ override platform = osx
+ override arch = intel
+ ifeq ($(shell uname -p),arm64)
+ override arch = arm
+ endif
+ ifeq ($(shell uname -p),powerpc)
+ override arch = ppc
+ endif
+ else ifneq ($(findstring win,$(shell uname -a)),)
+ override platform = win
+ endif
+
+ ifeq (,$(findstring 64,$(shell uname -m)))
+ BUILD_64BIT := 0
+ else
+ BUILD_64BIT := 1
+ endif
+ TARGET_64BIT := $(BUILD_64BIT)
+endif
+
+# Unix fallback
+ifeq ($(platform), unix)
+ TARGET := $(TARGET_NAME)_libretro.so
+ DEFINES += -DHAVE_POSIX_MEMALIGN=1 -DUSE_CXX11
+ LDFLAGS += -shared -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ CFLAGS += -fPIC
+ CXXFLAGS += $(CFLAGS) -std=c++11
+# Win fallback
+else ifeq ($(platform), win)
+ CC ?= gcc
+ TARGET := $(TARGET_NAME)_libretro.dll
+ DEFINES += -DHAVE_FSEEKO -DHAVE_INTTYPES_H -fPIC
+ CXXFLAGS += -fno-permissive
+ LDFLAGS += -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=$(ROOT_PATH)/link.T -fPIC
+ifneq ($(TARGET_64BIT),1)
+ DEFINES += -DRETRO_CALLCONV=__cdecl
+endif
+
+# OS X
+else ifeq ($(platform), osx)
+ TARGET := $(TARGET_NAME)_libretro.dylib
+ DEFINES += -fPIC -Wno-undefined-var-template -Wno-pragma-pack -DHAVE_POSIX_MEMALIGN=1 -DUSE_CXX11
+ LDFLAGS += -dynamiclib -fPIC
+ CXXFLAGS := -std=c++11
+
+ ifeq ($(CROSS_COMPILE),1)
+ TARGET_RULE = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
+ CFLAGS += $(TARGET_RULE)
+ CPPFLAGS += $(TARGET_RULE)
+ CXXFLAGS += $(TARGET_RULE)
+ LDFLAGS += $(TARGET_RULE)
+ # Hardcode TARGET_64BIT for now
+ TARGET_64BIT = 1
+ endif
+endif
+
+ifeq ($(DEBUG), 1)
+ DEFINES += -O0 -g
+else ifeq ($(platform), wiiu)
+ DEFINES += -Os
+else ifeq ($(platform),genode)
+ DEFINES += -O2
+else ifneq (,$(findstring msvc,$(platform)))
+ DEFINES += -O2
+else
+ DEFINES += -O3
+endif
+
+ifeq ($(TARGET_64BIT), 1)
+ DEFINES += -DSIZEOF_SIZE_T=8 -DSCUMM_64BITS
+else
+ DEFINES += -DSIZEOF_SIZE_T=4
+endif
+
+# Define toolset
+ifdef TOOLSET
+ CC = $(TOOLSET)gcc
+ CXX = $(TOOLSET)g++
+ LD = $(TOOLSET)g++
+ AR = $(TOOLSET)ar cru
+ RANLIB = $(TOOLSET)ranlib
+endif
+
+# Define build flags
+DEPDIR = .deps
+HAVE_GCC3 = true
+USE_RGB_COLOR = true
+
+CXXFLAGS += -Wno-reorder
+
+# Compile platform specific parts (e.g. filesystem)
+ifeq ($(platform), win)
+WIN32 = 1
+DEFINES += -DWIN32 -DUSE_CXX11
+CXXFLAGS += -std=c++11
+LIBS += -lwinmm
+endif
+
+$(info Platform is $(platform) $(shell test $(TARGET_64BIT) = 1 && echo 64bit || echo 32bit))
+
+include $(ROOT_PATH)/Makefile.common
+
+######################################################################
+# The build rules follow - normally you should have no need to
+# touch whatever comes after here.
+######################################################################
+
+# Concat DEFINES and INCLUDES to form the CPPFLAGS
+CPPFLAGS := $(DEFINES) $(INCLUDES)
+CXXFLAGS += $(DEFINES) $(INCLUDES)
+CFLAGS += $(DEFINES) $(INCLUDES)
+
+# Include the build instructions for all modules
+include $(addprefix $(SCUMMVM_PATH)/, $(addsuffix /module.mk,$(MODULES)))
+
+# Depdir information
+DEPDIRS := $(addsuffix $(DEPDIR),$(MODULE_PATHS))
+
+# Hack for libnx DEPSDIR issues
+libnx-ln:
+ifeq ($(platform), libnx)
+ ln -s $(SCUMMVM_PATH)/audio/ audio
+ ln -s $(SCUMMVM_PATH)/backends/ backends
+ ln -s $(SCUMMVM_PATH)/base/ base
+ ln -s $(SCUMMVM_PATH)/common/ common
+ ln -s $(SCUMMVM_PATH)/engines/ engines
+ ln -s $(SCUMMVM_PATH)/graphics/ graphics
+ ln -s $(SCUMMVM_PATH)/gui/ gui
+ ln -s $(SCUMMVM_PATH)/image/ image
+ ln -s $(SCUMMVM_PATH)/video/ video
+ touch libnx-ln
+endif
+
+OBJOUT = -o
+LINKOUT = -o
+
+ifneq (,$(findstring msvc,$(platform)))
+ OBJOUT = -Fo
+ LINKOUT = -out:
+ifeq ($(STATIC_LINKING),1)
+ LD ?= lib.exe
+ STATIC_LINKING=0
+else
+ LD ?= link.exe
+endif
+endif
+
+ifeq ($(platform), wiiu)
+$(TARGET): $(OBJS) libdeps.a libdetect.a
+ $(MKDIR) libtemp
+ $(CP) $+ libtemp/
+ $(AR_ALONE) -M < $(ROOT_PATH)/script.mri
+else ifeq ($(platform), libnx)
+$(TARGET): libnx-ln $(OBJS) libdeps.a libdetect.a
+ $(MKDIR) libtemp
+ cp $+ libtemp/
+ $(AR) -M < $(ROOT_PATH)/script.mri
+else ifeq ($(platform), ctr)
+$(TARGET): $(OBJS) libdeps.a libdetect.a
+ $(MKDIR) libtemp
+ cp $+ libtemp/
+ $(AR) -M < $(ROOT_PATH)/script.mri
+else ifeq ($(STATIC_LINKING), 1)
+$(TARGET): $(DETECT_OBJS) $(OBJS) libdeps.a libdetect.a
+ @echo Linking $@...
+ $(HIDE)$(AR) $@ $(wildcard *.o) $(wildcard */*.o) $(wildcard */*/*.o) $(wildcard */*/*/*.o) $(wildcard */*/*/*/*.o) $(wildcard */*/*/*/*/*.o)
+else
+$(TARGET): $(DETECT_OBJS) $(OBJS) libdeps.a
+ @echo Linking $@...
+ $(HIDE)$(LD) $(LDFLAGS) $+ $(LIBS) $(LINKOUT)$@
+endif
+
+libdeps.a: $(OBJS_DEPS)
+ @echo Linking $@...
+ $(HIDE)$(AR) $@ $^
+
+libdetect.a: $(DETECT_OBJS)
+ @echo Linking $@...
+ $(HIDE)$(AR) $@ $^
+
+%.o: %.c
+ @echo Compiling $(<F)...
+ @$(MKDIR) $(*D)
+ $(HIDE)$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
+
+%.o: %.cpp
+ @echo Compiling $(<F)...
+ @$(MKDIR) $(*D)
+ $(HIDE)$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $<
+
+# Dumb compile rule, for C++ compilers that don't allow dependency tracking or
+# where it is broken (such as GCC 2.95).
+.cpp.o:
+ @$(MKDIR) $(*D)
+ @echo Compiling $<...
+ $(HIDE)$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<) $(OBJOUT)$*.o
+
+clean:
+ @echo Cleaning project...
+ $(HIDE)$(RM_REC) $(DEPDIRS) $(DEPS_PATH)
+ $(HIDE)$(RM) $(OBJS) $(DETECT_OBJS) $(OBJS_DEPS) libdeps.a libdetect.a $(TARGET) *.a
+ $(HIDE)$(RM_REC) libtemp $(MODULES)
+ $(HIDE)$(RM) libnx-ln
+ $(HIDE)$(RM) scummvm.zip $(TARGET_NAME)_libretro.* script.mri config.mk.engines.lite ScummVM.dat
+ $(HIDE)$(RM) $(ROOT_PATH)/shared_modules.mk
+
+# Include the dependency tracking files.
+-include $(wildcard $(addsuffix /*.d,$(DEPDIRS)))
+
+# Mark *.d files and most *.mk files as PHONY. This stops make from trying to
+# recreate them (which it can't), and in particular from looking for potential
+# source files. This can save quite a bit of disk access time.
+.PHONY: $(wildcard $(addsuffix /*.d,$(DEPDIRS))) $(addprefix $(SCUMMVM_PATH)/, $(addsuffix /module.mk,$(MODULES))) \
+ $(SCUMMVM_PATH)/$(port_mk) $(SCUMMVM_PATH)/rules.mk $(SCUMMVM_PATH)/engines/engines.mk
+
+.PHONY: clean
diff --git a/backends/platform/libretro/Makefile.common b/backends/platform/libretro/Makefile.common
new file mode 100644
index 00000000000..0950ec71a12
--- /dev/null
+++ b/backends/platform/libretro/Makefile.common
@@ -0,0 +1,193 @@
+######################################################################
+# Common settings and defaults
+######################################################################
+
+# Reset flags (DEFINES not reset as used in Makefile)
+INCLUDES :=
+OBJS_DEPS :=
+OBJS :=
+DETECT_OBJS :=
+$(foreach MODULE,$(MODULES),$(MODULE_OBJS-$(MODULE)) :=)
+MODULES :=
+
+# Defaults
+USE_ZLIB ?= 1
+USE_TREMOR ?= 0
+USE_VORBIS ?= 1
+USE_FLAC ?= 1
+USE_MAD ?= 1
+USE_FAAD ?= 1
+USE_PNG ?= 1
+USE_JPEG ?= 1
+USE_THEORADEC ?= 1
+USE_FREETYPE2 ?= 1
+USE_MT32EMU ?= 1
+USE_FLUIDSYNTH ?= 1
+USE_LUA ?= 1
+USE_LIBCO ?= 1
+LOAD_RULES_MK = 1
+USE_TINYGL ?= 1
+USE_BINK ?= 1
+POSIX ?= 1
+NO_HIGH_DEF ?= 0
+NO_WIP ?= 1
+USE_LIBCO ?= 1
+USE_RGB_COLOR ?= 1
+ENABLE_VKEYBD = 1
+USE_CLOUD ?= 0
+STATIC_LINKING ?= 0
+LITE ?= 0
+
+DEBUG_ALLOW_DIRTY_SUBMODULES ?= 0
+
+# Paths
+SCUMMVM_PATH := $(ROOT_PATH)/../../..
+DEPS_PATH := $(ROOT_PATH)/deps
+CORE_PATH := $(ROOT_PATH)/src
+SCRIPTS_PATH := $(ROOT_PATH)/scripts
+srcdir := $(SCUMMVM_PATH)
+VPATH := $(SCUMMVM_PATH)
+UP_COMMON_MAKE := $(SCUMMVM_PATH)/Makefile.common
+SHARED_MOD_MAKE := $(ROOT_PATH)/shared_modules.mk
+
+# Core version shown in frontend
+GIT_TAG := $(shell cd $(ROOT_PATH) 2>/dev/null && git describe --tags 2>/dev/null)
+ifeq ($(GIT_TAG),)
+ GIT_HASH := $(shell cd $(ROOT_PATH) 2>/dev/null && git rev-parse --short HEAD 2>/dev/null)
+ ifneq ($(GIT_HASH),)
+ DEFINES += -DGIT_HASH=\"$(GIT_HASH)\"
+ endif
+else
+ DEFINES += -DGIT_TAG=\"$(GIT_TAG)\"
+endif
+
+# Nice name shown in frontend
+CORE_NAME = "ScummVM"
+# Pipe separated allowed file extensions that core can handle
+CORE_EXTENSIONS = "scummvm"
+
+INCLUDES += -I$(SCUMMVM_PATH)
+DEFINES += -D__LIBRETRO__ -DNONSTANDARD_PORT -DUSE_RGB_COLOR -DUSE_OSD -DDISABLE_TEXT_CONSOLE -DFRONTEND_SUPPORTS_RGB565 -DUSE_TRANSLATION -DDETECTION_STATIC -DHAVE_CONFIG_H -DUSE_BINK -DUSE_LUA -DUSE_TINYGL -DENABLE_VKEYBD
+DEFINES += -DCORE_NAME=\"$(CORE_NAME)\" -DCORE_EXTENSIONS=\"$(CORE_EXTENSIONS)\"
+CXXFLAGS += -Wno-long-long -Wno-multichar -Wno-unknown-pragmas -Wno-reorder
+
+ifeq ($(USE_LIBCO), 1)
+ DEFINES += -DUSE_LIBCO
+else
+ LDFLAGS += -lpthread
+endif
+
+ifeq ($(TARGET_64BIT), 1)
+ DEFINES += -DSIZEOF_SIZE_T=8 -DSCUMM_64BITS
+else
+ DEFINES += -DSIZEOF_SIZE_T=4
+endif
+
+######################################################################
+# Libretro settings
+######################################################################
+
+INCLUDES += -I$(ROOT_PATH)/include
+MODULE_PATHS += $(CORE_PATH)
+
+LIBRETRO_OBJS := $(CORE_PATH)/libretro-os.o \
+ $(CORE_PATH)/libretro-fs.o \
+ $(CORE_PATH)/libretro-fs-factory.o \
+ $(CORE_PATH)/libretro.o \
+ $(CORE_PATH)/libretro-threads.o
+
+OBJS += $(LIBRETRO_OBJS)
+
+######################################################################
+# External dependencies settings
+######################################################################
+
+ifeq (,$(filter datafiles coreinfo,$(MAKECMDGOALS)))
+$(info Configuring dependencies...)
+include $(ROOT_PATH)/dependencies.mk
+endif
+
+######################################################################
+# Module settings
+######################################################################
+
+INCLUDES += -I$(SCUMMVM_PATH)/engines -I$(SCUMMVM_PATH)/backends/vkeybd
+
+# Base modules
+MODULES := base
+
+# script.mri head
+ifeq ($(STATIC_LINKING),1)
+ $(shell printf "CREATE $(TARGET)\n" > $(ROOT_PATH)/script.mri)
+endif
+
+# Engine modules
+# Following script
+# - generates configuration engines files (build/config.mk.engines, engines/engines.mk, engines/plugins_table.h, engines/detection_table.h) from actual source in $(SCUMMVM_PATH).
+# or (if static linking is required)
+# - generates config.mk.engines.lite - static file with a reduced set of engines for minimal builds - from lite_engines.list
+# - generates script.mri engines part
+ifeq (,$(filter clean datafiles coreinfo,$(MAKECMDGOALS)))
+$(info Configuring ScummVM engines...)
+ifneq ($(shell cd $(SCRIPTS_PATH); ./configure_engines.sh $(ROOT_PATH) $(SCUMMVM_PATH) $(NO_HIGH_DEF) $(NO_WIP) $(STATIC_LINKING) $(LITE)),0)
+ $(error Configuring ScummVM engines failed)
+else
+ $(info - ScummVM engines configured)
+endif
+endif
+
+ifeq ($(LITE), 1)
+ -include $(ROOT_PATH)/config.mk.engines.lite
+else
+ -include $(SCUMMVM_PATH)/config.mk.engines
+endif
+
+-include $(SCUMMVM_PATH)/engines/engines.mk
+
+ifeq ($(USE_MT32EMU),1)
+ INCLUDES += -I$(SCUMMVM_PATH)/audio/softsynth/mt32/sha1
+ DEFINES += -DUSE_MT32EMU
+endif
+
+# Retrieve shared modules definitions from main Makefile.common
+RETRIEVED_SHARED_MODULES := $(shell cat $(UP_COMMON_MAKE) | tr -s "\n" "\00" | sed -e 's|.*-include engines/engines.mk||' -e 's|\#\#\#.*||' | tr -s "\00" "\n" > $(SHARED_MOD_MAKE) && printf ok || printf error)
+ifeq ($(RETRIEVED_SHARED_MODULES), ok)
+ include $(SHARED_MOD_MAKE)
+else
+ $(error Error retrieving shared modules definitions from main Makefile.common)
+endif
+
+# script.mri tail
+ifeq ($(STATIC_LINKING),1)
+ MRI_LIBS := $(shell res=""; for path in $(MODULES) ; do [ ! -z "$$(cat $(SCUMMVM_PATH)/$${path}/module.mk | grep rules.mk)" ] && res="$$res $$path" ; done ; printf "$$res")
+ $(shell printf "$(addprefix ADDLIB libtemp/lib, $(addsuffix .a\n,$(notdir $(MRI_LIBS))))" >> $(ROOT_PATH)/script.mri)
+ $(shell printf "ADDLIB libtemp/libdeps.a\n" >> $(ROOT_PATH)/script.mri)
+ $(shell printf "ADDLIB libtemp/libdetect.a\n" >> $(ROOT_PATH)/script.mri)
+ $(shell printf "$(addprefix ADDMOD libtemp/, $(addsuffix \n,$(notdir $(LIBRETRO_OBJS))))" >> $(ROOT_PATH)/script.mri)
+ $(shell printf "SAVE\n" >> $(ROOT_PATH)/script.mri)
+ $(shell printf "END\n" >> $(ROOT_PATH)/script.mri)
+ $(shell sed -i.bak -e "s/^ //g" $(ROOT_PATH)/script.mri;rm -f $(ROOT_PATH)/script.mri.bak)
+endif
+
+######################################################################
+# Rules
+######################################################################
+
+core: $(TARGET)
+
+datafiles: scummvm.zip
+
+coreinfo: $(TARGET_NAME)_libretro.info
+
+all: $(TARGET) scummvm.zip $(TARGET_NAME)_libretro.info
+
+#bundle_files
+scummvm.zip: $(SCUMMVM_PATH)/dists/scummvm.rc
+ @echo Preparing $@
+ @$(SCRIPTS_PATH)/bundle_datafiles.sh $(ROOT_PATH) $(SCUMMVM_PATH) bundle $(TARGET_NAME)
+
+$(TARGET_NAME)_libretro.info: $(SCUMMVM_PATH)/dists/scummvm.rc
+ @echo Preparing $@
+ @$(SCRIPTS_PATH)/bundle_datafiles.sh $(ROOT_PATH) $(SCUMMVM_PATH) info $(TARGET_NAME) $(CORE_NAME) $(CORE_EXTENSIONS)
+
+.PHONY: all core datafiles coreinfo
diff --git a/backends/platform/libretro/README.md b/backends/platform/libretro/README.md
new file mode 100644
index 00000000000..8cb5a3cf123
--- /dev/null
+++ b/backends/platform/libretro/README.md
@@ -0,0 +1,52 @@
+# ScummVM libretro core
+
+Libretro core built from untouched mainline ScummVM source.
+
+Datafiles and themes bundle (`scummvm.zip`) and `core.info` files are built automatically based on current submodule source.
+
+## Build
+To build the core with the default configuration, type in a shell the following:
+```
+git clone https://github.com/scummvm/scummvm
+cd scummvm/backends/platform/libretro
+make
+```
+Use `make all` to build the core along with datafiles/themes (`scummvm.zip`) and core info file (which can be built separately with `make datafiles`/`make coreinfo`).
+
+"Work in progress" engines are not built by default, to include them pass `NO_WIP=0` to make.
+
+To crossbuild for specific platforms, pass the relevant `platform` variable to make (refer to Makefile for supported ones).
+
+### Build for Android
+To build for Android:
+* install android-sdk
+* install android-ndk (e.g. through sdkmanager included in android-sdk/cmdline-tools)
+* make sure all needed sdk/ndk paths are included in PATH
+* type in a shell the following:
+```
+git clone https://github.com/scummvm/scummvm
+cd scummvm/backends/platform/libretro/jni
+ndk-build
+```
+Core will be built for all available android targets by default
+
+### Data files and themes
+[Data files](https://wiki.scummvm.org/index.php/Datafiles) required for certain games and core functions (e.g. virtual keyboard) and default [ScummVM GUI themes](https://wiki.scummvm.org/index.php/GUI_Themes) are bundled during the build process in `scummvm.zip` file. Libretro core info file is created during build as well.
+Extract `scummvm.zip` and select relevant paths in ScummVM GUI (or modify `scummvm.ini` accordingly) to load properly needed data files/themes in the core.
+
+Note that both datafiles and themes included in `scummvm.zip` need to be consistent with ScummVM version in use, hence need to be generally rebuilt and replaced for each new version.
+
+## Core options and configuration
+All options and configuration of the legacy libretro core are applicable to this one as well, refer to relevant link in Resources.
+Some additional options and features have been added to this core:
+
+### New options
+* Set D-pad cursor acceleration time
+
+### New features
+* On-screen virtual keyboard (retropad "select" button by default to activate/deactivate)<br>Resources from `scummvm.zip` are needed to be installed for this feature to be enabled.
+
+* Cloud saving<br>This feature is currently disabled by default (e.g. on buildbot) as it has shared dependencies (libcurl-openssl).<br>To enable the feature, compile passing `USE_CLOUD=1` to make. `libcurl` package with `openssl` must be installed on host system as described [here](https://wiki.scummvm.org/index.php/Compiling_ScummVM).<br>Note that by default the entire retroarch `saves` folder will be syncronized, which may be a plus considering that this feature is currently not available in retroarch.
+
+## Resources
+[Legacy ScummVM libretro core](https://github.com/libretro-mirrors/scummvm) documentation is [here](https://docs.libretro.com/library/scummvm).
diff --git a/backends/platform/libretro/dependencies.mk b/backends/platform/libretro/dependencies.mk
new file mode 100644
index 00000000000..7b075fd82a0
--- /dev/null
+++ b/backends/platform/libretro/dependencies.mk
@@ -0,0 +1,569 @@
+######################################################################
+# Common settings and functions
+######################################################################
+
+# Submodules functions
+DEPS_SUBMODULES := libretro-deps libretro-common
+
+DEPS_FOLDER_libretro-deps := libretro-deps
+DEPS_URL_libretro-deps := https://github.com/libretro/libretro-deps
+DEPS_COMMIT_libretro-deps := e362764d603eb4fa84560f2d009fef091f6d1447
+
+DEPS_FOLDER_libretro-common := libretro-common
+DEPS_URL_libretro-common := https://github.com/libretro/libretro-common
+DEPS_COMMIT_libretro-common := 20a43ba79fe6b4ec094b3b20b7bc88f4cfe916fa
+
+submodule_test = $(if $(shell result=$$($(SCRIPTS_PATH)/configure_submodules.sh $(DEPS_URL_$(1)) $(DEPS_COMMIT_$(1)) $(DEPS_PATH) $(DEBUG_ALLOW_DIRTY_SUBMODULES) $(DEPS_FOLDER_$(1))) ; { [ -z $$result ] || [ ! $$result = 0 ] ; } && printf error),$(1))
+$(info Configuring submodules...)
+SUBMODULE_FAILED = $(strip $(findstring $(foreach SUBMODULE,$(DEPS_SUBMODULES),$(call submodule_test,$(SUBMODULE))),$(DEPS_SUBMODULES)))
+
+ifneq ($(SUBMODULE_FAILED),)
+ $(error Configuration of following submodules failed: $(SUBMODULE_FAILED))
+else
+ $(info - Submodules configured)
+endif
+
+# Shared libs functions
+this_lib_available := no
+sharedlibs_test_cc = '\#include <$(this_lib_subpath)$(this_lib_header)>\nint main(){return 0;}'
+sharedlibs_get_include_path = $(shell printf $(sharedlibs_test_cc) | $(CC) -E -Wp,-v - 2>/dev/null | grep "$(this_lib_subpath)$(this_lib_header)" | cut -d \" -f 2 | sed "s|/$(this_lib_header)||")
+sharedlibs_this_lib_includes = $(if $(this_lib_subpath),-I$(call sharedlibs_get_include_path))
+sharedlibs_is_lib_available = $(if $(shell result=$$(printf $(sharedlibs_test_cc) | $(CC) -xc -Wall -O -o /dev/null $(this_lib_flags) $(sharedlibs_this_lib_includes) - > /dev/null 2>&1 ; printf $$?) ; { [ -z $$result ] || [ ! $$result = 0 ] ; } && printf error),no,yes)
+sharedlibs_system_lib_message = $(info - Use system shared $(shell printf ' $(this_lib_flags)' | sed -e "s|.*-l||" -e "s| .*||"): $(this_lib_available))
+
+######################################################################
+# libretro-common settings
+######################################################################
+
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/include \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/include/compat
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/file/file_path_io.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/file/file_path.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/file/retro_dirent.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/vfs/vfs_implementation.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/string/stdstring.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/time/rtime.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/streams/file_stream.o
+
+ifeq ($(USE_LIBCO), 1)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/libco/libco.o
+ifeq ($(platform), genode)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/libco/genode.o
+endif
+else
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/rthreads/rthreads.o
+endif
+
+ifneq ($(STATIC_LINKING), 1)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/encodings/encoding_utf.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/compat/fopen_utf8.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-common)/compat/compat_strl.o
+endif
+
+######################################################################
+# fluidsynth settings
+######################################################################
+
+ifeq ($(USE_FLUIDSYNTH), 1)
+DEFINES += -DUSE_FLUIDSYNTH
+this_lib_subpath :=
+this_lib_header := fluidsynth.h
+this_lib_flags := -lfluidsynth
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+DEFINES += -DUSE_FLUIDLITE
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/include \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/include \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libogg/include
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_chan.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_chorus.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_conv.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_defsfont.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_dsp_float.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_gen.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_hash.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_list.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_mod.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_ramsfont.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_rev.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_settings.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_synth.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_sys.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_tuning.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/fluidsynth/src/fluid_voice.o
+endif
+endif
+
+######################################################################
+# libFLAC settings
+######################################################################
+
+ifeq ($(USE_FLAC), 1)
+DEFINES += -DUSE_FLAC
+this_lib_subpath :=
+this_lib_header := FLAC/format.h
+this_lib_flags := -lFLAC
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/include
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/bitreader.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/cpu.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/crc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/fixed.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/format.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/lpc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/md5.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/memory.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/metadata_object.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/stream_decoder.o
+
+ifeq ($(platform), win)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libFLAC/share/win_utf8_io/win_utf8_io.o \
+ $(SCUMMVM_PATH)/backends/platform/sdl/win32/win32_wrapper.o
+endif
+endif
+endif
+
+######################################################################
+# libvorbis settings
+######################################################################
+
+ifeq ($(USE_VORBIS), 1)
+DEFINES += -DUSE_VORBIS
+this_lib_subpath :=
+this_lib_header := vorbis/codec.h
+this_lib_flags := -lvorbis
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libogg/include \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/include \
+ -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libogg/src/bitwise.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libogg/src/framing.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/analysis.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/bitrate.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/block.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/codebook.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/envelope.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/floor0.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/floor1.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/info.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/lookup.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/lpc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/lsp.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/mapping0.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/mdct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/psy.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/registry.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/res0.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/sharedbook.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/smallft.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/synthesis.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/vorbisenc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/vorbisfile.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libvorbis/lib/window.o
+endif
+endif
+
+######################################################################
+# tremor settings
+######################################################################
+
+ifeq ($(USE_TREMOR), 1)
+DEFINES += -DUSE_TREMOR -DUSE_VORBIS
+this_lib_subpath :=
+this_lib_header := tremor/ivorbiscodec.h
+this_lib_flags := -ltremor
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/bitwise.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/block.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/codebook.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/floor0.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/floor1.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/framing.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/info.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/mapping0.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/mdct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/registry.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/res012.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/sharedbook.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/synthesis.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/vorbisfile.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/tremor/window.o
+endif
+endif
+
+######################################################################
+# libz settings
+######################################################################
+
+ifeq ($(USE_ZLIB), 1)
+DEFINES += -DUSE_ZLIB -DWANT_ZLIB
+this_lib_subpath :=
+this_lib_header := zlib.h
+this_lib_flags := -lz
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/deflate.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/gzlib.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/uncompr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/zutil.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/inffast.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/gzread.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/crc32.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/gzwrite.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/inflate.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/infback.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/inftrees.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/trees.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/gzclose.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/compress.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libz/adler32.o
+endif
+endif
+
+######################################################################
+# libmad settings
+######################################################################
+
+ifeq ($(USE_MAD), 1)
+DEFINES += -DUSE_MAD -DFPM_DEFAULT
+this_lib_subpath :=
+this_lib_header := mad.h
+this_lib_flags := -lmad
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/bit.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/decoder.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/frame.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/huffman.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/layer12.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/layer3.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/stream.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/synth.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libmad/timer.o
+endif
+endif
+
+######################################################################
+# libfaad settings
+######################################################################
+
+ifeq ($(USE_FAAD), 1)
+DEFINES += -DUSE_FAAD
+this_lib_subpath :=
+this_lib_header := faad.h
+this_lib_flags := -lfaad
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/include -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/bits.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/cfft.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/common.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/decoder.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/drc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/error.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/filtbank.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/hcr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/huffman.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/ic_predict.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/is.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/lt_predict.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/mdct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/mp4.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/ms.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/output.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/pns.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/pulse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/ps_dec.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/ps_syntax.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/rvlc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_dct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_dec.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_e_nf.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_fbt.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_hfadj.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_hfgen.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_huff.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_qmf.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_syntax.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/sbr_tf_grid.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/specrec.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/syntax.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libfaad/libfaad/tns.o
+endif
+endif
+
+######################################################################
+# libpng settings
+######################################################################
+
+ifeq ($(USE_PNG), 1)
+DEFINES += -DUSE_PNG
+this_lib_subpath :=
+this_lib_header := png.h
+this_lib_flags := -lpng
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/png.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngerror.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngget.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngmem.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngpread.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngread.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngrio.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngrtran.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngrutil.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngset.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngtrans.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngwrite.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngwutil.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngwtran.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libpng/pngwio.o
+endif
+endif
+
+######################################################################
+# libjpeg settings
+######################################################################
+
+ifeq ($(USE_JPEG), 1)
+DEFINES += -DUSE_JPEG -DJDCT_DEFAULT=JDCT_IFAST
+this_lib_subpath :=
+this_lib_header := jerror.h
+this_lib_flags := -ljpeg
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jaricom.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcapimin.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcapistd.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcarith.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jccoefct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jccolor.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcdctmgr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcinit.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jchuff.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcmarker.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcmainct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcmaster.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcomapi.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcphuff.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcprepct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jcsample.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdapimin.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdapistd.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdarith.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdcoefct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdcolor.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jddctmgr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdhuff.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdinput.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdmarker.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdmainct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdmaster.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdmerge.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdphuff.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdpostct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdsample.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jdtrans.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jerror.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jidctflt.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jidctfst.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jidctint.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jidctred.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jfdctflt.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jfdctfst.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jfdctint.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jmemmgr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jmemnobs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jquant1.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jquant2.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jutils.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/libjpeg/jsimd_none.o
+endif
+endif
+
+######################################################################
+# theora settings
+######################################################################
+
+ifeq ($(USE_THEORADEC), 1)
+DEFINES += -DUSE_THEORADEC
+this_lib_subpath :=
+this_lib_header := theora/theoradec.h
+this_lib_flags := -ltheora
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/include
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/bitpack.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/decinfo.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/decode.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/dequant.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/fragment.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/huffdec.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/idct.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/info.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/internal.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/quant.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/theora/lib/state.o
+endif
+else
+#undefine as in ScummVM macro definition is tested to enable theora
+USE_THEORADEC =
+endif
+
+######################################################################
+# freetype settings
+######################################################################
+
+ifeq ($(USE_FREETYPE2), 1)
+DEFINES += -DUSE_FREETYPE2
+# ft2build.h is included in scummvm sources, while freetype2/ft2build.h is available in includes path
+this_lib_subpath := freetype2/
+this_lib_header := ft2build.h
+this_lib_flags := -lfreetype
+include $(ROOT_PATH)/sharedlib_test.mk
+ifneq ($(this_lib_available), yes)
+DEFINES += -DFT2_BUILD_LIBRARY
+INCLUDES += -I$(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/include
+OBJS_DEPS += $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afangles.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afblue.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afcjk.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afdummy.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afglobal.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afhints.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afindic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/aflatin.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afloader.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afmodule.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afpic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afranges.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afshaper.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/autofit/afwarp.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/basepic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftadvanc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftapi.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftbitmap.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftcalc.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftgloadr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftglyph.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/fthash.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftinit.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftsnames.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftsystem.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftoutln.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftrfork.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftstream.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftstroke.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/fttrigon.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/base/ftutil.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/bdf/bdfdrivr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/bdf/bdflib.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cid/cidriver.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cid/cidgload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cid/cidload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cid/cidobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cid/cidparse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2arrst.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2blues.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2error.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2ft.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2font.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2hints.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2intrp.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2read.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cf2stack.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffcmap.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffgload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffparse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffpic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/cff/cffdrivr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/gzip/ftgzip.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/lzw/ftlzw.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pcf/pcfdrivr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pcf/pcfread.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pcf/pcfutil.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrcmap.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrdrivr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrgload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pfr/pfrsbit.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/afmparse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/psconv.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/psobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/t1cmap.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/t1decode.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psaux/psauxmod.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pshinter/pshalgo.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pshinter/pshglob.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pshinter/pshmod.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pshinter/pshpic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/pshinter/pshrec.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/psnames/psmodule.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/raster/ftrend1.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/raster/ftraster.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/raster/rastpic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/sfdriver.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/sfntpic.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/sfobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttbdf.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttcmap.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttkern.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttmtx.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttpost.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/sfnt/ttsbit.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/smooth/ftgrays.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/smooth/ftsmooth.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1afm.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1driver.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1gload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1load.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1objs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type1/t1parse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type42/t42drivr.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type42/t42objs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/type42/t42parse.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttdriver.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttgload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttgxvar.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttinterp.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttobjs.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/truetype/ttpload.o \
+ $(DEPS_PATH)/$(DEPS_FOLDER_libretro-deps)/freetype/src/winfonts/winfnt.o
+endif
+endif
+
+######################################################################
+# libcurl settings
+######################################################################
+
+ifeq ($(USE_CLOUD), 1)
+this_lib_available := no
+this_lib_subpath :=
+this_lib_header := curl/curl.h
+this_lib_flags := -lcurl
+# No baked-in solution in libretro-deps, shared lib is the only option at this time
+# ifeq ($(USE_SYSTEM_LIBS), 1)
+this_lib_available := $(call sharedlibs_is_lib_available)
+# endif
+$(call sharedlibs_system_lib_message)
+ifeq ($(this_lib_available), yes)
+ LDFLAGS += $(this_lib_flags)
+ INCLUDES += $(sharedlibs_this_lib_includes)
+ USE_LIBCURL := 1
+ DEFINES += -DUSE_CLOUD -DUSE_LIBCURL
+else
+$(info System libcurl not available, dropping cloud feature.)
+endif
+endif
diff --git a/backends/platform/libretro/dist/README.md b/backends/platform/libretro/dist/README.md
new file mode 100644
index 00000000000..0dff50968b0
--- /dev/null
+++ b/backends/platform/libretro/dist/README.md
@@ -0,0 +1 @@
+Roland_SC-55.sf2 - Copyright (c) 2015 deemster. Licensed under the GPL v3 (or greater) https://www.gnu.org/licenses/quick-guide-gplv3.en.html
diff --git a/backends/platform/libretro/dist/Roland_SC-55.sf2 b/backends/platform/libretro/dist/Roland_SC-55.sf2
new file mode 100644
index 00000000000..27cc01c187c
Binary files /dev/null and b/backends/platform/libretro/dist/Roland_SC-55.sf2 differ
diff --git a/backends/platform/libretro/include/config.h b/backends/platform/libretro/include/config.h
new file mode 100644
index 00000000000..f373751434f
--- /dev/null
+++ b/backends/platform/libretro/include/config.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#if defined(WIIU) || defined(__CELLOS_LV2__) || defined(GEKKO)
+#undef SCUMM_LITTLE_ENDIAN
+#define SCUMM_BIG_ENDIAN
+#else
+#define SCUMM_LITTLE_ENDIAN
+#endif
+
+#define SCUMM_NEED_ALIGNMENT
+
+/* Data types */
+#ifndef SCUMMVM_DONT_DEFINE_TYPES
+typedef unsigned char byte;
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef unsigned int uint;
+typedef signed long long int64;
+typedef unsigned long long uint64;
+#endif
+
+#if defined(__x86_64__) || defined(_M_X64) || defined(__ppc64__) || defined(__powerpc64__) || defined(__LP64__) || defined(_M_ARM64)
+
+typedef uint64 uintptr;
+typedef int64 intptr;
+
+#else
+
+typedef uint32 uintptr;
+typedef int32 intptr;
+
+#endif
+
+#endif /* CONFIG_H */
diff --git a/backends/platform/libretro/include/libretro-core-options-intl.h b/backends/platform/libretro/include/libretro-core-options-intl.h
new file mode 100644
index 00000000000..42b1b6ad731
--- /dev/null
+++ b/backends/platform/libretro/include/libretro-core-options-intl.h
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__
+#define LIBRETRO_CORE_OPTIONS_INTL_H__
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
+/* https://support.microsoft.com/en-us/kb/980263 */
+#pragma execution_character_set("utf-8")
+#pragma warning(disable : 4566)
+#endif
+
+#include <libretro.h>
+
+/*
+ ********************************
+ * VERSION: 1.3
+ ********************************
+ *
+ * - 1.3: Move translations to libretro_core_options_intl.h
+ * - libretro_core_options_intl.h includes BOM and utf-8
+ * fix for MSVC 2010-2013
+ * - Added HAVE_NO_LANGEXTRA flag to disable translations
+ * on platforms/compilers without BOM support
+ * - 1.2: Use core options v1 interface when
+ * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
+ * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
+ * - 1.1: Support generation of core options v0 retro_core_option_value
+ * arrays containing options with a single value
+ * - 1.0: First commit
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ ********************************
+ * Core Option Definitions
+ ********************************
+ */
+
+/* RETRO_LANGUAGE_JAPANESE */
+
+/* RETRO_LANGUAGE_FRENCH */
+
+/* RETRO_LANGUAGE_SPANISH */
+
+/* RETRO_LANGUAGE_GERMAN */
+
+/* RETRO_LANGUAGE_ITALIAN */
+struct retro_core_option_definition option_defs_it[] = {
+ {
+ "scummvm_gamepad_cursor_speed",
+ "Velocità del cursore",
+ "Moltiplicatore della velocità del cursore del mouse quando si usa la leva analogica sinistra o il D-Pad del RetroPad. Il valore di default di '1.0' è ottimizzato per i giochi con risoluzione nativa di '320x200' o '320x240'. Per i giochi ad 'alta definizione' con risoluzione di '640x400' or '640x480', si raccomanda il valore di '2.0'",
+ {
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {
+ "scummvm_gamepad_cursor_acceleration_time",
+ "Accelerazione del cursore",
+ "Il tempo (in secondi) necessario al cursore del mouse per raggiungere la piena velocità quando si usa la leva analogica sinistra o il D-Pad del RetroPad.",
+ {
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {
+ "scummvm_analog_response",
+ "Risposta analogica del cursore",
+ "Modalità di risposta della velocità del cursore del mouse allo spostamento della leva analogica sinistra del RetroPad. 'Lineare': La velocità è direttamente proporzionale allo spostamento della leva. Questa è l'impostazione di default adatta alla maggior parte degli utenti. 'Quadratica': La velocità aumenta con il quadrato dello spostamento della leva. Questo permette maggior precisione nei piccoli movimenti senza sacrificare il raggiungimento della velocità massima a spostamento completo. Questa modalità può richiedere pratica per un uso efficace.",
+ {
+ {"linear", "Lineare"},
+ {"quadratic", "Quadratica"},
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {
+ "scummvm_analog_deadzone",
+ "Zona morta analogica (percentuale)",
+ "Zona morta della leva analogica sinistra del RetroPad. Può essere usata per eliminare scorrimenti indesiderati del cursore.",
+ {
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {
+ "scummvm_mouse_speed",
+ "Velocità del mouse",
+ "Moltiplicatore della velocità del cursore del mouse quando si usa RetroMouse.",
+ {
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {
+ "scummvm_speed_hack",
+ "Speed Hack (riavvio necessario)",
+ "Modalità che riduce significativamente le richeste di CPU consentendo lievi inaccuratezze di timing. Questo 'hack' è considerato 'sicuro' - non dovrebbe causare errori e la maggior parte delle inaccuratezze sono impercettibili. Rimane comunque un hack, e gli utenti con macchine di classe desktop dovrebbero lasciarlo disabilitato. Su hardware a basse prestazioni (vecchi device Android, computer su singola scheda), questo hack è essenziale per il funzionamento del core a velocità piena.",
+ {
+ {NULL, NULL},
+ },
+ NULL
+ },
+ {NULL, NULL, NULL, {{0}}, NULL},
+};
+/* RETRO_LANGUAGE_DUTCH */
+
+/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
+
+/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
+
+/* RETRO_LANGUAGE_RUSSIAN */
+
+/* RETRO_LANGUAGE_KOREAN */
+
+/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
+
+/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
+
+/* RETRO_LANGUAGE_ESPERANTO */
+
+/* RETRO_LANGUAGE_POLISH */
+
+/* RETRO_LANGUAGE_VIETNAMESE */
+
+/* RETRO_LANGUAGE_ARABIC */
+
+/* RETRO_LANGUAGE_GREEK */
+
+/* RETRO_LANGUAGE_TURKISH */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/platform/libretro/include/libretro-core-options.h b/backends/platform/libretro/include/libretro-core-options.h
new file mode 100644
index 00000000000..33d3df12c8f
--- /dev/null
+++ b/backends/platform/libretro/include/libretro-core-options.h
@@ -0,0 +1,359 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBRETRO_CORE_OPTIONS_H__
+#define LIBRETRO_CORE_OPTIONS_H__
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libretro.h>
+#include <retro_inline.h>
+
+#ifndef HAVE_NO_LANGEXTRA
+#include "libretro-core-options-intl.h"
+#endif
+
+/*
+ ********************************
+ * VERSION: 1.3
+ ********************************
+ *
+ * - 1.3: Move translations to libretro_core_options_intl.h
+ * - libretro_core_options_intl.h includes BOM and utf-8
+ * fix for MSVC 2010-2013
+ * - Added HAVE_NO_LANGEXTRA flag to disable translations
+ * on platforms/compilers without BOM support
+ * - 1.2: Use core options v1 interface when
+ * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1
+ * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1)
+ * - 1.1: Support generation of core options v0 retro_core_option_value
+ * arrays containing options with a single value
+ * - 1.0: First commit
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ ********************************
+ * Core Option Definitions
+ ********************************
+ */
+
+/* RETRO_LANGUAGE_ENGLISH */
+
+/* Default language:
+ * - All other languages must include the same keys and values
+ * - Will be used as a fallback in the event that frontend language
+ * is not available
+ * - Will be used as a fallback for any missing entries in
+ * frontend language definition */
+
+struct retro_core_option_definition option_defs_us[] = {
+ {
+ "scummvm_gamepad_cursor_speed",
+ "Gamepad Cursor Speed",
+ "Sets the mouse cursor speed multiplier when moving the cursor with the RetroPad left analog stick or D-Pad. The default value of '1.0' is optimised for games that have a native resolution of '320x200' or '320x240'. When running 'high definition' games with a resolution of '640x400' or '640x480', a Gamepad Cursor Speed of '2.0' is recommended.",
+ {
+ {"0.25", NULL},
+ {"0.5", NULL},
+ {"0.75", NULL},
+ {"1.0", NULL},
+ {"1.5", NULL},
+ {"2.0", NULL},
+ {"2.5", NULL},
+ {"3.0", NULL},
+ {NULL, NULL},
+ },
+ "1.0"
+ },
+ {
+ "scummvm_gamepad_cursor_acceleration_time",
+ "Gamepad Cursor Acceleration",
+ "The amount of time (In seconds) it takes for the cursor to reach full speed",
+ {
+ {"off", NULL},
+ {"0.1", NULL},
+ {"0.2", NULL},
+ {"0.3", NULL},
+ {"0.4", NULL},
+ {"0.5", NULL},
+ {"0.6", NULL},
+ {"0.7", NULL},
+ {"0.8", NULL},
+ {"0.9", NULL},
+ {"1.0", NULL},
+ {NULL, NULL},
+ },
+ "0.2"
+ },
+ {
+ "scummvm_analog_response",
+ "Analog Cursor Response",
+ "Determines how the speed of the cursor varies when tilting the RetroPad left analog stick. 'Linear': Speed is directly proportional to analog stick displacement. This is standard behaviour with which most users will be familiar. 'Quadratic': Speed increases quadratically with analog stick displacement. This allows for greater precision when making small movements without sacrificing maximum speed at full analog range. This mode may require practice for effective use.",
+ {
+ {"linear", "Linear"},
+ {"quadratic", "Quadratic"},
+ {NULL, NULL},
+ },
+ "linear"
+ },
+ {
+ "scummvm_analog_deadzone",
+ "Analog Deadzone (Percent)",
+ "Sets the deadzone of the RetroPad analog sticks. Used to eliminate cursor drift/unwanted input.",
+ {
+ {"0", NULL},
+ {"5", NULL},
+ {"10", NULL},
+ {"15", NULL},
+ {"20", NULL},
+ {"25", NULL},
+ {"30", NULL},
+ {NULL, NULL},
+ },
+ "15"
+ },
+ {
+ "scummvm_mouse_speed",
+ "Mouse Speed",
+ "Sets the mouse cursor speed multiplier when moving the cursor with the RetroMouse.",
+ {
+ {"0.05", NULL},
+ {"0.1", NULL},
+ {"0.15", NULL},
+ {"0.2", NULL},
+ {"0.25", NULL},
+ {"0.3", NULL},
+ {"0.35", NULL},
+ {"0.4", NULL},
+ {"0.45", NULL},
+ {"0.5", NULL},
+ {"0.6", NULL},
+ {"0.7", NULL},
+ {"0.8", NULL},
+ {"0.9", NULL},
+ {"1.0", NULL},
+ {"1.25", NULL},
+ {"1.5", NULL},
+ {"1.75", NULL},
+ {"2.0", NULL},
+ {"2.5", NULL},
+ {"3.0", NULL},
+ {NULL, NULL},
+ },
+ "1.0"
+ },
+ {
+ "scummvm_speed_hack",
+ "Speed Hack (Restart)",
+ "Enables a speed hack that significantly reduces CPU requirements by allowing subtle timing inaccuracies. This hack is considered 'safe' - it should cause no errors, and most timing deviations are imperceptible. It remains a hack, though, and users of desktop-class machines are advised to keep it disabled. On low power hardware (weak Android devices, single board computers), this hack is essential for full speed operation of the core.",
+ {
+ {"disabled", NULL},
+ {"enabled", NULL},
+ {NULL, NULL},
+ },
+#if defined(ANDROID) || defined(DINGUX) || defined(_3DS)
+ "enabled"
+#else
+ "disabled"
+#endif
+ },
+ {NULL, NULL, NULL, {{0}}, NULL},
+};
+
+/*
+ ********************************
+ * Language Mapping
+ ********************************
+ */
+
+#ifndef HAVE_NO_LANGEXTRA
+struct retro_core_option_definition *option_defs_intl[RETRO_LANGUAGE_LAST] = {
+ option_defs_us, /* RETRO_LANGUAGE_ENGLISH */
+ NULL, /* RETRO_LANGUAGE_JAPANESE */
+ NULL, /* RETRO_LANGUAGE_FRENCH */
+ NULL, /* RETRO_LANGUAGE_SPANISH */
+ NULL, /* RETRO_LANGUAGE_GERMAN */
+ option_defs_it, /* RETRO_LANGUAGE_ITALIAN */
+ NULL, /* RETRO_LANGUAGE_DUTCH */
+ NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */
+ NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */
+ NULL, /* RETRO_LANGUAGE_RUSSIAN */
+ NULL, /* RETRO_LANGUAGE_KOREAN */
+ NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */
+ NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */
+ NULL, /* RETRO_LANGUAGE_ESPERANTO */
+ NULL, /* RETRO_LANGUAGE_POLISH */
+ NULL, /* RETRO_LANGUAGE_VIETNAMESE */
+ NULL, /* RETRO_LANGUAGE_ARABIC */
+ NULL, /* RETRO_LANGUAGE_GREEK */
+ NULL, /* RETRO_LANGUAGE_TURKISH */
+};
+#endif
+
+/*
+ ********************************
+ * Functions
+ ********************************
+ */
+
+/* Handles configuration/setting of core options.
+ * Should be called as early as possible - ideally inside
+ * retro_set_environment(), and no later than retro_load_game()
+ * > We place the function body in the header to avoid the
+ * necessity of adding more .c files (i.e. want this to
+ * be as painless as possible for core devs)
+ */
+
+static INLINE void libretro_set_core_options(retro_environment_t environ_cb) {
+ unsigned version = 0;
+
+ if (!environ_cb)
+ return;
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version) && (version >= 1)) {
+#ifndef HAVE_NO_LANGEXTRA
+ struct retro_core_options_intl core_options_intl;
+ unsigned language = 0;
+
+ core_options_intl.us = option_defs_us;
+ core_options_intl.local = NULL;
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH))
+ core_options_intl.local = option_defs_intl[language];
+
+ environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_intl);
+#else
+ environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, &option_defs_us);
+#endif
+ } else {
+ size_t i;
+ size_t num_options = 0;
+ struct retro_variable *variables = NULL;
+ char **values_buf = NULL;
+
+ /* Determine number of options */
+ while (true) {
+ if (option_defs_us[num_options].key)
+ num_options++;
+ else
+ break;
+ }
+
+ /* Allocate arrays */
+ variables = (struct retro_variable *)calloc(num_options + 1, sizeof(struct retro_variable));
+ values_buf = (char **)calloc(num_options, sizeof(char *));
+
+ if (!variables || !values_buf)
+ goto error;
+
+ /* Copy parameters from option_defs_us array */
+ for (i = 0; i < num_options; i++) {
+ const char *key = option_defs_us[i].key;
+ const char *desc = option_defs_us[i].desc;
+ const char *default_value = option_defs_us[i].default_value;
+ struct retro_core_option_value *values = option_defs_us[i].values;
+ size_t buf_len = 3;
+ size_t default_index = 0;
+
+ values_buf[i] = NULL;
+
+ if (desc) {
+ size_t num_values = 0;
+
+ /* Determine number of values */
+ while (true) {
+ if (values[num_values].value) {
+ /* Check if this is the default value */
+ if (default_value)
+ if (strcmp(values[num_values].value, default_value) == 0)
+ default_index = num_values;
+
+ buf_len += strlen(values[num_values].value);
+ num_values++;
+ } else
+ break;
+ }
+
+ /* Build values string */
+ if (num_values > 0) {
+ size_t j;
+
+ buf_len += num_values - 1;
+ buf_len += strlen(desc);
+
+ values_buf[i] = (char *)calloc(buf_len, sizeof(char));
+ if (!values_buf[i])
+ goto error;
+
+ strcpy(values_buf[i], desc);
+ strcat(values_buf[i], "; ");
+
+ /* Default value goes first */
+ strcat(values_buf[i], values[default_index].value);
+
+ /* Add remaining values */
+ for (j = 0; j < num_values; j++) {
+ if (j != default_index) {
+ strcat(values_buf[i], "|");
+ strcat(values_buf[i], values[j].value);
+ }
+ }
+ }
+ }
+
+ variables[i].key = key;
+ variables[i].value = values_buf[i];
+ }
+
+ /* Set variables */
+ environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
+
+error:
+
+ /* Clean up */
+ if (values_buf) {
+ for (i = 0; i < num_options; i++) {
+ if (values_buf[i]) {
+ free(values_buf[i]);
+ values_buf[i] = NULL;
+ }
+ }
+
+ free(values_buf);
+ values_buf = NULL;
+ }
+
+ if (variables) {
+ free(variables);
+ variables = NULL;
+ }
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/platform/libretro/include/libretro-fs-factory.h b/backends/platform/libretro/include/libretro-fs-factory.h
new file mode 100644
index 00000000000..211688cf9b0
--- /dev/null
+++ b/backends/platform/libretro/include/libretro-fs-factory.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBRETRO_FILESYSTEM_FACTORY_H
+#define LIBRETRO_FILESYSTEM_FACTORY_H
+
+#include "backends/fs/fs-factory.h"
+
+/**
+ * Creates LibRetroFilesystemNode objects.
+ *
+ * Parts of this class are documented in the base interface class, FilesystemFactory.
+ */
+class LibRetroFilesystemFactory : public FilesystemFactory {
+protected:
+ virtual AbstractFSNode *makeRootFileNode() const;
+ virtual AbstractFSNode *makeCurrentDirectoryFileNode() const;
+ virtual AbstractFSNode *makeFileNodePath(const Common::String &path) const;
+};
+
+#endif /*LIBRETRO_FILESYSTEM_FACTORY_H*/
diff --git a/backends/platform/libretro/include/libretro-fs.h b/backends/platform/libretro/include/libretro-fs.h
new file mode 100644
index 00000000000..2522c254731
--- /dev/null
+++ b/backends/platform/libretro/include/libretro-fs.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBRETRO_FILESYSTEM_H
+#define LIBRETRO_FILESYSTEM_H
+
+#include "backends/fs/abstract-fs.h"
+
+#ifdef MACOSX
+#include <sys/types.h>
+#endif
+#include <unistd.h>
+
+#ifdef PLAYSTATION3
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#define F_OK 0 /* test for existence of file */
+#define W_OK 0x02 /* test for write permission */
+#define R_OK 0x04 /* test for read permission */
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (x & 0040000)
+#endif
+
+static inline int access(const char *pn, int mode) {
+ warning("access: pn %s\n", pn);
+ int fd = open(pn, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ // XXX lie about it, for now..
+ close(fd);
+ return 0;
+}
+
+static inline char *getenv(const char *name) {
+ return 0;
+}
+#endif
+
+/**
+ * Implementation of the ScummVM file system API based on LibRetro.
+ *
+ * Parts of this class are documented in the base interface class, AbstractFSNode.
+ */
+class LibRetroFilesystemNode : public AbstractFSNode {
+protected:
+ Common::String _displayName;
+ Common::String _path;
+ bool _isDirectory;
+ bool _isValid;
+
+ virtual AbstractFSNode *makeNode(const Common::String &path) const {
+ return new LibRetroFilesystemNode(path);
+ }
+
+ /**
+ * Plain constructor, for internal use only (hence protected).
+ */
+ LibRetroFilesystemNode() : _isDirectory(false), _isValid(false) {}
+
+public:
+ /**
+ * Creates a LibRetroFilesystemNode for a given path.
+ *
+ * @param path the path the new node should point to.
+ */
+ LibRetroFilesystemNode(const Common::String &path);
+
+ virtual bool exists() const {
+ return access(_path.c_str(), F_OK) == 0;
+ }
+ virtual Common::U32String getDisplayName() const {
+ return _displayName;
+ }
+ virtual Common::String getName() const {
+ return _displayName;
+ }
+ virtual Common::String getPath() const {
+ return _path;
+ }
+ virtual bool isDirectory() const {
+ return _isDirectory;
+ }
+ virtual bool isReadable() const {
+ return access(_path.c_str(), R_OK) == 0;
+ }
+ virtual bool isWritable() const {
+ return access(_path.c_str(), W_OK) == 0;
+ }
+
+ virtual AbstractFSNode *getChild(const Common::String &n) const;
+ virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const;
+ virtual AbstractFSNode *getParent() const;
+
+ virtual Common::SeekableReadStream *createReadStream();
+ virtual Common::SeekableWriteStream *createWriteStream();
+ virtual bool createDirectory();
+
+private:
+ /**
+ * Tests and sets the _isValid and _isDirectory flags, using the stat() function.
+ */
+ virtual void setFlags();
+};
+
+
+#ifndef __ANDROID__ //In this case definition in backends/fs/posix/posix-fs.h is used
+namespace Posix {
+
+/**
+ * Assure that a directory path exists.
+ *
+ * @param dir The path which is required to exist.
+ * @param prefix An (optional) prefix which should not be created if non existent.
+ * prefix is prepended to dir if supplied.
+ * @return true in case the directoy exists (or was created), false otherwise.
+ */
+bool assureDirectoryExists(const Common::String &dir, const char *prefix = nullptr);
+
+} // End of namespace Posix
+#endif
+#endif
diff --git a/backends/platform/libretro/include/libretro-threads.h b/backends/platform/libretro/include/libretro-threads.h
new file mode 100644
index 00000000000..80344c2986d
--- /dev/null
+++ b/backends/platform/libretro/include/libretro-threads.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBRETRO_THREADS_H
+#define LIBRETRO_THREADS_H
+
+/* ScummVM doesn't have a top-level main loop that we can use, so instead we run it in its own thread
+ * and switch between it and the main thread. Calling these function will block the current thread
+ * and unblock the other. Each function should be called from the other thread.
+ */
+void retro_switch_to_emu_thread(void);
+void retro_switch_to_main_thread(void);
+
+/* Initialize the emulation thread and any related resources.
+ *
+ * This function should be called from the main thread.
+ */
+bool retro_init_emu_thread(void);
+
+/* Destroy the emulation thread and any related resources. Only call this after the emulation thread
+ * has finished (or canceled) and joined.
+ *
+ * This function should be called from the main thread.
+ */
+void retro_deinit_emu_thread(void);
+
+/* Returns true if the emulation thread was initialized successfully.
+ *
+ * This function should be called from the main thread.
+ */
+bool retro_emu_thread_initialized(void);
+
+/* Returns true if the emulation thread has exited naturally.
+ *
+ * This function can be called from either the main or the emulation thread.
+ */
+bool retro_emu_thread_exited(void);
+
+#endif
+
diff --git a/backends/platform/libretro/include/os.h b/backends/platform/libretro/include/os.h
new file mode 100644
index 00000000000..96041ec16f3
--- /dev/null
+++ b/backends/platform/libretro/include/os.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_LIBRETRO_OS_H
+#define BACKENDS_LIBRETRO_OS_H
+
+// Preliminary scan results
+#define TEST_GAME_OK_TARGET_FOUND 0
+#define TEST_GAME_OK_ID_FOUND 1
+#define TEST_GAME_OK_ID_AUTODETECTED 2
+#define TEST_GAME_KO_NOT_FOUND 3
+#define TEST_GAME_KO_MULTIPLE_RESULTS 4
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "libretro.h"
+
+#include "base/main.h"
+#include "common/system.h"
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+
+#ifndef W_OK
+#define W_OK 2
+#endif
+
+#ifndef R_OK
+#define R_OK 4
+#endif
+
+extern char cmd_params[20][200];
+extern char cmd_params_num;
+
+#if (defined(GEKKO) && !defined(WIIU)) || defined(__CELLOS_LV2__)
+extern int access(const char *path, int amode);
+#endif
+
+OSystem *retroBuildOS(bool aEnableSpeedHack);
+const Graphics::Surface &getScreen();
+
+void retroProcessMouse(retro_input_state_t aCallback, int device, float gamepad_cursor_speed, float gamepad_acceleration_time, bool analog_response_is_quadratic, int analog_deadzone, float mouse_speed);
+void retroQuit(void);
+void retroReset(void);
+int retroTestGame(const char *game_id, bool autodetect);
+
+void retroSetSystemDir(const char *aPath);
+void retroSetSaveDir(const char *aPath);
+
+void retroKeyEvent(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers);
+
+#endif
diff --git a/backends/platform/libretro/include/portdefs.h b/backends/platform/libretro/include/portdefs.h
new file mode 100644
index 00000000000..8b02c4d9995
--- /dev/null
+++ b/backends/platform/libretro/include/portdefs.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <new>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+/* newlib ctype.h defines _X for hex digit flag.
+ This conflicts with the use of _X as a variable name. */
+#undef _X
+
+#define RES_W 640
+#define RES_H 480
+
+#if defined(DINGUX) || defined(_3DS)
+#define RES_W_OVERLAY 320
+#define RES_H_OVERLAY 240
+#else
+#define RES_W_OVERLAY 640
+#define RES_H_OVERLAY 480
+#endif
+
+// HACK: With MinGW, GRIM engine seems to crash when using setjmp and longjmp if not using builtin versions
+#if defined __MINGW64__ || defined __MINGW32__
+#include <setjmp.h>
+#undef setjmp
+#undef longjmp
+#define setjmp(a) (__builtin_setjmp(a))
+#define longjmp(a, b) (__builtin_longjmp(a, b))
+#endif
diff --git a/backends/platform/libretro/jni/Android.mk b/backends/platform/libretro/jni/Android.mk
new file mode 100644
index 00000000000..05315b6a1b7
--- /dev/null
+++ b/backends/platform/libretro/jni/Android.mk
@@ -0,0 +1,37 @@
+LOCAL_PATH := $(call my-dir)
+ROOT_PATH := $(LOCAL_PATH)/..
+TARGET_NAME := scummvm
+
+# Reset flags not reset to Makefile.common
+DEFINES :=
+
+# All current 64-bit archs have 64 in the abi name
+ifneq (,$(findstring 64,$(TARGET_ARCH_ABI)))
+ TARGET_64BIT := 1
+endif
+
+include $(ROOT_PATH)/Makefile.common
+
+include $(addprefix $(SCUMMVM_PATH)/, $(addsuffix /module.mk,$(MODULES)))
+
+OBJS_MODULES := $(addprefix $(SCUMMVM_PATH)/, $(foreach MODULE,$(MODULES),$(MODULE_OBJS-$(MODULE)))) \
+ $(CORE_PATH)/android-fs-factory.o
+
+COREFLAGS := $(DEFINES) -DUSE_CXX11
+
+ifeq ($(TARGET_ARCH),arm)
+ COREFLAGS += -D_ARM_ASSEM_
+endif
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := retro
+LOCAL_MODULE_FILENAME := libretro
+LOCAL_SRC_FILES := $(DETECT_OBJS:%.o=$(SCUMMVM_PATH)/%.cpp) $(OBJS_DEPS:%.o=%.c) $(OBJS_MODULES:%.o=%.cpp) $(OBJS:%.o=%.cpp)
+LOCAL_C_INCLUDES := $(subst -I,,$(INCLUDES))
+LOCAL_CPPFLAGS := $(COREFLAGS) -std=c++11
+LOCAL_CFLAGS := $(COREFLAGS)
+LOCAL_LDFLAGS := -Wl,-version-script=$(ROOT_PATH)/link.T
+LOCAL_LDLIBS := -lz -llog
+LOCAL_CPP_FEATURES := rtti
+LOCAL_ARM_MODE := arm
+include $(BUILD_SHARED_LIBRARY)
diff --git a/backends/platform/libretro/jni/Application.mk b/backends/platform/libretro/jni/Application.mk
new file mode 100644
index 00000000000..338f063ef70
--- /dev/null
+++ b/backends/platform/libretro/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := all
+APP_STL := c++_static
diff --git a/backends/platform/libretro/link.T b/backends/platform/libretro/link.T
new file mode 100644
index 00000000000..b0c262db9ef
--- /dev/null
+++ b/backends/platform/libretro/link.T
@@ -0,0 +1,5 @@
+{
+ global: retro_*;
+ local: *;
+};
+
diff --git a/backends/platform/libretro/lite_engines.list b/backends/platform/libretro/lite_engines.list
new file mode 100644
index 00000000000..bc681b60598
--- /dev/null
+++ b/backends/platform/libretro/lite_engines.list
@@ -0,0 +1,32 @@
+agi
+agos
+agos2
+cine
+cruise
+draci
+drascula
+eob
+gob
+groovie
+he
+ihnm
+kyra
+lol
+lure
+made
+mortevielle
+parallaction
+queen
+saga
+sci
+sci32
+scumm
+scumm_7_8
+sherlock
+sky
+sword1
+sword2
+teenagent
+tinsel
+touche
+tucker
diff --git a/backends/platform/libretro/scripts/bundle_datafiles.sh b/backends/platform/libretro/scripts/bundle_datafiles.sh
new file mode 100755
index 00000000000..8d5ec3b912a
--- /dev/null
+++ b/backends/platform/libretro/scripts/bundle_datafiles.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+
+ # Copyright (C) 2022 Giovanni Cascione <ing.cascione at gmail.com>
+ #
+ # This program 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 Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -e
+
+function get_firmware_entry(){
+ echo "
+firmware$1_desc = \"$2\"
+firmware$1_path = \"$3/$2\"
+firmware$1_opt = \"true\"
+"
+}
+
+function process_group(){
+ local dirname="$1"
+ shift
+ local target="$1"
+ shift
+ local arr=("$@")
+ for item in "${arr[@]}"; do
+ [ $target = "bundle" ] && cp $item "${TMP_PATH}/${dirname}/"
+ fragment=$(get_firmware_entry $count $(echo "$item" | sed "s|^.*/||g") "$dirname")
+ CORE_INFO_DATS="${CORE_INFO_DATS}${fragment}"
+ count=$(expr $count + 1)
+ done
+}
+
+# Externally passed variables shall be:
+# $1 [REQ] BUILD_PATH
+# $2 [REQ] SCUMMVM_PATH
+# $3 [REQ] target build ("bundle" to build scummvm.zip, any other string to build core info file)
+# $4 [OPT] target name (prefix for core info file)
+# $5 [OPT] displayed core name (shown in frontend)
+# $6 [OPT] allowed extensions - backup if ScummVM.dat is not available
+
+# Exit if in parameters are not provided
+if [ -z $1 ] || [ -z $2 ] || [ -z $3 ] ; then
+ exit 1
+fi
+
+[ -z $4 ] && INFO_FILE_PRE=scummvm || INFO_FILE_PRE=$4
+[ -z $5 ] && NICE_NAME=ScummVM || NICE_NAME=$5
+[ -z $6 ] && ALLOWED_EXT=scummvm || ALLOWED_EXT=$6
+
+# Set variables
+BUILD_PATH="$1"
+SCUMMVM_PATH="$2"
+TMP_PATH="${BUILD_PATH}/tmp_data"
+TARGET_PATH="${BUILD_PATH}"
+BUNDLE_DIR="scummvm"
+BUNDLE_DATAFILES_DIR="${BUNDLE_DIR}/extra"
+BUNDLE_THEME_DIR="${BUNDLE_DIR}/theme"
+BUNDLE_ZIP_FILE="${BUNDLE_DIR}.zip"
+BUNDLE_LOCAL_DATAFILES_DIR="${BUILD_PATH}/dist"
+
+# Retrieve data file info from ScummVM source
+THEMES_LIST=$(cat "${SCUMMVM_PATH}/dists/scummvm.rc" 2>/dev/null | grep FILE.*gui/themes.*\* | sed "s|.*\"\(.*\)\"|${SCUMMVM_PATH}/\1|g")
+DATAFILES_LIST=$(cat "${SCUMMVM_PATH}/dists/scummvm.rc" 2>/dev/null| grep FILE.*dists/engine-data | sed "s|.*\"\(.*\)\"|${SCUMMVM_PATH}/\1|g")
+
+# Put retrieved data into arrays
+set +e
+read -a THEME_ARRAY -d '' -r <<< "${THEMES_LIST}"
+read -a DATAFILES_ARRAY -d '' -r <<< "$DATAFILES_LIST"
+set -e
+
+# Add specific data files
+DATAFILES_ARRAY[${#DATAFILES_ARRAY[@]}]="${SCUMMVM_PATH}"/backends/vkeybd/packs/vkeybd_default.zip
+
+# Make sure target folders exist
+[ $3 = "bundle" ] && mkdir -p "${TMP_PATH}/${BUNDLE_THEME_DIR}/"
+[ $3 = "bundle" ] && mkdir -p "${TMP_PATH}/${BUNDLE_DATAFILES_DIR}/"
+
+count=0
+# Process themes
+ process_group "$BUNDLE_THEME_DIR" $3 ${THEME_ARRAY[@]}
+
+# Process datafiles
+ process_group "$BUNDLE_DATAFILES_DIR" $3 ${DATAFILES_ARRAY[@]}
+
+# Process additional local bundle files
+if [ -d "$BUNDLE_LOCAL_DATAFILES_DIR" -a ! -z "$(ls -A ${BUNDLE_LOCAL_DATAFILES_DIR} 2>/dev/null)" ] ; then
+ for item in $BUNDLE_LOCAL_DATAFILES_DIR/*; do
+ [ ! $(echo "$item" | sed "s|^.*/||g") = "README.md" ] && LOCAL_EXTRA_ARRAY+=("$item")
+ done
+ process_group "$BUNDLE_DATAFILES_DIR" $3 ${LOCAL_EXTRA_ARRAY[@]}
+fi
+
+if [ ! $3 = "bundle" ]; then
+
+# Update from libretro ScummVM.dat
+wget -NO "$BUILD_PATH"/ScummVM.dat https://raw.githubusercontent.com/libretro/libretro-database/master/dat/ScummVM.dat
+[ -f "$BUILD_PATH"/ScummVM.dat ] && SUPPORTED_EXTENSIONS="$(cat $BUILD_PATH/ScummVM.dat | grep 'rom (' | sed -e 's/\" .*//g' -e 's/.*\.//g' | sort -u | tr '\n' '|')" || SUPPORTED_EXTENSIONS="$ALLOWED_EXT"
+
+ # Create core.info file
+ set +e
+ read -d '' CORE_INFO_CONTENT <<EOF
+# Software Information
+display_name = "$NICE_NAME"
+authors = "SCUMMVMdev"
+supported_extensions = "$SUPPORTED_EXTENSIONS"
+corename = "$NICE_NAME"
+categories = "Game"
+license = "GPLv3"
+permissions = ""
+display_version = $(cat $SCUMMVM_PATH/base/internal_version.h 2>/dev/null | grep SCUMMVM_VERSION | sed "s|^.*SCUMMVM_VERSION *||g")
+
+# Hardware Information
+manufacturer = "Various"
+systemname = "Game engine"
+systemid = "scummvm"
+
+# Libretro Features
+database = "ScummVM"
+supports_no_game = "true"
+savestate = "false"
+savestate_features = "null"
+cheats = "false"
+input_descriptors = "true"
+memory_descriptors = "false"
+libretro_saves = "false"
+core_options = "true"
+core_options_version = "1.3"
+load_subsystem = "false"
+hw_render = "false"
+needs_fullpath = "true"
+disk_control = "false"
+is_experimental = "false"
+
+# Firmware / BIOS
+firmware_count = $count
+EOF
+ set -e
+
+ CORE_INFO_CONTENT="${CORE_INFO_CONTENT}${CORE_INFO_DATS}
+description = \"The ScummVM adventure game engine ported to libretro. This core is built directly from the upstream repo and is synced upon stable releases, though it is not supported upstream. So please report any bug to Libretro and/or make sure the same apply to the standalone ScummVM program as well, before making any report to ScummVM Team.\""
+ echo "$CORE_INFO_CONTENT" > "${TARGET_PATH}/${INFO_FILE_PRE}_libretro.info"
+ echo "${INFO_FILE_PRE}_libretro.info created successfully"
+else
+
+ # Create archive
+ rm -f "${TARGET_PATH}/$BUNDLE_ZIP_FILE"
+ cd "${TMP_PATH}"
+ zip -rq "${TARGET_PATH}/$BUNDLE_ZIP_FILE" "${BUNDLE_DIR}" > /dev/null 2>&1
+ cd - > /dev/null
+
+ # Remove temporary directories
+ rm -rf "$TMP_PATH"
+ echo "$BUNDLE_ZIP_FILE created successfully"
+fi
diff --git a/backends/platform/libretro/scripts/configure_engines.sh b/backends/platform/libretro/scripts/configure_engines.sh
new file mode 100755
index 00000000000..43d4634e4b8
--- /dev/null
+++ b/backends/platform/libretro/scripts/configure_engines.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+ # Copyright (C) 2022 Giovanni Cascione <ing.cascione at gmail.com>
+ #
+ # This program 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 Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Externally passed variables shall be:
+
+# $1 [REQ] BUILD_PATH
+# $2 [REQ] SCUMMVM_PATH
+# $3 [REQ] NO_HIGH_DEF [0,1]
+# $4 [REQ] NO_WIP [0,1]
+# $5 [REQ] STATIC_LINKING [0,1]
+# $6 [REQ] LITE [0,1]
+
+set -e
+
+# Exit if in parameters are not provided
+if [ -z $1 ] || [ -z $2 ] || [ -z $3 ] || [ -z $4 ] || [ -z $5 ] || [ -z $6 ] ; then
+ exit 1
+fi
+
+BUILD_PATH="$1"
+SCUMMVM_PATH="$2"
+
+cd "${SCUMMVM_PATH}"
+
+# Retrieve all configure functions
+sed -i.bak -e "s/exit 0/return 0/g" configure
+. configure -h > /dev/null 2>&1
+mv configure.bak configure > /dev/null 2>&1
+
+_parent_engines_list=""
+
+# Collect all default engines dependencies and force to yes
+tot_deps=""
+for a in $_engines ; do
+ engine_deps_var=_engine_${a}_deps
+ for dep in ${!engine_deps_var} ; do
+ found=0
+ for rec_dep in $tot_deps ; do
+ [ $dep = $rec_dep ] && found=1
+ done
+ [ $found -eq 0 ] && tot_deps+=" $dep"
+ done
+
+ # Static linking support files
+ not_subengine_var=_engine_${a}_sub
+ not_wip_engine_var=_engine_${a}_build_default
+ if [ $5 -eq 1 ] && [ -z ${!not_subengine_var} ] ; then
+ good_to_go=1
+ # Test NO_HIGH_DEF
+ [ $3 -eq 1 ] && [ $((echo ${!engine_deps_var} | grep -q highres); echo $?) -eq 0 ] && good_to_go=0
+ [ $4 -eq 1 ] && [ $(echo ${!not_wip_engine_var} = no) ] && good_to_go=0
+ [ $6 -eq 1 ] && [ $((cat ${BUILD_PATH}/lite_engines.list | grep -wq ${a}); echo $?) -eq 1 ] && good_to_go=0
+ [ $good_to_go -eq 1 ] && _parent_engines_list+="ADDLIB libtemp/lib${a}.a"$'\n'
+ fi
+done
+
+[ $5 -eq 1 ] && printf "$_parent_engines_list" >> "$BUILD_PATH"/script.mri
+
+for dep in $tot_deps ; do
+ eval _$dep=yes
+done
+
+# Test NO_HIGH_DEF
+if [ $3 -eq 1 ] ; then
+ _highres=no
+fi
+
+# Test LITE
+if [ $6 -eq 1 ] ; then
+ cp "${BUILD_PATH}"/lite_engines.list "${BUILD_PATH}"/config.mk.engines.lite
+ sed -i.bak -e "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/" -e "s/^/ENABLE_/g;s/$/ = STATIC_PLUGIN/g" "${BUILD_PATH}"/config.mk.engines.lite
+ rm "${BUILD_PATH}"/config.mk.engines.lite.bak
+fi
+
+# Create needed engines build files
+awk -f "engines.awk" < /dev/null > /dev/null 2>&1
+
+mkdir -p "engines"
+
+copy_if_changed engines/engines.mk.new "engines/engines.mk"
+copy_if_changed engines/detection_table.h.new "engines/detection_table.h"
+copy_if_changed engines/plugins_table.h.new "engines/plugins_table.h"
+
+# Test NO_WIP
+[ $4 -ne 1 ] && sed -i.bak -e "s/# \(.*\)/\1 = STATIC_PLUGIN/g" "config.mk.engines"
+
+echo 0
diff --git a/backends/platform/libretro/scripts/configure_submodules.sh b/backends/platform/libretro/scripts/configure_submodules.sh
new file mode 100755
index 00000000000..86dfff8417b
--- /dev/null
+++ b/backends/platform/libretro/scripts/configure_submodules.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+ # Copyright (C) 2022 Giovanni Cascione <ing.cascione at gmail.com>
+ #
+ # This program 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 Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Externally passed variables shall be:
+# $1 [REQ] repository URL
+# $2 [REQ] target commit
+# $3 [REQ] submodules folder
+# $4 [REQ] allow dirty submodules [0,1]
+# $5 [OPT] submodule folder (if not specified will be the same name of the repository)
+
+# Any error response will interrupt the script and will not reach the "0" output, to be used for success test upstream
+set -e
+
+TARGET_URL=$1
+TARGET_COMMIT=$2
+SUBMODULES_PATH=$3
+[ -z $4 ] && ALLOW_DIRTY=0 || ALLOW_DIRTY=$4
+[ -z $5 ] && SUBMODULE_FOLDER=$(echo $TARGET_URL | sed "s|.*/||") || SUBMODULE_FOLDER=$5
+
+clone=0
+reset=0
+
+# Exit if in parameters are not provided
+if [ -z $TARGET_URL ] || [ -z $TARGET_COMMIT ] || [ -z $SUBMODULES_PATH ] || [ -z $SUBMODULE_FOLDER ] ; then
+ exit 1
+fi
+
+# Create submodules folder if does not exist and move into it
+[ ! -d $SUBMODULES_PATH ] && mkdir -p $SUBMODULES_PATH
+
+cd $SUBMODULES_PATH
+
+# Folder exists
+if [ -d $SUBMODULE_FOLDER ]; then
+ # Folder is not our repo, not our commit or not git repo at all, remove and clone (no history) again
+ if [ ! $(cd $SUBMODULE_FOLDER && git config --get remote.origin.url 2>/dev/null) = $TARGET_URL ] || [ ! $(cd $SUBMODULE_FOLDER && git rev-parse HEAD 2>/dev/null) = $TARGET_COMMIT ] ; then
+ rm -rf ${SUBMODULE_FOLDER} && clone=1
+ fi
+
+ # Dirty repo in folder, reset hard
+ [ $ALLOW_DIRTY -ne 1 ] && [ $clone -ne 1 ] && [ ! -z "$(cd $SUBMODULE_FOLDER && git diff --shortstat 2>/dev/null)" ] && reset=1
+
+# Folder does not exist
+else
+ clone=1
+fi
+
+# Apply collected actions
+if [ $clone -eq 1 ] ; then
+ mkdir -p $SUBMODULE_FOLDER
+ git -C $SUBMODULE_FOLDER init > /dev/null 2>&1
+ git -C $SUBMODULE_FOLDER remote add origin $TARGET_URL > /dev/null 2>&1
+ git -C $SUBMODULE_FOLDER fetch --depth 1 origin $TARGET_COMMIT > /dev/null 2>&1
+ git -C $SUBMODULE_FOLDER checkout FETCH_HEAD > /dev/null 2>&1
+ git -C $SUBMODULE_FOLDER submodule update --init --recursive --depth 1 > /dev/null 2>&1
+fi
+
+cd $SUBMODULE_FOLDER
+[ $reset -eq 1 ] && git reset --hard > /dev/null 2>&1
+
+# Success
+echo 0
diff --git a/backends/platform/libretro/scripts/retrieve_modules.sh b/backends/platform/libretro/scripts/retrieve_modules.sh
new file mode 100755
index 00000000000..229b44953da
--- /dev/null
+++ b/backends/platform/libretro/scripts/retrieve_modules.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+ # Copyright (C) 2022 Giovanni Cascione <ing.cascione at gmail.com>
+ #
+ # This program 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 Foundation, either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is to extract shared modules definitions from ScummVM
+# Makefile.common, in order to be in sync with modifications upstream
+
+# Externally passed variables shall be:
+# $1 [REQ] ScummVM Makefile.common full path
+
+
+if [ -f "$1" ] ; then
+ sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/§/g' -e 's|.*-include engines/engines.mk||' -e 's/###.*//' -e 's/§/\n/g' -e 's|MODULES|SHARED_MODULES|g' "$1"
+else
+ printf "\$(error Error retrieving shared modules definitions from main Makefile.common)"
+fi
diff --git a/backends/platform/libretro/sharedlib_test.mk b/backends/platform/libretro/sharedlib_test.mk
new file mode 100644
index 00000000000..f6e3134f8b1
--- /dev/null
+++ b/backends/platform/libretro/sharedlib_test.mk
@@ -0,0 +1,9 @@
+this_lib_available := no
+ifeq ($(USE_SYSTEM_$(shell printf ' $(this_lib_flags)' | sed -e "s|.*-l||" -e "s| .*||")), 1)
+this_lib_available := $(call sharedlibs_is_lib_available)
+endif
+$(call sharedlibs_system_lib_message)
+ifeq ($(this_lib_available), yes)
+LDFLAGS += $(this_lib_flags)
+INCLUDES += $(sharedlibs_this_lib_includes)
+endif
diff --git a/backends/platform/libretro/src/android-fs-factory.cpp b/backends/platform/libretro/src/android-fs-factory.cpp
new file mode 100644
index 00000000000..ae8cd3c15e6
--- /dev/null
+++ b/backends/platform/libretro/src/android-fs-factory.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(__ANDROID__)
+
+/* TODO: Only purpose of this file is to return false to the call to
+ * AndroidFilesystemFactory::instance().hasSAF() in base/main.cpp used
+ * to show the SAF system popup, which would need android backend enabled.
+ * Cleaner fix to be found.
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/fs/android/android-fs-factory.h"
+#include "libretro-fs.h"
+
+namespace Common {
+DECLARE_SINGLETON(AndroidFilesystemFactory);
+}
+
+AndroidFilesystemFactory::AndroidFilesystemFactory() : _withSAF(false), _config(this) {
+}
+
+void AndroidFilesystemFactory::initSAF() {
+ _withSAF = false;
+}
+
+AbstractFSNode *AndroidFilesystemFactory::makeRootFileNode() const {
+ return new LibRetroFilesystemNode("/");
+}
+
+AbstractFSNode *AndroidFilesystemFactory::makeCurrentDirectoryFileNode() const {
+ return new LibRetroFilesystemNode("/");
+}
+
+AbstractFSNode *AndroidFilesystemFactory::makeFileNodePath(const Common::String &path) const {
+ assert(!path.empty());
+ return new LibRetroFilesystemNode(path);
+}
+
+AndroidFilesystemFactory::Config::Config(const AndroidFilesystemFactory *factory) {
+}
+
+void AndroidFilesystemFactory::getSAFTrees(AbstractFSList &list, bool allowSAFadd) const {
+ return;
+}
+
+bool AndroidFilesystemFactory::Config::getDrives(AbstractFSList &list, bool hidden) const {
+ return false;
+}
+
+bool AndroidFilesystemFactory::Config::isDrive(const Common::String &path) const {
+ return false;
+}
+
+#endif
diff --git a/backends/platform/libretro/src/libretro-fs-factory.cpp b/backends/platform/libretro/src/libretro-fs-factory.cpp
new file mode 100644
index 00000000000..ab4a6a72266
--- /dev/null
+++ b/backends/platform/libretro/src/libretro-fs-factory.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef __LIBRETRO__
+
+// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
+// Also with clock() in sys/time.h in some Mac OS X SDKs.
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
+#define FORBIDDEN_SYMBOL_EXCEPTION_exit // Needed for IRIX's unistd.h
+#ifdef PLAYSTATION3
+#include <stdlib.h>
+#include <string.h>
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+extern char *getwd(char *);
+extern int errno;
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#define ERANGE 34 // Result too large
+#define ENOMEM 12 // Cannot allocate memory
+
+static inline char *getcwd(char *buf, size_t len) {
+ return 0;
+}
+#endif
+
+#include "backends/platform/libretro/include/libretro-fs-factory.h"
+#include "backends/platform/libretro/include/libretro-fs.h"
+
+AbstractFSNode *LibRetroFilesystemFactory::makeRootFileNode() const {
+ return new LibRetroFilesystemNode("/");
+}
+
+AbstractFSNode *LibRetroFilesystemFactory::makeCurrentDirectoryFileNode() const {
+#ifdef PLAYSTATION3
+ return new LibRetroFilesystemNode("/");
+#else
+ char buf[MAXPATHLEN];
+ return getcwd(buf, MAXPATHLEN) ? new LibRetroFilesystemNode(buf) : NULL;
+#endif
+}
+
+AbstractFSNode *LibRetroFilesystemFactory::makeFileNodePath(const Common::String &path) const {
+ assert(!path.empty());
+ return new LibRetroFilesystemNode(path);
+}
+#endif
diff --git a/backends/platform/libretro/src/libretro-fs.cpp b/backends/platform/libretro/src/libretro-fs.cpp
new file mode 100644
index 00000000000..d11f0e89a90
--- /dev/null
+++ b/backends/platform/libretro/src/libretro-fs.cpp
@@ -0,0 +1,228 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Re-enable some forbidden symbols to avoid clashes with stat.h and unistd.h.
+// Also with clock() in sys/time.h in some Mac OS X SDKs.
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_unistd_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_mkdir
+#define FORBIDDEN_SYMBOL_EXCEPTION_getenv
+#define FORBIDDEN_SYMBOL_EXCEPTION_exit // Needed for IRIX's unistd.h
+
+#include <file/file_path.h>
+#include <retro_dirent.h>
+#include <retro_stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "backends/platform/libretro/include/libretro-fs.h"
+#include "backends/fs/stdiostream.h"
+#include "common/algorithm.h"
+
+void LibRetroFilesystemNode::setFlags() {
+ const char *fspath = _path.c_str();
+
+ _isValid = path_is_valid(fspath);
+ _isDirectory = path_is_directory(fspath);
+}
+
+LibRetroFilesystemNode::LibRetroFilesystemNode(const Common::String &p) {
+ assert(p.size() > 0);
+
+ // Expand "~/" to the value of the HOME env variable
+ if (p.hasPrefix("~/")) {
+ const char *home = getenv("HOME");
+ if (home != NULL && strlen(home) < MAXPATHLEN) {
+ _path = home;
+ // Skip over the tilda. We know that p contains at least
+ // two chars, so this is safe:
+ _path += p.c_str() + 1;
+ }
+ } else {
+ _path = p;
+ }
+
+ // Normalize the path (that is, remove unneeded slashes etc.)
+ _path = Common::normalizePath(_path, '/');
+ _displayName = Common::lastPathComponent(_path, '/');
+
+ setFlags();
+}
+
+AbstractFSNode *LibRetroFilesystemNode::getChild(const Common::String &n) const {
+ assert(!_path.empty());
+ assert(_isDirectory);
+
+ // Make sure the string contains no slashes
+ assert(!n.contains('/'));
+
+ // We assume here that _path is already normalized (hence don't bother to call
+ // Common::normalizePath on the final path).
+ Common::String newPath(_path);
+ if (_path.lastChar() != '/')
+ newPath += '/';
+ newPath += n;
+
+ return makeNode(newPath);
+}
+
+bool LibRetroFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
+ assert(_isDirectory);
+
+ struct RDIR *dirp = retro_opendir(_path.c_str());
+
+ if (dirp == NULL)
+ return false;
+
+ // loop over dir entries using readdir
+ while ((retro_readdir(dirp))) {
+ const char *d_name = retro_dirent_get_name(dirp);
+
+ // Skip 'invisible' files if necessary
+ if (d_name[0] == '.' && !hidden) {
+ continue;
+ }
+ // Skip '.' and '..' to avoid cycles
+ if ((d_name[0] == '.' && d_name[1] == 0) || (d_name[0] == '.' && d_name[1] == '.')) {
+ continue;
+ }
+
+ // Start with a clone of this node, with the correct path set
+ LibRetroFilesystemNode entry(*this);
+ entry._displayName = d_name;
+ if (_path.lastChar() != '/')
+ entry._path += '/';
+ entry._path += entry._displayName;
+
+ entry._isValid = true;
+ entry._isDirectory = retro_dirent_is_dir(dirp, entry._path.c_str());
+
+ // Skip files that are invalid for some reason (e.g. because we couldn't
+ // properly stat them).
+ if (!entry._isValid)
+ continue;
+
+ // Honor the chosen mode
+ if ((mode == Common::FSNode::kListFilesOnly && entry._isDirectory) || (mode == Common::FSNode::kListDirectoriesOnly && !entry._isDirectory))
+ continue;
+
+ myList.push_back(new LibRetroFilesystemNode(entry));
+ }
+ retro_closedir(dirp);
+
+ return true;
+}
+
+AbstractFSNode *LibRetroFilesystemNode::getParent() const {
+ if (_path == "/")
+ return 0; // The filesystem root has no parent
+
+ const char *start = _path.c_str();
+ const char *end = start + _path.size();
+
+ // Strip of the last component. We make use of the fact that at this
+ // point, _path is guaranteed to be normalized
+ while (end > start && *(end - 1) != '/')
+ end--;
+
+ if (end == start) {
+ return 0;
+ }
+
+ return makeNode(Common::String(start, end));
+}
+
+Common::SeekableReadStream *LibRetroFilesystemNode::createReadStream() {
+ return StdioStream::makeFromPath(getPath(), false);
+}
+
+Common::SeekableWriteStream *LibRetroFilesystemNode::createWriteStream() {
+ return StdioStream::makeFromPath(getPath(), true);
+}
+
+bool LibRetroFilesystemNode::createDirectory() {
+ if (path_mkdir(_path.c_str()))
+ setFlags();
+
+ return _isValid && _isDirectory;
+}
+
+namespace Posix {
+
+bool assureDirectoryExists(const Common::String &dir, const char *prefix) {
+ // Check whether the prefix exists if one is supplied.
+ if (prefix) {
+ if (!path_is_valid(prefix)) {
+ return false;
+ } else if (!path_is_directory(prefix)) {
+ return false;
+ }
+ }
+
+ // Obtain absolute path.
+ Common::String path;
+ if (prefix) {
+ path = prefix;
+ path += '/';
+ path += dir;
+ } else {
+ path = dir;
+ }
+
+ path = Common::normalizePath(path, '/');
+
+ const Common::String::iterator end = path.end();
+ Common::String::iterator cur = path.begin();
+ if (*cur == '/')
+ ++cur;
+
+ do {
+ if (cur + 1 != end) {
+ if (*cur != '/') {
+ continue;
+ }
+
+ // It is kind of ugly and against the purpose of Common::String to
+ // insert 0s inside, but this is just for a local string and
+ // simplifies the code a lot.
+ *cur = '\0';
+ }
+
+ if (!path_mkdir(path.c_str())) {
+ if (errno == EEXIST) {
+ if (!path_is_valid(path.c_str())) {
+ return false;
+ } else if (!path_is_directory(path.c_str())) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ *cur = '/';
+ } while (cur++ != end);
+
+ return true;
+}
+
+} // End of namespace Posix
diff --git a/backends/platform/libretro/src/libretro-os.cpp b/backends/platform/libretro/src/libretro-os.cpp
new file mode 100644
index 00000000000..ee3bb65a5d5
--- /dev/null
+++ b/backends/platform/libretro/src/libretro-os.cpp
@@ -0,0 +1,1309 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <libretro.h>
+#include <retro_inline.h>
+#include <retro_miscellaneous.h>
+#include <features/features_cpu.h>
+
+#include "audio/mixer_intern.h"
+#include "backends/base-backend.h"
+#include "base/commandLine.h"
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "common/tokenizer.h"
+#include "common/list.h"
+
+#if defined(_WIN32)
+#include "backends/fs/windows/windows-fs-factory.h"
+#define FS_SYSTEM_FACTORY WindowsFilesystemFactory
+#else
+#include "backends/platform/libretro/include/libretro-fs-factory.h"
+#define FS_SYSTEM_FACTORY LibRetroFilesystemFactory
+#endif
+
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "graphics/colormasks.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+#if defined(_WIN32)
+#include <direct.h>
+#ifdef _XBOX
+#include <xtl.h>
+#else
+#include <windows.h>
+#endif
+#elif defined(__CELLOS_LV2__)
+#include <sys/sys_time.h>
+#elif (defined(GEKKO) && !defined(WIIU))
+#include <ogc/lwp_watchdog.h>
+#else
+#include <time.h>
+#endif
+
+#include "backends/platform/libretro/include/libretro-threads.h"
+#include "backends/platform/libretro/include/os.h"
+
+extern retro_log_printf_t log_cb;
+
+#include "common/mutex.h"
+
+/**
+ * Dummy mutex implementation
+ */
+class LibretroMutexInternal final : public Common::MutexInternal {
+public:
+ LibretroMutexInternal() {};
+ ~LibretroMutexInternal() override {};
+
+ bool lock() override {
+ return 0;
+ }
+ bool unlock() override {
+ return 0;
+ };
+};
+
+Common::MutexInternal *createLibretroMutexInternal() {
+ return new LibretroMutexInternal();
+}
+
+struct RetroPalette {
+ unsigned char _colors[256 * 3];
+
+ RetroPalette() {
+ memset(_colors, 0, sizeof(_colors));
+ }
+
+ void set(const byte *colors, uint start, uint num) {
+ memcpy(_colors + start * 3, colors, num * 3);
+ }
+
+ void get(byte *colors, uint start, uint num) const {
+ memcpy(colors, _colors + start * 3, num * 3);
+ }
+
+ unsigned char *getColor(uint aIndex) const {
+ return (unsigned char *)&_colors[aIndex * 3];
+ }
+};
+
+static INLINE void blit_uint8_uint16_fast(Graphics::Surface &aOut, const Graphics::Surface &aIn, const RetroPalette &aColors) {
+ for (int i = 0; i < aIn.h; i++) {
+ if (i >= aOut.h)
+ continue;
+
+ uint8_t *const in = (uint8_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + (i * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if (j >= aOut.w)
+ continue;
+
+ uint8 r, g, b;
+
+ const uint8_t val = in[j];
+ // if(val != 0xFFFFFFFF)
+ {
+ if (aIn.format.bytesPerPixel == 1) {
+ unsigned char *col = aColors.getColor(val);
+ r = *col++;
+ g = *col++;
+ b = *col++;
+ } else
+ aIn.format.colorToRGB(in[j], r, g, b);
+
+ out[j] = aOut.format.RGBToColor(r, g, b);
+ }
+ }
+ }
+}
+
+static INLINE void blit_uint32_uint16(Graphics::Surface &aOut, const Graphics::Surface &aIn, const RetroPalette &aColors) {
+ for (int i = 0; i < aIn.h; i++) {
+ if (i >= aOut.h)
+ continue;
+
+ uint32_t *const in = (uint32_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + (i * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if (j >= aOut.w)
+ continue;
+
+ uint8 r, g, b;
+
+ // const uint32_t val = in[j];
+ // if(val != 0xFFFFFFFF)
+ {
+ aIn.format.colorToRGB(in[j], r, g, b);
+ out[j] = aOut.format.RGBToColor(r, g, b);
+ }
+ }
+ }
+}
+
+static INLINE void blit_uint16_uint16(Graphics::Surface &aOut, const Graphics::Surface &aIn, const RetroPalette &aColors) {
+ for (int i = 0; i < aIn.h; i++) {
+ if (i >= aOut.h)
+ continue;
+
+ uint16_t *const in = (uint16_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + (i * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if (j >= aOut.w)
+ continue;
+
+ uint8 r, g, b;
+
+ // const uint16_t val = in[j];
+ // if(val != 0xFFFFFFFF)
+ {
+ aIn.format.colorToRGB(in[j], r, g, b);
+ out[j] = aOut.format.RGBToColor(r, g, b);
+ }
+ }
+ }
+}
+
+static void blit_uint8_uint16(Graphics::Surface &aOut, const Graphics::Surface &aIn, int aX, int aY, const RetroPalette &aColors, uint32 aKeyColor) {
+ for (int i = 0; i < aIn.h; i++) {
+ if ((i + aY) < 0 || (i + aY) >= aOut.h)
+ continue;
+
+ uint8_t *const in = (uint8_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + ((i + aY) * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if ((j + aX) < 0 || (j + aX) >= aOut.w)
+ continue;
+
+ uint8 r, g, b;
+
+ const uint8_t val = in[j];
+ if (val != aKeyColor) {
+ unsigned char *col = aColors.getColor(val);
+ r = *col++;
+ g = *col++;
+ b = *col++;
+ out[j + aX] = aOut.format.RGBToColor(r, g, b);
+ }
+ }
+ }
+}
+
+static void blit_uint16_uint16(Graphics::Surface &aOut, const Graphics::Surface &aIn, int aX, int aY, const RetroPalette &aColors, uint32 aKeyColor) {
+ for (int i = 0; i < aIn.h; i++) {
+ if ((i + aY) < 0 || (i + aY) >= aOut.h)
+ continue;
+
+ uint16_t *const in = (uint16_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + ((i + aY) * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if ((j + aX) < 0 || (j + aX) >= aOut.w)
+ continue;
+
+ uint8 r, g, b;
+
+ const uint16_t val = in[j];
+ if (val != aKeyColor) {
+ aIn.format.colorToRGB(in[j], r, g, b);
+ out[j + aX] = aOut.format.RGBToColor(r, g, b);
+ }
+ }
+ }
+}
+
+static void blit_uint32_uint16(Graphics::Surface &aOut, const Graphics::Surface &aIn, int aX, int aY, const RetroPalette &aColors, uint32 aKeyColor) {
+ for (int i = 0; i < aIn.h; i++) {
+ if ((i + aY) < 0 || (i + aY) >= aOut.h)
+ continue;
+
+ uint32_t *const in = (uint32_t *)aIn.getPixels() + (i * aIn.w);
+ uint16_t *const out = (uint16_t *)aOut.getPixels() + ((i + aY) * aOut.w);
+
+ for (int j = 0; j < aIn.w; j++) {
+ if ((j + aX) < 0 || (j + aX) >= aOut.w)
+ continue;
+
+ uint8 in_a, in_r, in_g, in_b;
+ uint8 out_r, out_g, out_b;
+ uint32_t blend_r, blend_g, blend_b;
+
+ const uint32_t val = in[j];
+ if (val != aKeyColor) {
+ aIn.format.colorToARGB(in[j], in_a, in_r, in_g, in_b);
+
+ if (in_a) {
+ aOut.format.colorToRGB(out[j + aX], out_r, out_g, out_b);
+
+ blend_r = ((in_r * in_a) + (out_r * (255 - in_a))) / 255;
+ blend_g = ((in_g * in_a) + (out_g * (255 - in_a))) / 255;
+ blend_b = ((in_b * in_a) + (out_b * (255 - in_a))) / 255;
+
+ out[j + aX] = aOut.format.RGBToColor(blend_r, blend_g, blend_b);
+ }
+ }
+ }
+ }
+}
+
+static INLINE void copyRectToSurface(uint8_t *pixels, int out_pitch, const uint8_t *src, int pitch, int x, int y, int w, int h, int out_bpp) {
+ uint8_t *dst = pixels + y * out_pitch + x * out_bpp;
+
+ do {
+ memcpy(dst, src, w * out_bpp);
+ src += pitch;
+ dst += out_pitch;
+ } while (--h);
+}
+
+static Common::String s_systemDir;
+static Common::String s_saveDir;
+
+#ifdef FRONTEND_SUPPORTS_RGB565
+#define SURF_BPP 2
+#define SURF_RBITS 2
+#define SURF_GBITS 5
+#define SURF_BBITS 6
+#define SURF_ABITS 5
+#define SURF_ALOSS (8 - SURF_ABITS)
+#define SURF_RLOSS (8 - SURF_RBITS)
+#define SURF_GLOSS (8 - SURF_GBITS)
+#define SURF_BLOSS (8 - SURF_BBITS)
+#define SURF_RSHIFT 0
+#define SURF_GSHIFT 11
+#define SURF_BSHIFT 5
+#define SURF_ASHIFT 0
+#else
+#define SURF_BPP 2
+#define SURF_RBITS 5
+#define SURF_GBITS 5
+#define SURF_BBITS 5
+#define SURF_ABITS 1
+#define SURF_ALOSS (8 - SURF_ABITS)
+#define SURF_RLOSS (8 - SURF_RBITS)
+#define SURF_GLOSS (8 - SURF_GBITS)
+#define SURF_BLOSS (8 - SURF_BBITS)
+#define SURF_RSHIFT 10
+#define SURF_GSHIFT 5
+#define SURF_BSHIFT 0
+#define SURF_ASHIFT 15
+#endif
+
+Common::List<Common::Event> _events;
+
+class OSystem_RETRO : public EventsBaseBackend, public PaletteManager {
+public:
+ Graphics::Surface _screen;
+
+ Graphics::Surface _gameScreen;
+ RetroPalette _gamePalette;
+
+ Graphics::Surface _overlay;
+ bool _overlayVisible;
+ bool _overlayInGUI;
+
+ Graphics::Surface _mouseImage;
+ RetroPalette _mousePalette;
+ bool _mousePaletteEnabled;
+ bool _mouseVisible;
+ int _mouseX;
+ int _mouseY;
+ int _relMouseX;
+ int _relMouseY;
+ float _mouseXAcc;
+ float _mouseYAcc;
+ float _dpadXAcc;
+ float _dpadYAcc;
+ float _dpadXVel;
+ float _dpadYVel;
+ int _mouseHotspotX;
+ int _mouseHotspotY;
+ int _mouseKeyColor;
+ bool _mouseDontScale;
+ bool _mouseButtons[2];
+ bool _joypadmouseButtons[2];
+ bool _joypadkeyboardButtons[8];
+ unsigned _joypadnumpadLast;
+ bool _joypadnumpadActive;
+ bool _ptrmouseButton;
+
+ uint32 _startTime;
+ uint32 _threadExitTime;
+
+ bool _speed_hack_enabled;
+
+ Audio::MixerImpl *_mixer;
+
+ OSystem_RETRO(bool aEnableSpeedHack) : _mousePaletteEnabled(false), _mouseVisible(false), _mouseX(0), _mouseY(0), _mouseXAcc(0.0), _mouseYAcc(0.0), _mouseHotspotX(0), _mouseHotspotY(0), _dpadXAcc(0.0), _dpadYAcc(0.0), _dpadXVel(0.0f), _dpadYVel(0.0f), _mouseKeyColor(0), _mouseDontScale(false), _joypadnumpadLast(8), _joypadnumpadActive(false), _mixer(0), _startTime(0), _threadExitTime(10), _speed_hack_enabled(aEnableSpeedHack) {
+ _fsFactory = new FS_SYSTEM_FACTORY();
+ memset(_mouseButtons, 0, sizeof(_mouseButtons));
+ memset(_joypadmouseButtons, 0, sizeof(_joypadmouseButtons));
+ memset(_joypadkeyboardButtons, 0, sizeof(_joypadkeyboardButtons));
+
+ _startTime = getMillis();
+
+ if (s_systemDir.empty())
+ s_systemDir = ".";
+
+ if (s_saveDir.empty())
+ s_saveDir = ".";
+ }
+
+ virtual ~OSystem_RETRO() {
+ _gameScreen.free();
+ _overlay.free();
+ _mouseImage.free();
+ _screen.free();
+
+ delete _mixer;
+ }
+
+ virtual void initBackend() {
+ _savefileManager = new DefaultSaveFileManager(s_saveDir);
+#ifdef FRONTEND_SUPPORTS_RGB565
+ _overlay.create(RES_W_OVERLAY, RES_H_OVERLAY, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+#else
+ _overlay.create(RES_W_OVERLAY, RES_H_OVERLAY, Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15));
+#endif
+ _mixer = new Audio::MixerImpl(48000);
+ _timerManager = new DefaultTimerManager();
+
+ _mixer->setReady(true);
+
+ EventsBaseBackend::initBackend();
+ }
+
+ virtual void engineInit() {
+ Common::String engineId = ConfMan.get("engineid");
+ if (engineId.equalsIgnoreCase("scumm") && ConfMan.getBool("original_gui")) {
+ ConfMan.setBool("original_gui", false);
+ log_cb(RETRO_LOG_INFO, "\"original_gui\" setting forced to false\n");
+ }
+ }
+
+ virtual bool hasFeature(Feature f) {
+ return (f == OSystem::kFeatureCursorPalette);
+ }
+
+ virtual void setFeatureState(Feature f, bool enable) {
+ if (f == kFeatureCursorPalette)
+ _mousePaletteEnabled = enable;
+ }
+
+ virtual bool getFeatureState(Feature f) {
+ return (f == kFeatureCursorPalette) ? _mousePaletteEnabled : false;
+ }
+
+ virtual const GraphicsMode *getSupportedGraphicsModes() const {
+ static const OSystem::GraphicsMode s_noGraphicsModes[] = {{0, 0, 0}};
+ return s_noGraphicsModes;
+ }
+
+ virtual int getDefaultGraphicsMode() const {
+ return 0;
+ }
+
+ virtual bool isOverlayVisible() const {
+ return false;
+ }
+
+ virtual bool setGraphicsMode(int mode) {
+ return true;
+ }
+
+ virtual int getGraphicsMode() const {
+ return 0;
+ }
+
+ virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format) {
+ _gameScreen.create(width, height, format ? *format : Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ virtual int16 getHeight() {
+ return _gameScreen.h;
+ }
+
+ virtual int16 getWidth() {
+ return _gameScreen.w;
+ }
+
+ virtual Graphics::PixelFormat getScreenFormat() const {
+ return _gameScreen.format;
+ }
+
+ virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const {
+ Common::List<Graphics::PixelFormat> result;
+
+ /* RGBA8888 */
+ result.push_back(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+
+#ifdef FRONTEND_SUPPORTS_RGB565
+ /* RGB565 - overlay */
+ result.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+#endif
+ /* RGB555 - fmtowns */
+ result.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15));
+
+ /* Palette - most games */
+ result.push_back(Graphics::PixelFormat::createFormatCLUT8());
+ return result;
+ }
+
+ virtual PaletteManager *getPaletteManager() {
+ return this;
+ }
+
+protected:
+ // PaletteManager API
+ virtual void setPalette(const byte *colors, uint start, uint num) {
+ _gamePalette.set(colors, start, num);
+ }
+
+ virtual void grabPalette(byte *colors, uint start, uint num) const {
+ _gamePalette.get(colors, start, num);
+ }
+
+public:
+ virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
+ const uint8_t *src = (const uint8_t *)buf;
+ uint8_t *pix = (uint8_t *)_gameScreen.getPixels();
+ copyRectToSurface(pix, _gameScreen.pitch, src, pitch, x, y, w, h, _gameScreen.format.bytesPerPixel);
+ }
+
+ virtual void updateScreen() {
+ const Graphics::Surface &srcSurface = (_overlayInGUI) ? _overlay : _gameScreen;
+ if (srcSurface.w && srcSurface.h) {
+ switch (srcSurface.format.bytesPerPixel) {
+ case 1:
+ case 3:
+ blit_uint8_uint16_fast(_screen, srcSurface, _gamePalette);
+ break;
+ case 2:
+ blit_uint16_uint16(_screen, srcSurface, _gamePalette);
+ break;
+ case 4:
+ blit_uint32_uint16(_screen, srcSurface, _gamePalette);
+ break;
+ }
+ }
+
+ // Draw Mouse
+ if (_mouseVisible && _mouseImage.w && _mouseImage.h) {
+ const int x = _mouseX - _mouseHotspotX;
+ const int y = _mouseY - _mouseHotspotY;
+
+ switch (_mouseImage.format.bytesPerPixel) {
+ case 1:
+ case 3:
+ blit_uint8_uint16(_screen, _mouseImage, x, y, _mousePaletteEnabled ? _mousePalette : _gamePalette, _mouseKeyColor);
+ break;
+ case 2:
+ blit_uint16_uint16(_screen, _mouseImage, x, y, _mousePaletteEnabled ? _mousePalette : _gamePalette, _mouseKeyColor);
+ break;
+ case 4:
+ blit_uint32_uint16(_screen, _mouseImage, x, y, _mousePaletteEnabled ? _mousePalette : _gamePalette, _mouseKeyColor);
+ break;
+ }
+ }
+ }
+
+ virtual Graphics::Surface *lockScreen() {
+ return &_gameScreen;
+ }
+
+ virtual void unlockScreen() { /* EMPTY */ }
+
+ virtual void setShakePos(int shakeXOffset, int shakeYOffset) {
+ // TODO
+ }
+
+ virtual void showOverlay(bool inGUI) {
+ _overlayVisible = true;
+ _overlayInGUI = inGUI;
+ }
+
+ virtual void hideOverlay() {
+ _overlayVisible = false;
+ _overlayInGUI = false;
+ }
+
+ virtual void clearOverlay() {
+ _overlay.fillRect(Common::Rect(_overlay.w, _overlay.h), 0);
+ }
+
+ virtual void grabOverlay(Graphics::Surface &surface) {
+ const unsigned char *src = (unsigned char *)_overlay.getPixels();
+ unsigned char *dst = (byte *)surface.getPixels();
+ ;
+ unsigned i = RES_H_OVERLAY;
+
+ do {
+ memcpy(dst, src, RES_W_OVERLAY << 1);
+ dst += surface.pitch;
+ src += RES_W_OVERLAY << 1;
+ } while (--i);
+ }
+
+ virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
+ const uint8_t *src = (const uint8_t *)buf;
+ uint8_t *pix = (uint8_t *)_overlay.getPixels();
+ copyRectToSurface(pix, _overlay.pitch, src, pitch, x, y, w, h, _overlay.format.bytesPerPixel);
+ }
+
+ virtual int16 getOverlayHeight() {
+ return _overlay.h;
+ }
+
+ virtual int16 getOverlayWidth() {
+ return _overlay.w;
+ }
+
+ virtual Graphics::PixelFormat getOverlayFormat() const {
+ return _overlay.format;
+ }
+
+ virtual bool showMouse(bool visible) {
+ const bool wasVisible = _mouseVisible;
+ _mouseVisible = visible;
+ return wasVisible;
+ }
+
+ virtual void warpMouse(int x, int y) {
+ _mouseX = x;
+ _mouseY = y;
+ }
+
+ virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 255, bool dontScale = false, const Graphics::PixelFormat *format = NULL, const byte *mask = nullptr) {
+ const Graphics::PixelFormat mformat = format ? *format : Graphics::PixelFormat::createFormatCLUT8();
+
+ if (_mouseImage.w != w || _mouseImage.h != h || _mouseImage.format != mformat) {
+ _mouseImage.create(w, h, mformat);
+ }
+
+ memcpy(_mouseImage.getPixels(), buf, h * _mouseImage.pitch);
+
+ _mouseHotspotX = hotspotX;
+ _mouseHotspotY = hotspotY;
+ _mouseKeyColor = keycolor;
+ _mouseDontScale = dontScale;
+ }
+
+ virtual void setCursorPalette(const byte *colors, uint start, uint num) {
+ _mousePalette.set(colors, start, num);
+ _mousePaletteEnabled = true;
+ }
+
+ void retroCheckThread(uint32 offset = 0) {
+ if (_threadExitTime <= (getMillis() + offset)) {
+ retro_switch_to_main_thread();
+ _threadExitTime = getMillis() + 10;
+ }
+ }
+
+ virtual bool pollEvent(Common::Event &event) {
+ retroCheckThread();
+
+ ((DefaultTimerManager *)_timerManager)->handler();
+
+ if (!_events.empty()) {
+ event = _events.front();
+ _events.pop_front();
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual uint32 getMillis(bool skipRecord = false) {
+#if (defined(GEKKO) && !defined(WIIU))
+ return (ticks_to_microsecs(gettime()) / 1000.0) - _startTime;
+#elif defined(WIIU)
+ return ((cpu_features_get_time_usec()) / 1000) - _startTime;
+#elif defined(__CELLOS_LV2__)
+ return (sys_time_get_system_time() / 1000.0) - _startTime;
+#else
+ struct timeval t;
+ gettimeofday(&t, 0);
+
+ return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - _startTime;
+#endif
+ }
+
+ virtual void delayMillis(uint msecs) {
+ // Implement 'non-blocking' sleep...
+ uint32 start_time = getMillis();
+ if (_speed_hack_enabled) {
+ // Use janky inaccurate method...
+ uint32 elapsed_time = 0;
+ uint32 time_remaining = msecs;
+ while (time_remaining > 0) {
+ // If delay would take us past the next
+ // thread exit time, exit the thread immediately
+ // (i.e. start burning delay time in the main RetroArch
+ // thread as soon as possible...)
+ retroCheckThread(time_remaining);
+ // Check how much delay time remains...
+ elapsed_time = getMillis() - start_time;
+ if (time_remaining > elapsed_time) {
+ time_remaining = time_remaining - elapsed_time;
+ usleep(1000);
+ } else {
+ time_remaining = 0;
+ }
+ // Have to handle the timer manager here, since some engines
+ // (e.g. dreamweb) sit in a delayMillis() loop waiting for a
+ // timer callback...
+ ((DefaultTimerManager *)_timerManager)->handler();
+ }
+ } else {
+ // Use accurate method...
+ while (getMillis() < start_time + msecs) {
+ usleep(1000);
+ retroCheckThread();
+ // Have to handle the timer manager here, since some engines
+ // (e.g. dreamweb) sit in a delayMillis() loop waiting for a
+ // timer callback...
+ ((DefaultTimerManager *)_timerManager)->handler();
+ }
+ }
+ }
+
+ virtual Common::MutexInternal *createMutex(void) {
+ return createLibretroMutexInternal();
+ }
+
+ virtual void quit() {
+ // TODO:
+ }
+
+ virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0) {
+ // TODO: NOTHING?
+ }
+
+ virtual void getTimeAndDate(TimeDate &t, bool skipRecord) const {
+ time_t curTime = time(NULL);
+
+#define YEAR0 1900
+#define EPOCH_YR 1970
+#define SECS_DAY (24L * 60L * 60L)
+#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
+#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)
+ const int _ytab[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
+ int year = EPOCH_YR;
+ unsigned long dayclock = (unsigned long)curTime % SECS_DAY;
+ unsigned long dayno = (unsigned long)curTime / SECS_DAY;
+ t.tm_sec = dayclock % 60;
+ t.tm_min = (dayclock % 3600) / 60;
+ t.tm_hour = dayclock / 3600;
+ t.tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
+ while (dayno >= YEARSIZE(year)) {
+ dayno -= YEARSIZE(year);
+ year++;
+ }
+ t.tm_year = year - YEAR0;
+ t.tm_mon = 0;
+ while (dayno >= _ytab[LEAPYEAR(year)][t.tm_mon]) {
+ dayno -= _ytab[LEAPYEAR(year)][t.tm_mon];
+ t.tm_mon++;
+ }
+ t.tm_mday = dayno + 1;
+ }
+
+ virtual Audio::Mixer *getMixer() {
+ return _mixer;
+ }
+
+ virtual Common::String getDefaultConfigFileName() {
+ return s_systemDir + "/scummvm.ini";
+ }
+
+ virtual void logMessage(LogMessageType::Type type, const char *message) {
+ if (log_cb)
+ log_cb(RETRO_LOG_INFO, "%s\n", message);
+ }
+
+ //
+
+ const Graphics::Surface &getScreen() {
+ const Graphics::Surface &srcSurface = (_overlayInGUI) ? _overlay : _gameScreen;
+
+ if (srcSurface.w != _screen.w || srcSurface.h != _screen.h) {
+#ifdef FRONTEND_SUPPORTS_RGB565
+ _screen.create(srcSurface.w, srcSurface.h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+#else
+ _screen.create(srcSurface.w, srcSurface.h, Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 15));
+#endif
+ }
+
+ return _screen;
+ }
+
+#define ANALOG_RANGE 0x8000
+#define BASE_CURSOR_SPEED 4
+#define PI 3.141592653589793238
+
+ void updateMouseXY(float deltaAcc, float * cumulativeXYAcc, int doing_x){
+ int * mouseXY;
+ int16 * screen_wh;
+ int * relMouseXY;
+ int cumulativeXYAcc_int;
+ if (doing_x) {
+ mouseXY = &_mouseX;
+ screen_wh = &_screen.w;
+ relMouseXY = &_relMouseX;
+ } else {
+ mouseXY = &_mouseY;
+ screen_wh = &_screen.h;
+ relMouseXY = &_relMouseY;
+ }
+ *cumulativeXYAcc += deltaAcc;
+ cumulativeXYAcc_int = (int)*cumulativeXYAcc;
+ if (cumulativeXYAcc_int != 0) {
+ // Set mouse position
+ *mouseXY += cumulativeXYAcc_int;
+ *mouseXY = (*mouseXY < 0) ? 0 : *mouseXY;
+ *mouseXY = (*mouseXY >= *screen_wh) ? *screen_wh : *mouseXY;
+ // Update accumulator
+ *cumulativeXYAcc -= (float)cumulativeXYAcc_int;
+ }
+ *relMouseXY = (int)deltaAcc;
+
+ }
+
+ void processMouse(retro_input_state_t aCallback, int device, float gampad_cursor_speed, float gamepad_acceleration_time, bool analog_response_is_quadratic, int analog_deadzone, float mouse_speed) {
+ enum processMouse_status {
+ STATUS_DOING_JOYSTICK = (1 << 0),
+ STATUS_DOING_MOUSE = (1 << 1),
+ STATUS_DOING_X = (1 << 2),
+ STATUS_DOING_Y = (1 << 3)
+ };
+ uint8_t status = 0;
+ int16_t joy_x, joy_y, joy_rx, joy_ry, x, y;
+ float analog_amplitude_x, analog_amplitude_y;
+ float deltaAcc;
+ bool down;
+ float screen_adjusted_cursor_speed = (float)_screen.w / 320.0f; // Dpad cursor speed should always be based off a 320 wide screen, to keep speeds consistent
+ float adjusted_cursor_speed = (float)BASE_CURSOR_SPEED * gampad_cursor_speed * screen_adjusted_cursor_speed;
+ float inverse_acceleration_time = (gamepad_acceleration_time > 0.0) ? (1.0 / 60.0) * (1.0 / gamepad_acceleration_time) : 1.0;
+ int dpad_cursor_offset;
+ double rs_radius, rs_angle;
+ unsigned numpad_index;
+
+ static const uint32_t retroButtons[2] = {RETRO_DEVICE_ID_MOUSE_LEFT, RETRO_DEVICE_ID_MOUSE_RIGHT};
+ static const Common::EventType eventID[2][2] = {{Common::EVENT_LBUTTONDOWN, Common::EVENT_LBUTTONUP}, {Common::EVENT_RBUTTONDOWN, Common::EVENT_RBUTTONUP}};
+
+ static const unsigned gampad_key_map[8][4] = {
+ {RETRO_DEVICE_ID_JOYPAD_X, (unsigned)Common::KEYCODE_ESCAPE, (unsigned)Common::ASCII_ESCAPE, 0}, // Esc
+ {RETRO_DEVICE_ID_JOYPAD_Y, (unsigned)Common::KEYCODE_PERIOD, 46, 0}, // .
+ {RETRO_DEVICE_ID_JOYPAD_L, (unsigned)Common::KEYCODE_RETURN, (unsigned)Common::ASCII_RETURN, 0}, // Enter
+ {RETRO_DEVICE_ID_JOYPAD_R, (unsigned)Common::KEYCODE_KP5, 53, 0}, // Numpad 5
+ {RETRO_DEVICE_ID_JOYPAD_L2, (unsigned)Common::KEYCODE_BACKSPACE, (unsigned)Common::ASCII_BACKSPACE, 0}, // Backspace
+ {RETRO_DEVICE_ID_JOYPAD_L3, (unsigned)Common::KEYCODE_F10, (unsigned)Common::ASCII_F10, 0}, // F10
+ {RETRO_DEVICE_ID_JOYPAD_R3, (unsigned)Common::KEYCODE_KP0, 48, 0}, // Numpad 0
+ {RETRO_DEVICE_ID_JOYPAD_SELECT, (unsigned)Common::KEYCODE_F7, (unsigned)Common::ASCII_F7, RETROKMOD_CTRL}, // CTRL+F7 (virtual keyboard)
+ };
+
+ // Right stick circular wrap around: 1 -> 2 -> 3 -> 6 -> 9 -> 8 -> 7 -> 4
+ static const unsigned gampad_numpad_map[8][2] = {
+ {(unsigned)Common::KEYCODE_KP1, 49},
+ {(unsigned)Common::KEYCODE_KP2, 50},
+ {(unsigned)Common::KEYCODE_KP3, 51},
+ {(unsigned)Common::KEYCODE_KP6, 54},
+ {(unsigned)Common::KEYCODE_KP9, 57},
+ {(unsigned)Common::KEYCODE_KP8, 56},
+ {(unsigned)Common::KEYCODE_KP7, 55},
+ {(unsigned)Common::KEYCODE_KP4, 52},
+ };
+
+ // Reduce gamepad cursor speed, if required
+ if (device == RETRO_DEVICE_JOYPAD && aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) {
+ adjusted_cursor_speed = adjusted_cursor_speed * (1.0f / 5.0f);
+ }
+
+ status = 0;
+ x = aCallback(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X);
+ y = aCallback(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y);
+ joy_x = aCallback(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
+ joy_y = aCallback(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
+
+ // Left Analog X Axis
+ if (joy_x > analog_deadzone || joy_x < -analog_deadzone) {
+ status |= (STATUS_DOING_JOYSTICK | STATUS_DOING_X);
+ if (joy_x > analog_deadzone) {
+ // Reset accumulator when changing direction
+ _mouseXAcc = (_mouseXAcc < 0.0) ? 0.0 : _mouseXAcc;
+ joy_x = joy_x - analog_deadzone;
+ }
+ if (joy_x < -analog_deadzone) {
+ // Reset accumulator when changing direction
+ _mouseXAcc = (_mouseXAcc > 0.0) ? 0.0 : _mouseXAcc;
+ joy_x = joy_x + analog_deadzone;
+ }
+ // Update accumulator
+ analog_amplitude_x = (float)joy_x / (float)(ANALOG_RANGE - analog_deadzone);
+ if (analog_response_is_quadratic) {
+ if (analog_amplitude_x < 0.0)
+ analog_amplitude_x = -(analog_amplitude_x * analog_amplitude_x);
+ else
+ analog_amplitude_x = analog_amplitude_x * analog_amplitude_x;
+ }
+ // printf("analog_amplitude_x: %f\n", analog_amplitude_x);
+ deltaAcc = analog_amplitude_x * adjusted_cursor_speed;
+ updateMouseXY(deltaAcc, &_mouseXAcc, 1);
+ }
+
+ // Left Analog Y Axis
+ if (joy_y > analog_deadzone || joy_y < -analog_deadzone) {
+ status |= (STATUS_DOING_JOYSTICK | STATUS_DOING_Y);
+ if (joy_y > analog_deadzone) {
+ // Reset accumulator when changing direction
+ _mouseYAcc = (_mouseYAcc < 0.0) ? 0.0 : _mouseYAcc;
+ joy_y = joy_y - analog_deadzone;
+ }
+ if (joy_y < -analog_deadzone) {
+ // Reset accumulator when changing direction
+ _mouseYAcc = (_mouseYAcc > 0.0) ? 0.0 : _mouseYAcc;
+ joy_y = joy_y + analog_deadzone;
+ }
+ // Update accumulator
+ analog_amplitude_y = (float)joy_y / (float)(ANALOG_RANGE - analog_deadzone);
+ if (analog_response_is_quadratic) {
+ if (analog_amplitude_y < 0.0)
+ analog_amplitude_y = -(analog_amplitude_y * analog_amplitude_y);
+ else
+ analog_amplitude_y = analog_amplitude_y * analog_amplitude_y;
+ }
+ // printf("analog_amplitude_y: %f\n", analog_amplitude_y);
+ deltaAcc = analog_amplitude_y * adjusted_cursor_speed;
+ updateMouseXY(deltaAcc, &_mouseYAcc, 0);
+ }
+
+ if (device == RETRO_DEVICE_JOYPAD) {
+ bool dpadLeft = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT);
+ bool dpadRight = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT);
+ bool dpadUp = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP);
+ bool dpadDown = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN);
+
+ if (dpadLeft || dpadRight) {
+ status |= (STATUS_DOING_JOYSTICK | STATUS_DOING_X);
+ _dpadXVel = MIN(_dpadXVel + inverse_acceleration_time, 1.0f);
+
+ if (dpadLeft) {
+ deltaAcc = -(_dpadXVel * adjusted_cursor_speed);
+ _dpadXAcc = _dpadXAcc < deltaAcc ? _dpadXAcc : 0.0f;
+ } else { //dpadRight
+ deltaAcc = _dpadXVel * adjusted_cursor_speed;
+ _dpadXAcc = _dpadXAcc > deltaAcc ? _dpadXAcc : 0.0f;
+ }
+
+ updateMouseXY(deltaAcc, &_dpadXAcc, 1);
+ } else {
+ _dpadXVel = 0.0f;
+ }
+
+
+ if (dpadUp || dpadDown) {
+ status |= (STATUS_DOING_JOYSTICK | STATUS_DOING_Y);
+ _dpadYVel = MIN(_dpadYVel + inverse_acceleration_time, 1.0f);
+
+ if (dpadUp) {
+ deltaAcc = -(_dpadYVel * adjusted_cursor_speed);
+ _dpadYAcc = _dpadYAcc < deltaAcc ? _dpadYAcc : 0.0f;
+ } else { //dpadDown
+ deltaAcc = _dpadYVel * adjusted_cursor_speed;
+ _dpadYAcc = _dpadYAcc > deltaAcc ? _dpadYAcc : 0.0f;
+ }
+
+ updateMouseXY(deltaAcc, &_dpadYAcc, 0);
+
+
+ } else {
+ _dpadYVel = 0.0f;
+ }
+
+ if (aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) {
+ Common::Event ev;
+ ev.type = Common::EVENT_MAINMENU;
+ _events.push_back(ev);
+ }
+ }
+
+#if defined(WIIU) || defined(__SWITCH__)
+ int p_x = aCallback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
+ int p_y = aCallback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
+ int p_press = aCallback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED);
+ int px = (int)((p_x + 0x7fff) * _screen.w / 0xffff);
+ int py = (int)((p_y + 0x7fff) * _screen.h / 0xffff);
+ // printf("(%d,%d) p:%d\n",px,py,pp);
+
+ static int ptrhold = 0;
+
+ if (p_press)
+ ptrhold++;
+ else
+ ptrhold = 0;
+
+ if (ptrhold > 0) {
+ _mouseX = px;
+ _mouseY = py;
+
+ Common::Event ev;
+ ev.type = Common::EVENT_MOUSEMOVE;
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ }
+
+ if (ptrhold > 10 && _ptrmouseButton == 0) {
+ _ptrmouseButton = 1;
+ Common::Event ev;
+ ev.type = eventID[0][_ptrmouseButton ? 0 : 1];
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ } else if (ptrhold == 0 && _ptrmouseButton == 1) {
+ _ptrmouseButton = 0;
+ Common::Event ev;
+ ev.type = eventID[0][_ptrmouseButton ? 0 : 1];
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ }
+
+#endif
+
+ if (status & STATUS_DOING_JOYSTICK) {
+ Common::Event ev;
+ ev.type = Common::EVENT_MOUSEMOVE;
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ ev.relMouse.x = status & STATUS_DOING_X ? _relMouseX : 0;
+ ev.relMouse.y = status & STATUS_DOING_Y ? _relMouseY : 0;
+ _events.push_back(ev);
+ }
+
+ // Gampad mouse buttons
+ down = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A);
+ if (down != _joypadmouseButtons[0]) {
+ _joypadmouseButtons[0] = down;
+
+ Common::Event ev;
+ ev.type = eventID[0][down ? 0 : 1];
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ }
+
+ down = aCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B);
+ if (down != _joypadmouseButtons[1]) {
+ _joypadmouseButtons[1] = down;
+
+ Common::Event ev;
+ ev.type = eventID[1][down ? 0 : 1];
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ }
+
+ // Gamepad keyboard buttons
+ for (int i = 0; i < 8; i++) {
+ down = aCallback(0, RETRO_DEVICE_JOYPAD, 0, gampad_key_map[i][0]);
+ if (down != _joypadkeyboardButtons[i]) {
+ _joypadkeyboardButtons[i] = down;
+ bool state = down ? true : false;
+ processKeyEvent(state, gampad_key_map[i][1], (uint32_t)gampad_key_map[i][2], (uint32_t)gampad_key_map[i][3]);
+ }
+ }
+
+ // Gamepad right stick numpad emulation
+ joy_rx = aCallback(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X);
+ joy_ry = aCallback(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
+
+ if (joy_rx > analog_deadzone)
+ joy_rx = joy_rx - analog_deadzone;
+ else if (joy_rx < -analog_deadzone)
+ joy_rx = joy_rx + analog_deadzone;
+ else
+ joy_rx = 0;
+
+ if (joy_ry > analog_deadzone)
+ joy_ry = joy_ry - analog_deadzone;
+ else if (joy_ry < -analog_deadzone)
+ joy_ry = joy_ry + analog_deadzone;
+ else
+ joy_ry = 0;
+
+ // This is very ugly, but I don't have time to make it nicer...
+ if (joy_rx != 0 || joy_ry != 0) {
+ analog_amplitude_x = (float)joy_rx / (float)(ANALOG_RANGE - analog_deadzone);
+ analog_amplitude_y = (float)joy_ry / (float)(ANALOG_RANGE - analog_deadzone);
+
+ // Convert to polar coordinates: part 1
+ rs_radius = sqrt((double)(analog_amplitude_x * analog_amplitude_x) + (double)(analog_amplitude_y * analog_amplitude_y));
+
+ // Check if radius is above threshold
+ if (rs_radius > 0.5) {
+ // Convert to polar coordinates: part 2
+ rs_angle = atan2((double)analog_amplitude_y, (double)analog_amplitude_x);
+
+ // Adjust rotation offset...
+ rs_angle = (2.0 * PI) - (rs_angle + PI);
+ rs_angle = fmod(rs_angle - (0.125 * PI), 2.0 * PI);
+ if (rs_angle < 0)
+ rs_angle += 2.0 * PI;
+
+ // Convert angle into numpad key index
+ numpad_index = (unsigned)((rs_angle / (2.0 * PI)) * 8.0);
+ // Unnecessary safety check...
+ numpad_index = (numpad_index > 7) ? 7 : numpad_index;
+ // printf("numpad_index: %u\n", numpad_index);
+
+ if (numpad_index != _joypadnumpadLast) {
+ // Unset last key, if required
+ if (_joypadnumpadActive)
+ processKeyEvent(false, gampad_numpad_map[_joypadnumpadLast][0], (uint32_t)gampad_numpad_map[_joypadnumpadLast][1], 0);
+
+ // Set new key
+ processKeyEvent(true, gampad_numpad_map[numpad_index][0], (uint32_t)gampad_numpad_map[numpad_index][1], 0);
+
+ _joypadnumpadLast = numpad_index;
+ _joypadnumpadActive = true;
+ }
+ } else if (_joypadnumpadActive) {
+ processKeyEvent(false, gampad_numpad_map[_joypadnumpadLast][0], (uint32_t)gampad_numpad_map[_joypadnumpadLast][1], 0);
+ _joypadnumpadActive = false;
+ _joypadnumpadLast = 8;
+ }
+ } else if (_joypadnumpadActive) {
+ processKeyEvent(false, gampad_numpad_map[_joypadnumpadLast][0], (uint32_t)gampad_numpad_map[_joypadnumpadLast][1], 0);
+ _joypadnumpadActive = false;
+ _joypadnumpadLast = 8;
+ }
+
+ // Process input from physical mouse
+ // > X Axis
+ if (x != 0) {
+ status |= (STATUS_DOING_MOUSE | STATUS_DOING_X);
+ if (x > 0) {
+ // Reset accumulator when changing direction
+ _mouseXAcc = (_mouseXAcc < 0.0) ? 0.0 : _mouseXAcc;
+ }
+ if (x < 0) {
+ // Reset accumulator when changing direction
+ _mouseXAcc = (_mouseXAcc > 0.0) ? 0.0 : _mouseXAcc;
+ }
+ deltaAcc = (float)x * mouse_speed;
+ updateMouseXY(deltaAcc, &_mouseXAcc, 1);
+ }
+ // > Y Axis
+ if (y != 0) {
+ status |= (STATUS_DOING_MOUSE | STATUS_DOING_Y);
+ if (y > 0) {
+ // Reset accumulator when changing direction
+ _mouseYAcc = (_mouseYAcc < 0.0) ? 0.0 : _mouseYAcc;
+ }
+ if (y < 0) {
+ // Reset accumulator when changing direction
+ _mouseYAcc = (_mouseYAcc > 0.0) ? 0.0 : _mouseYAcc;
+ }
+ deltaAcc = (float)y * mouse_speed;
+ updateMouseXY(deltaAcc, &_mouseYAcc, 0);
+ }
+
+ if (status & STATUS_DOING_MOUSE) {
+ Common::Event ev;
+ ev.type = Common::EVENT_MOUSEMOVE;
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ ev.relMouse.x = status & STATUS_DOING_X ? _relMouseX : 0;
+ ev.relMouse.y = status & STATUS_DOING_Y ? _relMouseY : 0;
+ _events.push_back(ev);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ Common::Event ev;
+ bool down = aCallback(0, RETRO_DEVICE_MOUSE, 0, retroButtons[i]);
+ if (down != _mouseButtons[i]) {
+ _mouseButtons[i] = down;
+
+ ev.type = eventID[i][down ? 0 : 1];
+ ev.mouse.x = _mouseX;
+ ev.mouse.y = _mouseY;
+ _events.push_back(ev);
+ }
+ }
+ }
+
+ void processKeyEvent(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers) {
+ int _keyflags = 0;
+ _keyflags |= (key_modifiers & RETROKMOD_CTRL) ? Common::KBD_CTRL : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_ALT) ? Common::KBD_ALT : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_SHIFT) ? Common::KBD_SHIFT : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_META) ? Common::KBD_META : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_CAPSLOCK) ? Common::KBD_CAPS : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_NUMLOCK) ? Common::KBD_NUM : 0;
+ _keyflags |= (key_modifiers & RETROKMOD_SCROLLOCK) ? Common::KBD_SCRL : 0;
+
+ Common::Event ev;
+ ev.type = down ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
+ ev.kbd.keycode = (Common::KeyCode)keycode;
+ ev.kbd.flags = _keyflags;
+ ev.kbd.ascii = keycode;
+
+ /* If shift was down then send upper case letter to engine */
+ if (ev.kbd.ascii >= 97 && ev.kbd.ascii <= 122 && (_keyflags & Common::KBD_SHIFT))
+ ev.kbd.ascii = ev.kbd.ascii & ~0x20;
+
+ _events.push_back(ev);
+ }
+
+ bool parseGameName(const Common::String &gameName, Common::String &engineId,
+ Common::String &gameId) {
+ Common::StringTokenizer tokenizer(gameName, ":");
+ Common::String token1, token2;
+
+ if (!tokenizer.empty()) {
+ token1 = tokenizer.nextToken();
+ }
+
+ if (!tokenizer.empty()) {
+ token2 = tokenizer.nextToken();
+ }
+
+ if (!tokenizer.empty()) {
+ return false; // Stray colon
+ }
+
+ if (!token1.empty() && !token2.empty()) {
+ engineId = token1;
+ gameId = token2;
+ return true;
+ } else if (!token1.empty()) {
+ engineId.clear();
+ gameId = token1;
+ return true;
+ }
+
+ return false;
+ }
+
+ int TestGame(const char *filedata, bool autodetect) {
+ Common::String game_id;
+ Common::String engine_id;
+ Common::String data = filedata;
+ int res = TEST_GAME_KO_NOT_FOUND;
+
+ PluginManager::instance().init();
+ PluginManager::instance().loadAllPlugins();
+ PluginManager::instance().loadDetectionPlugin();
+
+ if (autodetect) {
+ Common::FSNode dir(data);
+ Common::FSList files;
+ dir.getChildren(files, Common::FSNode::kListAll);
+
+ DetectionResults detectionResults = EngineMan.detectGames(files);
+ if (!detectionResults.listRecognizedGames().empty()) {
+ res = TEST_GAME_OK_ID_AUTODETECTED;
+ }
+
+ } else {
+
+ ConfMan.loadDefaultConfigFile(getDefaultConfigFileName().c_str());
+ if (ConfMan.hasGameDomain(data)) {
+ res = TEST_GAME_OK_TARGET_FOUND;
+ } else {
+ parseGameName(data, engine_id, game_id);
+
+ QualifiedGameList games = EngineMan.findGamesMatching(engine_id, game_id);
+ if (games.size() == 1) {
+ res = TEST_GAME_OK_ID_FOUND;
+ } else if (games.size() > 1) {
+ res = TEST_GAME_KO_MULTIPLE_RESULTS;
+ }
+ }
+ }
+
+ PluginManager::instance().unloadDetectionPlugin();
+ PluginManager::instance().unloadAllPlugins();
+ PluginManager::destroy();
+ return res;
+ }
+
+ void Quit() {
+ Common::Event ev;
+ ev.type = Common::EVENT_QUIT;
+ dynamic_cast<OSystem_RETRO *>(g_system)->getEventManager()->pushEvent(ev);
+ }
+
+ void Reset() {
+ Common::Event ev;
+ ev.type = Common::EVENT_RETURN_TO_LAUNCHER;
+ dynamic_cast<OSystem_RETRO *>(g_system)->getEventManager()->pushEvent(ev);
+ }
+
+};
+
+OSystem *retroBuildOS(bool aEnableSpeedHack) {
+ return new OSystem_RETRO(aEnableSpeedHack);
+}
+
+const Graphics::Surface &getScreen() {
+ return dynamic_cast<OSystem_RETRO *>(g_system)->getScreen();
+}
+
+void retroProcessMouse(retro_input_state_t aCallback, int device, float gamepad_cursor_speed, float gamepad_acceleration_time, bool analog_response_is_quadratic, int analog_deadzone, float mouse_speed) {
+ dynamic_cast<OSystem_RETRO *>(g_system)->processMouse(aCallback, device, gamepad_cursor_speed, gamepad_acceleration_time, analog_response_is_quadratic, analog_deadzone, mouse_speed);
+}
+
+void retroQuit() {
+ dynamic_cast<OSystem_RETRO *>(g_system)->Quit();
+}
+
+int retroTestGame(const char *game_id, bool autodetect) {
+ return dynamic_cast<OSystem_RETRO *>(g_system)->TestGame(game_id, autodetect);
+}
+
+void retroSetSystemDir(const char *aPath) {
+ s_systemDir = Common::String(aPath ? aPath : ".");
+}
+
+void retroSetSaveDir(const char *aPath) {
+ s_saveDir = Common::String(aPath ? aPath : ".");
+}
+
+void retroKeyEvent(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers) {
+ dynamic_cast<OSystem_RETRO *>(g_system)->processKeyEvent(down, keycode, character, key_modifiers);
+}
+
+void retroReset() {
+ dynamic_cast<OSystem_RETRO *>(g_system)->Reset();
+}
diff --git a/backends/platform/libretro/src/libretro-threads.cpp b/backends/platform/libretro/src/libretro-threads.cpp
new file mode 100644
index 00000000000..e4966b137e9
--- /dev/null
+++ b/backends/platform/libretro/src/libretro-threads.cpp
@@ -0,0 +1,174 @@
+/* Copyright (C) 2022 Giovanni Cascione <ing.cascione at gmail.com>
+*
+* This program 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 Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include "backends/platform/libretro/include/libretro-threads.h"
+#include "backends/platform/libretro/include/os.h"
+#include "base/main.h"
+
+#define EMU_WAITING (1 << 0)
+#define MAIN_WAITING (1 << 1)
+#define EMU_EXITED (1 << 2)
+static uint8_t status = EMU_WAITING | MAIN_WAITING;
+
+#ifdef USE_LIBCO
+#include <libco.h>
+static cothread_t main_thread;
+static cothread_t emu_thread;
+#else
+#include <rthreads/rthreads.h>
+static uintptr_t main_thread_id;
+static sthread_t *emu_thread;
+static slock_t *emu_lock;
+static slock_t *main_lock;
+static scond_t *emu_cond;
+static scond_t *main_cond;
+#endif
+
+static bool retro_current_thread_is_main() {
+#ifdef USE_LIBCO
+ return (co_active() == main_thread);
+#else
+ return (sthread_get_current_thread_id() == main_thread_id);
+#endif
+}
+
+static void retro_exit_to_main_thread() {
+#ifdef USE_LIBCO
+ co_switch(main_thread);
+#else
+ slock_lock(main_lock);
+ status &= ~MAIN_WAITING;
+ slock_unlock(main_lock);
+ slock_lock(emu_lock);
+ scond_signal(main_cond);
+
+ status |= EMU_WAITING;
+ while (status & EMU_WAITING) {
+ scond_wait(emu_cond, emu_lock);
+ }
+ slock_unlock(emu_lock);
+#endif
+}
+
+static int retro_run_emulator(void) {
+ static const char *argv[20] = {0};
+ for (int i = 0; i < cmd_params_num; i++)
+ argv[i] = cmd_params[i];
+
+ return scummvm_main(cmd_params_num, argv);
+}
+
+static void retro_wrap_emulator(void) {
+
+ status &= ~EMU_EXITED;
+ retro_run_emulator();
+ status |= EMU_EXITED;
+
+ retro_exit_to_main_thread();
+}
+
+static void retro_wrap_emulator(void *args) {
+ retro_wrap_emulator();
+}
+
+static void retro_free_emu_thread() {
+#ifdef USE_LIBCO
+ if (emu_thread)
+ co_delete(emu_thread);
+#else
+ if (main_lock)
+ slock_free(main_lock);
+ if (emu_lock)
+ slock_free(emu_lock);
+ if (main_cond)
+ scond_free(main_cond);
+ if (emu_cond)
+ scond_free(emu_cond);
+#endif
+ emu_thread = NULL;
+}
+
+void retro_switch_to_emu_thread() {
+#ifdef USE_LIBCO
+ co_switch(emu_thread);
+#else
+ slock_lock(emu_lock);
+ status &= ~EMU_WAITING;
+ slock_unlock(emu_lock);
+ slock_lock(main_lock);
+ scond_signal(emu_cond);
+
+ status |= MAIN_WAITING;
+ while (status & MAIN_WAITING) {
+ scond_wait(main_cond, main_lock);
+ }
+ slock_unlock(main_lock);
+#endif
+}
+
+void retro_switch_to_main_thread() {
+ retro_exit_to_main_thread();
+#ifndef USE_LIBCO
+ status |= EMU_WAITING;
+ while (status & EMU_WAITING) {
+ scond_wait(emu_cond, emu_lock);
+ }
+ slock_unlock(emu_lock);
+#endif
+}
+
+bool retro_emu_thread_initialized() {
+ return (bool)emu_thread;
+}
+
+bool retro_emu_thread_exited() {
+ return (bool)(status & EMU_EXITED);
+}
+
+bool retro_init_emu_thread(void) {
+ if (retro_emu_thread_initialized())
+ return true;
+ bool success = true;
+#ifdef USE_LIBCO
+ main_thread = co_active();
+ emu_thread = co_create(65536 * sizeof(void *), retro_wrap_emulator);
+ if (!emu_thread)
+#else
+ main_thread_id = sthread_get_current_thread_id();
+ main_lock = slock_new();
+ emu_lock = slock_new();
+ main_cond = scond_new();
+ emu_cond = scond_new();
+ emu_thread = sthread_create(retro_wrap_emulator, NULL);
+
+ if (!main_lock || !emu_lock || !main_cond || !emu_cond || !emu_thread)
+#endif
+ success = false;
+
+ if (!success)
+ retro_free_emu_thread();
+
+ return success;
+}
+
+void retro_deinit_emu_thread() {
+ if (!retro_emu_thread_initialized())
+ return;
+ if (!retro_current_thread_is_main())
+ retro_switch_to_main_thread();
+ retro_free_emu_thread();
+}
diff --git a/backends/platform/libretro/src/libretro.cpp b/backends/platform/libretro/src/libretro.cpp
new file mode 100644
index 00000000000..39d835cec67
--- /dev/null
+++ b/backends/platform/libretro/src/libretro.cpp
@@ -0,0 +1,559 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <libretro.h>
+#include "audio/mixer_intern.h"
+#include "base/main.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/fs.h"
+#include "streams/file_stream.h"
+#include "graphics/surface.h"
+#ifdef _WIN32
+#include <direct.h>
+#else
+#include <unistd.h>
+#endif
+#ifndef _MSC_VER
+/**
+ * Include libgen.h for basename() and dirname().
+ * @see http://linux.die.net/man/3/basename
+ */
+#include <libgen.h>
+#endif
+
+/**
+ * Include base/internal_version.h to allow access to SCUMMVM_VERSION.
+ * @see retro_get_system_info()
+ */
+#define INCLUDED_FROM_BASE_VERSION_CPP
+#include "base/internal_version.h"
+
+#include "backends/platform/libretro/include/libretro-threads.h"
+#include "backends/platform/libretro/include/libretro-core-options.h"
+#include "backends/platform/libretro/include/os.h"
+
+retro_log_printf_t log_cb = NULL;
+static retro_video_refresh_t video_cb = NULL;
+static retro_audio_sample_batch_t audio_batch_cb = NULL;
+static retro_environment_t environ_cb = NULL;
+static retro_input_poll_t poll_cb = NULL;
+static retro_input_state_t input_cb = NULL;
+
+void retro_set_video_refresh(retro_video_refresh_t cb) {
+ video_cb = cb;
+}
+void retro_set_audio_sample(retro_audio_sample_t cb) {}
+void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
+ audio_batch_cb = cb;
+}
+void retro_set_input_poll(retro_input_poll_t cb) {
+ poll_cb = cb;
+}
+void retro_set_input_state(retro_input_state_t cb) {
+ input_cb = cb;
+}
+
+// System analog stick range is -0x8000 to 0x8000
+#define ANALOG_RANGE 0x8000
+// Default deadzone: 15%
+static int analog_deadzone = (int)(0.15f * ANALOG_RANGE);
+
+static float gampad_cursor_speed = 1.0f;
+static bool analog_response_is_quadratic = false;
+
+static float mouse_speed = 1.0f;
+static float gamepad_acceleration_time = 0.2f;
+
+static bool speed_hack_is_enabled = false;
+
+char cmd_params[20][200];
+char cmd_params_num;
+
+int adjusted_RES_W = 0;
+int adjusted_RES_H = 0;
+
+void retro_set_environment(retro_environment_t cb) {
+ environ_cb = cb;
+ bool tmp = true;
+
+ environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &tmp);
+ libretro_set_core_options(environ_cb);
+}
+
+unsigned retro_api_version(void) {
+ return RETRO_API_VERSION;
+}
+
+void retro_get_system_info(struct retro_system_info *info) {
+ info->library_name = CORE_NAME;
+#if defined GIT_TAG
+#define __GIT_VERSION GIT_TAG
+#elif defined GIT_HASH
+#define __GIT_VERSION GIT_HASH "-" SCUMMVM_VERSION
+#else
+#define __GIT_VERSION ""
+#endif
+ info->library_version = __GIT_VERSION;
+ info->valid_extensions = "scummvm";
+ info->need_fullpath = true;
+ info->block_extract = false;
+}
+
+void retro_get_system_av_info(struct retro_system_av_info *info) {
+ info->geometry.base_width = RES_W;
+ info->geometry.base_height = RES_H;
+ info->geometry.max_width = RES_W;
+ info->geometry.max_height = RES_H;
+ info->geometry.aspect_ratio = 4.0f / 3.0f;
+ info->timing.fps = 60.0;
+ info->timing.sample_rate = 48000.0;
+}
+
+void retro_init(void) {
+ struct retro_log_callback log;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
+ log_cb = log.log;
+ else
+ log_cb = NULL;
+}
+
+void retro_deinit(void) {}
+
+void parse_command_params(char *cmdline) {
+ int j = 0;
+ int cmdlen = strlen(cmdline);
+ bool quotes = false;
+
+ /* Append a new line to the end of the command to signify it's finished. */
+ cmdline[cmdlen] = '\n';
+ cmdline[++cmdlen] = '\0';
+
+ /* parse command line into array of arguments */
+ for (int i = 0; i < cmdlen; i++) {
+ switch (cmdline[i]) {
+ case '\"':
+ if (quotes) {
+ cmdline[i] = '\0';
+ strcpy(cmd_params[cmd_params_num], cmdline + j);
+ cmd_params_num++;
+ quotes = false;
+ } else
+ quotes = true;
+ j = i + 1;
+ break;
+ case ' ':
+ case '\n':
+ if (!quotes) {
+ if (i != j && !quotes) {
+ cmdline[i] = '\0';
+ strcpy(cmd_params[cmd_params_num], cmdline + j);
+ cmd_params_num++;
+ }
+ j = i + 1;
+ }
+ break;
+ }
+ }
+}
+
+#if defined(WIIU) || defined(__SWITCH__) || defined(_MSC_VER) || defined(_3DS)
+#include <stdio.h>
+#include <string.h>
+char *dirname(char *path) {
+ char *p;
+ if (path == NULL || *path == '\0')
+ return ".";
+ p = path + strlen(path) - 1;
+ while (*p == '/') {
+ if (p == path)
+ return path;
+ *p-- = '\0';
+ }
+ while (p >= path && *p != '/')
+ p--;
+ return p < path ? "." : p == path ? "/" : (*p = '\0', path);
+}
+#endif
+
+static void update_variables(void) {
+ struct retro_variable var;
+
+ var.key = "scummvm_gamepad_cursor_speed";
+ var.value = NULL;
+ gampad_cursor_speed = 1.0f;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ gampad_cursor_speed = (float)atof(var.value);
+ }
+
+ var.key = "scummvm_gamepad_cursor_acceleration_time";
+ var.value = NULL;
+ gamepad_acceleration_time = 0.2f;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ gamepad_acceleration_time = (float)atof(var.value);
+ }
+
+ var.key = "scummvm_analog_response";
+ var.value = NULL;
+ analog_response_is_quadratic = false;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ if (strcmp(var.value, "quadratic") == 0)
+ analog_response_is_quadratic = true;
+ }
+
+ var.key = "scummvm_analog_deadzone";
+ var.value = NULL;
+ analog_deadzone = (int)(0.15f * ANALOG_RANGE);
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ analog_deadzone = (int)(atoi(var.value) * 0.01f * ANALOG_RANGE);
+ }
+
+ var.key = "scummvm_mouse_speed";
+ var.value = NULL;
+ mouse_speed = 1.0f;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ mouse_speed = (float)atof(var.value);
+ }
+
+ var.key = "scummvm_speed_hack";
+ var.value = NULL;
+ speed_hack_is_enabled = false;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
+ if (strcmp(var.value, "enabled") == 0)
+ speed_hack_is_enabled = true;
+ }
+}
+
+static int retro_device = RETRO_DEVICE_JOYPAD;
+void retro_set_controller_port_device(unsigned port, unsigned device) {
+ if (port != 0) {
+ if (log_cb)
+ log_cb(RETRO_LOG_WARN, "Invalid controller port %d.\n", port);
+ return;
+ }
+
+ switch (device) {
+ case RETRO_DEVICE_JOYPAD:
+ case RETRO_DEVICE_MOUSE:
+ retro_device = device;
+ break;
+ default:
+ if (log_cb)
+ log_cb(RETRO_LOG_WARN, "Invalid controller device class %d.\n", device);
+ break;
+ }
+}
+
+bool retro_load_game(const struct retro_game_info *game) {
+ const char *sysdir;
+ const char *savedir;
+
+ update_variables();
+
+ cmd_params_num = 1;
+ strcpy(cmd_params[0], "scummvm\0");
+
+ struct retro_input_descriptor desc[] = {
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Mouse Cursor Left"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Mouse Cursor Up"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Mouse Cursor Down"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Mouse Cursor Right"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Right Mouse Button"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Left Mouse Button"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Esc"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "."},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Enter"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Numpad 5"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Backspace"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Cursor Fine Control"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "F10"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Numpad 0"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "ScummVM GUI"},
+ {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Virtual Keyboard"},
+ {0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT, "Left click"},
+ {0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT, "Right click"},
+ {0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X"},
+ {0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y"},
+ {0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X"},
+ {0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y"},
+ {0},
+ };
+
+ environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
+
+ /* Get color mode: 32 first as VGA has 6 bits per pixel */
+#if 0
+ RDOSGFXcolorMode = RETRO_PIXEL_FORMAT_XRGB8888;
+ if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &RDOSGFXcolorMode)) {
+ RDOSGFXcolorMode = RETRO_PIXEL_FORMAT_RGB565;
+ if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &RDOSGFXcolorMode))
+ RDOSGFXcolorMode = RETRO_PIXEL_FORMAT_0RGB1555;
+ }
+#endif
+
+#ifdef FRONTEND_SUPPORTS_RGB565
+ enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
+ if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565) && log_cb)
+ log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 -will use that instead of XRGB1555.\n");
+#endif
+
+ retro_keyboard_callback cb = {retroKeyEvent};
+ environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &cb);
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysdir))
+ retroSetSystemDir(sysdir);
+ else {
+ if (log_cb)
+ log_cb(RETRO_LOG_WARN, "No System directory specified, using current directory.\n");
+ retroSetSystemDir(".");
+ }
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &savedir))
+ retroSetSaveDir(savedir);
+ else {
+ if (log_cb)
+ log_cb(RETRO_LOG_WARN, "No Save directory specified, using current directory.\n");
+ retroSetSaveDir(".");
+ }
+
+ g_system = retroBuildOS(speed_hack_is_enabled);
+
+ if (!g_system) {
+ if (log_cb)
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Failed to initialize platform driver.\n");
+ return false;
+ }
+
+ if (game) {
+ // Retrieve the game path.
+ Common::FSNode detect_target = Common::FSNode(game->path);
+ Common::FSNode parent_dir = detect_target.getParent();
+ char target_id[400] = {0};
+ char buffer[400];
+ int test_game_status = TEST_GAME_KO_NOT_FOUND;
+
+ struct retro_message_ext retro_msg;
+ retro_msg.type = RETRO_MESSAGE_TYPE_NOTIFICATION;
+ retro_msg.target = RETRO_MESSAGE_TARGET_OSD;
+ retro_msg.duration = 3000;
+ retro_msg.msg = "";
+
+ const char * target_file_ext = ".scummvm";
+ int target_file_ext_pos = strlen(game->path) - strlen(target_file_ext);
+
+ // See if we are loading a .scummvm file.
+ if (!(target_file_ext_pos < 0) && strstr(game->path + target_file_ext_pos, target_file_ext) != NULL) {
+ // Open the file.
+ RFILE *gamefile = filestream_open(game->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
+ if (!gamefile) {
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Failed to load given game file '%s'.\n", game->path);
+ return false;
+ }
+
+ // Load the file data.
+ if (filestream_gets(gamefile, target_id, sizeof(target_id)) == NULL) {
+ filestream_close(gamefile);
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Failed to load contents of game file '%s'.\n", game->path);
+ return false;
+ }
+ filestream_close(gamefile);
+
+ Common::String tmp = target_id;
+ tmp.trim();
+ strcpy(target_id, tmp.c_str());
+
+ if (strlen(target_id) == 0) {
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Game file '%s' does not contain any target id.\n", game->path);
+ return false;
+ }
+
+ test_game_status = retroTestGame(target_id, false);
+ } else {
+ if (detect_target.isDirectory()) {
+ parent_dir = detect_target;
+ } else {
+ // If this node has no parent node, then it returns a duplicate of this node.
+ if (detect_target.getPath().equals(parent_dir.getPath())) {
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Autodetect not possible. No parent directory detected in '%s'.\n", game->path);
+ return false;
+ }
+ }
+
+ test_game_status = retroTestGame(parent_dir.getPath().c_str(), true);
+ }
+
+ // Preliminary game scan results
+ switch (test_game_status) {
+ case TEST_GAME_OK_ID_FOUND:
+ sprintf(buffer, "-p \"%s\" %s", parent_dir.getPath().c_str(), target_id);
+ log_cb(RETRO_LOG_DEBUG, "[scummvm] launch via target id and game dir\n");
+ break;
+ case TEST_GAME_OK_TARGET_FOUND:
+ sprintf(buffer, "%s", target_id);
+ log_cb(RETRO_LOG_DEBUG, "[scummvm] launch via target id and scummvm.ini\n");
+ break;
+ case TEST_GAME_OK_ID_AUTODETECTED:
+ sprintf(buffer, "-p \"%s\" --auto-detect", parent_dir.getPath().c_str());
+ log_cb(RETRO_LOG_DEBUG, "[scummvm] launch via autodetect\n");
+ break;
+ case TEST_GAME_KO_MULTIPLE_RESULTS:
+ log_cb(RETRO_LOG_WARN, "[scummvm] Multiple targets found for '%s' in scummvm.ini\n", target_id);
+ retro_msg.msg = "Multiple targets found";
+ break;
+ case TEST_GAME_KO_NOT_FOUND:
+ default:
+ log_cb(RETRO_LOG_WARN, "[scummvm] Game not found. Check path and content of '%s'\n", game->path);
+ retro_msg.msg = "Game not found";
+ }
+
+ if (retro_msg.msg[0] != '\0') {
+ environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &retro_msg);
+ } else {
+ parse_command_params(buffer);
+ }
+ }
+
+ if (!retro_init_emu_thread()) {
+ if (log_cb)
+ log_cb(RETRO_LOG_ERROR, "[scummvm] Failed to initialize emulation thread!\n");
+ return false;
+ }
+ return true;
+}
+
+bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) {
+ return false;
+}
+
+void retro_run(void) {
+ if (retro_emu_thread_exited())
+ retro_deinit_emu_thread();
+
+ if (!retro_emu_thread_initialized()) {
+ environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
+ return;
+ }
+
+ bool updated = false;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
+ update_variables();
+
+ retro_switch_to_emu_thread();
+ /* Mouse */
+ if (g_system) {
+ poll_cb();
+ retroProcessMouse(input_cb, retro_device, gampad_cursor_speed, gamepad_acceleration_time, analog_response_is_quadratic, analog_deadzone, mouse_speed);
+
+ /* Upload video: TODO: Check the CANDUPE env value */
+ const Graphics::Surface &screen = getScreen();
+
+ video_cb(screen.getPixels(), screen.w, screen.h, screen.pitch);
+
+ /* Upload audio */
+ static uint32 buf[800];
+ int count = ((Audio::MixerImpl *)g_system->getMixer())->mixCallback((byte *)buf, 800 * 4);
+
+#if defined(_3DS)
+ /* Hack: 3DS will produce static noise
+ * unless we manually send a zeroed
+ * audio buffer when no samples are
+ * available (i.e. when the overlay
+ * is shown) */
+ if (count == 0) {
+ memset(buf, 0, 735 * sizeof(uint32));
+ audio_batch_cb((int16_t *)buf, 735);
+ } else
+#endif
+ audio_batch_cb((int16_t *)buf, count);
+ }
+}
+
+void retro_unload_game(void) {
+ if (!retro_emu_thread_initialized())
+ return;
+ while (!retro_emu_thread_exited()) {
+ retroQuit();
+ retro_switch_to_emu_thread();
+ }
+ retro_deinit_emu_thread();
+ // g_system->destroy(); //TODO: This call causes "pure virtual method called" after frontend "Unloading core symbols". Check if needed at all.
+}
+
+void retro_reset(void) {
+ retroReset();
+}
+
+// Stubs
+void *retro_get_memory_data(unsigned type) {
+ return 0;
+}
+size_t retro_get_memory_size(unsigned type) {
+ return 0;
+}
+size_t retro_serialize_size(void) {
+ return 0;
+}
+bool retro_serialize(void *data, size_t size) {
+ return false;
+}
+bool retro_unserialize(const void *data, size_t size) {
+ return false;
+}
+void retro_cheat_reset(void) {}
+void retro_cheat_set(unsigned unused, bool unused1, const char *unused2) {}
+
+unsigned retro_get_region(void) {
+ return RETRO_REGION_NTSC;
+}
+
+#if (defined(GEKKO) && !defined(WIIU)) || defined(__CELLOS_LV2__)
+int access(const char *path, int amode) {
+ RFILE *f;
+ int mode;
+
+ switch (amode) {
+ // we don't really care if a file exists but isn't readable
+ case F_OK:
+ case R_OK:
+ mode = RETRO_VFS_FILE_ACCESS_READ;
+ break;
+
+ case W_OK:
+ mode = RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
+ break;
+
+ default:
+ return -1;
+ }
+
+ f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
+
+ if (f) {
+ filestream_close(f);
+ return 0;
+ }
+
+ return -1;
+}
+#endif
+
More information about the Scummvm-git-logs
mailing list