include_guard(GLOBAL) # Bind native guest-function replacements at build time. The original generated # bodies remain buildable as LLE, and are renamed only in their defining shards # for HLE reference tests. Dispatch declarations keep the original symbols. # Every target sharing these generated sources must use the same selection. function(psxrecomp_select_guest_implementation) cmake_parse_arguments(IMPL "" "NAME;IMPLEMENTATION;SNAPSHOT_TAG;REFERENCE_PREFIX" "TARGETS;GENERATED_SOURCES;HLE_SOURCES;SYMBOLS" ${ARGN}) if(IMPL_UNPARSED_ARGUMENTS OR NOT IMPL_NAME MATCHES "^[A-Za-z_][A-Za-z0-9_]*$") message(FATAL_ERROR "Guest implementation needs a C-identifier NAME and valid arguments") endif() if(NOT IMPL_IMPLEMENTATION MATCHES "^(HLE|LLE)$") message(FATAL_ERROR "${IMPL_NAME}: IMPLEMENTATION must be HLE or LLE") endif() if(NOT IMPL_TARGETS OR NOT IMPL_GENERATED_SOURCES OR NOT IMPL_SYMBOLS) message(FATAL_ERROR "${IMPL_NAME}: TARGETS, GENERATED_SOURCES and SYMBOLS are required") endif() if(NOT IMPL_REFERENCE_PREFIX) set(IMPL_REFERENCE_PREFIX "psx_lle_${IMPL_NAME}_") endif() if(NOT IMPL_REFERENCE_PREFIX MATCHES "^[A-Za-z_][A-Za-z0-9_]*$") message(FATAL_ERROR "${IMPL_NAME}: invalid REFERENCE_PREFIX") endif() set(_tag 0) if(IMPL_IMPLEMENTATION STREQUAL "HLE") if(NOT IMPL_HLE_SOURCES OR NOT IMPL_SNAPSHOT_TAG MATCHES "^(0[xX][0-9A-Fa-f]+|[0-9]+)$") message(FATAL_ERROR "${IMPL_NAME}: HLE needs HLE_SOURCES and a numeric nonzero SNAPSHOT_TAG") endif() math(EXPR _tag "${IMPL_SNAPSHOT_TAG}") if(_tag LESS 1 OR _tag GREATER 4294967295) message(FATAL_ERROR "${IMPL_NAME}: SNAPSHOT_TAG must fit a nonzero uint32_t") endif() endif() foreach(_symbol IN LISTS IMPL_SYMBOLS) if(NOT _symbol MATCHES "^[A-Za-z_][A-Za-z0-9_]*$") message(FATAL_ERROR "${IMPL_NAME}: invalid guest symbol ${_symbol}") endif() set(_definition_count_${_symbol} 0) endforeach() foreach(_source IN LISTS IMPL_GENERATED_SOURCES) get_filename_component(_source "${_source}" ABSOLUTE) if(NOT EXISTS "${_source}") message(FATAL_ERROR "${IMPL_NAME}: missing generated source ${_source}") endif() file(READ "${_source}" _text) foreach(_symbol IN LISTS IMPL_SYMBOLS) string(REGEX MATCHALL "(^|\n)[ \t]*void[ \t\r\n]+${_symbol}[ \t\r\n]*\\([^)]*\\)[ \t\r\n]*\\{" _definitions "${_text}") list(LENGTH _definitions _count) math(EXPR _definition_count_${_symbol} "${_definition_count_${_symbol}} + ${_count}") if(_count GREATER 0 AND IMPL_IMPLEMENTATION STREQUAL "HLE") get_property(_claimed SOURCE "${_source}" PROPERTY "PSX_GUEST_BINDING_${_symbol}") if(_claimed) message(FATAL_ERROR "${IMPL_NAME}: ${_symbol} is already bound by ${_claimed}") endif() set_property(SOURCE "${_source}" PROPERTY "PSX_GUEST_BINDING_${_symbol}" "${IMPL_NAME}") set_property(SOURCE "${_source}" APPEND PROPERTY COMPILE_DEFINITIONS "${_symbol}=${IMPL_REFERENCE_PREFIX}${_symbol}") endif() endforeach() endforeach() foreach(_symbol IN LISTS IMPL_SYMBOLS) if(NOT _definition_count_${_symbol} EQUAL 1) message(FATAL_ERROR "${IMPL_NAME}: expected one definition of ${_symbol}, found ${_definition_count_${_symbol}}") endif() endforeach() set(_identity "${CMAKE_CURRENT_BINARY_DIR}/guest-implementations/${IMPL_NAME}.c") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/guest-implementations") file(CONFIGURE OUTPUT "${_identity}" CONTENT "#include \"mod_plugins.h\"\n#include \nPSX_MOD_CONSTRUCTOR(psx_impl_${IMPL_NAME}) { fprintf(stdout, \"psxrecomp guest implementation: ${IMPL_NAME}=${IMPL_IMPLEMENTATION}\\n\"); }\n" @ONLY) foreach(_target IN LISTS IMPL_TARGETS) if(NOT TARGET ${_target}) message(FATAL_ERROR "${IMPL_NAME}: unknown target ${_target}") endif() get_target_property(_names ${_target} PSX_GUEST_IMPLEMENTATION_NAMES) if(NOT _names) set(_names "") endif() if(IMPL_NAME IN_LIST _names) message(FATAL_ERROR "${_target}: duplicate guest implementation ${IMPL_NAME}") endif() list(APPEND _names "${IMPL_NAME}") set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATION_NAMES "${_names}") get_target_property(_selections ${_target} PSX_GUEST_IMPLEMENTATIONS) if(NOT _selections) set(_selections "") endif() list(APPEND _selections "${IMPL_NAME}=${IMPL_IMPLEMENTATION}") set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATIONS "${_selections}") list(JOIN _selections "," _selection_text) get_target_property(_tags ${_target} PSX_GUEST_IMPLEMENTATION_TAGS) if(NOT _tags) set(_tags "") endif() if(_tag GREATER 0 AND _tag IN_LIST _tags) message(FATAL_ERROR "${_target}: each HLE family needs a distinct SNAPSHOT_TAG") endif() list(APPEND _tags "${_tag}") set_property(TARGET ${_target} PROPERTY PSX_GUEST_IMPLEMENTATION_TAGS "${_tags}") set(_combined_tag 0) foreach(_family_tag IN LISTS _tags) math(EXPR _combined_tag "${_combined_tag} ^ ${_family_tag}") endforeach() get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) if(NOT _definitions) set(_definitions "") endif() list(FILTER _definitions EXCLUDE REGEX "^PSX_(SAVESTATE_IMPL_TAG|BUILD_IMPLEMENTATIONS)=") set_property(TARGET ${_target} PROPERTY COMPILE_DEFINITIONS "${_definitions}") target_compile_definitions(${_target} PRIVATE "PSX_SAVESTATE_IMPL_TAG=${_combined_tag}u" "PSX_BUILD_IMPLEMENTATIONS=\"${_selection_text}\"") target_sources(${_target} PRIVATE "${_identity}") if(IMPL_IMPLEMENTATION STREQUAL "HLE") target_sources(${_target} PRIVATE ${IMPL_HLE_SOURCES}) endif() endforeach() message(STATUS "psxrecomp guest implementation: ${IMPL_NAME}=${IMPL_IMPLEMENTATION}, tag=${_tag}") endfunction()