cmake_minimum_required (VERSION 3.20)

set(basename "bootstrapper")
project(${basename} C CXX ASM)

onion_module_prologue(C_STANDARD 11 CXX_STANDARD 20)

set(D_CWD "${CMAKE_CURRENT_SOURCE_DIR}")
onion_output_dirs()
set(D_SRC ${D_CWD}/source)

# SVG menu icons are the source of truth. Rasterize them into the build tree so
# editing an icon cannot leave a stale checked-in PNG embedded in bootstrapper.
set(_icon_source_dir "${D_CWD}/assets/icons")
set(_icon_output_dir "${CMAKE_CURRENT_BINARY_DIR}/generated/assets")
file(GLOB _icon_svg_files CONFIGURE_DEPENDS "${_icon_source_dir}/icon_xml_*.svg")

find_program(RSVG_CONVERT_EXECUTABLE NAMES rsvg-convert)
if(NOT RSVG_CONVERT_EXECUTABLE)
	find_program(IMAGEMAGICK_EXECUTABLE NAMES magick convert)
endif()
if(NOT RSVG_CONVERT_EXECUTABLE AND NOT IMAGEMAGICK_EXECUTABLE)
	message(FATAL_ERROR
		"SVG icon conversion requires rsvg-convert (preferred) or ImageMagick")
endif()

set(_generated_icon_files)
foreach(_svg_file IN LISTS _icon_svg_files)
	get_filename_component(_icon_name "${_svg_file}" NAME_WE)
	set(_png_file "${_icon_output_dir}/${_icon_name}.png")
	if(RSVG_CONVERT_EXECUTABLE)
		add_custom_command(
			OUTPUT "${_png_file}"
			COMMAND ${CMAKE_COMMAND} -E make_directory "${_icon_output_dir}"
			COMMAND "${RSVG_CONVERT_EXECUTABLE}" --width 64 --height 64
				--output "${_png_file}" "${_svg_file}"
			DEPENDS "${_svg_file}"
			COMMENT "Rasterizing ${_icon_name}.svg"
			VERBATIM
		)
	else()
		add_custom_command(
			OUTPUT "${_png_file}"
			COMMAND ${CMAKE_COMMAND} -E make_directory "${_icon_output_dir}"
			COMMAND "${IMAGEMAGICK_EXECUTABLE}" -background none "${_svg_file}"
				-resize 64x64 "${_png_file}"
			DEPENDS "${_svg_file}"
			COMMENT "Rasterizing ${_icon_name}.svg"
			VERBATIM
		)
	endif()
	list(APPEND _generated_icon_files "${_png_file}")
endforeach()

# These raster-only icons do not have SVG sources. Copy them into the same
# generated directory so daemon.c has one stable asset location.
foreach(_raster_icon IN ITEMS
		icon_xml_monitor.png
		icon_xml_network.png
		icon_xml_author_avatar.png
		icon_xml_donator_ljf.png
		icon_xml_donator_szx.png
		icon_xml_donator_aglx.png)
	set(_raster_source "${D_CWD}/assets/${_raster_icon}")
	set(_raster_output "${_icon_output_dir}/${_raster_icon}")
	add_custom_command(
		OUTPUT "${_raster_output}"
		COMMAND ${CMAKE_COMMAND} -E make_directory "${_icon_output_dir}"
		COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_raster_source}" "${_raster_output}"
		DEPENDS "${_raster_source}"
		COMMENT "Copying ${_raster_icon}"
		VERBATIM
	)
	list(APPEND _generated_icon_files "${_raster_output}")
endforeach()

# Generate the icon embedding blocks and the runtime manifest from the icon
# list above. Previously each icon had to be added in three hand-maintained
# places — a 12-line .incbin block in daemon.c, an extern pair in main.cpp and
# a write_blob_file() call — none of which fail the build when forgotten.
set(_icon_asm "${_icon_output_dir}/icon_assets.inc")
set(_icon_manifest "${_icon_output_dir}/icon_manifest.inc")
set(_asm_body "/* Generated by CMake from bootstrapper/assets — do not edit. */\n")
set(_manifest_body "/* Generated by CMake from bootstrapper/assets — do not edit. */\n")
foreach(_icon_file IN LISTS _generated_icon_files)
	get_filename_component(_icon_name "${_icon_file}" NAME_WE)
	string(APPEND _asm_body
		"\".global ${_icon_name}_start\\n\"\n"
		"\".type   ${_icon_name}_start, @object\\n\"\n"
		"\".align  16\\n\"\n"
		"\"${_icon_name}_start:\\n\"\n"
		"\".incbin \\\"\" ONIONHEN_ICON_ASSET_DIR \"/${_icon_name}.png\\\"\\n\"\n"
		"\"${_icon_name}_end:\\n\"\n"
		"\".global ${_icon_name}_size\\n\"\n"
		"\".type   ${_icon_name}_size, @object\\n\"\n"
		"\".align  4\\n\"\n"
		"\"${_icon_name}_size:\\n\"\n"
		"\".int    ${_icon_name}_end - ${_icon_name}_start\\n\"\n")
	string(APPEND _manifest_body "ONION_ICON(${_icon_name})\n")
endforeach()
file(GENERATE OUTPUT "${_icon_asm}" CONTENT "${_asm_body}")
file(GENERATE OUTPUT "${_icon_manifest}" CONTENT "${_manifest_body}")

add_executable(${PROJECT_NAME})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}.elf")

# Finalize main target sources
target_compile_options(${PROJECT_NAME} PUBLIC
	$<$<COMPILE_LANGUAGE:C>:${C_DEFS} ${C_FLAGS}>
	$<$<COMPILE_LANGUAGE:CXX>:${CXX_DEFS} ${CXX_FLAGS}>
	$<$<COMPILE_LANGUAGE:ASM>:${ASM_FLAGS}>
)

# Byepervisor removed from OnionHEN — bootstrapper sources only.
# pt.c / elfldr.c live in libonion_elfldr (raise_privileges + inject primitives).
file(GLOB SrcFiles CONFIGURE_DEPENDS ${D_SRC}/*.c ${D_SRC}/*.cpp ${D_SRC}/../include/*.h ${D_SRC}/../include/*.hpp ${D_SRC}/*.s)
list(FILTER SrcFiles EXCLUDE REGEX ".*/(pt|elfldr)\\.c$")

set(CMAKE_C_FLAGS "${ONION_PS5_TARGET_FLAGS} ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-keyword-macro -fPIC -fPIE -march=znver2 -Wall -Werror -fstack-protector-all")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -gfull -gdwarf-2 -pedantic -pedantic-errors")

target_sources(${PROJECT_NAME} PRIVATE ${SrcFiles})
# .incbin in daemon.c is invisible to the compiler dependency scanner.
# Without OBJECT_DEPENDS, daemon/util/kstuff rebuilds do not re-embed into bootstrapper.
if(DEFINED ONIONHEN_OUT_BIN)
	set(_onion_daemon_elf "${ONIONHEN_OUT_BIN}/daemon.elf")
	set(_onion_util_elf "${ONIONHEN_OUT_BIN}/util.elf")
	set(_onion_elfldr_elf "${ONIONHEN_OUT_BIN}/onion_elfldr.elf")
else()
	set(_onion_daemon_elf "${PROJECT_ROOT}/bin/daemon.elf")
	set(_onion_util_elf "${PROJECT_ROOT}/bin/util.elf")
	set(_onion_elfldr_elf "${PROJECT_ROOT}/bin/onion_elfldr.elf")
endif()
set_source_files_properties(
	${D_SRC}/daemon.c
	PROPERTIES OBJECT_DEPENDS
		"${_onion_daemon_elf};${_onion_util_elf};${_onion_elfldr_elf};${ONIONHEN_KSTUFF_ELF};${D_CWD}/assets/onionhen_sicon.png;${_generated_icon_files}"
)
target_compile_definitions(${PROJECT_NAME} PRIVATE
	ONIONHEN_DAEMON_ELF="${_onion_daemon_elf}"
	ONIONHEN_UTIL_ELF="${_onion_util_elf}"
	ONIONHEN_ELFLDR_ELF="${_onion_elfldr_elf}"
	ONIONHEN_KSTUFF_ELF="${ONIONHEN_KSTUFF_ELF}"
	ONIONHEN_ICON_ASSET_DIR="${_icon_output_dir}"
)
# elfldr_raise_privileges from libonion_elfldr; launches go via elfldr_remote.c.
onion_target_debug_define(${PROJECT_NAME})
target_include_directories(${PROJECT_NAME} PRIVATE
	"${D_CWD}/include"
	# icon_assets.inc / icon_manifest.inc
	"${_icon_output_dir}"
	"${PROJECT_ROOT}/libonion_proc/include"
	"${PROJECT_ROOT}/libonion_platform/include"
	"${PROJECT_ROOT}/libonion_settings/include"
	"${PROJECT_ROOT}/libonion_payload/include"
	"${PROJECT_ROOT}/libonion_elfldr/include"
	"${PROJECT_ROOT}/include"
)
target_link_directories	(${PROJECT_NAME} PUBLIC "${ONIONHEN_PS5_STUB_DIR}")
target_sources(${PROJECT_NAME} PRIVATE "${ONIONHEN_COMMON_DIR}/elfldr_remote.c")
target_link_libraries	(${PROJECT_NAME} PUBLIC onion_proc onion_platform onion_settings onion_payload onion_ready onion_elfldr kernel_sys SceNotification SceSystemService)
add_custom_target(${PROJECT_NAME}_assets
	DEPENDS ${_generated_icon_files} "${_icon_asm}" "${_icon_manifest}")
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_assets)
add_dependencies(${PROJECT_NAME} daemon util shellui onion_elfldr_server onion_proc onion_platform onion_settings onion_payload onion_ready onion_elfldr)

# Copy bootstrapper.elf as test.elf before processing
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test.elf
    COMMENT "Copying ${PROJECT_NAME}.elf to test.elf"
)

# Add post-build command
onion_strip_debug(${PROJECT_NAME})

# Portable size + lzma pack (Linux GNU stat / macOS BSD stat; lzma or xz)
# PROJECT_ROOT is source/; pack script lives at repo scripts/
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E env "TARGET_ELF=$<TARGET_FILE:${PROJECT_NAME}>"
            ${PROJECT_ROOT}/../scripts/pack_bootstrapper.sh
    COMMENT "Recording size and compressing bootstrapper with lzma"
)
