├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── etc ├── CoreFsm.dot └── CoreFsm.fsm ├── examples └── sdl2 │ ├── Audio.cpp │ ├── Audio.h │ ├── Config.cpp │ ├── Config.h │ ├── DynLib.h │ ├── Entry.c │ ├── Input.cpp │ ├── Input.h │ ├── Logger.cpp │ ├── Logger.h │ ├── Main.cpp │ ├── Makefile │ ├── Perf.cpp │ ├── Perf.h │ ├── Player.cpp │ ├── Player.h │ ├── Video.cpp │ ├── Video.h │ └── sdl2lrcpp.cfg ├── include └── lrcpp │ ├── Components.h │ ├── Core.h │ ├── CoreFsm.h │ ├── Frontend.h │ └── libretro.h └── src ├── Components.cpp ├── CoreFsm.cpp └── Frontend.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | examples/sdl2/sdl2lrcpp 3 | .gradle/ 4 | build/ 5 | examples/sdl2/android/app/src/main/assets/ 6 | 7 | # Created by https://www.toptal.com/developers/gitignore/api/c,c++ 8 | # Edit at https://www.toptal.com/developers/gitignore?templates=c,c++ 9 | 10 | ### C ### 11 | # Prerequisites 12 | *.d 13 | 14 | # Object files 15 | *.o 16 | *.ko 17 | *.obj 18 | *.elf 19 | 20 | # Linker output 21 | *.ilk 22 | *.map 23 | *.exp 24 | 25 | # Precompiled Headers 26 | *.gch 27 | *.pch 28 | 29 | # Libraries 30 | *.lib 31 | *.a 32 | *.la 33 | *.lo 34 | 35 | # Shared objects (inc. Windows DLLs) 36 | *.dll 37 | *.so 38 | *.so.* 39 | *.dylib 40 | 41 | # Executables 42 | *.exe 43 | *.out 44 | *.app 45 | *.i*86 46 | *.x86_64 47 | *.hex 48 | 49 | # Debug files 50 | *.dSYM/ 51 | *.su 52 | *.idb 53 | *.pdb 54 | 55 | # Kernel Module Compile Results 56 | *.mod* 57 | *.cmd 58 | .tmp_versions/ 59 | modules.order 60 | Module.symvers 61 | Mkfile.old 62 | dkms.conf 63 | 64 | ### C++ ### 65 | # Prerequisites 66 | 67 | # Compiled Object files 68 | *.slo 69 | 70 | # Precompiled Headers 71 | 72 | # Compiled Dynamic libraries 73 | 74 | # Fortran module files 75 | *.mod 76 | *.smod 77 | 78 | # Compiled Static libraries 79 | *.lai 80 | 81 | # Executables 82 | 83 | # End of https://www.toptal.com/developers/gitignore/api/c,c++ 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andre Leiradella 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INCLUDES=-Iinclude 2 | CFLAGS=-O2 -std=c99 -Werror -Wall -Wpedantic 3 | CXXFLAGS=-O2 -std=c++11 -Werror -Wall -Wpedantic 4 | 5 | %.o: %.c 6 | gcc $(INCLUDES) $(CFLAGS) -c $< -o $@ 7 | 8 | %.o: %.cpp 9 | g++ $(INCLUDES) $(CXXFLAGS) -c $< -o $@ 10 | 11 | all: liblrcpp.a 12 | 13 | liblrcpp.a: src/Components.o src/CoreFsm.o src/Frontend.o 14 | ar -crs $@ $+ 15 | 16 | src/Components.o: src/Components.cpp include/lrcpp/Components.h include/lrcpp/libretro.h 17 | 18 | src/CoreFsm.o: src/CoreFsm.cpp include/lrcpp/CoreFsm.h include/lrcpp/libretro.h 19 | 20 | src/Frontend.o: src/Frontend.cpp include/lrcpp/Frontend.h include/lrcpp/Components.h include/lrcpp/libretro.h include/lrcpp/CoreFsm.h 21 | 22 | clean: 23 | rm -f liblrcpp.a src/Components.o src/CoreFsm.o src/Frontend.o 24 | 25 | .PHONY: clean 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **lrcpp** 2 | 3 | **lrcpp** is a library meant to ease the development of Libretro front-ends. It has three blocks: 4 | 5 | * A `Frontend` class that manages a Libretro core instance via the FSM, and takes care of all the callbacks and environment calls. It delegates all functionalities to the component classes, which must be implemented and passed to the `Frontend` instance. 6 | * Components, classes that provide front-end functionalities to the core. 7 | * A [Finite State Machine](https://en.wikipedia.org/wiki/Finite-state_machine) (FSM) that manages the state of the core, preventing it from entering an invalid state like running a frame before initializing the core. 8 | 9 | ## Usage 10 | 11 | In order to have a working application several classes must be implemented. Their methods map almost directly to the Libretro API and should be self explanatory. 12 | 13 | The minimum set of components required to run the more simple cores are`Audio` and `Video` (only the software framebuffer methods need be implemented for most of the cores, so `setHwRender` can return `false`). `Input` must also be provided in order to be able to interact with the emulation, of course. Some cores will also need configuration information to run, and thus will need a `Config` component. It doesn't hurt to provide a `Logger` component since it's easy to implement. 14 | 15 | ## API 16 | 17 | ### Frontend 18 | 19 | The `Frontend` class manages a `Core`'s' life-cycle, and connects it to the platform specific code needed for it to run. It does so via platform dependent components that are responsible for thigs like video and audio output, controller and camera input, and so on. `Frontend` also takes care of calling the core's functions and set the necessary callbacks for it to use, and routes the environment calls from the core to the correct components. 20 | 21 | * Life-cycle 22 | * Since it's not possible to run more than one Libretro core at the same time (imposed by a limitation of the Libretro API), the `Frontend` class is a singleton. 23 | * `static Frontend& getInstance()`: returns the front-end instance. 24 | * Components, each method will set a new component and return `true` if successful, of `false` if the component cannot be set. Setting (or leaving) a component as `nullptr` will make the corresponding functionality unavailable for the core. Components can only be set when there's no core loaded. 25 | * `bool setLogger(Logger* logger)` 26 | * `bool setConfig(Config* config)` 27 | * `bool setVideo(Video* video)` 28 | * `bool setLed(Led* led)` 29 | * `bool setAudio(Audio* audio)` 30 | * `bool setMidi(Midi* midi)` 31 | * `bool setInput(Input* input)` 32 | * `bool setRumble(Rumble* rumble)` 33 | * `bool setSensor(Sensor* sensor)` 34 | * `bool setCamera(Camera* camera)` 35 | * `bool setLocation(Location* location)` 36 | * `bool setVirtualFileSystem(VirtualFileSystem* virtualFileSystem)` 37 | * `bool setDiskControl(DiskControl* diskControl)` 38 | * `bool serPerf(Perf* perf)` 39 | * There are also getters for each of the components. 40 | * `Logger* getLogger()` 41 | * `Config* getConfig()` 42 | * `Video* getVideo()` 43 | * `Led* getLed()` 44 | * `Audio* getAudio()` 45 | * `Midi* getMidi()` 46 | * `Input* getInput()` 47 | * `Rumble* getRumble()` 48 | * `Sensor* getSensor()` 49 | * `Camera* getCamera()` 50 | * `Location* getLocation()` 51 | * `VirtualFileSystem* getVirtualFileSystem()` 52 | * `DiskControl* getDiskControl()` 53 | * `Perf* getPerf()` 54 | * Managed core life-cycle. These methods take into account the current state of the core and will return `false` if it detects inconsistencies like trying to run a frame with a core that has not being loaded. They also return `false` if they fail for any other reason. 55 | * `bool load(char const* corePath)`: Loads a core from the file system. 56 | * `bool loadGame()`: Starts the core without a game, only available for cores that call `RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME` with `true`. 57 | * `bool loadGame(char const* gamePath)`: Starts a core with the given content path. The core will be responsible for loading the content. Only valid if `retro_system_info.need_fullpath` is `true`. 58 | * `bool loadGame(char const* gamePath, void const* data, size_t size)`: Starts a core with the given content. `data` and `size` must be valid, but `gamePath` is allowed to be `nullptr`. 59 | * `bool loadGameSpecial(unsigned gameType, struct retro_game_info const* info, size_t numInfo)`: As the Libretro API says, loads a "special" kind of game. 60 | * `bool run()`: Runs one video frame worth of emulation, generating enough audio frames to cover for the time it takes to generate the video. 61 | * `bool reset()`: Resets the current game. 62 | * `bool unloadGame()`: Unloads the game. 63 | * `bool unload()`: Unloads the core. 64 | * Other Libretro API calls. These methods, like the ones above, will also return `false` if called when the core is in an invalid state. As an example, `getSystemAvInfo` can only be called when a game has been loaded or is running. The core states and available calls per state are in `CoreFsm.fsm`. 65 | * `bool apiVersion(unsigned* version)` 66 | * `bool getSystemInfo(struct retro_system_info* info)` 67 | * `bool getSystemAvInfo(struct retro_system_av_info* info)` 68 | * `bool serializeSize(size_t* size)` 69 | * `bool serialize(void* data, size_t size)` 70 | * `bool unserialize(void const* data, size_t size)` 71 | * `bool cheatReset()` 72 | * `bool cheatSet(unsigned index, bool enabled, char const* code)` 73 | * `bool getRegion(unsigned* region)` 74 | * `bool getMemoryData(unsigned id, void** data)` 75 | * `bool getMemorySize(unsigned id, size_t* size)` 76 | 77 | ### Components 78 | 79 | #### Logger 80 | 81 | Logs messages from the core. Only one method must be implemented. 82 | 83 | * Interface 84 | * `void vprintf(enum retro_log_level level, char const* format, va_list args)` 85 | 86 | #### Config 87 | 88 | Deals with everything related to configuration and then some. Several methods must be implemented. 89 | 90 | * Environment calls 91 | * `bool setPerformanceLevel(unsigned level)` 92 | * `bool getSystemDirectory(char const** directory)` 93 | * `bool getVariable(struct retro_variable* variable)` 94 | * `bool getVariableUpdate(bool* const updated)` 95 | * `bool setSupportNoGame(bool const supports)` 96 | * `bool getLibretroPath(char const** path)` 97 | * `bool getCoreAssetsDirectory(char const** directory)` 98 | * `bool getSaveDirectory(char const** directory)` 99 | * `bool setProcAddressCallback(struct retro_get_proc_address_interface const* callback)` 100 | * `bool setSubsystemInfo(struct retro_subsystem_info const* info)` 101 | * `bool setMemoryMaps(struct retro_memory_map const* map)` 102 | * `bool getUsername(char const** username)` 103 | * `bool getLanguage(unsigned* language)` 104 | * `bool setSupportAchievements(bool supports)` 105 | * `bool setSerializationQuirks(uint64_t quirks)` 106 | * `bool getAudioVideoEnable(int* enabled)` 107 | * `bool getFastForwarding(bool* is)` 108 | * `bool setCoreOptions(struct retro_core_option_definition const** options)` 109 | * `bool setCoreOptionsIntl(struct retro_core_options_intl const* intl)` 110 | * `bool setCoreOptionsDisplay(struct retro_core_option_display const* display)` 111 | * The following methods are already implemented: 112 | * `bool setVariables(struct retro_variable const* variables)`: Creates equivalent `retro_core_option_definition`s for the variables and calls `setCoreOptions`. 113 | * `bool getCoreOptionsVersion(unsigned* version)`: Always sets `version` to 1. 114 | * The following method that doesn't belong to the libretro API is implemented: 115 | * `bool preprocessMemoryDescriptors(struct retro_memory_descriptor* descriptors, unsigned count)`: Pre-processes the memory descriptors that are received in the `setMemoryMaps` method. 116 | 117 | #### Video 118 | 119 | Takes care of everything related with video output. The methods that must be implemented are the following: 120 | 121 | * Environment calls 122 | * `bool setRotation(unsigned rotation)` 123 | * `bool getOverscan(bool* data)` 124 | * `bool getCanDupe(bool* data)` 125 | * `bool showMessage(struct retro_message const* message)` 126 | * `bool setPixelFormat(enum retro_pixel_format format)` 127 | * `bool setHwRender(struct retro_hw_render_callback* callback)` 128 | * `bool setFrameTimeCallback(struct retro_frame_time_callback const* callback)` 129 | * `bool setSystemAvInfo(struct retro_system_av_info const* info)` 130 | * `bool setGeometry(struct retro_game_geometry const* geometry)` 131 | * `bool getCurrentSoftwareFramebuffer(struct retro_framebuffer* const framebuffer)` 132 | * `bool getHwRenderInterface(struct retro_hw_render_interface const** interface)` 133 | * `bool setHwRenderContextNegotiationInterface(struct retro_hw_render_context_negotiation_interface const* interface)` 134 | * `bool setHwSharedContext()` 135 | * `bool getTargetRefreshRate(float* rate)` 136 | * `bool getPreferredHwRender(unsigned* preferred)` 137 | * Callbacks 138 | * `void refresh(void const* data, unsigned width, unsigned height, size_t pitch)` 139 | 140 | #### Led 141 | 142 | Changes the state of the system's LEDs. 143 | 144 | * Interface 145 | * `void setState(int led, int state)` 146 | 147 | #### Audio 148 | 149 | Component that receives audio frames and must produce the audio output. 150 | 151 | * Environment calls 152 | * `bool setSystemAvInfo(retro_system_av_info const* info)` 153 | * `bool setAudioCallback(struct retro_audio_callback const* callback)` 154 | * Callbacks 155 | * `size_t sampleBatch(int16_t const* data, size_t frames)` 156 | * `void sample(int16_t left, int16_t right)` 157 | 158 | #### Midi 159 | 160 | Provides the core access to the a MIDI device for raw I/O. 161 | 162 | * Interface 163 | * `bool inputEnabled()` 164 | * `bool outputEnabled()` 165 | * `bool read(uint8_t* byte)` 166 | * `bool write(uint8_t byte, uint32_t deltaTime)` 167 | * `bool flush()` 168 | 169 | #### Input 170 | 171 | Deals with all input devices. 172 | 173 | * Environment calls 174 | * `bool setInputDescriptors(struct retro_input_descriptor const* descriptors)` 175 | * `bool setKeyboardCallback(struct retro_keyboard_callback const* callback)` 176 | * `bool getInputDeviceCapabilities(uint64_t* capabilities)` 177 | * `bool setControllerInfo(struct retro_controller_info const* info)` 178 | * `bool getInputBitmasks(bool* supports)` 179 | * Callbacks 180 | * `int16_t state(unsigned port, unsigned device, unsigned index, unsigned id)` 181 | * `void poll()` 182 | 183 | #### Rumble 184 | 185 | Allows to set the state of the rumble motors in controllers. 186 | 187 | * Interfaces 188 | * `bool setState(unsigned port, enum retro_rumble_effect effect, uint16_t strength)` 189 | 190 | #### Sensor 191 | 192 | Controls sensors like accelerometers. 193 | 194 | * Interface 195 | * `bool setState(unsigned port, enum retro_sensor_action action, unsigned rate)` 196 | * `float getInput(unsigned port, unsigned id)` 197 | 198 | #### Camera 199 | 200 | Provides access to an attached camera. 201 | 202 | * Environment calls 203 | * `bool getCameraInterface(retro_camera_callback const* callback)` 204 | * Interface 205 | * `bool start()` 206 | * `void stop()` 207 | 208 | #### Location 209 | 210 | Provides location information to the core. 211 | 212 | * Environment calls 213 | * `bool getLocationInterface(retro_location_callback const* callback)` 214 | * Interface 215 | * `bool start()` 216 | * `void stop()` 217 | * `bool getPosition(double* lat, double* lon, double* horizAccuracy, double* vertAccuracy)` 218 | * `void setInterval(unsigned intervalMs, unsigned intervalDistance)` 219 | 220 | #### VirtualFileSystem 221 | 222 | Provides the core with a virtual file system interface. 223 | 224 | * Environment calls 225 | * `bool getVfsInterface(retro_vfs_interface_info const* callback)` 226 | * Interface 227 | * `unsigned getVirtualFileSystemInterfaceVersion()` 228 | * `char const* getPath(struct retro_vfs_file_handle* stream)` 229 | * `struct retro_vfs_file_handle* open(char const* path, unsigned mode, unsigned hints)` 230 | * `int close(struct retro_vfs_file_handle* stream)` 231 | * `int64_t size(struct retro_vfs_file_handle* stream)` 232 | * `int64_t truncate(struct retro_vfs_file_handle* stream, int64_t length)` 233 | * `int64_t tell(struct retro_vfs_file_handle* stream)` 234 | * `int64_t seek(struct retro_vfs_file_handle* stream, int64_t offset, int seekPosition)` 235 | * `int64_t read(struct retro_vfs_file_handle* stream, void* s, uint64_t len)` 236 | * `int64_t write(struct retro_vfs_file_handle* stream, void const* s, uint64_t len)` 237 | * `int flush(struct retro_vfs_file_handle* stream)` 238 | * `int remove(char const* path)` 239 | * `int rename(char const* old_path, char const* new_path)` 240 | * `int stat(char const* path, int32_t* size)` 241 | * `int mkDir(char const* dir)` 242 | * `struct retro_vfs_dir_handle* openDir(char const* dir, bool includeHidden)` 243 | * `bool readDir(struct retro_vfs_dir_handle* dirstream)` 244 | * `char const* direntGetName(struct retro_vfs_dir_handle* dirstream)` 245 | * `bool direntIsDir(struct retro_vfs_dir_handle* dirstream)` 246 | * `int closeDir(struct retro_vfs_dir_handle* dirstream)` 247 | 248 | #### DiskControl 249 | 250 | Interface with the core to control removable midia. 251 | 252 | * Environment calls 253 | * `bool getDiskControlInterfaceVersion(unsigned* const version)` 254 | * `bool setDiskControlInterface(struct retro_disk_control_callback const* interface)` 255 | * `bool setDiskControlExtInterface(struct retro_disk_control_ext_callback const* interface)` 256 | 257 | #### Perf 258 | 259 | Utility interface for performance measurement. 260 | 261 | * Interfaces 262 | * `retro_time_t getTimeUsec()` 263 | * `uint64_t getCpuFeatures()` 264 | * `retro_perf_tick_t getCounter()` 265 | * `void register_(struct retro_perf_counter* counter)` 266 | * `void start(struct retro_perf_counter* counter)` 267 | * `void stop(struct retro_perf_counter* counter)` 268 | * `void log()` 269 | -------------------------------------------------------------------------------- /etc/CoreFsm.dot: -------------------------------------------------------------------------------- 1 | // Generated with FSM compiler, https://github.com/leiradel/luamods/ddlt 2 | 3 | digraph CoreFsm { 4 | CoreInitialized [label="CoreInitialized"]; 5 | CoreSet [label="CoreSet"]; 6 | EnvironmentSet [label="EnvironmentSet"]; 7 | GameLoaded [label="GameLoaded"]; 8 | GameRunning [label="GameRunning"]; 9 | Start [label="Start"]; 10 | 11 | CoreInitialized -> CoreInitialized [label="apiVersion"]; 12 | CoreInitialized -> EnvironmentSet [label="deinit"]; 13 | CoreInitialized -> CoreInitialized [label="getSystemInfo"]; 14 | CoreInitialized -> GameLoaded [label="loadGame"]; 15 | CoreInitialized -> GameLoaded [label="loadGameSpecial"]; 16 | CoreInitialized -> CoreInitialized [label="setControllerPortDevice"]; 17 | CoreSet -> CoreSet [label="apiVersion"]; 18 | CoreSet -> CoreSet [label="getSystemInfo"]; 19 | CoreSet -> EnvironmentSet [label="setEnvironment"]; 20 | CoreSet -> Start [label="unset"]; 21 | EnvironmentSet -> EnvironmentSet [label="apiVersion"]; 22 | EnvironmentSet -> EnvironmentSet [label="getSystemInfo"]; 23 | EnvironmentSet -> CoreSet [label="gotoCoreSet"]; 24 | EnvironmentSet -> CoreInitialized [label="init"]; 25 | EnvironmentSet -> EnvironmentSet [label="setControllerPortDevice"]; 26 | GameLoaded -> GameLoaded [label="apiVersion"]; 27 | GameLoaded -> GameLoaded [label="getMemoryData"]; 28 | GameLoaded -> GameLoaded [label="getMemorySize"]; 29 | GameLoaded -> GameLoaded [label="getRegion"]; 30 | GameLoaded -> GameLoaded [label="getSystemAvInfo"]; 31 | GameLoaded -> GameLoaded [label="getSystemInfo"]; 32 | GameLoaded -> GameRunning [label="setCallbacks"]; 33 | GameLoaded -> GameLoaded [label="setControllerPortDevice"]; 34 | GameLoaded -> CoreInitialized [label="unloadGame"]; 35 | GameRunning -> GameRunning [label="apiVersion"]; 36 | GameRunning -> GameRunning [label="cheatReset"]; 37 | GameRunning -> GameRunning [label="cheatSet"]; 38 | GameRunning -> GameRunning [label="getMemoryData"]; 39 | GameRunning -> GameRunning [label="getMemorySize"]; 40 | GameRunning -> GameRunning [label="getRegion"]; 41 | GameRunning -> GameRunning [label="getSystemAvInfo"]; 42 | GameRunning -> GameRunning [label="getSystemInfo"]; 43 | GameRunning -> GameRunning [label="reset"]; 44 | GameRunning -> GameRunning [label="run"]; 45 | GameRunning -> GameRunning [label="serialize"]; 46 | GameRunning -> GameRunning [label="serializeSize"]; 47 | GameRunning -> GameRunning [label="setControllerPortDevice"]; 48 | GameRunning -> CoreInitialized [label="unloadGame"]; 49 | GameRunning -> GameRunning [label="unserialize"]; 50 | Start -> CoreSet [label="coreSet"]; 51 | } 52 | -------------------------------------------------------------------------------- /etc/CoreFsm.fsm: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2020 Andre Leiradella 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | header { 26 | #include 27 | 28 | namespace lrcpp { 29 | struct Core; 30 | class Frontend; 31 | } 32 | 33 | typedef lrcpp::Core* CorePtr; 34 | typedef lrcpp::Frontend* FrontendPtr; 35 | typedef size_t* SizePtr; 36 | typedef char const* ConstCharPtr; 37 | typedef retro_game_info const* ConstRetroGameInfoPtr; 38 | typedef void* VoidPtr; 39 | typedef void const* ConstVoidPtr; 40 | typedef void** VoidPtrPtr; 41 | typedef unsigned* UnsignedPtr; 42 | typedef struct retro_system_info* RetroSystemInfoPtr; 43 | typedef struct retro_system_av_info* RetroSystemAvInfoPtr; 44 | } 45 | 46 | implementation { 47 | #include 48 | #include 49 | } 50 | 51 | fsm CoreFsm(CorePtr core, FrontendPtr frontend, FrontendPtr previous) { 52 | before { 53 | self->previous = lrcpp::Frontend::getCurrent(); 54 | lrcpp::Frontend::setCurrent(self->frontend); 55 | } 56 | 57 | after { 58 | lrcpp::Frontend::setCurrent(self->previous); 59 | self->previous = nullptr; 60 | } 61 | 62 | Start { 63 | coreSet() => CoreSet; 64 | } 65 | 66 | CoreSet { 67 | apiVersion(UnsignedPtr version) => CoreSet { 68 | *version = self->core->apiVersion(); 69 | } 70 | 71 | getSystemInfo(RetroSystemInfoPtr info) => CoreSet { 72 | self->core->getSystemInfo(info); 73 | } 74 | 75 | setEnvironment(retro_environment_t cb) => EnvironmentSet { 76 | self->core->setEnvironment(cb); 77 | } 78 | 79 | unset() => Start; 80 | } 81 | 82 | EnvironmentSet { 83 | apiVersion(UnsignedPtr version) => EnvironmentSet { 84 | *version = self->core->apiVersion(); 85 | } 86 | 87 | getSystemInfo(RetroSystemInfoPtr info) => EnvironmentSet { 88 | self->core->getSystemInfo(info); 89 | } 90 | 91 | setControllerPortDevice(unsigned port, unsigned device) => EnvironmentSet { 92 | self->core->setControllerPortDevice(port, device); 93 | } 94 | 95 | init() => CoreInitialized { 96 | self->core->init(); 97 | } 98 | 99 | gotoCoreSet() => CoreSet; // Only so that the state goes to CoreSet; 100 | 101 | unset() => gotoCoreSet() => unset(); 102 | } 103 | 104 | CoreInitialized { 105 | apiVersion(UnsignedPtr version) => CoreInitialized { 106 | *version = self->core->apiVersion(); 107 | } 108 | 109 | getSystemInfo(RetroSystemInfoPtr info) => CoreInitialized { 110 | self->core->getSystemInfo(info); 111 | } 112 | 113 | setControllerPortDevice(unsigned port, unsigned device) => CoreInitialized { 114 | self->core->setControllerPortDevice(port, device); 115 | } 116 | 117 | loadGame(ConstRetroGameInfoPtr gameInfo) => GameLoaded { 118 | if (!self->core->loadGame(gameInfo)) { 119 | forbid; 120 | } 121 | } 122 | 123 | loadGameSpecial(unsigned gameType, ConstRetroGameInfoPtr info, size_t numInfo) => GameLoaded { 124 | if (!self->core->loadGameSpecial(gameType, info, numInfo)) { 125 | forbid; 126 | } 127 | } 128 | 129 | deinit() => EnvironmentSet { 130 | self->core->deinit(); 131 | } 132 | 133 | unset() => deinit() => unset(); 134 | } 135 | 136 | GameLoaded { 137 | apiVersion(UnsignedPtr version) => GameLoaded { 138 | *version = self->core->apiVersion(); 139 | } 140 | 141 | getSystemInfo(RetroSystemInfoPtr info) => GameLoaded { 142 | self->core->getSystemInfo(info); 143 | } 144 | 145 | setControllerPortDevice(unsigned port, unsigned device) => GameLoaded { 146 | self->core->setControllerPortDevice(port, device); 147 | } 148 | 149 | getSystemAvInfo(RetroSystemAvInfoPtr info) => GameLoaded { 150 | self->core->getSystemAvInfo(info); 151 | } 152 | 153 | setCallbacks( 154 | retro_video_refresh_t videoRefresh, 155 | retro_audio_sample_t audioSample, 156 | retro_audio_sample_batch_t audioSampleBatch, 157 | retro_input_poll_t inputPoll, 158 | retro_input_state_t inputState 159 | ) => GameRunning { 160 | self->core->setVideoRefresh(videoRefresh); 161 | self->core->setAudioSample(audioSample); 162 | self->core->setAudioSampleBatch(audioSampleBatch); 163 | self->core->setInputPoll(inputPoll); 164 | self->core->setInputState(inputState); 165 | } 166 | 167 | unloadGame() => CoreInitialized { 168 | self->core->unloadGame(); 169 | } 170 | 171 | getRegion(UnsignedPtr region) => GameLoaded { 172 | *region = self->core->getRegion(); 173 | } 174 | 175 | getMemoryData(unsigned id, VoidPtrPtr data) => GameLoaded { 176 | *data = self->core->getMemoryData(id); 177 | } 178 | 179 | getMemorySize(unsigned id, SizePtr size) => GameLoaded { 180 | *size = self->core->getMemorySize(id); 181 | } 182 | 183 | deinit() => unloadGame() => deinit(); 184 | 185 | unset() => unloadGame() => unset(); 186 | } 187 | 188 | GameRunning { 189 | apiVersion(UnsignedPtr version) => GameRunning { 190 | *version = self->core->apiVersion(); 191 | } 192 | 193 | getSystemInfo(RetroSystemInfoPtr info) => GameRunning { 194 | self->core->getSystemInfo(info); 195 | } 196 | 197 | setControllerPortDevice(unsigned port, unsigned device) => GameRunning { 198 | self->core->setControllerPortDevice(port, device); 199 | } 200 | 201 | getSystemAvInfo(RetroSystemAvInfoPtr info) => GameRunning { 202 | self->core->getSystemAvInfo(info); 203 | } 204 | 205 | run() => GameRunning { 206 | self->core->run(); 207 | } 208 | 209 | reset() => GameRunning { 210 | self->core->reset(); 211 | } 212 | 213 | serializeSize(SizePtr size) => GameRunning { 214 | *size = self->core->serializeSize(); 215 | } 216 | 217 | serialize(VoidPtr data, size_t size) => GameRunning { 218 | if (!self->core->serialize(data, size)) { 219 | forbid; 220 | } 221 | } 222 | 223 | unserialize(ConstVoidPtr data, size_t size) => GameRunning { 224 | if (!self->core->unserialize(data, size)) { 225 | forbid; 226 | } 227 | } 228 | 229 | cheatReset() => GameRunning { 230 | self->core->cheatReset(); 231 | } 232 | 233 | cheatSet(unsigned index, bool enabled, ConstCharPtr code) => GameRunning { 234 | self->core->cheatSet(index, enabled, code); 235 | } 236 | 237 | getRegion(UnsignedPtr region) => GameRunning { 238 | *region = self->core->getRegion(); 239 | } 240 | 241 | getMemoryData(unsigned id, VoidPtrPtr data) => GameRunning { 242 | *data = self->core->getMemoryData(id); 243 | } 244 | 245 | getMemorySize(unsigned id, SizePtr size) => GameRunning { 246 | *size = self->core->getMemorySize(id); 247 | } 248 | 249 | unloadGame() => CoreInitialized { 250 | self->core->unloadGame(); 251 | } 252 | 253 | deinit() => unloadGame() => deinit(); 254 | 255 | unset() => unloadGame() => unset(); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /examples/sdl2/Audio.cpp: -------------------------------------------------------------------------------- 1 | #include "Audio.h" 2 | 3 | Audio::Audio() { 4 | reset(); 5 | } 6 | 7 | bool Audio::init(Config* config, lrcpp::Logger* logger) { 8 | reset(); 9 | 10 | _logger = logger; 11 | 12 | if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { 13 | logger->error("SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s", SDL_GetError()); 14 | return false; 15 | } 16 | 17 | _logger->info("Audio subsystem initialized"); 18 | 19 | char const* driverName = nullptr; 20 | 21 | if (config->getOption("sdl2lrcpp_audio_device", &driverName)) { 22 | _deviceName = driverName; 23 | } 24 | 25 | int const count = SDL_GetNumAudioDevices(0); 26 | 27 | for (int i = 0; i < count; i++) { 28 | _logger->info("Audio device %d: %s", i, SDL_GetAudioDeviceName(i, 0)); 29 | } 30 | 31 | return true; 32 | } 33 | 34 | void Audio::destroy() { 35 | if (_audioDev != 0) { 36 | SDL_CloseAudioDevice(_audioDev); 37 | } 38 | 39 | SDL_QuitSubSystem(SDL_INIT_AUDIO); 40 | reset(); 41 | } 42 | 43 | double Audio::getCoreSampleRate() const { 44 | return _coreSampleRate; 45 | } 46 | 47 | void Audio::clear() { 48 | _samples.clear(); 49 | } 50 | 51 | void Audio::present() { 52 | if (SDL_QueueAudio(_audioDev, _samples.data(), _samples.size() * 2) != 0) { 53 | _logger->error("SDL_QueueAudio() failed: %s", SDL_GetError()); 54 | return; 55 | } 56 | 57 | _logger->debug("%zu audio samples queued, %u bytes in output queue", _samples.size(), SDL_GetQueuedAudioSize(_audioDev)); 58 | } 59 | 60 | bool Audio::setSystemAvInfo(retro_system_av_info const* info) { 61 | if (info->timing.sample_rate == _coreSampleRate) { 62 | return true; 63 | } 64 | 65 | _coreSampleRate = info->timing.sample_rate; 66 | _logger->info("Core sample rate set to %f", _coreSampleRate); 67 | 68 | if (_audioDev != 0) { 69 | SDL_CloseAudioDevice(_audioDev); 70 | _audioDev = 0; 71 | } 72 | 73 | SDL_AudioSpec desired, obtained; 74 | memset(&desired, 0, sizeof(desired)); 75 | memset(&obtained, 0, sizeof(obtained)); 76 | 77 | desired.freq = _coreSampleRate; 78 | desired.format = AUDIO_S16SYS; 79 | desired.channels = 2; 80 | desired.samples = 1024; 81 | desired.callback = nullptr; 82 | desired.userdata = nullptr; 83 | 84 | if (_deviceName.length() != 0) { 85 | _logger->info("Using audio device %s", _deviceName.c_str()); 86 | _audioDev = SDL_OpenAudioDevice(_deviceName.c_str(), 0, &desired, &obtained, 0); 87 | } 88 | else { 89 | _logger->info("Using default audio device"); 90 | _audioDev = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); 91 | } 92 | 93 | if (_audioDev == 0) { 94 | _logger->error("SDL_OpenAudioDevice() failed: %s", SDL_GetError()); 95 | return false; 96 | } 97 | 98 | SDL_PauseAudioDevice(_audioDev, 0); 99 | 100 | _logger->info("Opened audio driver %s", SDL_GetCurrentAudioDriver()); 101 | _logger->info(" %d Hz", obtained.freq); 102 | _logger->info(" %u channels", obtained.channels); 103 | _logger->info(" %u bits per sample", SDL_AUDIO_BITSIZE(obtained.format)); 104 | 105 | _logger->info( 106 | " %s %s", 107 | SDL_AUDIO_ISSIGNED(obtained.format) ? "signed" : "unsigned", 108 | SDL_AUDIO_ISFLOAT(obtained.format) ? "float" : "integer" 109 | ); 110 | 111 | _logger->info(" %s endian", SDL_AUDIO_ISBIGENDIAN(obtained.format) ? "big" : "little"); 112 | 113 | return true; 114 | } 115 | 116 | bool Audio::setAudioCallback(retro_audio_callback const* callback) { 117 | (void)callback; 118 | _logger->warn("RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK not implemented"); 119 | return false; 120 | } 121 | 122 | size_t Audio::sampleBatch(int16_t const* data, size_t frames) { 123 | size_t const size = _samples.size(); 124 | _samples.resize(size + frames * 2); 125 | memcpy(_samples.data() + size, data, frames * 4); 126 | 127 | _logger->debug("%zu audio frames queued", frames); 128 | return frames; 129 | } 130 | 131 | void Audio::sample(int16_t left, int16_t right) { 132 | int16_t frame[2] = {left, right}; 133 | sampleBatch(frame, 1); 134 | } 135 | 136 | void Audio::reset() { 137 | _logger = nullptr; 138 | 139 | _deviceName.clear(); 140 | _coreSampleRate = 0.0; 141 | _audioDev = 0; 142 | 143 | _samples.clear(); 144 | } 145 | -------------------------------------------------------------------------------- /examples/sdl2/Audio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class Audio : public lrcpp::Audio { 14 | public: 15 | Audio(); 16 | 17 | bool init(Config* config, lrcpp::Logger* logger); 18 | void destroy(); 19 | 20 | double getCoreSampleRate() const; 21 | 22 | void clear(); 23 | void present(); 24 | 25 | // lrcpp::Audio 26 | virtual bool setSystemAvInfo(retro_system_av_info const* info) override; 27 | virtual bool setAudioCallback(retro_audio_callback const* callback) override; 28 | virtual size_t sampleBatch(int16_t const* data, size_t frames) override; 29 | virtual void sample(int16_t left, int16_t right) override; 30 | 31 | protected: 32 | void reset(); 33 | 34 | lrcpp::Logger* _logger; 35 | 36 | std::string _deviceName; 37 | double _coreSampleRate; 38 | SDL_AudioDeviceID _audioDev; 39 | 40 | std::vector _samples; 41 | }; 42 | -------------------------------------------------------------------------------- /examples/sdl2/Config.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #define SEPARATOR '\\' 10 | #define SEPARATOR2 '/' 11 | #else 12 | #define SEPARATOR '/' 13 | #endif 14 | 15 | Config::Config() { 16 | reset(); 17 | } 18 | 19 | bool Config::init(std::vector const& configPaths, 20 | char const* contentPath, 21 | char const* corePath, 22 | lrcpp::Logger* logger) { 23 | 24 | reset(); 25 | 26 | _logger = logger; 27 | 28 | for (auto const& path : configPaths) { 29 | _logger->info("Loading configuration from \"%s\"", path.c_str()); 30 | 31 | if (!loadOptions(path.c_str())) { 32 | return false; 33 | } 34 | } 35 | 36 | if (!getDirectory(contentPath, &_contentDir)) { 37 | return false; 38 | } 39 | 40 | _logger->info("Content directory is \"%s\"", _contentDir.c_str()); 41 | 42 | if (!getDirectory(corePath, &_coreDir)) { 43 | return false; 44 | } 45 | 46 | _logger->info("Core directory is \"%s\"", _coreDir.c_str()); 47 | return true; 48 | } 49 | 50 | void Config::destroy() { 51 | reset(); 52 | } 53 | 54 | bool Config::getOption(char const* key, char const** value) const { 55 | auto const it = _options.find(key); 56 | 57 | if (it == _options.end()) { 58 | _logger->error("Could't find \"%s\" in the configuration file", key); 59 | return false; 60 | } 61 | 62 | *value = it->second.c_str(); 63 | 64 | _logger->debug("Found value \"%s\" for key \"%s\"", *value, key); 65 | return true; 66 | } 67 | 68 | bool Config::getOption(char const* key, unsigned long* value) const { 69 | char const* valueStr = nullptr; 70 | 71 | if (!getOption(key, &valueStr)) { 72 | return false; 73 | } 74 | 75 | errno = 0; 76 | char* endptr = nullptr; 77 | unsigned long const val = strtoul(valueStr, &endptr, 0); 78 | 79 | if (*valueStr == 0 || *endptr != 0) { 80 | errno = EINVAL; 81 | } 82 | 83 | if (errno != 0) { 84 | _logger->error("Could not convert \"%s\" to a number: %s", valueStr, strerror(errno)); 85 | return false; 86 | } 87 | 88 | *value = val; 89 | return true; 90 | } 91 | 92 | bool Config::getOption(char const* key, bool* value) const { 93 | char const* valueStr = nullptr; 94 | 95 | if (!getOption(key, &valueStr)) { 96 | return false; 97 | } 98 | 99 | if (strcmp(valueStr, "true") == 0) { 100 | *value = true; 101 | } 102 | else if (strcmp(valueStr, "false") == 0) { 103 | *value = false; 104 | } 105 | else { 106 | _logger->error("Could not convert \"%s\" to a boolean", valueStr); 107 | return false; 108 | } 109 | 110 | return true; 111 | } 112 | 113 | bool Config::setPerformanceLevel(unsigned level) { 114 | (void)level; 115 | _logger->warn("RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL not implemented"); 116 | return false; 117 | } 118 | 119 | bool Config::getSystemDirectory(char const** directory) { 120 | *directory = _coreDir.c_str(); 121 | return true; 122 | } 123 | 124 | bool Config::getVariable(retro_variable* variable) { 125 | auto const found = _options.find(variable->key); 126 | 127 | if (found != _options.end()) { 128 | variable->value = found->second.c_str(); 129 | _logger->info("Found value \"%s\" for variable \"%s\"", variable->value, variable->key); 130 | return true; 131 | } 132 | 133 | variable->value = nullptr; 134 | _logger->error("Variable \"%s\" not found", variable->key); 135 | return false; 136 | } 137 | 138 | bool Config::getVariableUpdate(bool* const updated) { 139 | *updated = _optionsUpdated; 140 | _optionsUpdated = false; 141 | return true; 142 | } 143 | 144 | bool Config::setSupportNoGame(bool const supports) { 145 | _supportsNoGame = supports; 146 | return true; 147 | } 148 | 149 | bool Config::getLibretroPath(char const** path) { 150 | *path = _coreDir.c_str(); 151 | return true; 152 | } 153 | 154 | bool Config::getCoreAssetsDirectory(char const** directory) { 155 | *directory = _coreDir.c_str(); 156 | return true; 157 | } 158 | 159 | bool Config::getSaveDirectory(char const** directory) { 160 | *directory = _contentDir.c_str(); 161 | return true; 162 | } 163 | 164 | bool Config::setProcAddressCallback(retro_get_proc_address_interface const* callback) { 165 | (void)callback; 166 | _logger->warn("RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK not implemented"); 167 | return false; 168 | } 169 | 170 | bool Config::setSubsystemInfo(retro_subsystem_info const* info) { 171 | (void)info; 172 | _logger->warn("RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO not implemented"); 173 | return false; 174 | } 175 | 176 | bool Config::setMemoryMaps(retro_memory_map const* map) { 177 | (void)map; 178 | _logger->warn("RETRO_ENVIRONMENT_SET_MEMORY_MAPS not implemented"); 179 | return false; 180 | } 181 | 182 | bool Config::getUsername(char const** username) { 183 | *username = nullptr; 184 | return true; 185 | } 186 | 187 | bool Config::getLanguage(unsigned* language) { 188 | *language = RETRO_LANGUAGE_ENGLISH; 189 | return true; 190 | } 191 | 192 | bool Config::setSupportAchievements(bool supports) { 193 | (void)supports; 194 | _logger->warn("RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS not implemented"); 195 | return false; 196 | } 197 | 198 | bool Config::setSerializationQuirks(uint64_t quirks) { 199 | (void)quirks; 200 | _logger->warn("RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS not implemented"); 201 | return false; 202 | } 203 | 204 | bool Config::getAudioVideoEnable(int* enabled) { 205 | *enabled = true; 206 | return true; 207 | } 208 | 209 | bool Config::getFastForwarding(bool* is) { 210 | *is = false; 211 | return true; 212 | } 213 | 214 | bool Config::setCoreOptions(retro_core_option_definition const* options) { 215 | _logger->info("Setting core options"); 216 | 217 | for (; options->key != nullptr; options++) { 218 | auto const found = _options.find(options->key); 219 | 220 | if (found == _options.end()) { 221 | _options.emplace(options->key, options->default_value); 222 | } 223 | 224 | _logger->info(" %s set to \"%s\"", options->key, _options[options->key].c_str()); 225 | } 226 | 227 | return true; 228 | } 229 | 230 | bool Config::setCoreOptionsIntl(retro_core_options_intl const* intl) { 231 | _logger->warn("Using English for the core options"); 232 | return setCoreOptions(intl->us); 233 | } 234 | 235 | bool Config::setCoreOptionsDisplay(retro_core_option_display const* display) { 236 | (void)display; 237 | _logger->warn("RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY not implemented"); 238 | return false; 239 | } 240 | 241 | bool Config::getDirectory(char const* path, std::string* directory) { 242 | size_t const length = strlen(path); 243 | 244 | if (length == 0) { 245 | _logger->error("Cannot get directory from an empty path"); 246 | return false; 247 | } 248 | 249 | struct stat statbuf; 250 | 251 | if (stat(path, &statbuf) != 0) { 252 | _logger->error("Error info for path \"%s\": %s", path, strerror(errno)); 253 | return false; 254 | } 255 | 256 | if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { 257 | // It's a directory. 258 | *directory = path; 259 | 260 | #ifdef SEPARATOR2 261 | if (path[length - 1] != SEPARATOR && path[length - 1] != SEPARATOR2) { 262 | #else 263 | if (path[length - 1] != SEPARATOR) { 264 | #endif 265 | directory->append(1, SEPARATOR); 266 | } 267 | } 268 | else { 269 | bool separator = false; 270 | 271 | // Assume it's a file. 272 | for (size_t i = length - 1; i > 0; i--) { 273 | #ifdef SEPARATOR2 274 | if (path[i] == SEPARATOR || path[i] == SEPARATOR2) { 275 | #else 276 | if (path[i] == SEPARATOR) { 277 | #endif 278 | *directory = std::string(path, i + 1); 279 | separator = true; 280 | break; 281 | } 282 | } 283 | 284 | if (!separator) { 285 | *directory = "."; 286 | } 287 | } 288 | 289 | _logger->debug("Directory for \"%s\" is \"%s\"", path, directory->c_str()); 290 | return true; 291 | } 292 | 293 | bool Config::loadOptions(char const* configPath) { 294 | _logger->info("Reading settings from \"%s\"", configPath); 295 | 296 | FILE* const file = fopen(configPath, "r"); 297 | 298 | if (file == nullptr) { 299 | _logger->error("Error opening \"%s\" for reading: %s", configPath, strerror(errno)); 300 | return false; 301 | } 302 | 303 | char line[1024]; 304 | 305 | while (fgets(line, sizeof(line), file) != nullptr) { 306 | line[sizeof(line) - 1] = 0; 307 | 308 | const char* keyBegin = line; 309 | 310 | while ((*keyBegin == ' ' || *keyBegin == '\t') && (*keyBegin != '#' && *keyBegin != '\n' && *keyBegin != 0)) { 311 | keyBegin++; 312 | } 313 | 314 | if (*keyBegin == '#' || *keyBegin == '\n' || *keyBegin == 0) { 315 | // Comment or empty line. 316 | continue; 317 | } 318 | 319 | const char* keyEnd = keyBegin; 320 | 321 | while ((*keyEnd != ' ' && *keyEnd != '\t' && *keyEnd != '=') && (*keyEnd != '\n' && *keyEnd != 0)) { 322 | keyEnd++; 323 | } 324 | 325 | if (*keyEnd == '\n' || *keyEnd == 0) { 326 | _logger->error("Missing value for key \"%.*s\"", keyBegin, (int)(keyEnd - keyBegin)); 327 | fclose(file); 328 | return false; 329 | } 330 | 331 | const std::string key(keyBegin, keyEnd - keyBegin); 332 | const char* valueBegin = keyEnd; 333 | 334 | while ((*valueBegin == ' ' || *valueBegin == '\t') && (*valueBegin != '=' && *valueBegin != '\n' && *valueBegin != 0)) { 335 | valueBegin++; 336 | } 337 | 338 | if (*valueBegin != '=') { 339 | _logger->error("Missing value for key \"%s\"", key.c_str()); 340 | fclose(file); 341 | return false; 342 | } 343 | 344 | valueBegin++; 345 | 346 | while ((*valueBegin == ' ' || *valueBegin == '\t') && (*valueBegin != '"' && *valueBegin != '\n' && *valueBegin != 0)) { 347 | valueBegin++; 348 | } 349 | 350 | char const* valueEnd = valueBegin; 351 | 352 | if (*valueBegin == '"') { 353 | // String value. 354 | valueBegin++; 355 | valueEnd++; 356 | 357 | while (*valueEnd != '"' && *valueEnd != '\n' && *valueEnd != 0) { 358 | valueEnd++; 359 | } 360 | 361 | if (*valueEnd != '"') { 362 | _logger->error("Unterminated string for value of key \"%s\"", key.c_str()); 363 | fclose(file); 364 | return false; 365 | } 366 | } 367 | else { 368 | while (*valueEnd != ' ' && *valueEnd != '\t' && *valueEnd != '\n' && *valueEnd != 0) { 369 | valueEnd++; 370 | } 371 | } 372 | 373 | if (_options.find(key) != _options.end()) { 374 | _logger->warn("Duplicated key \"%s\"", key.c_str()); 375 | } 376 | else { 377 | const std::string value(valueBegin, valueEnd - valueBegin); 378 | _logger->debug("Found key \"%s\" with value \"%s\"", key.c_str(), value.c_str()); 379 | _options.emplace(key, value); 380 | } 381 | } 382 | 383 | if (ferror(file)) { 384 | _logger->error("Error reading from configuration file"); 385 | fclose(file); 386 | return false; 387 | } 388 | 389 | fclose(file); 390 | return true; 391 | } 392 | 393 | void Config::reset() { 394 | _logger = nullptr; 395 | 396 | _contentDir.clear(); 397 | _coreDir.clear(); 398 | _supportsNoGame = false; 399 | 400 | _options.clear(); 401 | _optionsUpdated =false; 402 | } 403 | -------------------------------------------------------------------------------- /examples/sdl2/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Config : public lrcpp::Config { 11 | public: 12 | Config(); 13 | 14 | bool init(std::vector const& configPaths, char const* contentPath, char const* corePath, lrcpp::Logger* logger); 15 | void destroy(); 16 | 17 | bool getOption(char const* key, char const** value) const; 18 | bool getOption(char const* key, unsigned long* value) const; 19 | bool getOption(char const* key, bool* value) const; 20 | 21 | // lrcpp::Config 22 | virtual bool setPerformanceLevel(unsigned level) override; 23 | virtual bool getSystemDirectory(char const** directory) override; 24 | virtual bool getVariable(retro_variable* variable) override; 25 | virtual bool getVariableUpdate(bool* const updated) override; 26 | virtual bool setSupportNoGame(bool const supports) override; 27 | virtual bool getLibretroPath(char const** path) override; 28 | virtual bool getCoreAssetsDirectory(char const** directory) override; 29 | virtual bool getSaveDirectory(char const** directory) override; 30 | virtual bool setProcAddressCallback(retro_get_proc_address_interface const* callback) override; 31 | virtual bool setSubsystemInfo(retro_subsystem_info const* info) override; 32 | virtual bool setMemoryMaps(retro_memory_map const* map) override; 33 | virtual bool getUsername(char const** username) override; 34 | virtual bool getLanguage(unsigned* language) override; 35 | virtual bool setSupportAchievements(bool supports) override; 36 | virtual bool setSerializationQuirks(uint64_t quirks) override; 37 | virtual bool getAudioVideoEnable(int* enabled) override; 38 | virtual bool getFastForwarding(bool* is) override; 39 | virtual bool setCoreOptions(retro_core_option_definition const* options) override; 40 | virtual bool setCoreOptionsIntl(retro_core_options_intl const* intl) override; 41 | virtual bool setCoreOptionsDisplay(retro_core_option_display const* display) override; 42 | 43 | protected: 44 | bool getDirectory(char const* path, std::string* directory); 45 | bool loadOptions(char const* configPath); 46 | void reset(); 47 | 48 | lrcpp::Logger* _logger; 49 | 50 | std::string _contentDir; 51 | std::string _coreDir; 52 | bool _supportsNoGame; 53 | 54 | std::map _options; 55 | bool _optionsUpdated; 56 | }; 57 | -------------------------------------------------------------------------------- /examples/sdl2/DynLib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef _WIN32 6 | # include 7 | # define WIN32_LEAN_AND_MEAN 8 | # include 9 | #else 10 | # include 11 | #endif 12 | 13 | // C++ wrapper around lrcpp/dynlib.h 14 | class DynLib { 15 | public: 16 | DynLib() : _handle(nullptr) {} 17 | ~DynLib() { unload(); } 18 | 19 | bool load(const std::string& path) { 20 | unload(); // Unload previous if any 21 | #ifdef _WIN32 22 | _handle = LoadLibrary(path.c_str()); 23 | #else 24 | _handle = dlopen(path.c_str(), RTLD_LAZY); 25 | #endif 26 | return _handle != nullptr; 27 | } 28 | 29 | void unload() { 30 | if (_handle) { 31 | #ifdef _WIN32 32 | FreeLibrary(_handle); 33 | #else 34 | dlclose(_handle); 35 | #endif 36 | _handle = nullptr; 37 | } 38 | } 39 | 40 | void* getSymbol(const char* name) const { 41 | if (!_handle) return nullptr; 42 | #ifdef _WIN32 43 | return (void *)GetProcAddress(_handle, name); 44 | #else 45 | return dlsym(_handle, name); 46 | #endif 47 | } 48 | 49 | const char* error() const { 50 | #ifdef _WIN32 51 | static char msg[ 512 ]; 52 | 53 | DWORD err = GetLastError(); 54 | 55 | DWORD res = FormatMessage( 56 | FORMAT_MESSAGE_FROM_SYSTEM, 57 | NULL, 58 | err, 59 | MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ), 60 | msg, 61 | sizeof( msg ) - 1, 62 | NULL 63 | ); 64 | 65 | if ( res == 0 ) 66 | { 67 | snprintf( msg, sizeof( msg ) - 1, "Error %lu", err ); 68 | msg[ sizeof( msg ) - 1 ] = 0; 69 | } 70 | 71 | return msg; 72 | #else 73 | return dlerror(); 74 | #endif 75 | } 76 | 77 | bool isLoaded() const { return _handle != nullptr; } 78 | 79 | private: 80 | #ifdef _WIN32 81 | HMODULE _handle; 82 | #else 83 | void* _handle; 84 | #endif 85 | }; 86 | -------------------------------------------------------------------------------- /examples/sdl2/Entry.c: -------------------------------------------------------------------------------- 1 | // It seems SDL doesn't like main being in a C++ file. 2 | int Main(int argc, char const* argv[]); 3 | 4 | int main(int argc, char const* argv[]) { 5 | return Main(argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /examples/sdl2/Input.cpp: -------------------------------------------------------------------------------- 1 | #include "Input.h" 2 | 3 | Input::Input() { 4 | reset(); 5 | } 6 | 7 | bool Input::init(lrcpp::Logger* logger) { 8 | reset(); 9 | 10 | _logger = logger; 11 | 12 | if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0) { 13 | logger->error("SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) failed: %s", SDL_GetError()); 14 | return false; 15 | } 16 | 17 | _logger->info("Audio subsystem initialized"); 18 | return true; 19 | } 20 | 21 | void Input::destroy() { 22 | SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); 23 | reset(); 24 | } 25 | 26 | void Input::process(SDL_Event const* event) { 27 | switch (event->type) { 28 | case SDL_JOYDEVICEADDED: 29 | process(&event->jdevice); 30 | break; 31 | 32 | case SDL_CONTROLLERDEVICEADDED: 33 | case SDL_CONTROLLERDEVICEREMOVED: 34 | process(&event->cdevice); 35 | break; 36 | 37 | case SDL_CONTROLLERBUTTONUP: 38 | case SDL_CONTROLLERBUTTONDOWN: 39 | process(&event->cbutton); 40 | break; 41 | 42 | case SDL_CONTROLLERAXISMOTION: 43 | process(&event->caxis); 44 | break; 45 | 46 | case SDL_KEYUP: 47 | case SDL_KEYDOWN: 48 | process(&event->key); 49 | break; 50 | 51 | case SDL_MOUSEBUTTONUP: 52 | case SDL_MOUSEBUTTONDOWN: 53 | process(&event->button); 54 | break; 55 | 56 | case SDL_MOUSEMOTION: 57 | process(&event->motion); 58 | break; 59 | } 60 | } 61 | 62 | bool Input::setInputDescriptors(retro_input_descriptor const* descriptors) { 63 | _logger->info("Setting input descriptors"); 64 | _logger->info(" port device index id description"); 65 | 66 | for (size_t i = 0; descriptors[i].description != nullptr; i++) { 67 | /** 68 | * At least the Frodo core doesn't properly terminate the input 69 | * descriptor list with a zeroed entry, we do our best to avoid a crash 70 | * here. 71 | */ 72 | if ((descriptors[i].device & RETRO_DEVICE_MASK) > RETRO_DEVICE_POINTER) { 73 | break; 74 | } 75 | 76 | if (descriptors[i].id > RETRO_DEVICE_ID_LIGHTGUN_RELOAD) { 77 | break; 78 | } 79 | 80 | retro_input_descriptor const* desc = descriptors + i; 81 | _logger->info(" %4u %6u %5u %2u %s", desc->port, desc->device, desc->index, desc->id, desc->description); 82 | } 83 | 84 | return true; 85 | } 86 | 87 | bool Input::setKeyboardCallback(retro_keyboard_callback const* callback) { 88 | _keyboardCallback = *callback; 89 | return true; 90 | } 91 | 92 | bool Input::getInputDeviceCapabilities(uint64_t* capabilities) { 93 | *capabilities = 1 << RETRO_DEVICE_JOYPAD | 1 << RETRO_DEVICE_MOUSE | 1 << RETRO_DEVICE_KEYBOARD | 1 << RETRO_DEVICE_ANALOG; 94 | return true; 95 | } 96 | 97 | bool Input::setControllerInfo(retro_controller_info const* info) { 98 | static char const* const deviceNames[] = {"none", "joypad", "mouse", "keyboard", "lightgun", "analog", "pointer"}; 99 | 100 | _logger->info("Setting controller info"); 101 | _logger->info(" port id type description"); 102 | 103 | for (size_t i = 0; info[i].types != nullptr; i++) { 104 | for (unsigned j = 0; j < info[i].num_types; j++) { 105 | retro_controller_description const* type = info[i].types + j; 106 | 107 | unsigned const deviceType = type->id & RETRO_DEVICE_MASK; 108 | char const* deviceName = deviceType < sizeof(deviceNames) / sizeof(deviceNames[0]) ? deviceNames[deviceType] : "?"; 109 | 110 | _logger->info(" %4zu %2u %-8s %s", i + 1, type->id >> RETRO_DEVICE_TYPE_SHIFT, deviceName, type->desc); 111 | } 112 | } 113 | 114 | return true; 115 | } 116 | 117 | bool Input::getInputBitmasks(bool* supports) { 118 | *supports = false; 119 | return false; 120 | } 121 | 122 | int16_t Input::state(unsigned port, unsigned device, unsigned index, unsigned id) { 123 | unsigned const base = device & RETRO_DEVICE_MASK; 124 | 125 | switch (base) { 126 | case RETRO_DEVICE_JOYPAD: 127 | case RETRO_DEVICE_ANALOG: { 128 | if (port >= _ports.size()) { 129 | return 0; 130 | } 131 | 132 | Gamepad* const gamepad = _ports[port]; 133 | 134 | if (base == RETRO_DEVICE_JOYPAD) { 135 | return gamepad->state[id]; 136 | } 137 | else { 138 | return id == RETRO_DEVICE_ID_ANALOG_X ? gamepad->analogs[index].x : gamepad->analogs[index].y; 139 | } 140 | 141 | break; 142 | } 143 | 144 | case RETRO_DEVICE_KEYBOARD: return _keyboardState[id] ? 32767 : 0; 145 | 146 | case RETRO_DEVICE_MOUSE: { 147 | switch (id) { 148 | case RETRO_DEVICE_ID_MOUSE_X: return _mouseX; 149 | case RETRO_DEVICE_ID_MOUSE_Y: return _mouseY; 150 | case RETRO_DEVICE_ID_MOUSE_LEFT: return _mouseButtons[0] ? 32767 : 0; 151 | case RETRO_DEVICE_ID_MOUSE_MIDDLE: return _mouseButtons[1] ? 32767 : 0; 152 | case RETRO_DEVICE_ID_MOUSE_RIGHT: return _mouseButtons[2] ? 32767 : 0; 153 | case RETRO_DEVICE_ID_MOUSE_BUTTON_4: return _mouseButtons[3] ? 32767 : 0; 154 | case RETRO_DEVICE_ID_MOUSE_BUTTON_5: return _mouseButtons[4] ? 32767 : 0; 155 | } 156 | 157 | break; 158 | } 159 | } 160 | 161 | return 0; 162 | } 163 | 164 | void Input::poll() { 165 | if (_keyboardCallback.callback != nullptr) { 166 | for (int i = RETROK_FIRST; i < RETROK_LAST; i++) { 167 | if (_keyboardState[i] != _keyboardPreviousState[i]) { 168 | _keyboardCallback.callback(_keyboardState[i], i, 0, 0); 169 | _keyboardPreviousState[i] = _keyboardState[i]; 170 | } 171 | } 172 | } 173 | } 174 | 175 | void Input::process(SDL_JoyDeviceEvent const* event) { 176 | if (event->type != SDL_JOYDEVICEADDED) { 177 | return; 178 | } 179 | 180 | SDL_JoystickGUID const guid = SDL_JoystickGetDeviceGUID(event->which); 181 | char const* const mapping = SDL_GameControllerMappingForGUID(guid); 182 | char const* const name = SDL_JoystickNameForIndex(event->which); 183 | 184 | if (mapping == nullptr) { 185 | char guidStr[128]; 186 | SDL_JoystickGetGUIDString(guid, guidStr, sizeof(guidStr)); 187 | _logger->error("No mapping for joystick \"%s\" (GUID %s), joystick unusable", name, guidStr); 188 | } 189 | else { 190 | SDL_free((void*)mapping); 191 | } 192 | } 193 | 194 | void Input::process(SDL_ControllerDeviceEvent const* event) { 195 | if (!SDL_IsGameController(event->which)) { 196 | _logger->warn("SDL device %d is not a controller", event->which); 197 | return; 198 | } 199 | 200 | if (event->type == SDL_CONTROLLERDEVICEADDED) { 201 | SDL_GameController* const controller = SDL_GameControllerOpen(event->which); 202 | 203 | if (controller == nullptr) { 204 | _logger->error("SDL_GameControllerOpen() failed: %s", SDL_GetError()); 205 | return; 206 | } 207 | 208 | SDL_Joystick* const joystick = SDL_GameControllerGetJoystick(controller); 209 | 210 | if (joystick == nullptr) { 211 | _logger->error("SDL_GameControllerGetJoystick() failed: %s", SDL_GetError()); 212 | SDL_GameControllerClose(controller); 213 | return; 214 | } 215 | 216 | auto inserted = _gamepads.insert(std::make_pair(event->which, Gamepad())); 217 | Gamepad* const gamepad = &inserted.first->second; 218 | 219 | gamepad->controller = controller; 220 | gamepad->joystick = joystick; 221 | gamepad->deviceIndex = event->which; 222 | gamepad->instanceId = SDL_JoystickInstanceID(joystick); 223 | gamepad->controllerName = SDL_GameControllerName(controller); 224 | gamepad->joystickName = SDL_JoystickName(joystick); 225 | 226 | _ports.emplace_back(gamepad); 227 | _logger->info("Controller %s (%s) added", gamepad->controllerName.c_str(), gamepad->joystickName.c_str()); 228 | 229 | size_t const count = _ports.size(); 230 | 231 | for (size_t i = 0; i < count; i++) { 232 | Gamepad const* const gamepad = _ports[i]; 233 | _logger->info(" Port %zu has controller %s (%s)", i + 1, gamepad->controllerName.c_str(), gamepad->joystickName.c_str()); 234 | } 235 | } 236 | else if (event->type == SDL_CONTROLLERDEVICEREMOVED) { 237 | auto found = _gamepads.find(event->which); 238 | 239 | if (found == _gamepads.end()) { 240 | return; 241 | } 242 | 243 | Gamepad* const gamepad = &found->second; 244 | SDL_GameControllerClose(gamepad->controller); 245 | 246 | for (auto it = _ports.begin(); it != _ports.end(); ++it) { 247 | if (*it == gamepad) { 248 | _ports.erase(it); 249 | break; 250 | } 251 | } 252 | 253 | _gamepads.erase(found); 254 | _logger->info("Controller %s (%s) removed", gamepad->controllerName.c_str(), gamepad->joystickName.c_str()); 255 | 256 | size_t const count = _ports.size(); 257 | 258 | for (size_t i = 0; i < count; i++) { 259 | Gamepad const* const gamepad = _ports[i]; 260 | _logger->info(" Port %zu has controller %s (%s)", i + 1, gamepad->controllerName.c_str(), gamepad->joystickName.c_str()); 261 | } 262 | } 263 | } 264 | 265 | void Input::process(SDL_ControllerButtonEvent const* event) { 266 | auto found = _gamepads.find(event->which); 267 | 268 | if (found == _gamepads.end()) { 269 | return; 270 | } 271 | 272 | Gamepad* const gamepad = &found->second; 273 | unsigned button = 0; 274 | 275 | switch (event->button) { 276 | case SDL_CONTROLLER_BUTTON_A: button = RETRO_DEVICE_ID_JOYPAD_B; break; 277 | case SDL_CONTROLLER_BUTTON_B: button = RETRO_DEVICE_ID_JOYPAD_A; break; 278 | case SDL_CONTROLLER_BUTTON_X: button = RETRO_DEVICE_ID_JOYPAD_Y; break; 279 | case SDL_CONTROLLER_BUTTON_Y: button = RETRO_DEVICE_ID_JOYPAD_X; break; 280 | case SDL_CONTROLLER_BUTTON_BACK: button = RETRO_DEVICE_ID_JOYPAD_SELECT; break; 281 | case SDL_CONTROLLER_BUTTON_START: button = RETRO_DEVICE_ID_JOYPAD_START; break; 282 | case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = RETRO_DEVICE_ID_JOYPAD_L3; break; 283 | case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = RETRO_DEVICE_ID_JOYPAD_R3; break; 284 | case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = RETRO_DEVICE_ID_JOYPAD_L; break; 285 | case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = RETRO_DEVICE_ID_JOYPAD_R; break; 286 | case SDL_CONTROLLER_BUTTON_DPAD_UP: button = RETRO_DEVICE_ID_JOYPAD_UP; break; 287 | case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = RETRO_DEVICE_ID_JOYPAD_DOWN; break; 288 | case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = RETRO_DEVICE_ID_JOYPAD_LEFT; break; 289 | case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = RETRO_DEVICE_ID_JOYPAD_RIGHT; break; 290 | case SDL_CONTROLLER_BUTTON_GUIDE: // fallthrough 291 | default: return; 292 | } 293 | 294 | gamepad->state[button] = event->state == SDL_PRESSED; 295 | } 296 | 297 | void Input::process(SDL_ControllerAxisEvent const* event) { 298 | auto found = _gamepads.find(event->which); 299 | 300 | if (found == _gamepads.end()) { 301 | return; 302 | } 303 | 304 | Gamepad* const gamepad = &found->second; 305 | 306 | int const threshold = 32767 * (1.0f - gamepad->sensitivity); 307 | int positive = 0, negative = 0; 308 | int button = 0; 309 | int* lastDir = nullptr; 310 | 311 | switch (event->axis) { 312 | case SDL_CONTROLLER_AXIS_LEFTX: 313 | case SDL_CONTROLLER_AXIS_LEFTY: 314 | case SDL_CONTROLLER_AXIS_RIGHTX: 315 | case SDL_CONTROLLER_AXIS_RIGHTY: 316 | switch (event->axis) { 317 | case SDL_CONTROLLER_AXIS_LEFTX: 318 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_LEFT].x = event->value; 319 | positive = RETRO_DEVICE_ID_JOYPAD_RIGHT; 320 | negative = RETRO_DEVICE_ID_JOYPAD_LEFT; 321 | lastDir = gamepad->lastDir + 0; 322 | break; 323 | 324 | case SDL_CONTROLLER_AXIS_LEFTY: 325 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_LEFT].y = event->value; 326 | positive = RETRO_DEVICE_ID_JOYPAD_DOWN; 327 | negative = RETRO_DEVICE_ID_JOYPAD_UP; 328 | lastDir = gamepad->lastDir + 1; 329 | break; 330 | 331 | case SDL_CONTROLLER_AXIS_RIGHTX: 332 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_RIGHT].x = event->value; 333 | positive = RETRO_DEVICE_ID_JOYPAD_RIGHT; 334 | negative = RETRO_DEVICE_ID_JOYPAD_LEFT; 335 | lastDir = gamepad->lastDir + 2; 336 | break; 337 | 338 | case SDL_CONTROLLER_AXIS_RIGHTY: 339 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_RIGHT].y = event->value; 340 | positive = RETRO_DEVICE_ID_JOYPAD_DOWN; 341 | negative = RETRO_DEVICE_ID_JOYPAD_UP; 342 | lastDir = gamepad->lastDir + 3; 343 | break; 344 | } 345 | 346 | if (event->value < -threshold) { 347 | button = negative; 348 | } 349 | else if (event->value > threshold) { 350 | button = positive; 351 | } 352 | else { 353 | button = -1; 354 | } 355 | 356 | break; 357 | 358 | case SDL_CONTROLLER_AXIS_TRIGGERLEFT: 359 | case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: 360 | if (event->axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) { 361 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_BUTTON].x = event->value; 362 | button = RETRO_DEVICE_ID_JOYPAD_L2; 363 | lastDir = gamepad->lastDir + 4; 364 | } 365 | else { 366 | gamepad->analogs[RETRO_DEVICE_INDEX_ANALOG_BUTTON].y = event->value; 367 | button = RETRO_DEVICE_ID_JOYPAD_R2; 368 | lastDir = gamepad->lastDir + 5; 369 | } 370 | 371 | break; 372 | 373 | default: 374 | return; 375 | } 376 | 377 | if (gamepad->digital) { 378 | if (*lastDir != -1) { 379 | gamepad->state[*lastDir] = false; 380 | } 381 | 382 | if (event->value < -threshold || event->value > threshold) { 383 | gamepad->state[button] = true; 384 | } 385 | 386 | *lastDir = button; 387 | } 388 | } 389 | 390 | void Input::process(SDL_KeyboardEvent const* event) { 391 | if (event->repeat) { 392 | return; 393 | } 394 | 395 | unsigned const key = keycodeToLibretro(event->keysym.sym); 396 | 397 | if (key != RETROK_UNKNOWN) { 398 | _keyboardState[key] = event->state == SDL_PRESSED; 399 | } 400 | } 401 | 402 | void Input::process(SDL_MouseButtonEvent const* event) { 403 | switch (event->button) { 404 | case SDL_BUTTON_LEFT: 405 | _mouseButtons[0] = event->state == SDL_PRESSED; 406 | break; 407 | 408 | case SDL_BUTTON_MIDDLE: 409 | _mouseButtons[1] = event->state == SDL_PRESSED; 410 | break; 411 | 412 | case SDL_BUTTON_RIGHT: 413 | _mouseButtons[2] = event->state == SDL_PRESSED; 414 | break; 415 | 416 | case SDL_BUTTON_X1: 417 | _mouseButtons[3] = event->state == SDL_PRESSED; 418 | break; 419 | 420 | case SDL_BUTTON_X2: 421 | _mouseButtons[4] = event->state == SDL_PRESSED; 422 | break; 423 | } 424 | } 425 | 426 | void Input::process(SDL_MouseMotionEvent const* event) { 427 | _mouseX += event->xrel; 428 | _mouseY += event->yrel; 429 | } 430 | 431 | void Input::reset() { 432 | _logger = nullptr; 433 | 434 | _gamepads.clear(); 435 | _ports.clear(); 436 | 437 | _mouseX = _mouseY = 0; 438 | memset(_mouseButtons, 0, sizeof(_mouseButtons)); 439 | 440 | _keyboardCallback.callback = nullptr; 441 | memset(_keyboardPreviousState, 0, sizeof(_keyboardPreviousState)); 442 | memset(_keyboardState, 0, sizeof(_keyboardState)); 443 | } 444 | 445 | unsigned Input::keycodeToLibretro(SDL_Keycode code) { 446 | switch (code) { 447 | case SDLK_RETURN: return RETROK_RETURN; 448 | case SDLK_ESCAPE: return RETROK_ESCAPE; 449 | case SDLK_BACKSPACE: return RETROK_BACKSPACE; 450 | case SDLK_TAB: return RETROK_TAB; 451 | case SDLK_SPACE: return RETROK_SPACE; 452 | case SDLK_EXCLAIM: return RETROK_EXCLAIM; 453 | case SDLK_QUOTEDBL: return RETROK_QUOTEDBL; 454 | case SDLK_HASH: return RETROK_HASH; 455 | case SDLK_DOLLAR: return RETROK_DOLLAR; 456 | case SDLK_AMPERSAND: return RETROK_AMPERSAND; 457 | case SDLK_QUOTE: return RETROK_QUOTE; 458 | case SDLK_LEFTPAREN: return RETROK_LEFTPAREN; 459 | case SDLK_RIGHTPAREN: return RETROK_RIGHTPAREN; 460 | case SDLK_ASTERISK: return RETROK_ASTERISK; 461 | case SDLK_PLUS: return RETROK_PLUS; 462 | case SDLK_COMMA: return RETROK_COMMA; 463 | case SDLK_MINUS: return RETROK_MINUS; 464 | case SDLK_PERIOD: return RETROK_PERIOD; 465 | case SDLK_SLASH: return RETROK_SLASH; 466 | case SDLK_0: return RETROK_0; 467 | case SDLK_1: return RETROK_1; 468 | case SDLK_2: return RETROK_2; 469 | case SDLK_3: return RETROK_3; 470 | case SDLK_4: return RETROK_4; 471 | case SDLK_5: return RETROK_5; 472 | case SDLK_6: return RETROK_6; 473 | case SDLK_7: return RETROK_7; 474 | case SDLK_8: return RETROK_8; 475 | case SDLK_9: return RETROK_9; 476 | case SDLK_COLON: return RETROK_COLON; 477 | case SDLK_SEMICOLON: return RETROK_SEMICOLON; 478 | case SDLK_LESS: return RETROK_LESS; 479 | case SDLK_EQUALS: return RETROK_EQUALS; 480 | case SDLK_GREATER: return RETROK_GREATER; 481 | case SDLK_QUESTION: return RETROK_QUESTION; 482 | case SDLK_AT: return RETROK_AT; 483 | case SDLK_LEFTBRACKET: return RETROK_LEFTBRACKET; 484 | case SDLK_BACKSLASH: return RETROK_BACKSLASH; 485 | case SDLK_RIGHTBRACKET: return RETROK_RIGHTBRACKET; 486 | case SDLK_CARET: return RETROK_CARET; 487 | case SDLK_UNDERSCORE: return RETROK_UNDERSCORE; 488 | case SDLK_BACKQUOTE: return RETROK_BACKQUOTE; 489 | case SDLK_a: return RETROK_a; 490 | case SDLK_b: return RETROK_b; 491 | case SDLK_c: return RETROK_c; 492 | case SDLK_d: return RETROK_d; 493 | case SDLK_e: return RETROK_e; 494 | case SDLK_f: return RETROK_f; 495 | case SDLK_g: return RETROK_g; 496 | case SDLK_h: return RETROK_h; 497 | case SDLK_i: return RETROK_i; 498 | case SDLK_j: return RETROK_j; 499 | case SDLK_k: return RETROK_k; 500 | case SDLK_l: return RETROK_l; 501 | case SDLK_m: return RETROK_m; 502 | case SDLK_n: return RETROK_n; 503 | case SDLK_o: return RETROK_o; 504 | case SDLK_p: return RETROK_p; 505 | case SDLK_q: return RETROK_q; 506 | case SDLK_r: return RETROK_r; 507 | case SDLK_s: return RETROK_s; 508 | case SDLK_t: return RETROK_t; 509 | case SDLK_u: return RETROK_u; 510 | case SDLK_v: return RETROK_v; 511 | case SDLK_w: return RETROK_w; 512 | case SDLK_x: return RETROK_x; 513 | case SDLK_y: return RETROK_y; 514 | case SDLK_z: return RETROK_z; 515 | case SDLK_CAPSLOCK: return RETROK_CAPSLOCK; 516 | case SDLK_F1: return RETROK_F1; 517 | case SDLK_F2: return RETROK_F2; 518 | case SDLK_F3: return RETROK_F3; 519 | case SDLK_F4: return RETROK_F4; 520 | case SDLK_F5: return RETROK_F5; 521 | case SDLK_F6: return RETROK_F6; 522 | case SDLK_F7: return RETROK_F7; 523 | case SDLK_F8: return RETROK_F8; 524 | case SDLK_F9: return RETROK_F9; 525 | case SDLK_F10: return RETROK_F10; 526 | case SDLK_F11: return RETROK_F11; 527 | case SDLK_F12: return RETROK_F12; 528 | case SDLK_PRINTSCREEN: return RETROK_PRINT; 529 | case SDLK_SCROLLLOCK: return RETROK_SCROLLOCK; 530 | case SDLK_PAUSE: return RETROK_PAUSE; 531 | case SDLK_INSERT: return RETROK_INSERT; 532 | case SDLK_HOME: return RETROK_HOME; 533 | case SDLK_PAGEUP: return RETROK_PAGEUP; 534 | case SDLK_DELETE: return RETROK_DELETE; 535 | case SDLK_END: return RETROK_END; 536 | case SDLK_PAGEDOWN: return RETROK_PAGEDOWN; 537 | case SDLK_RIGHT: return RETROK_RIGHT; 538 | case SDLK_LEFT: return RETROK_LEFT; 539 | case SDLK_DOWN: return RETROK_DOWN; 540 | case SDLK_UP: return RETROK_UP; 541 | case SDLK_NUMLOCKCLEAR: return RETROK_NUMLOCK; 542 | case SDLK_KP_DIVIDE: return RETROK_KP_DIVIDE; 543 | case SDLK_KP_MULTIPLY: return RETROK_KP_MULTIPLY; 544 | case SDLK_KP_MINUS: return RETROK_KP_MINUS; 545 | case SDLK_KP_PLUS: return RETROK_KP_PLUS; 546 | case SDLK_KP_ENTER: return RETROK_KP_ENTER; 547 | case SDLK_KP_1: return RETROK_KP1; 548 | case SDLK_KP_2: return RETROK_KP2; 549 | case SDLK_KP_3: return RETROK_KP3; 550 | case SDLK_KP_4: return RETROK_KP4; 551 | case SDLK_KP_5: return RETROK_KP5; 552 | case SDLK_KP_6: return RETROK_KP6; 553 | case SDLK_KP_7: return RETROK_KP7; 554 | case SDLK_KP_8: return RETROK_KP8; 555 | case SDLK_KP_9: return RETROK_KP9; 556 | case SDLK_KP_0: return RETROK_KP0; 557 | case SDLK_KP_PERIOD: return RETROK_KP_PERIOD; 558 | case SDLK_APPLICATION: return RETROK_COMPOSE; 559 | case SDLK_POWER: return RETROK_POWER; 560 | case SDLK_KP_EQUALS: return RETROK_KP_EQUALS; 561 | case SDLK_F13: return RETROK_F13; 562 | case SDLK_F14: return RETROK_F14; 563 | case SDLK_F15: return RETROK_F15; 564 | case SDLK_HELP: return RETROK_HELP; 565 | case SDLK_MENU: return RETROK_MENU; 566 | case SDLK_UNDO: return RETROK_UNDO; 567 | case SDLK_SYSREQ: return RETROK_SYSREQ; 568 | case SDLK_LCTRL: return RETROK_LCTRL; 569 | case SDLK_LSHIFT: return RETROK_LSHIFT; 570 | case SDLK_LALT: return RETROK_LALT; 571 | case SDLK_LGUI: return RETROK_LMETA; 572 | case SDLK_RCTRL: return RETROK_RCTRL; 573 | case SDLK_RSHIFT: return RETROK_RSHIFT; 574 | case SDLK_RALT: return RETROK_RALT; 575 | case SDLK_RGUI: return RETROK_RMETA; 576 | case SDLK_MODE: return RETROK_MODE; 577 | 578 | default: return RETROK_UNKNOWN; 579 | } 580 | } 581 | 582 | Input::Gamepad::Gamepad() { 583 | deviceIndex = 0; 584 | instanceId = 0; 585 | controller = nullptr; 586 | joystick = nullptr; 587 | 588 | lastDir[0] = 0; lastDir[1] = 0; lastDir[2] = 0; 589 | lastDir[3] = 0; lastDir[4] = 0; lastDir[5] = 0; 590 | 591 | state[0] = false; state[1] = false; state[2] = false; state[3] = false; 592 | state[4] = false; state[5] = false; state[6] = false; state[7] = false; 593 | state[8] = false; state[9] = false; state[10] = false; state[11] = false; 594 | state[12] = false; state[13] = false; state[14] = false; state[15] = false; 595 | 596 | analogs[0].x = 0; analogs[0].y = 0; 597 | analogs[1].x = 0; analogs[1].y = 0; 598 | analogs[2].x = 0; analogs[2].y = 0; 599 | 600 | sensitivity = 0.5f; 601 | digital = false; 602 | } 603 | -------------------------------------------------------------------------------- /examples/sdl2/Input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | class Input: public lrcpp::Input { 12 | public: 13 | Input(); 14 | 15 | bool init(lrcpp::Logger* logger); 16 | void destroy(); 17 | 18 | void process(SDL_Event const* event); 19 | 20 | // lrcpp::Input 21 | virtual bool setInputDescriptors(retro_input_descriptor const* descriptors) override; 22 | virtual bool setKeyboardCallback(retro_keyboard_callback const* callback) override; 23 | virtual bool getInputDeviceCapabilities(uint64_t* capabilities) override; 24 | virtual bool setControllerInfo(retro_controller_info const* info) override; 25 | virtual bool getInputBitmasks(bool* supports) override; 26 | 27 | virtual int16_t state(unsigned port, unsigned device, unsigned index, unsigned id) override; 28 | virtual void poll() override; 29 | 30 | protected: 31 | void process(SDL_JoyDeviceEvent const* event); 32 | void process(SDL_ControllerDeviceEvent const* event); 33 | void process(SDL_ControllerButtonEvent const* event); 34 | void process(SDL_ControllerAxisEvent const* event); 35 | void process(SDL_KeyboardEvent const* event); 36 | void process(SDL_MouseButtonEvent const* event); 37 | void process(SDL_MouseMotionEvent const* event); 38 | void reset(); 39 | 40 | static unsigned keycodeToLibretro(SDL_Keycode code); 41 | 42 | struct Gamepad { 43 | Gamepad(); 44 | 45 | struct Axes { 46 | int16_t x, y; 47 | }; 48 | 49 | Sint32 deviceIndex; 50 | SDL_JoystickID instanceId; 51 | SDL_GameController* controller; 52 | std::string controllerName; 53 | SDL_Joystick* joystick; 54 | std::string joystickName; 55 | int lastDir[6]; 56 | bool state[16]; 57 | Axes analogs[3]; 58 | float sensitivity; 59 | bool digital; 60 | }; 61 | 62 | lrcpp::Logger* _logger; 63 | 64 | std::map _gamepads; 65 | std::vector _ports; 66 | 67 | int _mouseX; 68 | int _mouseY; 69 | bool _mouseButtons[5]; 70 | 71 | retro_keyboard_callback _keyboardCallback; 72 | bool _keyboardPreviousState[RETROK_LAST]; 73 | bool _keyboardState[RETROK_LAST]; 74 | }; 75 | -------------------------------------------------------------------------------- /examples/sdl2/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | Logger::Logger() {} 4 | 5 | bool Logger::init() { 6 | _priority = SDL_LOG_PRIORITY_INFO; 7 | return true; 8 | } 9 | 10 | void Logger::destroy() {} 11 | 12 | void Logger::setLevel(retro_log_level level) { 13 | _priority = levelToPriority(level); 14 | SDL_LogSetAllPriority(_priority); 15 | } 16 | 17 | void Logger::vprintf(retro_log_level level, char const* format, va_list args) { 18 | SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, levelToPriority(level), format, args); 19 | } 20 | 21 | SDL_LogPriority Logger::levelToPriority(retro_log_level level) { 22 | switch (level) { 23 | case RETRO_LOG_DEBUG: return SDL_LOG_PRIORITY_DEBUG; 24 | case RETRO_LOG_INFO: return SDL_LOG_PRIORITY_INFO; 25 | case RETRO_LOG_WARN: return SDL_LOG_PRIORITY_WARN; 26 | case RETRO_LOG_ERROR: return SDL_LOG_PRIORITY_ERROR; 27 | 28 | default: return SDL_LOG_PRIORITY_VERBOSE; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/sdl2/Logger.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | class Logger : public lrcpp::Logger { 6 | public: 7 | Logger(); 8 | 9 | bool init(); 10 | void destroy(); 11 | 12 | void setLevel(retro_log_level level); 13 | 14 | // lrcpp::Logger 15 | virtual void vprintf(retro_log_level level, char const* format, va_list args) override; 16 | 17 | protected: 18 | static SDL_LogPriority levelToPriority(retro_log_level level); 19 | 20 | SDL_LogPriority _priority; 21 | }; 22 | -------------------------------------------------------------------------------- /examples/sdl2/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static void Usage(FILE* out) { 10 | fprintf(out, "Usage: sdl2lrcpp [options...] \n\n"); 11 | fprintf(out, "-L, --libretro The path to the libretro core\n"); 12 | fprintf(out, "-c, --config The path to the configuration file (optional)\n"); 13 | fprintf(out, "--appendconfig Paths for additional configuration files, separated by commas\n"); 14 | fprintf(out, "-v, --verbose Increase the verboseness\n"); 15 | fprintf(out, "-h, --help Shows this help screen and exit\n"); 16 | fprintf(out, " The path to the content file to use with the core\n"); 17 | } 18 | 19 | extern "C" int Main(int argc, char const* argv[]) { 20 | char const* corePath = nullptr; 21 | std::vector configPaths; 22 | bool configSpecified = false; 23 | char const* contentPath = nullptr; 24 | int verboseness = 0; 25 | 26 | for (int i = 1; i < argc; i++) { 27 | if (strcmp(argv[i], "-L") == 0 || strcmp(argv[i], "--libretro") == 0) { 28 | if (++i == argc) { 29 | fprintf(stderr, "Error: missing core path after --libretro\n"); 30 | return EXIT_FAILURE; 31 | } 32 | 33 | if (corePath != nullptr) { 34 | fprintf(stderr, "Error: core path already specified\n"); 35 | return EXIT_FAILURE; 36 | } 37 | 38 | corePath = argv[i]; 39 | } 40 | else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) { 41 | if (++i == argc) { 42 | fprintf(stderr, "Error: missing configuration path after --config\n"); 43 | return EXIT_FAILURE; 44 | } 45 | 46 | if (configSpecified) { 47 | fprintf(stderr, "Error: configuration path already specified\n"); 48 | return EXIT_FAILURE; 49 | } 50 | 51 | configPaths.emplace(configPaths.begin(), argv[i]); 52 | configSpecified = true; 53 | } 54 | else if (strcmp(argv[i], "--appendconfig") == 0) { 55 | if (++i == argc) { 56 | fprintf(stderr, "Error: missing configuration path after --config\n"); 57 | return EXIT_FAILURE; 58 | } 59 | 60 | char const* aux = argv[i]; 61 | 62 | while (*aux != 0) { 63 | char const* const comma = strchr(aux, ','); 64 | 65 | if (comma == nullptr) { 66 | configPaths.emplace_back(aux); 67 | break; 68 | } 69 | 70 | configPaths.emplace_back(aux, comma - aux); 71 | aux = comma + 1; 72 | } 73 | } 74 | else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { 75 | verboseness++; 76 | } 77 | else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { 78 | Usage(stdout); 79 | return EXIT_SUCCESS; 80 | } 81 | else { 82 | if (contentPath != nullptr) { 83 | fprintf(stderr, "Error: content path already specified\n"); 84 | return EXIT_FAILURE; 85 | } 86 | 87 | contentPath = argv[i]; 88 | } 89 | } 90 | 91 | if (corePath == nullptr || contentPath == nullptr) { 92 | Usage(stderr); 93 | return EXIT_FAILURE; 94 | } 95 | 96 | Player player; 97 | 98 | if (!player.init(configPaths, corePath, contentPath, verboseness)) { 99 | return EXIT_FAILURE; 100 | } 101 | 102 | player.run(); 103 | player.destroy(); 104 | 105 | return EXIT_SUCCESS; 106 | } 107 | -------------------------------------------------------------------------------- /examples/sdl2/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDES=-I../../include -I. 2 | CFLAGS=-O2 -std=c99 -Werror -Wall -Wpedantic `sdl2-config --cflags` 3 | CXXFLAGS=-O2 -std=c++11 -Werror -Wall -Wpedantic `sdl2-config --cflags` 4 | LDFLAGS=`sdl2-config --libs` 5 | 6 | ifneq ($(findstring Linux,$(shell uname -a)),) 7 | LDFLAGS+=-ldl 8 | endif 9 | 10 | %.o: %.c 11 | gcc $(INCLUDES) $(CFLAGS) -c $< -o $@ 12 | 13 | %.o: %.cpp 14 | g++ $(INCLUDES) $(CXXFLAGS) -c $< -o $@ 15 | 16 | OBJECTS=\ 17 | Audio.o Config.o Entry.o Input.o Logger.o Main.o Perf.o Player.o Video.o \ 18 | ../../src/Components.o ../../src/CoreFsm.o ../../src/Frontend.o 19 | 20 | all: sdl2lrcpp 21 | 22 | sdl2lrcpp: $(OBJECTS) 23 | g++ -o $@ $+ $(LDFLAGS) 24 | 25 | clean: 26 | rm -f sdl2lrcpp $(OBJECTS) 27 | 28 | .PHONY: clean 29 | -------------------------------------------------------------------------------- /examples/sdl2/Perf.cpp: -------------------------------------------------------------------------------- 1 | #include "Perf.h" 2 | 3 | #include 4 | #include 5 | 6 | bool Perf::init() { 7 | return true; 8 | } 9 | 10 | void Perf::destroy() {} 11 | 12 | uint64_t Perf::getTimeUs() { 13 | struct timespec ts = {0}; 14 | 15 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { 16 | return 0; 17 | } 18 | 19 | return static_cast(ts.tv_sec * 1000000 + ts.tv_nsec / 1000); 20 | } 21 | 22 | uint64_t Perf::getTimeNs() { 23 | struct timespec ts = {0}; 24 | 25 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { 26 | return 0; 27 | } 28 | 29 | return static_cast(ts.tv_sec * 1000000000 + ts.tv_nsec); 30 | } 31 | 32 | retro_time_t Perf::getTimeUsec() { 33 | return static_cast(getTimeUs()); 34 | } 35 | 36 | uint64_t Perf::getCpuFeatures() { 37 | // TODO detect other CPU features. 38 | uint64_t features = 0; 39 | 40 | features |= SDL_HasAVX() ? RETRO_SIMD_AVX : 0; 41 | features |= SDL_HasAVX2() ? RETRO_SIMD_AVX2 : 0; 42 | features |= SDL_HasMMX() ? RETRO_SIMD_MMX : 0; 43 | features |= SDL_HasSSE() ? RETRO_SIMD_SSE : 0; 44 | features |= SDL_HasSSE2() ? RETRO_SIMD_SSE2 : 0; 45 | features |= SDL_HasSSE3() ? RETRO_SIMD_SSE3 : 0; 46 | features |= SDL_HasSSE42() ? RETRO_SIMD_SSE42 : 0; 47 | 48 | return features; 49 | } 50 | 51 | retro_perf_tick_t Perf::getCounter() { 52 | return static_cast(getTimeNs()); 53 | } 54 | 55 | void Perf::register_(retro_perf_counter* counter) { 56 | (void)counter; 57 | } 58 | 59 | void Perf::start(retro_perf_counter* counter) { 60 | (void)counter; 61 | } 62 | 63 | void Perf::stop(retro_perf_counter* counter) { 64 | (void)counter; 65 | } 66 | 67 | void Perf::log() {} 68 | -------------------------------------------------------------------------------- /examples/sdl2/Perf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | class Perf : public lrcpp::Perf { 8 | public: 9 | bool init(); 10 | void destroy(); 11 | 12 | static uint64_t getTimeUs(); 13 | static uint64_t getTimeNs(); 14 | 15 | // lrcpp::Perf 16 | virtual retro_time_t getTimeUsec() override; 17 | virtual uint64_t getCpuFeatures() override; 18 | virtual retro_perf_tick_t getCounter() override; 19 | virtual void register_(retro_perf_counter* counter) override; 20 | virtual void start(retro_perf_counter* counter) override; 21 | virtual void stop(retro_perf_counter* counter) override; 22 | virtual void log() override; 23 | }; 24 | -------------------------------------------------------------------------------- /examples/sdl2/Player.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.h" 2 | 3 | #include 4 | #include 5 | 6 | bool Player::loadCore(char const *path) 7 | { 8 | if (!_dynlib.load(path)) { 9 | _logger.error("Failed to load library: "); 10 | return false; 11 | } 12 | 13 | #define LOAD_CORE_FUNC(member_name, name) \ 14 | _core.member_name = reinterpret_cast(_dynlib.getSymbol("retro_" #name)); \ 15 | if (!_core.member_name) { \ 16 | _logger.error("Missing symbol: retro_" #name); \ 17 | return false; \ 18 | } 19 | 20 | LOAD_CORE_FUNC(init, init) 21 | LOAD_CORE_FUNC(deinit, deinit) 22 | LOAD_CORE_FUNC(apiVersion, api_version) 23 | LOAD_CORE_FUNC(getSystemInfo, get_system_info) 24 | LOAD_CORE_FUNC(getSystemAvInfo, get_system_av_info) 25 | LOAD_CORE_FUNC(setEnvironment, set_environment) 26 | LOAD_CORE_FUNC(setVideoRefresh, set_video_refresh) 27 | LOAD_CORE_FUNC(setAudioSample, set_audio_sample) 28 | LOAD_CORE_FUNC(setAudioSampleBatch, set_audio_sample_batch) 29 | LOAD_CORE_FUNC(setInputPoll, set_input_poll) 30 | LOAD_CORE_FUNC(setInputState, set_input_state) 31 | LOAD_CORE_FUNC(setControllerPortDevice, set_controller_port_device) 32 | LOAD_CORE_FUNC(reset, reset) 33 | LOAD_CORE_FUNC(run, run) 34 | LOAD_CORE_FUNC(serializeSize, serialize_size) 35 | LOAD_CORE_FUNC(serialize, serialize) 36 | LOAD_CORE_FUNC(unserialize, unserialize) 37 | LOAD_CORE_FUNC(cheatReset, cheat_reset) 38 | LOAD_CORE_FUNC(cheatSet, cheat_set) 39 | LOAD_CORE_FUNC(loadGame, load_game) 40 | LOAD_CORE_FUNC(loadGameSpecial, load_game_special) 41 | LOAD_CORE_FUNC(unloadGame, unload_game) 42 | LOAD_CORE_FUNC(getRegion, get_region) 43 | LOAD_CORE_FUNC(getMemoryData, get_memory_data) 44 | LOAD_CORE_FUNC(getMemorySize, get_memory_size) 45 | 46 | #undef LOAD_CORE_FUNC 47 | 48 | // If needed: keep lib alive by returning it or capturing in a smart pointer 49 | return true; 50 | } 51 | 52 | bool Player::init(std::vector const& configPaths, char const* corePath, char const* contentPath, int verboseness) { 53 | 54 | if (!_logger.init()) { 55 | // Doesn't really happen 56 | return false; 57 | } 58 | 59 | _logger.setLevel(RETRO_LOG_WARN); 60 | 61 | if (!_config.init(configPaths, contentPath, corePath, &_logger)) { 62 | _logger.error("Could not initialize the configuration component"); 63 | _logger.destroy(); 64 | return false; 65 | } 66 | 67 | { 68 | unsigned long level = 2; 69 | _config.getOption("libretro_log_level", &level); 70 | long realLevel = (long)level - verboseness; 71 | 72 | if (realLevel <= 0) { 73 | _logger.setLevel(RETRO_LOG_DEBUG); 74 | } 75 | else if (realLevel == 1) { 76 | _logger.setLevel(RETRO_LOG_INFO); 77 | } 78 | else if (realLevel == 2) { 79 | _logger.setLevel(RETRO_LOG_WARN); 80 | } 81 | else if (realLevel >= 3) { 82 | _logger.setLevel(RETRO_LOG_ERROR); 83 | } 84 | } 85 | 86 | if (SDL_InitSubSystem(SDL_INIT_EVENTS) != 0) { 87 | _logger.error("SDL_InitSubSystem(SDL_INIT_EVENTS) failed: %s", SDL_GetError()); 88 | _config.destroy(); 89 | _logger.destroy(); 90 | SDL_Quit(); 91 | return false; 92 | } 93 | 94 | if (!_perf.init()) { 95 | _logger.error("Could not initialize the perf component"); 96 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 97 | _config.destroy(); 98 | _logger.destroy(); 99 | SDL_Quit(); 100 | return false; 101 | } 102 | 103 | if (!_audio.init(&_config, &_logger)) { 104 | _logger.error("Could not initialize the audio component"); 105 | _perf.destroy(); 106 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 107 | _config.destroy(); 108 | _logger.destroy(); 109 | SDL_Quit(); 110 | return false; 111 | } 112 | 113 | if (!_video.init(&_config, &_logger)) { 114 | _logger.error("Could not initialize the video component"); 115 | _audio.destroy(); 116 | _perf.destroy(); 117 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 118 | _config.destroy(); 119 | _logger.destroy(); 120 | SDL_Quit(); 121 | return false; 122 | } 123 | 124 | if (!_input.init(&_logger)) { 125 | _logger.error("Could not initialize the input component"); 126 | _video.destroy(); 127 | _audio.destroy(); 128 | _perf.destroy(); 129 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 130 | _config.destroy(); 131 | _logger.destroy(); 132 | SDL_Quit(); 133 | return false; 134 | } 135 | 136 | if (!_frontend.setLogger(&_logger) || !_frontend.setConfig(&_config) || !_frontend.setVideo(&_video)) { 137 | _logger.error("Could not set components in the frontend"); 138 | 139 | error: 140 | _input.destroy(); 141 | _video.destroy(); 142 | _audio.destroy(); 143 | _perf.destroy(); 144 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 145 | _config.destroy(); 146 | _logger.destroy(); 147 | SDL_Quit(); 148 | return false; 149 | } 150 | 151 | if (!_frontend.setPerf(&_perf) || !_frontend.setAudio(&_audio) || !_frontend.setInput(&_input)) { 152 | _logger.error("Could not set components in the frontend"); 153 | goto error; 154 | } 155 | 156 | _logger.info("Loading core from \"%s\"", corePath); 157 | 158 | if (!loadCore(corePath)) { 159 | _logger.error("Failed to initialize core."); 160 | goto error; 161 | } 162 | 163 | if (!_frontend.setCore(&_core)) { 164 | _logger.error("Could not load the core from \"%s\"", corePath); 165 | _dynlib.unload(); 166 | goto error; 167 | } 168 | 169 | retro_system_info sysinfo; 170 | 171 | if (!_frontend.getSystemInfo(&sysinfo)) { 172 | _logger.error("Could not get the system info from the core"); 173 | _frontend.unset(); 174 | _dynlib.unload(); 175 | goto error; 176 | } 177 | 178 | _logger.info("System Info"); 179 | _logger.info(" library_name = %s", sysinfo.library_name); 180 | _logger.info(" library_version = %s", sysinfo.library_version); 181 | _logger.info(" valid_extensions = %s", sysinfo.valid_extensions); 182 | _logger.info(" need_fullpath = %s", sysinfo.need_fullpath ? "true" : "false"); 183 | _logger.info(" block_extract = %s", sysinfo.block_extract ? "true" : "false"); 184 | 185 | _logger.info("Loading content from \"%s\"", contentPath); 186 | 187 | bool ok = false; 188 | 189 | if (sysinfo.need_fullpath) { 190 | ok = _frontend.loadGame(contentPath); 191 | } 192 | else { 193 | size_t size = 0; 194 | void const* data = readAll(contentPath, &size); 195 | 196 | if (data != nullptr) { 197 | ok = _frontend.loadGame(contentPath, data, size); 198 | free(const_cast(data)); 199 | } 200 | } 201 | 202 | if (!ok) { 203 | _logger.error("Could not load content from \"%s\"", contentPath); 204 | _frontend.unset(); 205 | _dynlib.unload(); 206 | goto error; 207 | } 208 | 209 | return true; 210 | } 211 | 212 | void Player::destroy() { 213 | _frontend.unloadGame(); 214 | _frontend.unset(); 215 | 216 | _input.destroy(); 217 | _video.destroy(); 218 | _audio.destroy(); 219 | _config.destroy(); 220 | _perf.destroy(); 221 | SDL_QuitSubSystem(SDL_INIT_EVENTS); 222 | _logger.destroy(); 223 | 224 | _dynlib.unload(); 225 | 226 | SDL_Quit(); 227 | } 228 | 229 | void Player::run() { 230 | uint64_t const coreUsPerFrame = 1000000 / _video.getCoreFps(); 231 | uint64_t nextFrameTime = Perf::getTimeUs() + coreUsPerFrame; 232 | bool done = false; 233 | 234 | do { 235 | SDL_Event event; 236 | 237 | while (SDL_PollEvent(&event)) { 238 | // TODO process SDL events in the Input class 239 | 240 | if (event.type == SDL_QUIT) { 241 | done = true; 242 | } 243 | else { 244 | _input.process(&event); 245 | } 246 | } 247 | 248 | if (Perf::getTimeUs() >= nextFrameTime) { 249 | nextFrameTime += coreUsPerFrame; 250 | 251 | _audio.clear(); 252 | _video.clear(); 253 | _frontend.run(); 254 | _audio.present(); 255 | _video.present(); 256 | 257 | _logger.debug("Ran one frame"); 258 | } 259 | 260 | // TODO pause for less time for greater granularity 261 | SDL_Delay(1); 262 | } 263 | while (!done); 264 | } 265 | 266 | void const* Player::readAll(char const* path, size_t* size) { 267 | struct stat statbuf; 268 | 269 | if (stat(path, &statbuf) != 0) { 270 | _logger.error("Error getting content info: %s", strerror(errno)); 271 | return nullptr; 272 | } 273 | 274 | void* data = malloc(statbuf.st_size); 275 | 276 | if (data == nullptr) { 277 | _logger.error("Out of memory allocating %zu bytes", statbuf.st_size); 278 | return nullptr; 279 | } 280 | 281 | FILE* file = fopen(path, "rb"); 282 | 283 | if (file == nullptr) { 284 | _logger.error("Error opening content: %s", strerror(errno)); 285 | free(data); 286 | return nullptr; 287 | } 288 | 289 | size_t numread = fread(data, 1, statbuf.st_size, file); 290 | 291 | if (numread != (size_t)statbuf.st_size) { 292 | _logger.error("Error reading content: %s", strerror(errno)); 293 | fclose(file); 294 | free(data); 295 | return nullptr; 296 | } 297 | 298 | fclose(file); 299 | 300 | _logger.debug("Loaded data from \"%s\", %zu bytes", path, numread); 301 | *size = numread; 302 | return data; 303 | } 304 | -------------------------------------------------------------------------------- /examples/sdl2/Player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Logger.h" 4 | #include "Perf.h" 5 | #include "Config.h" 6 | #include "Audio.h" 7 | #include "Video.h" 8 | #include "Input.h" 9 | #include "DynLib.h" 10 | #include "lrcpp/Frontend.h" 11 | 12 | #include 13 | #include 14 | 15 | class Player { 16 | public: 17 | bool init(std::vector const& configPaths, char const* corePath, char const* contentPath, int verboseness); 18 | void destroy(); 19 | 20 | void run(); 21 | 22 | protected: 23 | void const* readAll(char const* path, size_t* size); 24 | bool loadCore(char const *path); 25 | 26 | lrcpp::Frontend _frontend; 27 | Logger _logger; 28 | Perf _perf; 29 | Config _config; 30 | Audio _audio; 31 | Video _video; 32 | Input _input; 33 | 34 | lrcpp::Core _core; 35 | DynLib _dynlib; 36 | }; 37 | -------------------------------------------------------------------------------- /examples/sdl2/Video.cpp: -------------------------------------------------------------------------------- 1 | #include "Video.h" 2 | 3 | Video::Video() { 4 | reset(); 5 | } 6 | 7 | bool Video::init(Config* config, lrcpp::Logger* logger) { 8 | reset(); 9 | 10 | _logger = logger; 11 | 12 | if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { 13 | logger->error("SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s", SDL_GetError()); 14 | return false; 15 | } 16 | 17 | _logger->info("Video subsystem initialized"); 18 | 19 | _window = SDL_CreateWindow("lrcpp example with SDL2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 20 | 800, 600, SDL_WINDOW_RESIZABLE); 21 | 22 | if (_window == nullptr) { 23 | logger->error("SDL_CreateWindow() failed: %s", SDL_GetError()); 24 | destroy(); 25 | return false; 26 | } 27 | 28 | _logger->info("Window created"); 29 | 30 | int const count = SDL_GetNumRenderDrivers(); 31 | int renderer = -1; 32 | char const* rendererName = nullptr; 33 | config->getOption("sdl2lrcpp_video_renderer", &rendererName); 34 | 35 | if (count > 0) { 36 | for (int i = 0; i < count; i++) { 37 | SDL_RendererInfo info; 38 | 39 | if (SDL_GetRenderDriverInfo(i, &info) != 0) { 40 | _logger->error("SDL_GetRenderDriverInfo() failed: %s", SDL_GetError()); 41 | } 42 | else { 43 | if (rendererName != nullptr && strcmp(rendererName, info.name) == 0) { 44 | renderer = i; 45 | } 46 | 47 | _logger->info("Render driver %d: %s", i, info.name); 48 | 49 | _logger->info( 50 | " flags:%s%s%s%s", 51 | (info.flags & SDL_RENDERER_SOFTWARE) != 0 ? " SDL_RENDERER_SOFTWARE" : "", 52 | (info.flags & SDL_RENDERER_ACCELERATED) != 0 ? " SDL_RENDERER_ACCELERATED" : "", 53 | (info.flags & SDL_RENDERER_PRESENTVSYNC) != 0 ? " SDL_RENDERER_PRESENTVSYNC" : "", 54 | (info.flags & SDL_RENDERER_TARGETTEXTURE) != 0 ? " SDL_RENDERER_TARGETTEXTURE" : "" 55 | ); 56 | 57 | for (Uint32 j = 0; j < info.num_texture_formats; j++) { 58 | _logger->info(" texture_formats[%u]: %s", j, SDL_GetPixelFormatName(info.texture_formats[j])); 59 | } 60 | 61 | _logger->info(" max_texture: %d x %d", info.max_texture_width, info.max_texture_height); 62 | } 63 | } 64 | } 65 | 66 | if (renderer == -1) { 67 | _logger->info("Using default video renderer"); 68 | } 69 | else { 70 | SDL_RendererInfo info; 71 | 72 | if (SDL_GetRenderDriverInfo(renderer, &info) != 0) { 73 | _logger->error("SDL_GetRenderDriverInfo() failed: %s", SDL_GetError()); 74 | } 75 | else { 76 | _logger->info("Using video renderer %s", info.name); 77 | } 78 | } 79 | 80 | _renderer = SDL_CreateRenderer(_window, renderer, SDL_RENDERER_ACCELERATED); 81 | 82 | if (_renderer == nullptr) { 83 | logger->error("SDL_CreateRenderer() failed: %s", SDL_GetError()); 84 | destroy(); 85 | return false; 86 | } 87 | 88 | _logger->info("Renderer created"); 89 | 90 | bool smooth = true; 91 | config->getOption("video_smooth", &smooth); 92 | 93 | if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, smooth ? "1" : "0")) { 94 | _logger->error("SDL_SetHint() failed: %s", SDL_GetError()); 95 | } 96 | 97 | return true; 98 | } 99 | 100 | void Video::destroy() { 101 | if (_texture != nullptr) { 102 | SDL_DestroyTexture(_texture); 103 | } 104 | 105 | if (_renderer != nullptr) { 106 | SDL_DestroyRenderer(_renderer); 107 | } 108 | 109 | if (_window != nullptr) { 110 | SDL_DestroyWindow(_window); 111 | } 112 | 113 | SDL_QuitSubSystem(SDL_INIT_VIDEO); 114 | reset(); 115 | } 116 | 117 | double Video::getCoreFps() const { 118 | return _coreFps; 119 | } 120 | 121 | void Video::clear() { 122 | if (SDL_RenderClear(_renderer) != 0) { 123 | _logger->error("SDL_RenderClear() failed: %s", SDL_GetError()); 124 | } 125 | } 126 | 127 | void Video::present() { 128 | int windowWidth = 0, windowHeight = 0; 129 | SDL_GetWindowSize(_window, &windowWidth, &windowHeight); 130 | 131 | float height = windowHeight; 132 | float width = height * _aspectRatio; 133 | 134 | if (width > windowWidth) { 135 | width = windowWidth; 136 | height = width / _aspectRatio; 137 | } 138 | 139 | SDL_Rect src; 140 | src.x = src.y = 0; 141 | src.w = _usedWidth; 142 | src.h = _usedHeight; 143 | 144 | SDL_FRect dest; 145 | dest.x = (windowWidth - width) / 2.0f; 146 | dest.y = (windowHeight - height) / 2.0f; 147 | dest.w = width; 148 | dest.h = height; 149 | 150 | _logger->debug("Rendering %d x %d texture to %f x %f window at (%f, %f)", src.w, src.h, dest.w, dest.h, dest.x, dest.y); 151 | 152 | if (SDL_RenderCopyF(_renderer, _texture, &src, &dest) != 0) { 153 | _logger->error("SDL_RenderCopyF() failed: %s", SDL_GetError()); 154 | } 155 | 156 | _logger->debug("Presenting rendered framebuffer"); 157 | SDL_RenderPresent(_renderer); 158 | } 159 | 160 | bool Video::setRotation(unsigned rotation) { 161 | (void)rotation; 162 | _logger->warn("RETRO_ENVIRONMENT_SET_ROTATION not implemented"); 163 | return false; 164 | } 165 | 166 | bool Video::getOverscan(bool* overscan) { 167 | (void)overscan; 168 | _logger->warn("RETRO_ENVIRONMENT_GET_OVERSCAN not implemented"); 169 | return false; 170 | } 171 | 172 | bool Video::getCanDupe(bool* canDupe) { 173 | *canDupe = true; 174 | return true; 175 | } 176 | 177 | bool Video::showMessage(retro_message const* message) { 178 | _logger->warn("RETRO_ENVIRONMENT_SET_MESSAGE not implemented (%u, \"%s\")", message->frames, message->msg); 179 | return true; 180 | } 181 | 182 | bool Video::setPixelFormat(retro_pixel_format format) { 183 | _pixelFormat = format; 184 | 185 | switch (_pixelFormat) { 186 | case RETRO_PIXEL_FORMAT_0RGB1555: _logger->info("Pixel format set to RETRO_PIXEL_FORMAT_0RGB1555"); break; 187 | case RETRO_PIXEL_FORMAT_XRGB8888: _logger->info("Pixel format set to RETRO_PIXEL_FORMAT_XRGB8888"); break; 188 | case RETRO_PIXEL_FORMAT_RGB565: _logger->info("Pixel format set to RETRO_PIXEL_FORMAT_RGB565"); break; 189 | 190 | default: _pixelFormat = RETRO_PIXEL_FORMAT_UNKNOWN; return false; 191 | } 192 | 193 | return true; 194 | } 195 | 196 | bool Video::setHwRender(retro_hw_render_callback* callback) { 197 | (void)callback; 198 | _logger->warn("RETRO_ENVIRONMENT_SET_HW_RENDER not implemented"); 199 | return false; 200 | } 201 | 202 | bool Video::setFrameTimeCallback(retro_frame_time_callback const* callback) { 203 | (void)callback; 204 | _logger->warn("RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK not implemented"); 205 | return false; 206 | } 207 | 208 | bool Video::setSystemAvInfo(retro_system_av_info const* info) { 209 | _coreFps = info->timing.fps; 210 | _logger->info("Core FPS set to %f", _coreFps); 211 | return setGeometry(&info->geometry); 212 | } 213 | 214 | bool Video::setGeometry(retro_game_geometry const* geometry) { 215 | _aspectRatio = geometry->aspect_ratio; 216 | 217 | if (_aspectRatio <= 0) { 218 | _aspectRatio = (float)geometry->base_width / (float)geometry->base_height; 219 | } 220 | 221 | _logger->info("Core aspect ratio set to %f", _aspectRatio); 222 | 223 | if (_texture != nullptr) { 224 | if (geometry->max_width <= _textureWidth && geometry->max_height <= _textureHeight) { 225 | return true; 226 | } 227 | 228 | SDL_DestroyTexture(_texture); 229 | _texture = nullptr; 230 | _textureWidth = _textureHeight = 0; 231 | } 232 | 233 | SDL_PixelFormatEnum format = SDL_PIXELFORMAT_UNKNOWN; 234 | 235 | switch (_pixelFormat) { 236 | case RETRO_PIXEL_FORMAT_0RGB1555: format = SDL_PIXELFORMAT_RGB555; break; 237 | case RETRO_PIXEL_FORMAT_XRGB8888: format = SDL_PIXELFORMAT_RGB888; break; 238 | case RETRO_PIXEL_FORMAT_RGB565: format = SDL_PIXELFORMAT_RGB565; break; 239 | 240 | default: 241 | _logger->error("Unknown pixel format, cannot create texture"); 242 | return false; 243 | } 244 | 245 | _texture = SDL_CreateTexture(_renderer, format, SDL_TEXTUREACCESS_STREAMING, geometry->max_width, geometry->max_height); 246 | 247 | if (_texture == nullptr) { 248 | _logger->error("SDL_CreateTexture() failed: %s", SDL_GetError()); 249 | return false; 250 | } 251 | 252 | int width = 0, height = 0, access = 0; 253 | Uint32 uformat = SDL_PIXELFORMAT_UNKNOWN; 254 | 255 | if (SDL_QueryTexture(_texture, &uformat, &access, &width, &height) != 0) { 256 | _logger->error("SDL_QueryTexture() failed: %s", SDL_GetError()); 257 | SDL_DestroyTexture(_texture); 258 | _texture = nullptr; 259 | return false; 260 | } 261 | 262 | switch (uformat) { 263 | case SDL_PIXELFORMAT_RGB555: _textureFormat = RETRO_PIXEL_FORMAT_0RGB1555; break; 264 | case SDL_PIXELFORMAT_RGB888: _textureFormat = RETRO_PIXEL_FORMAT_XRGB8888; break; 265 | case SDL_PIXELFORMAT_RGB565: _textureFormat = RETRO_PIXEL_FORMAT_RGB565; break; 266 | 267 | default: 268 | _logger->error( 269 | "Could not create a texture with the correct pixel format, requested %s, got %s", 270 | SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(uformat) 271 | ); 272 | 273 | SDL_DestroyTexture(_texture); 274 | _texture = nullptr; 275 | return false; 276 | } 277 | 278 | _textureWidth = width; 279 | _textureHeight = height; 280 | 281 | _logger->info("Texture created with %d x %d pixels with format %s", width, height, SDL_GetPixelFormatName(uformat)); 282 | return true; 283 | } 284 | 285 | bool Video::getCurrentSoftwareFramebuffer(retro_framebuffer* framebuffer) { 286 | if ((framebuffer->access_flags & RETRO_MEMORY_ACCESS_READ) != 0) { 287 | _logger->debug("Software framebuffer doesn't support reading"); 288 | return false; 289 | } 290 | 291 | if (framebuffer->width != _textureWidth || framebuffer->height != _textureHeight) { 292 | SDL_DestroyTexture(_texture); 293 | _texture = nullptr; 294 | 295 | retro_game_geometry geometry; 296 | geometry.base_width = geometry.max_width = _usedWidth = framebuffer->width; 297 | geometry.base_height = geometry.max_height = _usedHeight = framebuffer->height; 298 | geometry.aspect_ratio = _aspectRatio; // maintain the aspect ration 299 | 300 | if (!setGeometry(&geometry)) { 301 | return false; 302 | } 303 | } 304 | 305 | void* texturePixels = nullptr; 306 | int texturePitch = 0; 307 | 308 | if (SDL_LockTexture(_texture, nullptr, &texturePixels, &texturePitch) != 0) { 309 | _logger->error("SDL_LockTexture() failed: %s", SDL_GetError()); 310 | return false; 311 | } 312 | 313 | framebuffer->data = texturePixels; 314 | framebuffer->pitch = texturePitch; 315 | framebuffer->format = _textureFormat; 316 | framebuffer->memory_flags = RETRO_MEMORY_ACCESS_WRITE | RETRO_MEMORY_TYPE_CACHED; 317 | 318 | _swFramebuffer = texturePixels; 319 | _logger->debug("Returning software framebuffer %p", texturePixels); 320 | return true; 321 | } 322 | 323 | bool Video::getHwRenderInterface(retro_hw_render_interface const** interface) { 324 | (void)interface; 325 | _logger->warn("RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE not implemented"); 326 | return false; 327 | } 328 | 329 | bool Video::setHwRenderContextNegotiationInterface(retro_hw_render_context_negotiation_interface const* interface) { 330 | (void)interface; 331 | _logger->warn("RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE not implemented"); 332 | return false; 333 | } 334 | 335 | bool Video::setHwSharedContext() { 336 | _logger->warn("RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT not implemented"); 337 | return false; 338 | } 339 | 340 | bool Video::getTargetRefreshRate(float* rate) { 341 | // TODO return the real monitor refresh rate? 342 | *rate = 60.0f; 343 | return true; 344 | } 345 | 346 | bool Video::getPreferredHwRender(unsigned* preferred) { 347 | *preferred = RETRO_HW_CONTEXT_NONE; 348 | return true; 349 | } 350 | 351 | void Video::refresh(void const* data, unsigned width, unsigned height, size_t pitch) { 352 | if (data == nullptr) { 353 | _logger->debug("Last frame duplicated"); 354 | return; 355 | } 356 | 357 | if (_swFramebuffer != nullptr) { 358 | // Software framebuffer was requested, unlock the texture. 359 | SDL_UnlockTexture(_texture); 360 | } 361 | 362 | if (data != _swFramebuffer) { 363 | if (_texture != nullptr) { 364 | // Not using the software framebuffer, copy the pixels. 365 | SDL_Rect rect; 366 | rect.x = rect.y = 0; 367 | rect.w = _usedWidth = width; 368 | rect.h = _usedHeight = height; 369 | 370 | void* texturePixels = nullptr; 371 | int texturePitch = 0; 372 | 373 | _logger->debug("Refreshing %d x %d rectangle in the texture", rect.w, rect.h); 374 | 375 | if (SDL_LockTexture(_texture, &rect, &texturePixels, &texturePitch) != 0) { 376 | _logger->error("SDL_LockTexture() failed: %s", SDL_GetError()); 377 | return; 378 | } 379 | 380 | auto source = static_cast(data); 381 | auto dest = static_cast(texturePixels); 382 | 383 | for (unsigned y = 0; y < height; y++) { 384 | memcpy(dest, source, pitch); 385 | source += pitch; 386 | dest += texturePitch; 387 | } 388 | 389 | SDL_UnlockTexture(_texture); 390 | } 391 | else { 392 | _logger->error("Could not refresh the texture, it's null"); 393 | } 394 | } 395 | else { 396 | _logger->debug("No need to refresh the texture, core rendered directly to the frontend framebuffer"); 397 | } 398 | 399 | _swFramebuffer = nullptr; 400 | } 401 | 402 | uintptr_t Video::getCurrentFramebuffer() { 403 | return 0; 404 | } 405 | 406 | retro_proc_address_t Video::getProcAddress(char const* symbol) { 407 | (void)symbol; 408 | _logger->warn("RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT not implemented"); 409 | return nullptr; 410 | } 411 | 412 | void Video::reset() { 413 | _logger = nullptr; 414 | 415 | _window = nullptr; 416 | _renderer = nullptr; 417 | 418 | _pixelFormat = RETRO_PIXEL_FORMAT_UNKNOWN; 419 | _coreFps = 0.0; 420 | _aspectRatio = 0.0f; 421 | 422 | _texture = nullptr; 423 | _textureWidth = _textureHeight = 0; 424 | _textureFormat = RETRO_PIXEL_FORMAT_UNKNOWN; 425 | _usedWidth = _usedHeight = 0; 426 | 427 | _swFramebuffer = nullptr; 428 | } 429 | -------------------------------------------------------------------------------- /examples/sdl2/Video.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | class Video : public lrcpp::Video { 10 | public: 11 | Video(); 12 | 13 | bool init(Config* config, lrcpp::Logger* logger); 14 | void destroy(); 15 | 16 | double getCoreFps() const; 17 | 18 | void clear(); 19 | void present(); 20 | 21 | // lrcpp::Video 22 | virtual bool setRotation(unsigned rotation) override; 23 | virtual bool getOverscan(bool* overscan) override; 24 | virtual bool getCanDupe(bool* canDupe) override; 25 | virtual bool showMessage(retro_message const* message) override; 26 | virtual bool setPixelFormat(retro_pixel_format format) override; 27 | virtual bool setHwRender(retro_hw_render_callback* callback) override; 28 | virtual bool setFrameTimeCallback(retro_frame_time_callback const* callback) override; 29 | virtual bool setSystemAvInfo(retro_system_av_info const* info) override; 30 | virtual bool setGeometry(retro_game_geometry const* geometry) override; 31 | virtual bool getCurrentSoftwareFramebuffer(retro_framebuffer* framebuffer) override; 32 | virtual bool getHwRenderInterface(retro_hw_render_interface const** interface) override; 33 | virtual bool setHwRenderContextNegotiationInterface(retro_hw_render_context_negotiation_interface const* interface) override; 34 | virtual bool setHwSharedContext() override; 35 | virtual bool getTargetRefreshRate(float* rate) override; 36 | virtual bool getPreferredHwRender(unsigned* preferred) override; 37 | 38 | virtual void refresh(void const* data, unsigned width, unsigned height, size_t pitch) override; 39 | 40 | virtual uintptr_t getCurrentFramebuffer() override; 41 | virtual retro_proc_address_t getProcAddress(char const* symbol) override; 42 | 43 | protected: 44 | void reset(); 45 | 46 | lrcpp::Logger* _logger; 47 | 48 | SDL_Window* _window; 49 | SDL_Renderer* _renderer; 50 | 51 | retro_pixel_format _pixelFormat; 52 | double _coreFps; 53 | float _aspectRatio; 54 | 55 | SDL_Texture* _texture; 56 | unsigned _textureWidth; 57 | unsigned _textureHeight; 58 | retro_pixel_format _textureFormat; 59 | unsigned _usedWidth; 60 | unsigned _usedHeight; 61 | 62 | void* _swFramebuffer; 63 | }; 64 | -------------------------------------------------------------------------------- /examples/sdl2/sdl2lrcpp.cfg: -------------------------------------------------------------------------------- 1 | # Sets log level for libretro cores (GET_LOG_INTERFACE). 2 | # If a log level issued by a libretro core is below libretro_log_level, it is ignored. 3 | # DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3. 4 | # libretro_log_level = 0 5 | 6 | # The audio device to use, as reported in the log as "Audio device X:" 7 | # sdl2lrcpp_audio_device = "Built-in Audio Analog Stereo" 8 | 9 | # The video renderer to use, as reported in the log as "Render driver X:" 10 | # sdl2lrcpp_video_renderer = "opengl" 11 | 12 | # Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders. 13 | # video_smooth = false 14 | -------------------------------------------------------------------------------- /include/lrcpp/Components.h: -------------------------------------------------------------------------------- 1 | #ifndef LRCPP_COMPONENTS_H__ 2 | #define LRCPP_COMPONENTS_H__ 3 | 4 | #include 5 | #include 6 | 7 | namespace lrcpp { 8 | /** 9 | * A logger component for Core instances. 10 | */ 11 | class Logger { 12 | public: 13 | virtual ~Logger() {} 14 | 15 | // RETRO_ENVIRONMENT_GET_LOG_INTERFACE 16 | // retro_log_callback.log is set in Frontend and will call the interface methods 17 | 18 | // Interface 19 | virtual void vprintf(retro_log_level level, char const* format, va_list args) { (void)level; (void)format; (void)args; } 20 | 21 | // Convenience. 22 | void debug(char const* format, ...); 23 | void info(char const* format, ...); 24 | void warn(char const* format, ...); 25 | void error(char const* format, ...); 26 | }; 27 | 28 | /** 29 | * A component that returns configuration values for SimpleCore instances. 30 | */ 31 | class Config { 32 | public: 33 | virtual ~Config() {} 34 | 35 | // RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 36 | virtual bool setPerformanceLevel(unsigned level) { (void)level; return false; } 37 | // RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 38 | virtual bool getSystemDirectory(char const** directory) { (void)directory; return false; } 39 | // RETRO_ENVIRONMENT_GET_VARIABLE 40 | virtual bool getVariable(retro_variable* variable) { (void)variable; return false; } 41 | // RETRO_ENVIRONMENT_SET_VARIABLES 42 | bool setVariables(retro_variable const* variables); 43 | // RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 44 | virtual bool getVariableUpdate(bool* const updated) { (void)updated; return false; } 45 | // RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME 46 | virtual bool setSupportNoGame(bool const supports) { (void)supports; return false; } 47 | // RETRO_ENVIRONMENT_GET_LIBRETRO_PATH 48 | virtual bool getLibretroPath(char const** path) { (void)path; return false; } 49 | // RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY 50 | virtual bool getCoreAssetsDirectory(char const** directory) { (void)directory; return false;} 51 | // RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY 52 | virtual bool getSaveDirectory(char const** directory) { (void)directory; return false; } 53 | // RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK 54 | virtual bool setProcAddressCallback(retro_get_proc_address_interface const* callback) { (void)callback; return false; } 55 | // RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO 56 | virtual bool setSubsystemInfo(retro_subsystem_info const* info) { (void)info; return false; } 57 | // RETRO_ENVIRONMENT_SET_MEMORY_MAPS 58 | virtual bool setMemoryMaps(retro_memory_map const* map) { (void)map; return false; } 59 | // RETRO_ENVIRONMENT_GET_USERNAME 60 | virtual bool getUsername(char const** username) { (void)username; return false; } 61 | // RETRO_ENVIRONMENT_GET_LANGUAGE 62 | virtual bool getLanguage(unsigned* language) { (void)language; return false; } 63 | // RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS 64 | virtual bool setSupportAchievements(bool supports) { (void)supports; return false; } 65 | // RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS 66 | virtual bool setSerializationQuirks(uint64_t quirks) { (void)quirks; return false; } 67 | // RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE 68 | virtual bool getAudioVideoEnable(int* enabled) { (void)enabled; return false; } 69 | // RETRO_ENVIRONMENT_GET_FASTFORWARDING 70 | virtual bool getFastForwarding(bool* is) { (void)is; return false; } 71 | // RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION 72 | bool getCoreOptionsVersion(unsigned* version); 73 | // RETRO_ENVIRONMENT_SET_CORE_OPTIONS 74 | virtual bool setCoreOptions(retro_core_option_definition const* options) { (void)options; return false; } 75 | // RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL 76 | virtual bool setCoreOptionsIntl(retro_core_options_intl const* intl) { (void)intl; return false; } 77 | // RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 78 | virtual bool setCoreOptionsDisplay(retro_core_option_display const* display) { (void)display; return false; } 79 | 80 | protected: 81 | // Misc 82 | static bool preprocessMemoryDescriptors(retro_memory_descriptor* descriptors, unsigned count); 83 | }; 84 | 85 | /** 86 | * A Video component. 87 | */ 88 | class Video { 89 | public: 90 | virtual ~Video() {} 91 | 92 | // RETRO_ENVIRONMENT_SET_ROTATION 93 | virtual bool setRotation(unsigned rotation) { (void)rotation; return false; } 94 | // RETRO_ENVIRONMENT_GET_OVERSCAN 95 | virtual bool getOverscan(bool* overscan) { (void)overscan; return false; } 96 | // RETRO_ENVIRONMENT_GET_CAN_DUPE 97 | virtual bool getCanDupe(bool* canDupe) { (void)canDupe; return false; } 98 | // RETRO_ENVIRONMENT_SET_MESSAGE 99 | virtual bool showMessage(retro_message const* message) { (void)message; return false; } 100 | // RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 101 | virtual bool setPixelFormat(retro_pixel_format format) { (void)format; return false; } 102 | // RETRO_ENVIRONMENT_SET_HW_RENDER 103 | // get_current_framebuffer and get_proc_address are set by Frontend and will call the interface methods 104 | virtual bool setHwRender(retro_hw_render_callback* callback) { (void)callback; return false; } 105 | // RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 106 | virtual bool setFrameTimeCallback(retro_frame_time_callback const* callback) { (void)callback; return false; } 107 | // RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 108 | virtual bool setSystemAvInfo(retro_system_av_info const* info) { (void)info; return false; } 109 | // RETRO_ENVIRONMENT_SET_GEOMETRY 110 | virtual bool setGeometry(retro_game_geometry const* geometry) { (void)geometry; return false; } 111 | // RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER 112 | virtual bool getCurrentSoftwareFramebuffer(retro_framebuffer* framebuffer) { (void)framebuffer; return false; } 113 | // RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE 114 | virtual bool getHwRenderInterface(retro_hw_render_interface const** interface) { (void)interface; return false; } 115 | 116 | // RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE 117 | virtual bool setHwRenderContextNegotiationInterface(retro_hw_render_context_negotiation_interface const* interface) { 118 | (void)interface; 119 | return false; 120 | } 121 | 122 | // RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT 123 | virtual bool setHwSharedContext() { return false; } 124 | // RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE 125 | virtual bool getTargetRefreshRate(float* rate) { (void)rate; return false; } 126 | // RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 127 | virtual bool getPreferredHwRender(unsigned* preferred) { (void)preferred; return false; } 128 | 129 | // Callbacks 130 | virtual void refresh(void const* data, unsigned width, unsigned height, size_t pitch) { 131 | (void)data; 132 | (void)width; 133 | (void)height; 134 | (void)pitch; 135 | } 136 | 137 | // Interface 138 | virtual uintptr_t getCurrentFramebuffer() { return 0; } 139 | virtual retro_proc_address_t getProcAddress(char const* symbol) { (void)symbol; return nullptr; } 140 | }; 141 | 142 | /** 143 | * A component to set LED states. 144 | */ 145 | class Led { 146 | public: 147 | virtual ~Led() {} 148 | 149 | // RETRO_ENVIRONMENT_GET_LED_INTERFACE 150 | // retro_led_interface.set_led_state is set in Frontend and will call the interface methods 151 | 152 | // Interface 153 | virtual void setState(int led, int state) { (void)led; (void)state; } 154 | }; 155 | 156 | /** 157 | * An audio component. 158 | */ 159 | class Audio { 160 | public: 161 | virtual ~Audio() {} 162 | 163 | // RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 164 | virtual bool setSystemAvInfo(retro_system_av_info const* info) { (void)info; return false; } 165 | // RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK 166 | virtual bool setAudioCallback(retro_audio_callback const* callback) { (void)callback; return false; } 167 | 168 | // Callbacks 169 | virtual size_t sampleBatch(int16_t const* data, size_t frames) { (void)data; (void)frames; return 0; } 170 | virtual void sample(int16_t left, int16_t right) { (void)left; (void)right; } 171 | }; 172 | 173 | /** 174 | * A component to directly use MIDI devices. 175 | */ 176 | class Midi { 177 | public: 178 | virtual ~Midi() {} 179 | 180 | // RETRO_ENVIRONMENT_GET_MIDI_INTERFACE 181 | // retro_midi_interface.input_enabled, output_enabled, read, write, and flush are set in Frontend and will call the 182 | // interface methods 183 | 184 | // Interface 185 | virtual bool inputEnabled() { return false; } 186 | virtual bool outputEnabled() { return false; } 187 | virtual bool read(uint8_t* byte) { (void)byte; return false; } 188 | virtual bool write(uint8_t byte, uint32_t deltaTime) { (void)byte; (void)deltaTime; return false; } 189 | virtual bool flush() { return false; } 190 | }; 191 | 192 | /** 193 | * A component that provides input state to CoreWrap instances. 194 | */ 195 | class Input { 196 | public: 197 | virtual ~Input() {} 198 | 199 | // RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS 200 | virtual bool setInputDescriptors(retro_input_descriptor const* descriptors) { (void)descriptors; return false; } 201 | // RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 202 | virtual bool setKeyboardCallback(retro_keyboard_callback const* callback) { (void)callback; return false; } 203 | // RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 204 | virtual bool getInputDeviceCapabilities(uint64_t* capabilities) { (void)capabilities; return false; } 205 | // RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 206 | virtual bool setControllerInfo(retro_controller_info const* info) { (void)info; return false; } 207 | // RETRO_ENVIRONMENT_GET_INPUT_BITMASKS 208 | virtual bool getInputBitmasks(bool* supports) { (void)supports; return false; } 209 | 210 | // Callbacks 211 | virtual int16_t state(unsigned port, unsigned device, unsigned index, unsigned id) { 212 | (void)port; 213 | (void)device; 214 | (void)index; 215 | (void)id; 216 | return 0; 217 | } 218 | 219 | virtual void poll() {} 220 | }; 221 | 222 | /** 223 | * A component to make rumble effects. 224 | */ 225 | class Rumble { 226 | public: 227 | virtual ~Rumble() {} 228 | 229 | // RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE 230 | // retro_rumble_interface.set_rumble_state is set in Frontend and will call the interface methods 231 | 232 | // Interface 233 | virtual bool setState(unsigned port, retro_rumble_effect effect, uint16_t strength) { 234 | (void)port; 235 | (void)effect; 236 | (void)strength; 237 | return false; 238 | } 239 | }; 240 | 241 | /** 242 | * A component to read sensor information. 243 | */ 244 | class Sensor { 245 | public: 246 | virtual ~Sensor() {} 247 | 248 | // RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE 249 | // retro_sensor_interface.set_sensor_state and get_sensor_input are set in Frontend and will call the interface methods 250 | 251 | // Interfaces 252 | virtual bool setState(unsigned port, retro_sensor_action action, unsigned rate) { 253 | (void)port; 254 | (void)action; 255 | (void)rate; 256 | return false; 257 | } 258 | 259 | virtual float getInput(unsigned port, unsigned id) { (void)port; (void)id; return 0.0f; } 260 | }; 261 | 262 | /** 263 | * A component to read camera framebuffers. 264 | */ 265 | class Camera { 266 | public: 267 | virtual ~Camera() {} 268 | 269 | // RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE 270 | // retro_camera_callback.start and stop are set by Frontend and will call the interface methods 271 | virtual bool getCameraInterface(retro_camera_callback const* callback) { (void)callback; return false; } 272 | 273 | // Interface 274 | virtual bool start() { return false; } 275 | virtual void stop() {} 276 | }; 277 | 278 | /** 279 | * A component to get location data. 280 | */ 281 | class Location { 282 | public: 283 | virtual ~Location() {} 284 | 285 | // RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE 286 | // retro_location_callback.start, stop, get_position, and set_interval are set by Frontend and will call the interface 287 | // methods 288 | virtual bool getLocationInterface(retro_location_callback const* callback) { (void)callback; return false; } 289 | 290 | // Interface 291 | virtual bool start() { return false; } 292 | virtual void stop() {} 293 | 294 | virtual bool getPosition(double* lat, double* lon, double* horizAccuracy, double* vertAccuracy) { 295 | (void)lat; 296 | (void)lon; 297 | (void)horizAccuracy; 298 | (void)vertAccuracy; 299 | return false; 300 | } 301 | 302 | virtual void setInterval(unsigned intervalMs, unsigned intervalDistance) { (void)intervalMs; (void)intervalDistance; } 303 | }; 304 | 305 | class VirtualFileSystem { 306 | public: 307 | virtual ~VirtualFileSystem() {} 308 | 309 | // RETRO_ENVIRONMENT_GET_VFS_INTERFACE 310 | // retro_vfs_interface_info.retro_vfs_interface is set by Frontend and will call the interface methods 311 | virtual bool getVfsInterface(retro_vfs_interface_info* callback) { return false; } 312 | 313 | // Interface 314 | virtual char const* getPath(retro_vfs_file_handle* stream) { (void)stream; return nullptr; } 315 | 316 | virtual retro_vfs_file_handle* open(char const* path, unsigned mode, unsigned hints) { 317 | (void)path; 318 | (void)mode; 319 | (void)hints; 320 | return nullptr; 321 | } 322 | 323 | virtual int close(retro_vfs_file_handle* stream) { (void)stream; return -1; } 324 | virtual int64_t size(retro_vfs_file_handle* stream) { (void)stream; return -1; } 325 | virtual int64_t truncate(retro_vfs_file_handle* stream, int64_t length) { (void)stream; (void)length; return -1; } 326 | virtual int64_t tell(retro_vfs_file_handle* stream) { (void)stream; return -1; } 327 | 328 | virtual int64_t seek(retro_vfs_file_handle* stream, int64_t offset, int seekPosition) { 329 | (void)stream; 330 | (void)offset; 331 | (void)seekPosition; 332 | return -1; 333 | } 334 | 335 | virtual int64_t read(retro_vfs_file_handle* stream, void* s, uint64_t len) { 336 | (void)stream; 337 | (void)s; 338 | (void)len; 339 | return -1; 340 | } 341 | 342 | virtual int64_t write(retro_vfs_file_handle* stream, void const* s, uint64_t len) { 343 | (void)stream; 344 | (void)s; 345 | (void)len; 346 | return -1; 347 | } 348 | 349 | virtual int flush(retro_vfs_file_handle* stream) { (void)stream; return -1; } 350 | virtual int remove(char const* path) { (void)path; return -1; } 351 | virtual int rename(char const* oldPath, char const* newPath) { (void)oldPath; (void)newPath; return -1; } 352 | virtual int stat(char const* path, int32_t* size) { (void)path; (void)size; return -1; } 353 | virtual int mkDir(char const* dir) { (void)dir; return -1; } 354 | 355 | virtual retro_vfs_dir_handle* openDir(char const* dir, bool includeHidden) { 356 | (void)dir; 357 | (void)includeHidden; 358 | return nullptr; 359 | } 360 | 361 | virtual bool readDir(retro_vfs_dir_handle* dirstream) { (void)dirstream; return false; } 362 | virtual char const* direntGetName(retro_vfs_dir_handle* dirstream) { (void)dirstream; return nullptr; } 363 | virtual bool direntIsDir(retro_vfs_dir_handle* dirstream) { (void)dirstream; return false; } 364 | virtual int closeDir(retro_vfs_dir_handle* dirstream) { (void)dirstream; return -1; } 365 | }; 366 | 367 | class DiskControl { 368 | public: 369 | virtual ~DiskControl() {} 370 | 371 | // RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 372 | virtual bool getDiskControlInterfaceVersion(unsigned* const version) { (void)version; return false; } 373 | // RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE 374 | virtual bool setDiskControlInterface(retro_disk_control_callback const* interface) { (void)interface; return false; } 375 | 376 | // RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 377 | virtual bool setDiskControlExtInterface(retro_disk_control_ext_callback const* interface) { 378 | (void)interface; 379 | return false; 380 | } 381 | }; 382 | 383 | /** 384 | * A component to get performance information from the system. 385 | */ 386 | class Perf { 387 | public: 388 | virtual ~Perf() {} 389 | 390 | // RETRO_ENVIRONMENT_GET_PERF_INTERFACE 391 | // retro_perf_callback.get_time_usec, get_cpu_features, get_perf_counter, perf_register, perf_start, perf_stop, and 392 | // perf_log are set by Frontend and will call the interface methods 393 | 394 | // Interface 395 | virtual retro_time_t getTimeUsec() { return 0; } 396 | virtual uint64_t getCpuFeatures() { return 0; } 397 | virtual retro_perf_tick_t getCounter() { return 0; } 398 | virtual void register_(retro_perf_counter* counter) { (void)counter; } 399 | virtual void start(retro_perf_counter* counter) { (void)counter; } 400 | virtual void stop(retro_perf_counter* counter) { (void)counter; } 401 | virtual void log() {} 402 | }; 403 | } 404 | 405 | #endif // LRCPP_COMPONENTS_H__ 406 | -------------------------------------------------------------------------------- /include/lrcpp/Core.h: -------------------------------------------------------------------------------- 1 | #ifndef LRCPP_CORE_H__ 2 | #define LRCPP_CORE_H__ 3 | 4 | #include 5 | #include 6 | 7 | namespace lrcpp { 8 | struct Core { 9 | void (*init)(); 10 | void (*deinit)(); 11 | unsigned (*apiVersion)(); 12 | void (*getSystemInfo)(struct retro_system_info*); 13 | void (*getSystemAvInfo)(struct retro_system_av_info*); 14 | void (*setEnvironment)(retro_environment_t); 15 | void (*setVideoRefresh)(retro_video_refresh_t); 16 | void (*setAudioSample)(retro_audio_sample_t); 17 | void (*setAudioSampleBatch)(retro_audio_sample_batch_t); 18 | void (*setInputPoll)(retro_input_poll_t); 19 | void (*setInputState)(retro_input_state_t); 20 | void (*setControllerPortDevice)(unsigned, unsigned); 21 | void (*reset)(); 22 | void (*run)(); 23 | size_t (*serializeSize)(); 24 | bool (*serialize)(void*, size_t); 25 | bool (*unserialize)(void const*, size_t); 26 | void (*cheatReset)(); 27 | void (*cheatSet)(unsigned, bool, char const*); 28 | bool (*loadGame)(struct retro_game_info const*); 29 | bool (*loadGameSpecial)(unsigned, struct retro_game_info const*, size_t); 30 | void (*unloadGame)(); 31 | unsigned (*getRegion)(); 32 | void* (*getMemoryData)(unsigned); 33 | size_t (*getMemorySize)(unsigned); 34 | 35 | }; 36 | } 37 | 38 | #define LRCPP_CORE_INIT_FUNCTIONS_INTERNAL(core, prefix) \ 39 | do { \ 40 | core.init = prefix ## init; \ 41 | core.deinit = prefix ## deinit; \ 42 | core.apiVersion = prefix ## api_version; \ 43 | core.getSystemInfo = prefix ## get_system_info; \ 44 | core.getSystemAvInfo = prefix ## get_system_av_info; \ 45 | core.setEnvironment = prefix ## set_environment; \ 46 | core.setVideoRefresh = prefix ## set_video_refresh; \ 47 | core.setAudioSample = prefix ## set_audio_sample; \ 48 | core.setAudioSampleBatch = prefix ## set_audio_sample_batch; \ 49 | core.setInputPoll = prefix ## set_input_poll; \ 50 | core.setInputState = prefix ## set_input_state; \ 51 | core.setControllerPortDevice = prefix ## set_controller_port_device; \ 52 | core.reset = prefix ## reset; \ 53 | core.run = prefix ## run; \ 54 | core.serializeSize = prefix ## serialize_size; \ 55 | core.serialize = prefix ## serialize; \ 56 | core.unserialize = prefix ## unserialize; \ 57 | core.cheatReset = prefix ## cheat_reset; \ 58 | core.cheatSet = prefix ## cheat_set; \ 59 | core.loadGame = prefix ## load_game; \ 60 | core.loadGameSpecial = prefix ## load_game_special; \ 61 | core.unloadGame = prefix ## unload_game; \ 62 | core.getRegion = prefix ## get_region; \ 63 | core.getMemoryData = prefix ## get_memory_data; \ 64 | core.getMemorySize = prefix ## get_memory_size; \ 65 | } while (0) 66 | 67 | #define LRCPP_CORE_INIT_FUNCTIONS_DEFAULT(core) LRCPP_CORE_INIT_FUNCTIONS_INTERNAL(core, retro_) 68 | #define LRCPP_CORE_INIT_FUNCTIONS(core, prefix) LRCPP_CORE_INIT_FUNCTIONS_INTERNAL(core, prefix ## retro_) 69 | 70 | #endif // LRCPP_CORE_H__ 71 | -------------------------------------------------------------------------------- /include/lrcpp/CoreFsm.h: -------------------------------------------------------------------------------- 1 | #ifndef COREFSM_H__ 2 | #define COREFSM_H__ 3 | 4 | /* Generated with FSM compiler: https://github.com/leiradel/luamods/ddlt */ 5 | 6 | #include 7 | 8 | /*#line 25 "/home/leiradel/Develop/retromancer/TinyDungeons/src/retromancer/src/3rdparty/lrcpp/module/etc/CoreFsm.fsm"*/ 9 | 10 | #include 11 | 12 | namespace lrcpp { 13 | struct Core; 14 | class Frontend; 15 | } 16 | 17 | typedef lrcpp::Core* CorePtr; 18 | typedef lrcpp::Frontend* FrontendPtr; 19 | typedef size_t* SizePtr; 20 | typedef char const* ConstCharPtr; 21 | typedef retro_game_info const* ConstRetroGameInfoPtr; 22 | typedef void* VoidPtr; 23 | typedef void const* ConstVoidPtr; 24 | typedef void** VoidPtrPtr; 25 | typedef unsigned* UnsignedPtr; 26 | typedef struct retro_system_info* RetroSystemInfoPtr; 27 | typedef struct retro_system_av_info* RetroSystemAvInfoPtr; 28 | 29 | 30 | /* FSM states */ 31 | typedef enum { 32 | CoreFsm_State_CoreInitialized, 33 | CoreFsm_State_CoreSet, 34 | CoreFsm_State_EnvironmentSet, 35 | CoreFsm_State_GameLoaded, 36 | CoreFsm_State_GameRunning, 37 | CoreFsm_State_Start, 38 | } 39 | CoreFsm_State; 40 | 41 | /* The FSM */ 42 | typedef struct { 43 | CoreFsm_State state; 44 | CorePtr core; 45 | FrontendPtr frontend; 46 | FrontendPtr previous; 47 | 48 | #ifdef DEBUG_FSM 49 | /* Set those after calling CoreFsm_Init when DEBUG_FSM is defined */ 50 | void (*vprintf)(void* const ud, char const* const fmt, va_list args); 51 | void* vprintf_ud; 52 | #endif 53 | } 54 | CoreFsm_Context; 55 | 56 | /* Initialization */ 57 | void CoreFsm_Init(CoreFsm_Context* const self, CorePtr const core, FrontendPtr const frontend, FrontendPtr const previous); 58 | 59 | /* Query */ 60 | CoreFsm_State CoreFsm_CurrentState(CoreFsm_Context const* const self); 61 | int CoreFsm_CanTransitionTo(CoreFsm_Context const* const self, CoreFsm_State const next); 62 | 63 | /* Transitions */ 64 | int CoreFsm_Transition_apiVersion(CoreFsm_Context* const self, UnsignedPtr version); 65 | int CoreFsm_Transition_cheatReset(CoreFsm_Context* const self); 66 | int CoreFsm_Transition_cheatSet(CoreFsm_Context* const self, unsigned index, bool enabled, ConstCharPtr code); 67 | int CoreFsm_Transition_coreSet(CoreFsm_Context* const self); 68 | int CoreFsm_Transition_deinit(CoreFsm_Context* const self); 69 | int CoreFsm_Transition_getMemoryData(CoreFsm_Context* const self, unsigned id, VoidPtrPtr data); 70 | int CoreFsm_Transition_getMemorySize(CoreFsm_Context* const self, unsigned id, SizePtr size); 71 | int CoreFsm_Transition_getRegion(CoreFsm_Context* const self, UnsignedPtr region); 72 | int CoreFsm_Transition_getSystemAvInfo(CoreFsm_Context* const self, RetroSystemAvInfoPtr info); 73 | int CoreFsm_Transition_getSystemInfo(CoreFsm_Context* const self, RetroSystemInfoPtr info); 74 | int CoreFsm_Transition_gotoCoreSet(CoreFsm_Context* const self); 75 | int CoreFsm_Transition_init(CoreFsm_Context* const self); 76 | int CoreFsm_Transition_loadGame(CoreFsm_Context* const self, ConstRetroGameInfoPtr gameInfo); 77 | int CoreFsm_Transition_loadGameSpecial(CoreFsm_Context* const self, unsigned gameType, ConstRetroGameInfoPtr info, size_t numInfo); 78 | int CoreFsm_Transition_reset(CoreFsm_Context* const self); 79 | int CoreFsm_Transition_run(CoreFsm_Context* const self); 80 | int CoreFsm_Transition_serialize(CoreFsm_Context* const self, VoidPtr data, size_t size); 81 | int CoreFsm_Transition_serializeSize(CoreFsm_Context* const self, SizePtr size); 82 | int CoreFsm_Transition_setCallbacks(CoreFsm_Context* const self, retro_video_refresh_t videoRefresh, retro_audio_sample_t audioSample, retro_audio_sample_batch_t audioSampleBatch, retro_input_poll_t inputPoll, retro_input_state_t inputState); 83 | int CoreFsm_Transition_setControllerPortDevice(CoreFsm_Context* const self, unsigned port, unsigned device); 84 | int CoreFsm_Transition_setEnvironment(CoreFsm_Context* const self, retro_environment_t cb); 85 | int CoreFsm_Transition_unloadGame(CoreFsm_Context* const self); 86 | int CoreFsm_Transition_unserialize(CoreFsm_Context* const self, ConstVoidPtr data, size_t size); 87 | int CoreFsm_Transition_unset(CoreFsm_Context* const self); 88 | 89 | /* Debug */ 90 | #ifdef DEBUG_FSM 91 | char const* CoreFsm_StateName(CoreFsm_State const state); 92 | #endif 93 | 94 | #endif /* COREFSM_H__ */ 95 | -------------------------------------------------------------------------------- /include/lrcpp/Frontend.h: -------------------------------------------------------------------------------- 1 | #ifndef LRCPP_FRONTEND_H__ 2 | #define LRCPP_FRONTEND_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define LRCPP_DECLARE_CORE_FUNCTIONS(prefix) \ 9 | extern "C" { \ 10 | void prefix ## retro_init(void); \ 11 | void prefix ## retro_deinit(void); \ 12 | unsigned prefix ## retro_api_version(void); \ 13 | void prefix ## retro_get_system_info(struct retro_system_info*); \ 14 | void prefix ## retro_get_system_av_info(struct retro_system_av_info*); \ 15 | void prefix ## retro_set_environment(retro_environment_t); \ 16 | void prefix ## retro_set_video_refresh(retro_video_refresh_t); \ 17 | void prefix ## retro_set_audio_sample(retro_audio_sample_t); \ 18 | void prefix ## retro_set_audio_sample_batch(retro_audio_sample_batch_t); \ 19 | void prefix ## retro_set_input_poll(retro_input_poll_t); \ 20 | void prefix ## retro_set_input_state(retro_input_state_t); \ 21 | void prefix ## retro_set_controller_port_device(unsigned, unsigned); \ 22 | void prefix ## retro_reset(void); \ 23 | void prefix ## retro_run(void); \ 24 | size_t prefix ## retro_serialize_size(void); \ 25 | bool prefix ## retro_serialize(void*, size_t); \ 26 | bool prefix ## retro_unserialize(void const*, size_t); \ 27 | void prefix ## retro_cheat_reset(void); \ 28 | void prefix ## retro_cheat_set(unsigned, bool, char const*); \ 29 | bool prefix ## retro_load_game(struct retro_game_info const*); \ 30 | bool prefix ## retro_load_game_special(unsigned, struct retro_game_info const*, size_t); \ 31 | void prefix ## retro_unload_game(void); \ 32 | unsigned prefix ## retro_get_region(void); \ 33 | void* prefix ## retro_get_memory_data(unsigned); \ 34 | size_t prefix ## retro_get_memory_size(unsigned); \ 35 | } 36 | 37 | #define LRCPP_INIT_CORE_FUNCTIONS(core, prefix) \ 38 | do { \ 39 | core.init = prefix ## retro_init; \ 40 | core.deinit = prefix ## retro_deinit; \ 41 | core.apiVersion = prefix ## retro_api_version; \ 42 | core.getSystemInfo = prefix ## retro_get_system_info; \ 43 | core.getSystemAvInfo = prefix ## retro_get_system_av_info; \ 44 | core.setEnvironment = prefix ## retro_set_environment; \ 45 | core.setVideoRefresh = prefix ## retro_set_video_refresh; \ 46 | core.setAudioSample = prefix ## retro_set_audio_sample; \ 47 | core.setAudioSampleBatch = prefix ## retro_set_audio_sample_batch; \ 48 | core.setInputPoll = prefix ## retro_set_input_poll; \ 49 | core.setInputState = prefix ## retro_set_input_state; \ 50 | core.setControllerPortDevice = prefix ## retro_set_controller_port_device; \ 51 | core.reset = prefix ## retro_reset; \ 52 | core.run = prefix ## retro_run; \ 53 | core.serializeSize = prefix ## retro_serialize_size; \ 54 | core.serialize = prefix ## retro_serialize; \ 55 | core.unserialize = prefix ## retro_unserialize; \ 56 | core.cheatReset = prefix ## retro_cheat_reset; \ 57 | core.cheatSet = prefix ## retro_cheat_set; \ 58 | core.loadGame = prefix ## retro_load_game; \ 59 | core.loadGameSpecial = prefix ## retro_load_game_special; \ 60 | core.unloadGame = prefix ## retro_unload_game; \ 61 | core.getRegion = prefix ## retro_get_region; \ 62 | core.getMemoryData = prefix ## retro_get_memory_data; \ 63 | core.getMemorySize = prefix ## retro_get_memory_size; \ 64 | } while (0) 65 | 66 | namespace lrcpp { 67 | struct Core final { 68 | void (*init)(); 69 | void (*deinit)(); 70 | unsigned (*apiVersion)(); 71 | void (*getSystemInfo)(struct retro_system_info*); 72 | void (*getSystemAvInfo)(struct retro_system_av_info*); 73 | void (*setEnvironment)(retro_environment_t); 74 | void (*setVideoRefresh)(retro_video_refresh_t); 75 | void (*setAudioSample)(retro_audio_sample_t); 76 | void (*setAudioSampleBatch)(retro_audio_sample_batch_t); 77 | void (*setInputPoll)(retro_input_poll_t); 78 | void (*setInputState)(retro_input_state_t); 79 | void (*setControllerPortDevice)(unsigned, unsigned); 80 | void (*reset)(); 81 | void (*run)(); 82 | size_t (*serializeSize)(); 83 | bool (*serialize)(void*, size_t); 84 | bool (*unserialize)(void const*, size_t); 85 | void (*cheatReset)(); 86 | void (*cheatSet)(unsigned, bool, char const*); 87 | bool (*loadGame)(struct retro_game_info const*); 88 | bool (*loadGameSpecial)(unsigned, struct retro_game_info const*, size_t); 89 | void (*unloadGame)(); 90 | unsigned (*getRegion)(); 91 | void* (*getMemoryData)(unsigned); 92 | size_t (*getMemorySize)(unsigned); 93 | }; 94 | 95 | class Frontend final { 96 | public: 97 | Frontend(); 98 | 99 | Frontend(Frontend const&) = delete; 100 | Frontend(Frontend&&) = delete; 101 | void operator=(Frontend const&) = delete; 102 | void operator=(Frontend&&) = delete; 103 | 104 | ~Frontend(); 105 | 106 | // Get thread local storage for the current frontend 107 | static Frontend* getCurrent(); 108 | static void setCurrent(Frontend* frontend); 109 | 110 | // Components 111 | Logger* getLogger(); 112 | Config* getConfig(); 113 | Video* getVideo(); 114 | Led* getLed(); 115 | Audio* getAudio(); 116 | Midi* getMidi(); 117 | Input* getInput(); 118 | Rumble* getRumble(); 119 | Sensor* getSensor(); 120 | Camera* getCamera(); 121 | Location* getLocation(); 122 | VirtualFileSystem* getVirtualFileSystem(); 123 | DiskControl* getDiskControl(); 124 | Perf* getPerf(); 125 | 126 | bool setLogger(Logger* logger); 127 | bool setConfig(Config* config); 128 | bool setVideo(Video* video); 129 | bool setLed(Led* led); 130 | bool setAudio(Audio* audio); 131 | bool setMidi(Midi* midi); 132 | bool setInput(Input* input); 133 | bool setRumble(Rumble* rumble); 134 | bool setSensor(Sensor* sensor); 135 | bool setCamera(Camera* camera); 136 | bool setLocation(Location* location); 137 | bool setVirtualFileSystem(VirtualFileSystem* virtualFileSystem); 138 | bool setDiskControl(DiskControl* diskControl); 139 | bool setPerf(Perf* perf); 140 | 141 | // Core life-cycle 142 | bool setCore(Core const* core); 143 | bool loadGame(); 144 | bool loadGame(char const* gamePath); 145 | bool loadGame(char const* gamePath, void const* data, size_t size); 146 | bool loadGameSpecial(unsigned gameType, struct retro_game_info const* info, size_t numInfo); 147 | bool run(); 148 | bool reset(); 149 | bool unloadGame(); 150 | bool unset(); 151 | 152 | // Other Libretro API calls 153 | bool apiVersion(unsigned* version); 154 | bool getSystemInfo(struct retro_system_info* info); 155 | bool getSystemAvInfo(struct retro_system_av_info* info); 156 | bool serializeSize(size_t* size); 157 | bool serialize(void* data, size_t size); 158 | bool unserialize(void const* data, size_t size); 159 | bool cheatReset(); 160 | bool cheatSet(unsigned index, bool enabled, char const* code); 161 | bool getRegion(unsigned* region); 162 | bool getMemoryData(unsigned id, void** data); 163 | bool getMemorySize(unsigned id, size_t* size); 164 | bool setControllerPortDevice(unsigned port, unsigned device); 165 | 166 | // lrcpp API 167 | bool shutdownRequested() const; 168 | 169 | protected: 170 | // Environment functions 171 | bool setRotation(unsigned data); 172 | bool getOverscan(bool* data); 173 | bool getCanDupe(bool* data); 174 | bool showMessage(struct retro_message const* data); 175 | bool shutdown(); 176 | bool setPerformanceLevel(unsigned data); 177 | bool getSystemDirectory(char const** data); 178 | bool setPixelFormat(enum retro_pixel_format data); 179 | bool setInputDescriptors(struct retro_input_descriptor const* data); 180 | bool setKeyboardCallback(struct retro_keyboard_callback const* data); 181 | bool setDiskControlInterface(struct retro_disk_control_callback const* data); 182 | bool setHwRender(struct retro_hw_render_callback* data); 183 | bool getVariable(struct retro_variable* data); 184 | bool setVariables(struct retro_variable const* data); 185 | bool getVariableUpdate(bool* data); 186 | bool setSupportNoGame(bool data); 187 | bool getLibretroPath(char const** data); 188 | bool setFrameTimeCallback(struct retro_frame_time_callback const* data); 189 | bool setAudioCallback(struct retro_audio_callback const* data); 190 | bool getRumbleInterface(struct retro_rumble_interface* data); 191 | bool getInputDeviceCapabilities(uint64_t* data); 192 | bool getSensorInterface(struct retro_sensor_interface* data); 193 | bool getCameraInterface(struct retro_camera_callback* data); 194 | bool getLogInterface(struct retro_log_callback* data); 195 | bool getPerfInterface(struct retro_perf_callback* data); 196 | bool getLocationInterface(struct retro_location_callback* data); 197 | bool getCoreAssetsDirectory(char const** data); 198 | bool getSaveDirectory(char const** data); 199 | bool setSystemAvInfo(struct retro_system_av_info const* data); 200 | bool setProcAddressCallback(struct retro_get_proc_address_interface const* data); 201 | bool setSubsystemInfo(struct retro_subsystem_info const* data); 202 | bool setControllerInfo(struct retro_controller_info const* data); 203 | bool setMemoryMaps(struct retro_memory_map const* data); 204 | bool setGeometry(struct retro_game_geometry const* data); 205 | bool getUsername(char const** data); 206 | bool getLanguage(unsigned* data); 207 | bool getCurrentSoftwareFramebuffer(struct retro_framebuffer* data); 208 | bool getHwRenderInterface(struct retro_hw_render_interface const** data); 209 | bool setSupportAchievements(bool data); 210 | bool setHwRenderContextNegotiationInterface(struct retro_hw_render_context_negotiation_interface const* data); 211 | bool setSerializationQuirks(uint64_t data); 212 | bool setHwSharedContext(); 213 | bool getVfsInterface(struct retro_vfs_interface_info* data); 214 | bool getLedInterface(struct retro_led_interface* data); 215 | bool getAudioVideoEnable(int* data); 216 | bool getMidiInterface(struct retro_midi_interface** data); 217 | bool getFastForwarding(bool* data); 218 | bool getTargetRefreshRate(float* data); 219 | bool getInputBitmasks(bool* data); 220 | bool getCoreOptionsVersion(unsigned* data); 221 | bool setCoreOptions(struct retro_core_option_definition const* data); 222 | bool setCoreOptionsIntl(struct retro_core_options_intl const* data); 223 | bool setCoreOptionsDisplay(struct retro_core_option_display const* data); 224 | bool getPreferredHwRender(unsigned* data); 225 | bool getDiskControlInterfaceVersion(unsigned* data); 226 | bool setDiskControlExtInterface(struct retro_disk_control_ext_callback const* data); 227 | 228 | // Callbacks and interfaces 229 | bool environmentCallback(unsigned cmd, void* data); 230 | 231 | static bool staticEnvironmentCallback(unsigned cmd, void* data); 232 | 233 | static bool rumbleSetState(unsigned port, enum retro_rumble_effect effect, uint16_t strength); 234 | 235 | static bool sensorSetState(unsigned port, enum retro_sensor_action action, unsigned rate); 236 | static float sensorGetInput(unsigned port, unsigned id); 237 | 238 | static bool cameraStart(); 239 | static void cameraStop(); 240 | 241 | static void loggerVprintf(enum retro_log_level level, char const* format, ...); 242 | 243 | static retro_time_t perfGetTimeUsec(); 244 | static uint64_t perfGetCpuFeatures(); 245 | static retro_perf_tick_t perfGetCounter(); 246 | static void perfRegister(struct retro_perf_counter* counter); 247 | static void perfStart(struct retro_perf_counter* counter); 248 | static void perfStop(struct retro_perf_counter* counter); 249 | static void perfLog(); 250 | 251 | static bool locationStart(); 252 | static void locationStop(); 253 | static bool locationGetPosition(double* lat, double* lon, double* horizAccuracy, double* vertAccuracy); 254 | static void locationSetInterval(unsigned intervalMs, unsigned intervalDistance); 255 | 256 | static char const* virtualFileSystemGetPath(struct retro_vfs_file_handle* stream); 257 | static struct retro_vfs_file_handle* virtualFileSystemOpen(char const* path, unsigned mode, unsigned hints); 258 | static int virtualFileSystemClose(struct retro_vfs_file_handle* stream); 259 | static int64_t virtualFileSystemSize(struct retro_vfs_file_handle* stream); 260 | static int64_t virtualFileSystemTruncate(struct retro_vfs_file_handle* stream, int64_t length); 261 | static int64_t virtualFileSystemTell(struct retro_vfs_file_handle* stream); 262 | static int64_t virtualFileSystemSeek(struct retro_vfs_file_handle* stream, int64_t offset, int seekPosition); 263 | static int64_t virtualFileSystemRead(struct retro_vfs_file_handle* stream, void* s, uint64_t len); 264 | static int64_t virtualFileSystemWrite(struct retro_vfs_file_handle* stream, void const* s, uint64_t len); 265 | static int virtualFileSystemFlush(struct retro_vfs_file_handle* stream); 266 | static int virtualFileSystemRemove(char const* path); 267 | static int virtualFileSystemRename(char const* old_path, char const* new_path); 268 | static int virtualFileSystemStat(char const* path, int32_t* size); 269 | static int virtualFileSystemMkDir(char const* dir); 270 | static struct retro_vfs_dir_handle* virtualFileSystemOpenDir(char const* dir, bool includeHidden); 271 | static bool virtualFileSystemReadDir(struct retro_vfs_dir_handle* dirstream); 272 | static char const* virtualFileSystemDirentGetName(struct retro_vfs_dir_handle* dirstream); 273 | static bool virtualFileSystemDirentIsDir(struct retro_vfs_dir_handle* dirstream); 274 | static int virtualFileSystemCloseDir(struct retro_vfs_dir_handle* dirstream); 275 | 276 | static void ledSetState(int led, int state); 277 | 278 | static bool midiInputEnabled(); 279 | static bool midiOutputEnabled(); 280 | static bool midiRead(uint8_t* byte); 281 | static bool midiWrite(uint8_t byte, uint32_t deltaTime); 282 | static bool midiFlush(); 283 | 284 | static uintptr_t videoGetCurrentFramebuffer(); 285 | static retro_proc_address_t videoGetProcAddress(char const* symbol); 286 | static void videoRefresh(void const* data, unsigned width, unsigned height, size_t pitch); 287 | 288 | static size_t audioSampleBatch(int16_t const* data, size_t frames); 289 | static void audioSample(int16_t left, int16_t right); 290 | 291 | static int16_t inputState(unsigned port, unsigned device, unsigned index, unsigned id); 292 | static void inputPoll(); 293 | 294 | Logger* _logger; 295 | Config* _config; 296 | Video* _video; 297 | Led* _led; 298 | Audio* _audio; 299 | Midi* _midi; 300 | Input* _input; 301 | Rumble* _rumble; 302 | Sensor* _sensor; 303 | Camera* _camera; 304 | Location* _location; 305 | VirtualFileSystem* _virtualFileSystem; 306 | DiskControl* _diskControl; 307 | Perf* _perf; 308 | 309 | Core _core; 310 | CoreFsm_Context _fsm; 311 | 312 | bool _supportsNoGame; 313 | 314 | struct retro_vfs_interface _virtualFileSystemInterface; 315 | struct retro_midi_interface _midiInterface; 316 | 317 | bool _shutdownRequested; 318 | }; 319 | } // namespace lrcpp 320 | 321 | #endif // LRCPP_FRONTEND_H__ 322 | -------------------------------------------------------------------------------- /src/Components.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | void lrcpp::Logger::debug(char const* format, ...) { 7 | va_list args; 8 | va_start(args, format); 9 | vprintf(RETRO_LOG_DEBUG, format, args); 10 | va_end(args); 11 | } 12 | 13 | void lrcpp::Logger::info(char const* format, ...) { 14 | va_list args; 15 | va_start(args, format); 16 | vprintf(RETRO_LOG_INFO, format, args); 17 | va_end(args); 18 | } 19 | 20 | void lrcpp::Logger::warn(char const* format, ...) { 21 | va_list args; 22 | va_start(args, format); 23 | vprintf(RETRO_LOG_WARN, format, args); 24 | va_end(args); 25 | } 26 | 27 | void lrcpp::Logger::error(char const* format, ...) { 28 | va_list args; 29 | va_start(args, format); 30 | vprintf(RETRO_LOG_ERROR, format, args); 31 | va_end(args); 32 | } 33 | 34 | bool lrcpp::Config::setVariables(struct retro_variable const* const variables) { 35 | size_t count = 0; 36 | size_t totalLength = 0; 37 | 38 | for (count = 0; variables[count].key != nullptr; count++) { 39 | totalLength += strlen(variables[count].key) + 1; 40 | totalLength += strlen(variables[count].value) + 1; 41 | } 42 | 43 | retro_core_option_definition* const defs = (retro_core_option_definition*)malloc((count + 1) * sizeof(*defs)); 44 | char* const strings = (char*)malloc(totalLength); 45 | 46 | if (defs == nullptr || strings == nullptr) { 47 | free(defs); 48 | free(strings); 49 | return false; 50 | } 51 | 52 | char* ptr = strings; 53 | 54 | for (size_t i = 0; i < count; i++) { 55 | retro_core_option_definition* const def = defs + i; 56 | def->key = variables[i].key; 57 | 58 | char const* option = strchr(variables[i].value, ';'); 59 | 60 | if (option == nullptr) { 61 | free(defs); 62 | free(strings); 63 | return false; 64 | } 65 | 66 | def->desc = ptr; 67 | def->info = nullptr; 68 | 69 | memcpy(ptr, variables[i].value, option - variables[i].value); 70 | ptr += option - variables[i].value; 71 | *ptr++ = 0; 72 | 73 | option++; 74 | 75 | while (*option == ' ' || *option == '\t') { 76 | option++; 77 | } 78 | 79 | if (*option == 0) { 80 | free(defs); 81 | free(strings); 82 | return false; 83 | } 84 | 85 | size_t j = 0; 86 | 87 | for (; j < RETRO_NUM_CORE_OPTION_VALUES_MAX - 1; j++) { 88 | char const* const pipe = strchr(option, '|'); 89 | def->values[j].value = ptr; 90 | 91 | if (pipe == nullptr) { 92 | strcpy(ptr, option); 93 | ptr += strlen(ptr) + 1; 94 | } 95 | else { 96 | memcpy(ptr, option, pipe - option); 97 | ptr += pipe - option; 98 | *ptr++ = 0; 99 | } 100 | 101 | def->values[j].label = nullptr; 102 | 103 | if (pipe == nullptr) { 104 | break; 105 | } 106 | 107 | option = pipe + 1; 108 | } 109 | 110 | if (j == RETRO_NUM_CORE_OPTION_VALUES_MAX - 1) { 111 | free(defs); 112 | free(strings); 113 | return false; 114 | } 115 | 116 | def->values[j + 1].value = def->values[j + 1].label = nullptr; 117 | def->default_value = def->values[0].value; 118 | } 119 | 120 | defs[count].key = nullptr; 121 | defs[count].desc = nullptr; 122 | defs[count].info = nullptr; 123 | defs[count].values[0].value = nullptr; 124 | defs[count].values[0].label = nullptr; 125 | defs[count].default_value = nullptr; 126 | 127 | bool const ok = setCoreOptions(defs); 128 | free(strings); 129 | free(defs); 130 | return ok; 131 | } 132 | 133 | bool lrcpp::Config::getCoreOptionsVersion(unsigned* const version) { 134 | *version = 1; 135 | return true; 136 | } 137 | 138 | static size_t addBitsDown(size_t n) { 139 | n |= n >> 1; 140 | n |= n >> 2; 141 | n |= n >> 4; 142 | n |= n >> 8; 143 | n |= n >> 16; 144 | 145 | if (sizeof(size_t) > 4) { 146 | // double shift to avoid warnings on 32bit 147 | n |= n >> 16 >> 16; 148 | } 149 | 150 | return n; 151 | } 152 | 153 | static size_t inflate(size_t addr, size_t mask) { 154 | while (mask) 155 | { 156 | size_t tmp = (mask - 1) & ~mask; 157 | // to put in an 1 bit instead, OR in tmp+1 158 | addr = ((addr & ~tmp) << 1) | (addr & tmp); 159 | mask = mask & (mask - 1); 160 | } 161 | 162 | return addr; 163 | } 164 | 165 | static size_t reduce(size_t addr, size_t mask) { 166 | while (mask) { 167 | size_t tmp = (mask - 1) & ~mask; 168 | addr = (addr & tmp) | ((addr >> 1) & ~tmp); 169 | mask = (mask & (mask - 1)) >> 1; 170 | } 171 | 172 | return addr; 173 | } 174 | 175 | static size_t highestBit(size_t n) { 176 | n = addBitsDown(n); 177 | return n ^ (n >> 1); 178 | } 179 | 180 | bool lrcpp::Config::preprocessMemoryDescriptors(struct retro_memory_descriptor* descriptors, unsigned count) { 181 | size_t disconnect_mask; 182 | size_t top_addr = 1; 183 | 184 | for (unsigned i = 0; i < count; i++) { 185 | struct retro_memory_descriptor* const desc = descriptors + i; 186 | 187 | if (desc->select != 0) { 188 | top_addr |= desc->select; 189 | } 190 | else { 191 | top_addr |= desc->start + desc->len - 1; 192 | } 193 | } 194 | 195 | top_addr = addBitsDown(top_addr); 196 | 197 | for (unsigned i = 0; i < count; i++) { 198 | struct retro_memory_descriptor* const desc = descriptors + i; 199 | 200 | if (desc->select == 0) { 201 | if (desc->len == 0) { 202 | return false; 203 | } 204 | 205 | if ((desc->len & (desc->len - 1)) != 0) { 206 | return false; 207 | } 208 | 209 | desc->select = top_addr & ~inflate(addBitsDown(desc->len - 1), desc->disconnect); 210 | } 211 | 212 | if (desc->len == 0) { 213 | desc->len = addBitsDown(reduce(top_addr & ~desc->select, desc->disconnect)) + 1; 214 | } 215 | 216 | if (desc->start & ~desc->select) { 217 | return false; 218 | } 219 | 220 | while (reduce(top_addr & ~desc->select, desc->disconnect) >> 1 > desc->len - 1) { 221 | desc->disconnect |= highestBit(top_addr & ~desc->select & ~desc->disconnect); 222 | } 223 | 224 | disconnect_mask = addBitsDown(desc->len - 1); 225 | desc->disconnect &= disconnect_mask; 226 | 227 | while ((~disconnect_mask) >> 1 & desc->disconnect) { 228 | disconnect_mask >>= 1; 229 | desc->disconnect &= disconnect_mask; 230 | } 231 | } 232 | 233 | return true; 234 | } 235 | -------------------------------------------------------------------------------- /src/Frontend.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2020 Andre Leiradella 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace { 32 | class DummyLogger : public lrcpp::Logger { 33 | public: 34 | virtual void vprintf(retro_log_level level, char const* format, va_list args) override { 35 | (void)level; 36 | (void)format; 37 | (void)args; 38 | } 39 | }; 40 | } 41 | 42 | // Storage for the current Frontend instance 43 | static thread_local lrcpp::Frontend* s_frontend; 44 | // Dummy logger to use if the caller doesn't set one 45 | static DummyLogger s_logger; 46 | 47 | lrcpp::Frontend::Frontend() 48 | : _logger(&s_logger) 49 | , _config(nullptr) 50 | , _video(nullptr) 51 | , _led(nullptr) 52 | , _audio(nullptr) 53 | , _midi(nullptr) 54 | , _input(nullptr) 55 | , _rumble(nullptr) 56 | , _sensor(nullptr) 57 | , _camera(nullptr) 58 | , _location(nullptr) 59 | , _virtualFileSystem(nullptr) 60 | , _diskControl(nullptr) 61 | , _perf(nullptr) 62 | , _supportsNoGame(false) 63 | , _shutdownRequested(false) 64 | { 65 | CoreFsm_Init(&_fsm, &_core, this, nullptr); 66 | } 67 | 68 | lrcpp::Frontend::~Frontend() { 69 | CoreFsm_Transition_unset(&_fsm); 70 | } 71 | 72 | lrcpp::Frontend* lrcpp::Frontend::getCurrent() { 73 | return s_frontend; 74 | } 75 | 76 | void lrcpp::Frontend::setCurrent(Frontend* frontend) { 77 | s_frontend = frontend; 78 | } 79 | 80 | lrcpp::Logger* lrcpp::Frontend::getLogger() { 81 | return _logger; 82 | } 83 | 84 | lrcpp::Config* lrcpp::Frontend::getConfig() { 85 | return _config; 86 | } 87 | 88 | lrcpp::Video* lrcpp::Frontend::getVideo() { 89 | return _video; 90 | } 91 | 92 | lrcpp::Led* lrcpp::Frontend::getLed() { 93 | return _led; 94 | } 95 | 96 | lrcpp::Audio* lrcpp::Frontend::getAudio() { 97 | return _audio; 98 | } 99 | 100 | lrcpp::Midi* lrcpp::Frontend::getMidi() { 101 | return _midi; 102 | } 103 | 104 | lrcpp::Input* lrcpp::Frontend::getInput() { 105 | return _input; 106 | } 107 | 108 | lrcpp::Rumble* lrcpp::Frontend::getRumble() { 109 | return _rumble; 110 | } 111 | 112 | lrcpp::Sensor* lrcpp::Frontend::getSensor() { 113 | return _sensor; 114 | } 115 | 116 | lrcpp::Camera* lrcpp::Frontend::getCamera() { 117 | return _camera; 118 | } 119 | 120 | lrcpp::Location* lrcpp::Frontend::getLocation() { 121 | return _location; 122 | } 123 | 124 | lrcpp::VirtualFileSystem* lrcpp::Frontend::getVirtualFileSystem() { 125 | return _virtualFileSystem; 126 | } 127 | 128 | lrcpp::DiskControl* lrcpp::Frontend::getDiskControl() { 129 | return _diskControl; 130 | } 131 | 132 | lrcpp::Perf* lrcpp::Frontend::getPerf() { 133 | return _perf; 134 | } 135 | 136 | bool lrcpp::Frontend::setLogger(Logger* logger) { 137 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 138 | _logger = logger; 139 | return true; 140 | } 141 | 142 | return false; 143 | } 144 | 145 | bool lrcpp::Frontend::setConfig(Config* config) { 146 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 147 | _config = config; 148 | return true; 149 | } 150 | 151 | return false; 152 | } 153 | 154 | bool lrcpp::Frontend::setVideo(Video* video) { 155 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 156 | _video = video; 157 | return true; 158 | } 159 | 160 | return false; 161 | } 162 | 163 | bool lrcpp::Frontend::setLed(Led* led) { 164 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 165 | _led = led; 166 | return true; 167 | } 168 | 169 | return false; 170 | } 171 | 172 | bool lrcpp::Frontend::setAudio(Audio* audio) { 173 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 174 | _audio = audio; 175 | return true; 176 | } 177 | 178 | return false; 179 | } 180 | 181 | bool lrcpp::Frontend::setMidi(Midi* midi) { 182 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 183 | _midi = midi; 184 | return true; 185 | } 186 | 187 | return false; 188 | } 189 | 190 | bool lrcpp::Frontend::setInput(Input* input) { 191 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 192 | _input = input; 193 | return true; 194 | } 195 | 196 | return false; 197 | } 198 | 199 | bool lrcpp::Frontend::setRumble(Rumble* rumble) { 200 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 201 | _rumble = rumble; 202 | return true; 203 | } 204 | 205 | return false; 206 | } 207 | 208 | bool lrcpp::Frontend::setSensor(Sensor* sensor) { 209 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 210 | _sensor = sensor; 211 | return true; 212 | } 213 | 214 | return false; 215 | } 216 | 217 | bool lrcpp::Frontend::setCamera(Camera* camera) { 218 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 219 | _camera = camera; 220 | return true; 221 | } 222 | 223 | return false; 224 | } 225 | 226 | bool lrcpp::Frontend::setLocation(Location* location) { 227 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 228 | _location = location; 229 | return true; 230 | } 231 | 232 | return false; 233 | } 234 | 235 | bool lrcpp::Frontend::setVirtualFileSystem(VirtualFileSystem* virtualFileSystem) { 236 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 237 | _virtualFileSystem = virtualFileSystem; 238 | return true; 239 | } 240 | 241 | return false; 242 | } 243 | 244 | bool lrcpp::Frontend::setDiskControl(DiskControl* diskControl) { 245 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 246 | _diskControl = diskControl; 247 | return true; 248 | } 249 | 250 | return false; 251 | } 252 | 253 | bool lrcpp::Frontend::setPerf(Perf* perf) { 254 | if (CoreFsm_CurrentState(&_fsm) == CoreFsm_State_Start) { 255 | _perf = perf; 256 | return true; 257 | } 258 | 259 | return false; 260 | } 261 | 262 | bool lrcpp::Frontend::setCore(Core const* core) { 263 | _core = *core; 264 | 265 | if (!CoreFsm_Transition_coreSet(&_fsm)) { 266 | _logger->error("Error in coreSet transition\n"); 267 | return false; 268 | } 269 | 270 | if (!CoreFsm_Transition_setEnvironment(&_fsm, staticEnvironmentCallback)) { 271 | _logger->error("Error in setEnvironment transition\n"); 272 | return false; 273 | } 274 | 275 | if (!CoreFsm_Transition_init(&_fsm)) { 276 | _logger->error("Error in init transition\n"); 277 | return false; 278 | } 279 | 280 | return true; 281 | } 282 | 283 | bool lrcpp::Frontend::loadGame() { 284 | _logger->debug("Starting core without a content\n"); 285 | 286 | if (!_supportsNoGame) { 287 | _logger->error("Core does not support being started without a content\n"); 288 | return false; 289 | } 290 | 291 | return loadGame(nullptr, nullptr, 0); 292 | } 293 | 294 | bool lrcpp::Frontend::loadGame(char const* gamePath) { 295 | _logger->debug("Asking core to load \"%s\"\n", gamePath); 296 | 297 | retro_system_info info; 298 | 299 | if (!CoreFsm_Transition_getSystemInfo(&_fsm, &info)) { 300 | _logger->error("Error in getSystemInfo transition\n"); 301 | return false; 302 | } 303 | 304 | if (!info.need_fullpath) { 305 | _logger->error("Core needs the content handed by the frontend\n"); 306 | return false; 307 | } 308 | 309 | return loadGame(gamePath, nullptr, 0); 310 | } 311 | 312 | bool lrcpp::Frontend::loadGame(char const* gamePath, void const* data, size_t size) { 313 | _logger->debug("Asking core to load \"%s\" (%p and %zu)\n", gamePath, data, size); 314 | 315 | retro_game_info game; 316 | game.path = gamePath; 317 | game.data = data; 318 | game.size = size; 319 | game.meta = nullptr; 320 | 321 | if (!CoreFsm_Transition_loadGame(&_fsm, &game)) { 322 | _logger->error("Error in loadGame transition\n"); 323 | return false; 324 | } 325 | 326 | int const ok = CoreFsm_Transition_setCallbacks( 327 | &_fsm, 328 | videoRefresh, 329 | audioSample, 330 | audioSampleBatch, 331 | inputPoll, 332 | inputState 333 | ); 334 | 335 | if (!ok) { 336 | _logger->error("Error in setCallbacks transition\n"); 337 | return false; 338 | } 339 | 340 | retro_system_av_info avinfo; 341 | 342 | if (!CoreFsm_Transition_getSystemAvInfo(&_fsm, &avinfo)) { 343 | _logger->error("Error in getSystemAvInfo transition\n"); 344 | return false; 345 | } 346 | 347 | _shutdownRequested = false; 348 | return setSystemAvInfo(&avinfo); 349 | } 350 | 351 | bool lrcpp::Frontend::loadGameSpecial(unsigned gameType, struct retro_game_info const* info, size_t numInfo) { 352 | bool ok = CoreFsm_Transition_loadGameSpecial(&_fsm, gameType, info, numInfo); 353 | 354 | ok = ok && CoreFsm_Transition_setCallbacks( 355 | &_fsm, 356 | videoRefresh, 357 | audioSample, 358 | audioSampleBatch, 359 | inputPoll, 360 | inputState 361 | ); 362 | 363 | retro_system_av_info avinfo; 364 | ok = ok && CoreFsm_Transition_getSystemAvInfo(&_fsm, &avinfo); 365 | ok = ok && setSystemAvInfo(&avinfo); 366 | 367 | _shutdownRequested = false; 368 | return ok; 369 | } 370 | 371 | bool lrcpp::Frontend::run() { 372 | return CoreFsm_Transition_run(&_fsm); 373 | } 374 | 375 | bool lrcpp::Frontend::reset() { 376 | return CoreFsm_Transition_reset(&_fsm); 377 | } 378 | 379 | bool lrcpp::Frontend::unloadGame() { 380 | return CoreFsm_Transition_unloadGame(&_fsm); 381 | } 382 | 383 | bool lrcpp::Frontend::unset() { 384 | return CoreFsm_Transition_unset(&_fsm); 385 | } 386 | 387 | bool lrcpp::Frontend::apiVersion(unsigned* version) { 388 | return CoreFsm_Transition_apiVersion(&_fsm, version); 389 | } 390 | 391 | bool lrcpp::Frontend::getSystemInfo(struct retro_system_info* info) { 392 | return CoreFsm_Transition_getSystemInfo(&_fsm, info); 393 | } 394 | 395 | bool lrcpp::Frontend::getSystemAvInfo(struct retro_system_av_info* info) { 396 | return CoreFsm_Transition_getSystemAvInfo(&_fsm, info); 397 | } 398 | 399 | bool lrcpp::Frontend::serializeSize(size_t* size) { 400 | return CoreFsm_Transition_serializeSize(&_fsm, size); 401 | } 402 | 403 | bool lrcpp::Frontend::serialize(void* data, size_t size) { 404 | return CoreFsm_Transition_serialize(&_fsm, data, size); 405 | } 406 | 407 | bool lrcpp::Frontend::unserialize(void const* data, size_t size) { 408 | return CoreFsm_Transition_unserialize(&_fsm, data, size); 409 | } 410 | 411 | bool lrcpp::Frontend::cheatReset() { 412 | return CoreFsm_Transition_cheatReset(&_fsm); 413 | } 414 | 415 | bool lrcpp::Frontend::cheatSet(unsigned index, bool enabled, char const* code) { 416 | return CoreFsm_Transition_cheatSet(&_fsm, index, enabled, code); 417 | } 418 | 419 | bool lrcpp::Frontend::getRegion(unsigned* region) { 420 | return CoreFsm_Transition_getRegion(&_fsm, region); 421 | } 422 | 423 | bool lrcpp::Frontend::getMemoryData(unsigned id, void** data) { 424 | return CoreFsm_Transition_getMemoryData(&_fsm, id, data); 425 | } 426 | 427 | bool lrcpp::Frontend::getMemorySize(unsigned id, size_t* size) { 428 | return CoreFsm_Transition_getMemorySize(&_fsm, id, size); 429 | } 430 | 431 | bool lrcpp::Frontend::shutdownRequested() const { 432 | return _shutdownRequested; 433 | } 434 | 435 | 436 | bool lrcpp::Frontend::setControllerPortDevice(unsigned port, unsigned device) { 437 | return CoreFsm_Transition_setControllerPortDevice(&_fsm, port, device); 438 | } 439 | 440 | bool lrcpp::Frontend::setRotation(unsigned data) { 441 | return _video != nullptr && _video->setRotation(data); 442 | } 443 | 444 | bool lrcpp::Frontend::getOverscan(bool* data) { 445 | return _video != nullptr && _video->getOverscan(data); 446 | } 447 | 448 | bool lrcpp::Frontend::getCanDupe(bool* data) { 449 | return _video != nullptr && _video->getCanDupe(data); 450 | } 451 | 452 | bool lrcpp::Frontend::showMessage(struct retro_message const* data) { 453 | return _video != nullptr && _video->showMessage(data); 454 | } 455 | 456 | bool lrcpp::Frontend::shutdown() { 457 | _shutdownRequested = true; 458 | return true; 459 | } 460 | 461 | bool lrcpp::Frontend::setPerformanceLevel(unsigned data) { 462 | return _config != nullptr && _config->setPerformanceLevel(data); 463 | } 464 | 465 | bool lrcpp::Frontend::getSystemDirectory(char const** data) { 466 | return _config != nullptr && _config->getSystemDirectory(data); 467 | } 468 | 469 | bool lrcpp::Frontend::setPixelFormat(enum retro_pixel_format data) { 470 | return _video != nullptr && _video->setPixelFormat(data); 471 | } 472 | 473 | bool lrcpp::Frontend::setInputDescriptors(struct retro_input_descriptor const* data) { 474 | return _input != nullptr && _input->setInputDescriptors(data); 475 | } 476 | 477 | bool lrcpp::Frontend::setKeyboardCallback(struct retro_keyboard_callback const* data) { 478 | return _input != nullptr && _input->setKeyboardCallback(data); 479 | } 480 | 481 | bool lrcpp::Frontend::setDiskControlInterface(struct retro_disk_control_callback const* data) { 482 | return _diskControl != nullptr && _diskControl->setDiskControlInterface(data); 483 | } 484 | 485 | bool lrcpp::Frontend::setHwRender(struct retro_hw_render_callback* data) { 486 | return _video != nullptr && _video->setHwRender(data); 487 | } 488 | 489 | bool lrcpp::Frontend::getVariable(struct retro_variable* data) { 490 | return _config != nullptr && _config->getVariable(data); 491 | } 492 | 493 | bool lrcpp::Frontend::setVariables(struct retro_variable const* data) { 494 | return _config != nullptr && _config->setVariables(data); 495 | } 496 | 497 | bool lrcpp::Frontend::getVariableUpdate(bool* data) { 498 | return _config != nullptr && _config->getVariableUpdate(data); 499 | } 500 | 501 | bool lrcpp::Frontend::setSupportNoGame(bool data) { 502 | _supportsNoGame = data; 503 | return _config != nullptr && _config->setSupportNoGame(data); 504 | } 505 | 506 | bool lrcpp::Frontend::getLibretroPath(char const** data) { 507 | return _config != nullptr && _config->getLibretroPath(data); 508 | } 509 | 510 | bool lrcpp::Frontend::setFrameTimeCallback(struct retro_frame_time_callback const* data) { 511 | return _video != nullptr && _video->setFrameTimeCallback(data); 512 | } 513 | 514 | bool lrcpp::Frontend::setAudioCallback(struct retro_audio_callback const* data) { 515 | return _audio != nullptr && _audio->setAudioCallback(data); 516 | } 517 | 518 | bool lrcpp::Frontend::getRumbleInterface(struct retro_rumble_interface* data) { 519 | if (_rumble == nullptr) { 520 | return false; 521 | } 522 | 523 | data->set_rumble_state = rumbleSetState; 524 | return true; 525 | } 526 | 527 | bool lrcpp::Frontend::getInputDeviceCapabilities(uint64_t* data) { 528 | return _input != nullptr && _input->getInputDeviceCapabilities(data); 529 | } 530 | 531 | bool lrcpp::Frontend::getSensorInterface(struct retro_sensor_interface* data) { 532 | if (_sensor == nullptr) { 533 | return false; 534 | } 535 | 536 | data->set_sensor_state = sensorSetState; 537 | data->get_sensor_input = sensorGetInput; 538 | return true; 539 | } 540 | 541 | bool lrcpp::Frontend::getCameraInterface(struct retro_camera_callback* data) { 542 | if (_camera == nullptr) { 543 | return false; 544 | } 545 | 546 | if (!_camera->getCameraInterface(data)) { 547 | return false; 548 | } 549 | 550 | data->start = cameraStart; 551 | data->stop = cameraStop; 552 | return true; 553 | } 554 | 555 | bool lrcpp::Frontend::getLogInterface(struct retro_log_callback* data) { 556 | if (_logger == nullptr) { 557 | return false; 558 | } 559 | 560 | data->log = loggerVprintf; 561 | return true; 562 | } 563 | 564 | bool lrcpp::Frontend::getPerfInterface(struct retro_perf_callback* data) { 565 | if (_perf == nullptr) { 566 | return false; 567 | } 568 | 569 | data->get_time_usec = perfGetTimeUsec; 570 | data->get_cpu_features = perfGetCpuFeatures; 571 | data->get_perf_counter = perfGetCounter; 572 | data->perf_register = perfRegister; 573 | data->perf_start = perfStart; 574 | data->perf_stop = perfStop; 575 | data->perf_log = perfLog; 576 | return true; 577 | } 578 | 579 | bool lrcpp::Frontend::getLocationInterface(struct retro_location_callback* data) { 580 | return _location != nullptr && _location->getLocationInterface(data); 581 | } 582 | 583 | bool lrcpp::Frontend::getCoreAssetsDirectory(char const** data) { 584 | return _config != nullptr && _config->getCoreAssetsDirectory(data); 585 | } 586 | 587 | bool lrcpp::Frontend::getSaveDirectory(char const** data) { 588 | return _config != nullptr && _config->getSaveDirectory(data); 589 | } 590 | 591 | bool lrcpp::Frontend::setSystemAvInfo(struct retro_system_av_info const* data) { 592 | bool videoOk = _video != nullptr && _video->setSystemAvInfo(data); 593 | bool audioOk = _audio != nullptr && _audio->setSystemAvInfo(data); 594 | return videoOk || audioOk; 595 | } 596 | 597 | bool lrcpp::Frontend::setProcAddressCallback(struct retro_get_proc_address_interface const* data) { 598 | return _config != nullptr && _config->setProcAddressCallback(data); 599 | } 600 | 601 | bool lrcpp::Frontend::setSubsystemInfo(struct retro_subsystem_info const* data) { 602 | return _config != nullptr && _config->setSubsystemInfo(data); 603 | } 604 | 605 | bool lrcpp::Frontend::setControllerInfo(struct retro_controller_info const* data) { 606 | return _input != nullptr && _input->setControllerInfo(data); 607 | } 608 | 609 | bool lrcpp::Frontend::setMemoryMaps(struct retro_memory_map const* data) { 610 | return _config != nullptr && _config->setMemoryMaps(data); 611 | } 612 | 613 | bool lrcpp::Frontend::setGeometry(struct retro_game_geometry const* data) { 614 | return _video != nullptr && _video->setGeometry(data);; 615 | } 616 | 617 | bool lrcpp::Frontend::getUsername(char const** data) { 618 | return _config != nullptr && _config->getUsername(data); 619 | } 620 | 621 | bool lrcpp::Frontend::getLanguage(unsigned* data) { 622 | return _config != nullptr && _config->getLanguage(data); 623 | } 624 | 625 | bool lrcpp::Frontend::getCurrentSoftwareFramebuffer(struct retro_framebuffer* data) { 626 | return _video != nullptr && _video->getCurrentSoftwareFramebuffer(data); 627 | } 628 | 629 | bool lrcpp::Frontend::getHwRenderInterface(struct retro_hw_render_interface const** data) { 630 | return _video != nullptr && _video->getHwRenderInterface(data); 631 | } 632 | 633 | bool lrcpp::Frontend::setSupportAchievements(bool data) { 634 | return _config != nullptr && _config->setSupportAchievements(data); 635 | } 636 | 637 | bool lrcpp::Frontend::setHwRenderContextNegotiationInterface(struct retro_hw_render_context_negotiation_interface const* data) { 638 | return _video != nullptr && _video->setHwRenderContextNegotiationInterface(data); 639 | } 640 | 641 | bool lrcpp::Frontend::setSerializationQuirks(uint64_t data) { 642 | return _config != nullptr && _config->setSerializationQuirks(data); 643 | } 644 | 645 | bool lrcpp::Frontend::setHwSharedContext() { 646 | return _video != nullptr && _video->setHwSharedContext(); 647 | } 648 | 649 | bool lrcpp::Frontend::getVfsInterface(struct retro_vfs_interface_info* data) { 650 | if (_virtualFileSystem == nullptr) { 651 | return false; 652 | } 653 | 654 | if (!_virtualFileSystem->getVfsInterface(data)) { 655 | return false; 656 | } 657 | 658 | _virtualFileSystemInterface.get_path = virtualFileSystemGetPath; 659 | _virtualFileSystemInterface.open = virtualFileSystemOpen; 660 | _virtualFileSystemInterface.close = virtualFileSystemClose; 661 | _virtualFileSystemInterface.size = virtualFileSystemSize; 662 | _virtualFileSystemInterface.tell = virtualFileSystemTell; 663 | _virtualFileSystemInterface.seek = virtualFileSystemSeek; 664 | _virtualFileSystemInterface.read = virtualFileSystemRead; 665 | _virtualFileSystemInterface.write = virtualFileSystemWrite; 666 | _virtualFileSystemInterface.flush = virtualFileSystemFlush; 667 | _virtualFileSystemInterface.remove = virtualFileSystemRemove; 668 | _virtualFileSystemInterface.rename = virtualFileSystemRename; 669 | _virtualFileSystemInterface.truncate = virtualFileSystemTruncate; 670 | _virtualFileSystemInterface.stat = virtualFileSystemStat; 671 | _virtualFileSystemInterface.mkdir = virtualFileSystemMkDir; 672 | _virtualFileSystemInterface.opendir = virtualFileSystemOpenDir; 673 | _virtualFileSystemInterface.readdir = virtualFileSystemReadDir; 674 | _virtualFileSystemInterface.dirent_get_name = virtualFileSystemDirentGetName; 675 | _virtualFileSystemInterface.dirent_is_dir = virtualFileSystemDirentIsDir; 676 | _virtualFileSystemInterface.closedir = virtualFileSystemCloseDir; 677 | 678 | data->iface = &_virtualFileSystemInterface; 679 | return true; 680 | } 681 | 682 | bool lrcpp::Frontend::getLedInterface(struct retro_led_interface* data) { 683 | if (_led == nullptr) { 684 | return false; 685 | } 686 | 687 | data->set_led_state = ledSetState; 688 | return true; 689 | } 690 | 691 | bool lrcpp::Frontend::getAudioVideoEnable(int* data) { 692 | return _config != nullptr && _config->getAudioVideoEnable(data); 693 | } 694 | 695 | bool lrcpp::Frontend::getMidiInterface(struct retro_midi_interface** data) { 696 | if (_midi == nullptr) { 697 | return false; 698 | } 699 | 700 | _midiInterface.input_enabled = midiInputEnabled; 701 | _midiInterface.output_enabled = midiOutputEnabled; 702 | _midiInterface.read = midiRead; 703 | _midiInterface.write = midiWrite; 704 | _midiInterface.flush = midiFlush; 705 | 706 | *data = &_midiInterface; 707 | return true; 708 | } 709 | 710 | bool lrcpp::Frontend::getFastForwarding(bool* data) { 711 | return _config != nullptr && _config->getFastForwarding(data); 712 | } 713 | 714 | bool lrcpp::Frontend::getTargetRefreshRate(float* data) { 715 | return _video != nullptr && _video->getTargetRefreshRate(data); 716 | } 717 | 718 | bool lrcpp::Frontend::getInputBitmasks(bool* data) { 719 | // HACK: At least the vice core calls this with a null data 720 | bool dummy = false; 721 | return _input != nullptr && _input->getInputBitmasks(data != nullptr ? data : &dummy); 722 | } 723 | 724 | bool lrcpp::Frontend::getCoreOptionsVersion(unsigned* data) { 725 | return _config != nullptr && _config->getCoreOptionsVersion(data); 726 | } 727 | 728 | bool lrcpp::Frontend::setCoreOptions(struct retro_core_option_definition const* data) { 729 | return _config != nullptr && _config->setCoreOptions(data); 730 | } 731 | 732 | bool lrcpp::Frontend::setCoreOptionsIntl(struct retro_core_options_intl const* data) { 733 | return _config != nullptr && _config->setCoreOptionsIntl(data); 734 | } 735 | 736 | bool lrcpp::Frontend::setCoreOptionsDisplay(struct retro_core_option_display const* data) { 737 | return _config != nullptr && _config->setCoreOptionsDisplay(data); 738 | } 739 | 740 | bool lrcpp::Frontend::getPreferredHwRender(unsigned* data) { 741 | return _video != nullptr && _video->getPreferredHwRender(data); 742 | } 743 | 744 | bool lrcpp::Frontend::getDiskControlInterfaceVersion(unsigned* data) { 745 | return _diskControl != nullptr && _diskControl->getDiskControlInterfaceVersion(data); 746 | } 747 | 748 | bool lrcpp::Frontend::setDiskControlExtInterface(struct retro_disk_control_ext_callback const* data) { 749 | return _diskControl != nullptr && _diskControl->setDiskControlExtInterface(data); 750 | } 751 | 752 | bool lrcpp::Frontend::environmentCallback(unsigned cmd, void* data) { 753 | switch (cmd) { 754 | case RETRO_ENVIRONMENT_SET_ROTATION: 755 | return setRotation(*(unsigned*)data); 756 | 757 | case RETRO_ENVIRONMENT_GET_OVERSCAN: 758 | return getOverscan((bool*)data); 759 | 760 | case RETRO_ENVIRONMENT_GET_CAN_DUPE: 761 | return getCanDupe((bool*)data); 762 | 763 | case RETRO_ENVIRONMENT_SET_MESSAGE: 764 | return showMessage((struct retro_message const*)data); 765 | 766 | case RETRO_ENVIRONMENT_SHUTDOWN: 767 | return shutdown(); 768 | 769 | case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: 770 | return setPerformanceLevel(*(unsigned*)data); 771 | 772 | case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: 773 | return getSystemDirectory((char const**)data); 774 | 775 | case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: 776 | return setPixelFormat(*(enum retro_pixel_format*)data); 777 | 778 | case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: 779 | return setInputDescriptors((struct retro_input_descriptor const*)data); 780 | 781 | case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: 782 | return setKeyboardCallback((struct retro_keyboard_callback const*)data); 783 | 784 | case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: 785 | return setDiskControlInterface((struct retro_disk_control_callback const*)data); 786 | 787 | case RETRO_ENVIRONMENT_SET_HW_RENDER: 788 | return setHwRender((struct retro_hw_render_callback*)data); 789 | 790 | case RETRO_ENVIRONMENT_GET_VARIABLE: 791 | return getVariable((struct retro_variable*)data); 792 | 793 | case RETRO_ENVIRONMENT_SET_VARIABLES: 794 | return setVariables((struct retro_variable const*)data); 795 | 796 | case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: 797 | return getVariableUpdate((bool*)data); 798 | 799 | case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: 800 | return setSupportNoGame(*(bool*)data); 801 | 802 | case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: 803 | return getLibretroPath((char const**)data); 804 | 805 | case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK: 806 | return setFrameTimeCallback((struct retro_frame_time_callback const*)data); 807 | 808 | case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK: 809 | return setAudioCallback((struct retro_audio_callback const*)data); 810 | 811 | case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: 812 | return getRumbleInterface((struct retro_rumble_interface*)data); 813 | 814 | case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES: 815 | return getInputDeviceCapabilities((uint64_t*)data); 816 | 817 | case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE: 818 | return getSensorInterface((struct retro_sensor_interface*)data); 819 | 820 | case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE: 821 | return getCameraInterface((struct retro_camera_callback*)data); 822 | 823 | case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: 824 | return getLogInterface((struct retro_log_callback*)data); 825 | 826 | case RETRO_ENVIRONMENT_GET_PERF_INTERFACE: 827 | return getPerfInterface((struct retro_perf_callback*)data); 828 | 829 | case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE: 830 | return getLocationInterface((struct retro_location_callback*)data); 831 | 832 | case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: 833 | return getCoreAssetsDirectory((char const**)data); 834 | 835 | case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: 836 | return getSaveDirectory((char const**)data); 837 | 838 | case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: 839 | return setSystemAvInfo((struct retro_system_av_info const*)data); 840 | 841 | case RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK: 842 | return setProcAddressCallback((struct retro_get_proc_address_interface const*)data); 843 | 844 | case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO: 845 | return setSubsystemInfo((struct retro_subsystem_info const*)data); 846 | 847 | case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: 848 | return setControllerInfo((struct retro_controller_info const*)data); 849 | 850 | case RETRO_ENVIRONMENT_SET_MEMORY_MAPS: 851 | return setMemoryMaps((struct retro_memory_map const*)data); 852 | 853 | case RETRO_ENVIRONMENT_SET_GEOMETRY: 854 | return setGeometry((struct retro_game_geometry const*)data); 855 | 856 | case RETRO_ENVIRONMENT_GET_USERNAME: 857 | return getUsername((char const**)data); 858 | 859 | case RETRO_ENVIRONMENT_GET_LANGUAGE: 860 | return getLanguage((unsigned*)data); 861 | 862 | case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER: 863 | return getCurrentSoftwareFramebuffer((struct retro_framebuffer*)data); 864 | 865 | case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE: 866 | return getHwRenderInterface((struct retro_hw_render_interface const**)data); 867 | 868 | case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS: 869 | return setSupportAchievements(*(bool*)data); 870 | 871 | case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE: 872 | return setHwRenderContextNegotiationInterface((struct retro_hw_render_context_negotiation_interface const*)data); 873 | 874 | case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS: 875 | return setSerializationQuirks(*(uint64_t*)data); 876 | 877 | case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT: 878 | return setHwSharedContext(); 879 | 880 | case RETRO_ENVIRONMENT_GET_VFS_INTERFACE: 881 | return getVfsInterface((struct retro_vfs_interface_info*)data); 882 | 883 | case RETRO_ENVIRONMENT_GET_LED_INTERFACE: 884 | return getLedInterface((struct retro_led_interface*)data); 885 | 886 | case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE: 887 | return getAudioVideoEnable((int*)data); 888 | 889 | case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE: 890 | return getMidiInterface((struct retro_midi_interface**)data); 891 | 892 | case RETRO_ENVIRONMENT_GET_FASTFORWARDING: 893 | return getFastForwarding((bool*)data); 894 | 895 | case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE: 896 | return getTargetRefreshRate((float*)data); 897 | 898 | case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS: 899 | return getInputBitmasks((bool*)data); 900 | 901 | case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION: 902 | return getCoreOptionsVersion((unsigned*)data); 903 | 904 | case RETRO_ENVIRONMENT_SET_CORE_OPTIONS: 905 | return setCoreOptions((struct retro_core_option_definition const*)data); 906 | 907 | case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL: 908 | return setCoreOptionsIntl((struct retro_core_options_intl const*)data); 909 | 910 | case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY: 911 | return setCoreOptionsDisplay((struct retro_core_option_display const*)data); 912 | 913 | case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: 914 | return getPreferredHwRender((unsigned*)data); 915 | 916 | case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION: 917 | return getDiskControlInterfaceVersion((unsigned*)data); 918 | 919 | case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE: 920 | return setDiskControlExtInterface((struct retro_disk_control_ext_callback const*)data); 921 | 922 | default: 923 | return false; 924 | } 925 | } 926 | 927 | bool lrcpp::Frontend::staticEnvironmentCallback(unsigned cmd, void* data) { 928 | return getCurrent()->environmentCallback(cmd, data); 929 | } 930 | 931 | bool lrcpp::Frontend::rumbleSetState(unsigned port, enum retro_rumble_effect effect, uint16_t strength) { 932 | return getCurrent()->_rumble->setState(port, effect, strength); 933 | } 934 | 935 | bool lrcpp::Frontend::sensorSetState(unsigned port, enum retro_sensor_action action, unsigned rate) { 936 | return getCurrent()->_sensor->setState(port, action, rate); 937 | } 938 | 939 | float lrcpp::Frontend::sensorGetInput(unsigned port, unsigned id) { 940 | return getCurrent()->_sensor->getInput(port, id); 941 | } 942 | 943 | bool lrcpp::Frontend::cameraStart() { 944 | return getCurrent()->_camera->start(); 945 | } 946 | 947 | void lrcpp::Frontend::cameraStop() { 948 | getCurrent()->_camera->stop(); 949 | } 950 | 951 | void lrcpp::Frontend::loggerVprintf(enum retro_log_level level, char const* format, ...) { 952 | va_list args; 953 | va_start(args, format); 954 | getCurrent()->_logger->vprintf(level, format, args); 955 | va_end(args); 956 | } 957 | 958 | retro_time_t lrcpp::Frontend::perfGetTimeUsec() { 959 | return getCurrent()->_perf->getTimeUsec(); 960 | } 961 | 962 | uint64_t lrcpp::Frontend::perfGetCpuFeatures() { 963 | return getCurrent()->_perf->getCpuFeatures(); 964 | } 965 | 966 | retro_perf_tick_t lrcpp::Frontend::perfGetCounter() { 967 | return getCurrent()->_perf->getCounter(); 968 | } 969 | 970 | void lrcpp::Frontend::perfRegister(struct retro_perf_counter* counter) { 971 | getCurrent()->_perf->register_(counter); 972 | } 973 | 974 | void lrcpp::Frontend::perfStart(struct retro_perf_counter* counter) { 975 | getCurrent()->_perf->start(counter); 976 | } 977 | 978 | void lrcpp::Frontend::perfStop(struct retro_perf_counter* counter) { 979 | getCurrent()->_perf->stop(counter); 980 | } 981 | 982 | void lrcpp::Frontend::perfLog() { 983 | getCurrent()->_perf->log(); 984 | } 985 | 986 | bool lrcpp::Frontend::locationStart() { 987 | return getCurrent()->_location->start(); 988 | } 989 | 990 | void lrcpp::Frontend::locationStop() { 991 | getCurrent()->_location->stop(); 992 | } 993 | 994 | bool lrcpp::Frontend::locationGetPosition(double* lat, double* lon, double* horizAccuracy, double* vertAccuracy) { 995 | return getCurrent()->_location->getPosition(lat, lon, horizAccuracy, vertAccuracy); 996 | } 997 | 998 | void lrcpp::Frontend::locationSetInterval(unsigned intervalMs, unsigned intervalDistance) { 999 | getCurrent()->_location->setInterval(intervalMs, intervalDistance); 1000 | } 1001 | 1002 | char const* lrcpp::Frontend::virtualFileSystemGetPath(struct retro_vfs_file_handle* stream) { 1003 | return getCurrent()->_virtualFileSystem->getPath(stream); 1004 | } 1005 | 1006 | struct retro_vfs_file_handle* lrcpp::Frontend::virtualFileSystemOpen(char const* path, unsigned mode, unsigned hints) { 1007 | return getCurrent()->_virtualFileSystem->open(path, mode, hints); 1008 | } 1009 | 1010 | int lrcpp::Frontend::virtualFileSystemClose(struct retro_vfs_file_handle* stream) { 1011 | return getCurrent()->_virtualFileSystem->close(stream); 1012 | } 1013 | 1014 | int64_t lrcpp::Frontend::virtualFileSystemSize(struct retro_vfs_file_handle* stream) { 1015 | return getCurrent()->_virtualFileSystem->size(stream); 1016 | } 1017 | 1018 | int64_t lrcpp::Frontend::virtualFileSystemTruncate(struct retro_vfs_file_handle* stream, int64_t length) { 1019 | return getCurrent()->_virtualFileSystem->truncate(stream, length); 1020 | } 1021 | 1022 | int64_t lrcpp::Frontend::virtualFileSystemTell(struct retro_vfs_file_handle* stream) { 1023 | return getCurrent()->_virtualFileSystem->tell(stream); 1024 | } 1025 | 1026 | int64_t lrcpp::Frontend::virtualFileSystemSeek(struct retro_vfs_file_handle* stream, int64_t offset, int seekPosition) { 1027 | return getCurrent()->_virtualFileSystem->seek(stream, offset, seekPosition); 1028 | } 1029 | 1030 | int64_t lrcpp::Frontend::virtualFileSystemRead(struct retro_vfs_file_handle* stream, void* s, uint64_t len) { 1031 | return getCurrent()->_virtualFileSystem->read(stream, s, len); 1032 | } 1033 | 1034 | int64_t lrcpp::Frontend::virtualFileSystemWrite(struct retro_vfs_file_handle* stream, void const* s, uint64_t len) { 1035 | return getCurrent()->_virtualFileSystem->write(stream, s, len); 1036 | } 1037 | 1038 | int lrcpp::Frontend::virtualFileSystemFlush(struct retro_vfs_file_handle* stream) { 1039 | return getCurrent()->_virtualFileSystem->flush(stream); 1040 | } 1041 | 1042 | int lrcpp::Frontend::virtualFileSystemRemove(char const* path) { 1043 | return getCurrent()->_virtualFileSystem->remove(path); 1044 | } 1045 | 1046 | int lrcpp::Frontend::virtualFileSystemRename(char const* oldPath, char const* newPath) { 1047 | return getCurrent()->_virtualFileSystem->rename(oldPath, newPath); 1048 | } 1049 | 1050 | int lrcpp::Frontend::virtualFileSystemStat(char const* path, int32_t* size) { 1051 | return getCurrent()->_virtualFileSystem->stat(path, size); 1052 | } 1053 | 1054 | int lrcpp::Frontend::virtualFileSystemMkDir(char const* dir) { 1055 | return getCurrent()->_virtualFileSystem->mkDir(dir); 1056 | } 1057 | 1058 | struct retro_vfs_dir_handle* lrcpp::Frontend::virtualFileSystemOpenDir(char const* dir, bool includeHidden) { 1059 | return getCurrent()->_virtualFileSystem->openDir(dir, includeHidden); 1060 | } 1061 | 1062 | bool lrcpp::Frontend::virtualFileSystemReadDir(struct retro_vfs_dir_handle* dirstream) { 1063 | return getCurrent()->_virtualFileSystem->readDir(dirstream); 1064 | } 1065 | 1066 | char const* lrcpp::Frontend::virtualFileSystemDirentGetName(struct retro_vfs_dir_handle* dirstream) { 1067 | return getCurrent()->_virtualFileSystem->direntGetName(dirstream); 1068 | } 1069 | 1070 | bool lrcpp::Frontend::virtualFileSystemDirentIsDir(struct retro_vfs_dir_handle* dirstream) { 1071 | return getCurrent()->_virtualFileSystem->direntIsDir(dirstream); 1072 | } 1073 | 1074 | int lrcpp::Frontend::virtualFileSystemCloseDir(struct retro_vfs_dir_handle* dirstream) { 1075 | return getCurrent()->_virtualFileSystem->closeDir(dirstream); 1076 | } 1077 | 1078 | void lrcpp::Frontend::ledSetState(int led, int state) { 1079 | return getCurrent()->_led->setState(led, state); 1080 | } 1081 | 1082 | bool lrcpp::Frontend::midiInputEnabled() { 1083 | return getCurrent()->_midi->inputEnabled(); 1084 | } 1085 | 1086 | bool lrcpp::Frontend::midiOutputEnabled() { 1087 | return getCurrent()->_midi->outputEnabled(); 1088 | } 1089 | 1090 | bool lrcpp::Frontend::midiRead(uint8_t* byte) { 1091 | return getCurrent()->_midi->read(byte); 1092 | } 1093 | 1094 | bool lrcpp::Frontend::midiWrite(uint8_t byte, uint32_t deltaTime) { 1095 | return getCurrent()->_midi->write(byte, deltaTime); 1096 | } 1097 | 1098 | bool lrcpp::Frontend::midiFlush() { 1099 | return getCurrent()->_midi->flush(); 1100 | } 1101 | 1102 | uintptr_t lrcpp::Frontend::videoGetCurrentFramebuffer() { 1103 | return getCurrent()->_video->getCurrentFramebuffer(); 1104 | } 1105 | 1106 | retro_proc_address_t lrcpp::Frontend::videoGetProcAddress(char const* symbol) { 1107 | return getCurrent()->_video->getProcAddress(symbol); 1108 | } 1109 | 1110 | void lrcpp::Frontend::videoRefresh(void const* data, unsigned width, unsigned height, size_t pitch) { 1111 | if (getCurrent()->_video != nullptr) { 1112 | getCurrent()->_video->refresh(data, width, height, pitch); 1113 | } 1114 | } 1115 | 1116 | size_t lrcpp::Frontend::audioSampleBatch(int16_t const* data, size_t frames) { 1117 | if (getCurrent()->_audio != nullptr) { 1118 | return getCurrent()->_audio->sampleBatch(data, frames); 1119 | } 1120 | 1121 | return 0; 1122 | } 1123 | 1124 | void lrcpp::Frontend::audioSample(int16_t left, int16_t right) { 1125 | if (getCurrent()->_audio != nullptr) { 1126 | getCurrent()->_audio->sample(left, right); 1127 | } 1128 | } 1129 | 1130 | int16_t lrcpp::Frontend::inputState(unsigned port, unsigned device, unsigned index, unsigned id) { 1131 | if (getCurrent()->_input != nullptr) { 1132 | return getCurrent()->_input->state(port, device, index, id); 1133 | } 1134 | 1135 | return 0; 1136 | } 1137 | 1138 | void lrcpp::Frontend::inputPoll() { 1139 | if (getCurrent()->_input != nullptr) { 1140 | getCurrent()->_input->poll(); 1141 | } 1142 | } 1143 | --------------------------------------------------------------------------------