diff --git a/CMakeLists.txt b/CMakeLists.txt index f71b1ef5a..32e3727f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,10 +33,12 @@ endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() +set(PKGCONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/" CACHE STRING "Base directory for pkgconfig files.") option(BUILD_TESTS "Should tests be built" ON) option(BUILD_UI "Should integration UI be built" OFF) option(BUILD_LUAJIT "Should static LuaJIT be used instead of system Lua" ON) option(BUILD_MANYLINUX "Should use static libraries compatible with manylinux1" OFF) +option(BUILD_C "Build C API" OFF) set(BUILD_PYTHON ON CACHE BOOL "Build Python module") mark_as_advanced(BUILD_PYTHON) @@ -265,6 +267,12 @@ if(CapnProto_FOUND) target_link_libraries(retro-capnp retro-base CapnProto::capnp) endif() +if(WIN32) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") + add_definitions(-DMS_WIN64) + endif() +endif() + include_directories(src retro third-party/pybind11/include third-party third-party/gtest/googletest/include ${PYTHON_INCLUDE_DIRS}) if(BUILD_PYTHON) add_library(retro SHARED src/retro.cpp) @@ -277,14 +285,31 @@ if(BUILD_PYTHON) if(APPLE) set(PYBIND_LIBS "-undefined dynamic_lookup") elseif(WIN32) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") - add_definitions(-DMS_WIN64) - endif() set(PYBIND_LIBS "${PYTHON_LIBRARY}") endif() target_link_libraries(retro retro-base ${PYBIND_LIBS} ${STATIC_LDFLAGS}) endif() +if(BUILD_C) + add_library(retro-c SHARED src/retro-c.cpp) + set_target_properties(retro-c PROPERTIES + OUTPUT_NAME retro + CXX_VISIBILITY_PRESET default) + target_link_libraries(retro-c retro-base ${STATIC_LDFLAGS}) + install(TARGETS retro-c) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/retro-c.pc +" +Name: ${PROJECT_NAME} +Description: Retro C API. +URL: https://github.com/openai/retro +Version: ${PROJECT_VERSION} +Requires: +Libs: -L${CMAKE_INSTALL_PREFIX}/lib -lretro +Cflags: -I${CMAKE_INSTALL_PREFIX}/include +") + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/retro-c.pc DESTINATION ${PKGCONFIG_INSTALL_DIR}) +endif() + if(BUILD_TESTS) enable_testing() add_subdirectory(third-party/gtest/googlemock) diff --git a/src/coreinfo.cpp b/src/coreinfo.cpp index 1246bf985..5ea954a42 100644 --- a/src/coreinfo.cpp +++ b/src/coreinfo.cpp @@ -108,7 +108,7 @@ bool loadCoreInfo(const string& jsonData) { for (auto ext = core->at("ext").cbegin(); ext != core->at("ext").cend(); ++ext) { s_extensionToCore[*ext] = core.key(); } - s_coreToLib[core.key()] = core->at("lib"); + s_coreToLib[core.key()] = core->at("lib").get(); } return true; } diff --git a/src/emulator.cpp b/src/emulator.cpp index b16b2f7f0..5cd2b27b4 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -360,7 +360,7 @@ bool Emulator::cbEnvironment(unsigned cmd, void* data) { return false; } case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: - *reinterpret_cast(data) = corePath().c_str(); + *reinterpret_cast(data) = strdup(corePath().c_str()); return true; case RETRO_ENVIRONMENT_GET_CAN_DUPE: *reinterpret_cast(data) = true; diff --git a/src/retro-c.cpp b/src/retro-c.cpp new file mode 100644 index 000000000..a24bdeb03 --- /dev/null +++ b/src/retro-c.cpp @@ -0,0 +1,552 @@ +#include "retro-c.h" + +#include "coreinfo.h" +#include "data.h" +#include "emulator.h" +#include "imageops.h" +#include "memory.h" +#include "search.h" +#include "script.h" +#include "movie.h" +#include "movie-bk2.h" + +#include +#include +#include + +using namespace Retro; + +CEmulator* emulatorCreate(const char* romPath) { + if (Emulator::isLoaded()) { + fprintf( + stderr, + "Cannot create multiple emulator instances per process, make sure to call " + "'env.close()' on each environment before creating a new one."); + return nullptr; + } + Emulator* m_re = new Emulator(); + if (!m_re->loadRom(romPath)) { + fprintf(stderr, "Could not load ROM."); + delete m_re; + return nullptr; + } + // The following is necessary because otherwise, you get a segfault + // when you try to get the screen for the first time. + m_re->run(); + return new CEmulator {m_re, 0}; +} + +void emulatorDelete(CEmulator* emulator) { + delete static_cast(emulator->emulator); + delete emulator; +} + +void emulatorStep(CEmulator* emulator) { + static_cast(emulator->emulator)->run(); +} + +CBytes* emulatorGetState(CEmulator* emulator) { + auto* m_re = static_cast(emulator->emulator); + size_t numBytes = m_re->serializeSize(); + void* bytes = malloc(numBytes); + m_re->serialize(bytes, numBytes); + return new CBytes {reinterpret_cast(bytes), numBytes}; +} + +void emulatorStateDelete(CBytes* state) { + delete state->bytes; + delete state; +} + +bool emulatorSetState(CEmulator* emulator, CBytes* state) { + return static_cast(emulator->emulator)->unserialize(state->bytes, state->numBytes); +} + +CEmulatorScreen* emulatorGetScreen(CEmulator* emulator) { + auto* m_re = static_cast(emulator->emulator); + long w = m_re->getImageWidth(); + long h = m_re->getImageHeight(); + uint8_t* data = new uint8_t[w * h * 3]; + Image out(Image::Format::RGB888, data, w, h, w); + Image in; + if (m_re->getImageDepth() == 16) { + in = Image(Image::Format::RGB565, m_re->getImageData(), w, h, m_re->getImagePitch()); + } else if (m_re->getImageDepth() == 32) { + in = Image(Image::Format::RGBX888, m_re->getImageData(), w, h, m_re->getImagePitch()); + } + in.copyTo(&out); + return new CEmulatorScreen {data, (size_t) w, (size_t) h, 3}; +} + +void emulatorScreenDelete(CEmulatorScreen* screen) { + delete screen->values; + delete screen; +} + +double emulatorGetScreenRate(CEmulator* emulator) { + return static_cast(emulator->emulator)->getFrameRate(); +} + +CEmulatorAudio* emulatorGetAudio(CEmulator* emulator) { + auto* m_re = static_cast(emulator->emulator); + size_t numSamples = m_re->getAudioSamples() * 2; + int16_t* samples = new int16_t[numSamples]; + memcpy(samples, m_re->getAudioData(), numSamples * 2); + return new CEmulatorAudio {samples, numSamples}; +} + +void emulatorAudioDelete(CEmulatorAudio* audio) { + delete audio->samples; + delete audio; +} + +double emulatorGetAudioRate(CEmulator* emulator) { + return static_cast(emulator->emulator)->getAudioRate(); +} + +CEmulatorResolution* emulatorGetResolution(CEmulator* emulator) { + auto* m_re = static_cast(emulator->emulator); + auto w = m_re->getImageWidth(); + auto h = m_re->getImageHeight(); + return new CEmulatorResolution {w, h}; +} + +void emulatorResolutionDelete(CEmulatorResolution* resolution) { + delete resolution; +} + +void emulatorSetButtonMask(CEmulator* emulator, const uint8_t* mask, size_t maskSize, unsigned int player) { + if (maskSize > N_BUTTONS) { + fprintf(stderr, "mask.size() > N_BUTTONS."); + return; + } + if (player >= MAX_PLAYERS) { + fprintf(stderr, "player >= MAX_PLAYERS."); + return; + } + for (int key = 0; key < maskSize; key++) { + static_cast(emulator->emulator)->setKey(player, key, mask[key]); + } +} + +void emulatorAddCheat(CEmulator* emulator, const char* code) { + static_cast(emulator->emulator)->setCheat(emulator->cheats, true, code); + emulator->cheats++; +} + +void emulatorClearCheats(CEmulator* emulator) { + static_cast(emulator->emulator)->clearCheats(); + emulator->cheats = 0; +} + +void emulatorConfigureData(CEmulator* emulator, CGameData* gameData) { + static_cast(emulator->emulator)->configureData(static_cast(gameData->data)); +} + +CMemoryView* memoryViewCreate(void* addressSpace) { + return new CMemoryView { static_cast(addressSpace) }; +} + +void memoryViewDelete(CMemoryView* memoryView) { + delete memoryView; +} + +int64_t memoryViewExtract(CMemoryView* memoryView, size_t address, const char* type) { + return (*static_cast(memoryView->addressSpace))[Variable{ type, address }]; +} + +void memoryViewAssign(CMemoryView* memoryView, size_t address, const char* type, int64_t value) { + (*static_cast(memoryView->addressSpace))[Variable{ type, address }] = value; +} + +CMemoryBlocks* memoryViewBlocks(CMemoryView* memoryView) { + auto& internalBlocks = static_cast(memoryView->addressSpace)->blocks(); + auto numBlocks = internalBlocks.size(); + auto* blocks = new CMemoryBlock[numBlocks]; + auto i = 0; + for (const auto& iter : internalBlocks) { + blocks[i] = {iter.first, static_cast(iter.second.offset(0)), iter.second.size()}; + i++; + } + return new CMemoryBlocks {blocks, numBlocks}; +} + +void memoryViewBlocksDelete(CMemoryBlocks* memoryBlocks) { + delete memoryBlocks->blocks; + delete memoryBlocks; +} + +CSearch* searchCreate(const char** types, size_t numTypes) { + Search* search = nullptr; + if (numTypes > 0) { + std::vector dtypes; + for (int i = 0; i < numTypes; i++) { + dtypes.emplace_back(types[i]); + } + search = new Search(dtypes); + } + return new CSearch {search, true}; +} + +CSearch* searchCreateUnmanaged(Retro::Search* search) { + return new CSearch {search, false}; +} + +void searchDelete(CSearch* search) { + if (search->managed) { + delete static_cast(search->search); + } + delete search; +} + +int searchNumResults(CSearch* search) { + return static_cast(search->search)->numResults(); +} + +bool searchHasUniqueResult(CSearch* search) { + return static_cast(search->search)->hasUniqueResult(); +} + +CSearchResult* searchUniqueResult(CSearch* search) { + TypedSearchResult result = static_cast(search->search)->uniqueResult(); + return new CSearchResult {result.address, strdup(result.type.type)}; +} + +void searchResultDelete(CSearchResult* searchResult) { + delete searchResult->type; + delete searchResult; +} + +CSearchTypedResults* searchTypedResults(CSearch* search) { + std::map> results; + for (const auto& result : static_cast(search->search)->typedResults()) { + results[static_cast(result)].emplace(result.type); + } + CSearchTypedResult* cResults = new CSearchTypedResult[results.size()]; + int i = 0; + for (const auto& result : results) { + const char** types = new const char*[result.second.size()]; + int j = 0; + for (const auto& type : result.second) { + types[j] = type.type; + j++; + } + cResults[i] = { + result.first.address, + result.first.mult, + result.first.div, + result.first.bias, + types, + result.second.size()}; + i++; + } + return new CSearchTypedResults {cResults, results.size()}; +} + +void searchTypedResultsDelete(CSearchTypedResults* searchTypedResults) { + for (int i = 0; i < searchTypedResults->numResults; i++) { + for (int j = 0; j < searchTypedResults->results[i].numTypes; j++) { + delete searchTypedResults->results[i].types[j]; + } + } + delete searchTypedResults->results; + delete searchTypedResults; +} + +CGameData* gameDataCreate() { + auto* data = new GameData(); + auto* scenario = new Scenario(*data); + return new CGameData {data, scenario}; +} + +void gameDataDelete(CGameData* gameData) { + delete static_cast(gameData->data); + delete static_cast(gameData->scenario); + delete gameData; +} + +bool gameDataLoad(CGameData* gameData, const char* dataFilename, const char* scenarioFilename) { + ScriptContext::reset(); + + bool success = true; + if (dataFilename != nullptr) { + success = success && static_cast(gameData->data)->load(dataFilename); + } + if (scenarioFilename != nullptr) { + success = success && static_cast(gameData->scenario)->load(scenarioFilename); + } + return success; +} + +bool gameDataSave(CGameData* gameData, const char* dataFilename, const char* scenarioFilename) { + bool success = true; + if (dataFilename != nullptr) { + success = success && static_cast(gameData->data)->save(dataFilename); + } + if (scenarioFilename != nullptr) { + success = success && static_cast(gameData->scenario)->save(scenarioFilename); + } + return success; +} + +void gameDataReset(CGameData* gameData) { + auto* m_scen = static_cast(gameData->scenario); + m_scen->restart(); + m_scen->reloadScripts(); +} + +uint16_t gameDataFilterAction(CGameData* gameData, uint16_t action) { + return static_cast(gameData->scenario)->filterAction(action); +} + +CValidActions* gameDataValidActions(CGameData* gameData) { + auto* m_scen = static_cast(gameData->scenario); + std::map> validActions = m_scen->validActions(); + size_t numActionsOuter = validActions.size(); + int** actions = new int*[numActionsOuter]; + size_t* numActionsInner = new size_t[numActionsOuter]; + int i = 0; + for (const auto& action : validActions) { + numActionsInner[i] = action.second.size(); + actions[i] = new int[numActionsInner[i]]; + int j = 0; + for (const auto& act : action.second) { + actions[i][j] = act; + j++; + } + i++; + } + return new CValidActions {actions, numActionsInner, numActionsOuter}; +} + +void gameDataValidActionsDelete(CValidActions* validActions) { + for (int i = 0; i < validActions->numActionsOuter; i++) { + delete validActions->actions[i]; + } + delete validActions->numActionsInner; + delete validActions; +} + +void gameDataUpdateRam(CGameData* gameData) { + static_cast(gameData->data)->updateRam(); + static_cast(gameData->scenario)->update(); +} + +bool gameDataLookupBoolValue(CGameData* gameData, const char* name) { + return bool(static_cast(gameData->data)->lookupValue(name)); +} + +int64_t gameDataLookupIntValue(CGameData* gameData, const char* name) { + return int64_t(static_cast(gameData->data)->lookupValue(name)); +} + +double gameDataLookupDoubleValue(CGameData* gameData, const char* name) { + return double(static_cast(gameData->data)->lookupValue(name)); +} + +void gameDataSetBoolValue(CGameData* gameData, const char* name, bool value) { + static_cast(gameData->data)->setValue(name, Variant(value)); +} + +void gameDataSetIntValue(CGameData* gameData, const char* name, int64_t value) { + static_cast(gameData->data)->setValue(name, Variant(value)); +} + +void gameDataSetDoubleValue(CGameData* gameData, const char* name, double value) { + static_cast(gameData->data)->setValue(name, Variant(value)); +} + +CNames* gameDataLookupKeys(CGameData* gameData) { + auto* m_data = static_cast(gameData->data); + std::unordered_map allValues = m_data->lookupAll(); + const char** namesArray = new const char*[allValues.size()]; + auto i = 0; + for (const auto& var : allValues) { + namesArray[i] = strdup(var.first.c_str()); + i++; + } + return new CNames {namesArray, allValues.size()}; +} + +CVariable* gameDataGetVariable(CGameData* gameData, const char* name) { + Retro::Variable var = static_cast(gameData->data)->getVariable(name); + auto size = sizeof(char) * 5; + auto* typeCopy = malloc(size); + memcpy(typeCopy, var.type.type, size); + auto* type = reinterpret_cast(typeCopy); + return new CVariable {name, var.address, type}; +} + +void gameDataSetVariable(CGameData* gameData, const char* name, CVariable* value) { + Retro::Variable var { value->type, value->address }; + static_cast(gameData->data)->setVariable(name, var); +} + +void gameDataRemoveVariable(CGameData* gameData, const char* name) { + static_cast(gameData->data)->removeVariable(name); +} + +CVariables* gameDataListVariables(CGameData* gameData) { + const auto& vars = static_cast(gameData->data)->listVariables(); + auto numVariables = vars.size(); + auto* variables = new CVariable[numVariables]; + auto i = 0; + for (const auto& var : vars) { + const auto& v = var.second; + variables[i] = {strdup(var.first.c_str()), v.address, v.type.type}; + i++; + } + return new CVariables {variables, numVariables}; +} + +float gameDataCurrentReward(CGameData* gameData, unsigned int player) { + return static_cast(gameData->scenario)->currentReward(player); +} + +float gameDataTotalReward(CGameData* gameData, unsigned int player) { + return static_cast(gameData->scenario)->totalReward(player); +} + +bool gameDataIsDone(CGameData* gameData) { + return static_cast(gameData->scenario)->isDone(); +} + +CCropInfo* gameDataCropInfo(CGameData* gameData, unsigned int player) { + size_t x = 0; + size_t y = 0; + size_t width = 0; + size_t height = 0; + static_cast(gameData->scenario)->getCrop(&x, &y, &width, &height, player); + return new CCropInfo {x, y, width, height}; +} + +void gameDataCropInfoDelete(CCropInfo* cropInfo) { + delete cropInfo; +} + +CMemoryView* gameDataMemory(CGameData* gameData) { + return memoryViewCreate(&static_cast(gameData->data)->addressSpace()); +} + +void gameDataSearch(CGameData* gameData, const char* name, int64_t value) { + static_cast(gameData->data)->search(name, value); +} + +void gameDataDeltaSearch(CGameData* gameData, const char* name, const char* op, int64_t ref) { + static_cast(gameData->data)->deltaSearch(name, Scenario::op(op), ref); +} + +CSearch* gameDataSearch(CGameData* gameData, const char* name) { + return searchCreateUnmanaged(static_cast(gameData->data)->getSearch(name)); +} + +void gameDataRemoveSearch(CGameData* gameData, const char* name) { + static_cast(gameData->data)->removeSearch(name); +} + +CNames* gameDataListSearchNames(CGameData* gameData) { + std::vector names = static_cast(gameData->data)->listSearches(); + const char** namesArray = new const char*[names.size()]; + for (int i = 0; i < names.size(); i++) { + namesArray[i] = strdup(names[i].c_str()); + } + return new CNames {namesArray, names.size()}; +} + +void gameDataNamesDelete(CNames* names) { + for (int i = 0; i < names->numNames; i++) { + delete names->names[i]; + } + delete names->names; + delete names; +} + +void gameDataVariableDelete(CVariable* variable) { + delete variable->name; + delete variable->type; + delete variable; +} + +void gameDataVariablesDelete(CVariables* variables) { + for (int i = 0; i < variables->numVariables; i++) { + gameDataVariableDelete(&variables->variables[i]); + } + delete variables->variables; + delete variables; +} + +CMovie* movieCreate(const char* name, bool record, unsigned int players) { + Movie* movie; + if (record) { + movie = new MovieBK2(name, true, players); + } else { + movie = Movie::load(name).release(); + } + if (!movie) { + fprintf(stderr, "Could not load movie."); + return nullptr; + } + return new CMovie {movie, record}; +} + +void movieDelete(CMovie* movie) { + delete static_cast(movie->movie); + delete movie; +} + +void movieConfigure(CMovie* movie, const char* name, CEmulator* emulator) { + if (movie->recording) { + static_cast(movie->movie)->setGameName(name); + static_cast(movie->movie)->loadKeymap(static_cast(emulator->emulator)->core()); + } +} + +const char* movieGetGameName(CMovie* movie) { + return strdup(static_cast(movie->movie)->getGameName().c_str()); +} + +bool movieStep(CMovie* movie) { + return static_cast(movie->movie)->step(); +} + +void movieClose(CMovie* movie) { + static_cast(movie->movie)->close(); +} + +unsigned int moviePlayers(CMovie* movie) { + return static_cast(movie->movie)->players(); +} + +bool movieGetKey(CMovie* movie, int key, unsigned int player) { + return static_cast(movie->movie)->getKey(key, player); +} + +void movieSetKey(CMovie* movie, int key, bool set, unsigned int player) { + static_cast(movie->movie)->setKey(key, set, player); +} + +CBytes* movieGetState(CMovie* movie) { + std::vector data; + static_cast(movie->movie)->getState(&data); + auto* cData = data.data(); + auto size = sizeof(uint8_t) * data.size(); + auto* copy = malloc(size); + memcpy(copy, cData, size); + return new CBytes {reinterpret_cast(copy), data.size()}; +} + +void movieSetState(CMovie* movie, CBytes* state) { + static_cast(movie->movie)->setState(state->bytes, state->numBytes); +} + +bool retroLoadCoreInfo(const char* json) { + return Retro::loadCoreInfo(json); +} + +const char* retroCorePath(const char* hint) { + return strdup(Retro::corePath(hint).c_str()); +} + +const char* retroDataPath(const char* hint) { + return strdup(Retro::GameData::dataPath(hint).c_str()); +} diff --git a/src/retro-c.h b/src/retro-c.h new file mode 100644 index 000000000..9de29b60d --- /dev/null +++ b/src/retro-c.h @@ -0,0 +1,203 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CEmulator { + void* emulator; + size_t cheats; +} CEmulator; + +typedef struct CEmulatorScreen { + uint8_t* values; + size_t width; + size_t height; + size_t channels; +} CEmulatorScreen; + +typedef struct CEmulatorAudio { + int16_t* samples; + size_t numSamples; +} CEmulatorAudio; + +typedef struct CEmulatorResolution { + int width; + int height; +} CEmulatorResolution; + +typedef struct CMemoryView { + void* addressSpace; +} CMemoryView; + +typedef struct CMemoryBlock { + size_t address; + const uint8_t* bytes; + size_t numBytes; +} CMemoryBlock; + +typedef struct CMemoryBlocks { + CMemoryBlock* blocks; + size_t numBlocks; +} CMemoryBlocks; + +typedef struct CSearch { + void* search; + bool managed; +} CSearch; + +typedef struct CNames { + const char** names; + size_t numNames; +} CNames; + +typedef struct CSearchResult { + size_t address; + const char* type; +} CSearchResult; + +typedef struct CSearchTypedResult { + size_t address; + uint64_t mult; + uint64_t div; + int64_t bias; + const char** types; + size_t numTypes; +} CSearchTypedResult; + +typedef struct CSearchTypedResults { + CSearchTypedResult* results; + size_t numResults; +} CSearchTypedResults; + +typedef struct CGameData { + void* data; + void* scenario; +} CGameData; + +typedef struct CVariable { + const char* name; + size_t address; + const char* type; +} CVariable; + +typedef struct CVariables { + CVariable* variables; + size_t numVariables; +} CVariables; + +typedef struct CValidActions { + int** actions; + size_t* numActionsInner; + size_t numActionsOuter; +} CValidActions; + +typedef struct CMovie { + void* movie; + bool recording; +} CMovie; + +typedef struct CBytes { + const uint8_t* bytes; + size_t numBytes; +} CBytes; + +typedef struct CCropInfo { + size_t x; + size_t y; + size_t width; + size_t height; +} CCropInfo; + +CEmulator* emulatorCreate(const char* romPath); +void emulatorDelete(CEmulator* emulator); +void emulatorStep(CEmulator* emulator); +CBytes* emulatorGetState(CEmulator* emulator); +void emulatorStateDelete(CBytes* state); +bool emulatorSetState(CEmulator* emulator, CBytes* state); +CEmulatorScreen* emulatorGetScreen(CEmulator* emulator); +void emulatorScreenDelete(CEmulatorScreen* screen); +double emulatorGetScreenRate(CEmulator* emulator); +CEmulatorAudio* emulatorGetAudio(CEmulator* emulator); +void emulatorAudioDelete(CEmulatorAudio* audio); +double emulatorGetAudioRate(CEmulator* emulator); +CEmulatorResolution* emulatorGetResolution(CEmulator* emulator); +void emulatorResolutionDelete(CEmulatorResolution* resolution); +void emulatorSetButtonMask(CEmulator* emulator, const uint8_t* mask, size_t maskSize, unsigned int player); +void emulatorAddCheat(CEmulator* emulator, const char* code); +void emulatorClearCheats(CEmulator* emulator); +void emulatorConfigureData(CEmulator* emulator, CGameData* gameData); + +CMemoryView* memoryViewCreate(void* addressSpace); +void memoryViewDelete(CMemoryView* memoryView); +int64_t memoryViewExtract(CMemoryView* memoryView, size_t address, const char* type); +void memoryViewAssign(CMemoryView* memoryView, size_t address, const char* type, int64_t value); +CMemoryBlocks* memoryViewBlocks(CMemoryView* memoryView); +void memoryViewBlocksDelete(CMemoryBlocks* memoryBlocks); + +CSearch* searchCreate(const char** types, size_t numTypes); +CSearch* searchCreateUnmanaged(void* search); +void searchDelete(CSearch* search); +int searchNumResults(CSearch* search); +bool searchHasUniqueResult(CSearch* search); +CSearchResult* searchUniqueResult(CSearch* search); +void searchResultDelete(CSearchResult* searchResult); +CSearchTypedResults* searchTypedResults(CSearch* search); +void searchTypedResultsDelete(CSearchTypedResults* searchTypedResults); + +CGameData* gameDataCreate(); +void gameDataDelete(CGameData* gameData); +bool gameDataLoad(CGameData* gameData, const char* dataFilename, const char* scenarioFilename); +bool gameDataSave(CGameData* gameData, const char* dataFilename, const char* scenarioFilename); +void gameDataReset(CGameData* gameData); +uint16_t gameDataFilterAction(CGameData* gameData, uint16_t action); +CValidActions* gameDataValidActions(CGameData* gameData); +void gameDataValidActionsDelete(CValidActions* validActions); +void gameDataUpdateRam(CGameData* gameData); +bool gameDataLookupBoolValue(CGameData* gameData, const char* name); +int64_t gameDataLookupIntValue(CGameData* gameData, const char* name); +double gameDataLookupDoubleValue(CGameData* gameData, const char* name); +void gameDataSetBoolValue(CGameData* gameData, const char* name, bool value); +void gameDataSetIntValue(CGameData* gameData, const char* name, int64_t value); +void gameDataSetDoubleValue(CGameData* gameData, const char* name, double value); +CNames* gameDataLookupKeys(CGameData* gameData); +CVariable* gameDataGetVariable(CGameData* gameData, const char* name); +void gameDataSetVariable(CGameData* gameData, const char* name, CVariable* value); +void gameDataRemoveVariable(CGameData* gameData, const char* name); +CVariables* gameDataListVariables(CGameData* gameData); +float gameDataCurrentReward(CGameData* gameData, unsigned int player); +float gameDataTotalReward(CGameData* gameData, unsigned int player); +bool gameDataIsDone(CGameData* gameData); +CCropInfo* gameDataCropInfo(CGameData* gameData, unsigned int player); +void gameDataCropInfoDelete(CCropInfo* cropInfo); +CMemoryView* gameDataMemory(CGameData* gameData); +void gameDataSearch(CGameData* gameData, const char* name, int64_t value); +void gameDataDeltaSearch(CGameData* gameData, const char* name, const char* op, int64_t ref); +CSearch* gameDataGetSearch(CGameData* gameData, const char* name); +void gameDataRemoveSearch(CGameData* gameData, const char* name); +CNames* gameDataListSearchNames(CGameData* gameData); +void gameDataNamesDelete(CNames* names); +void gameDataVariableDelete(CVariable* variable); +void gameDataVariablesDelete(CVariables* variables); + +CMovie* movieCreate(const char* name, bool record, unsigned int players); +void movieDelete(CMovie* movie); +void movieConfigure(CMovie* movie, const char* name, CEmulator* emulator); +const char* movieGetGameName(CMovie* movie); +bool movieStep(CMovie* movie); +void movieClose(CMovie* movie); +unsigned int moviePlayers(CMovie* movie); +bool movieGetKey(CMovie* movie, int key, unsigned int player); +void movieSetKey(CMovie* movie, int key, bool set, unsigned int player); +CBytes* movieGetState(CMovie* movie); +void movieSetState(CMovie* movie, CBytes* state); + +bool retroLoadCoreInfo(const char* json); +const char* retroCorePath(const char* hint); +const char* retroDataPath(const char* hint); + +#ifdef __cplusplus +} /* extern "C" */ +#endif