diff --git a/boot/remaster.cmd b/boot/remaster.cmd new file mode 100644 index 00000000000..497891353cd --- /dev/null +++ b/boot/remaster.cmd @@ -0,0 +1,201 @@ +:: +:: PROJECT: ReactOS ISO Remastering Script +:: LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) +:: PURPOSE: Allows easy remastering of customized ReactOS ISO images. +:: Based on the boot/boot_images.cmake script in the ReactOS +:: source tree. Requires a MKISOFS-compatible utility. +:: COPYRIGHT: Copyright 2023-2025 Hermès Bélusca-Maïto +:: + +@echo off +:: title ReactOS ISO Remastering Script + +setlocal enabledelayedexpansion + +:: +:: Customizable settings +:: +:: ISO image identifier names +set ISO_MANUFACTURER="ReactOS Project"& :: For both the publisher and the preparer +set ISO_VOLNAME="ReactOS"& :: For both the Volume ID and the Volume set ID + +:: Image names of the MKISOFS and ISOHYBRID tools +set MKISOFS=MKISOFS.EXE +set ISOHYBRID=ISOHYBRID.EXE + + +:: +:: Main script +:: +cls +echo ******************************************************************************* +echo * * +echo * ReactOS ISO Remastering Script * +echo * * +echo ******************************************************************************* +echo. + +:: Newline macro, see https://stackoverflow.com/a/6379940 +(set \n=^ +%=Don't remove this line=% +) + + +:: Verify that we have access to a temporary directory. +:: See https://stackoverflow.com/a/21041546 for more details. +if defined TEMP ( if exist "%TEMP%\" goto :TEMP0 ) + if defined TMP ( if exist "%TMP%\" goto :TEMP1 ) + echo No temporary directory exists on your system. + echo Please create one and assign it to the TEMP environment variable. + echo. + goto :Quit + :TEMP1 + set "TEMP=%TMP%" +:TEMP0 + + +:: Try to auto-locate MKISOFS and if not, prompt the user for a directory. +set TOOL_DIR= +set TOOL_PATH= +for /f "delims=" %%f in ('WHERE %MKISOFS% 2^>NUL') do ( + set "TOOL_DIR=%%~dpf" & if "!TOOL_DIR:~-1!"=="\" set "TOOL_DIR=!TOOL_DIR:~0,-1!" + set "TOOL_PATH=%%f" + goto :mkisofs_found +) +if not defined TOOL_PATH ( + set /p TOOL_DIR="Please enter the directory path where %MKISOFS% can be found:!\n!" + echo. + if "!TOOL_DIR:~-1!"=="\" set "TOOL_DIR=!TOOL_DIR:~0,-1!" + set "TOOL_PATH=!TOOL_DIR!\%MKISOFS%" +) +:mkisofs_found +set "MKISOFS=%TOOL_PATH%" + + +set /p INPUT_DIR="Please enter the path of the directory tree to image into the ISO:!\n!" +echo. +set /p OUTPUT_ISO="Please enter the file path of the ISO image that will be created:!\n!" +echo. + + +:: Retrieve the full paths to the 'isombr', 'isoboot', 'isobtrt' and 'efisys' files +set isombr_file=loader/isombr.bin +set isoboot_file=loader/isoboot.bin +set isobtrt_file=loader/isobtrt.bin +set efisys_file=loader/efisys.bin + +set ISOBOOT_PATH=%isoboot_file% +::CHOICE /c 12 /n /m "Please choose the ISO boot file: 1) isoboot.bin ; 2) isobtrt.bin!\n![default: 1]: " +CHOICE /c YN /n /m "Do you want the ReactOS media to wait for a key-press before booting [Y,N]? " +echo. +if errorlevel 2 set ISOBOOT_PATH=%isobtrt_file% + +:: Enable (U)EFI boot support if possible: check the +:: presence of %efisys_file% in the ISO directory tree. +:: NOTE: Convert forward to backslashes. +::ISO_EFI_BOOT_PARAMS +set ISO_BOOT_EFI_OPTIONS= +if exist "%INPUT_DIR%\%efisys_file:/=\%" ( + set "ISO_BOOT_EFI_OPTIONS=-eltorito-alt-boot -eltorito-platform efi -eltorito-boot %efisys_file% -no-emul-boot" +) + +:: Summary of the boot files. +echo ISO boot file: '%ISOBOOT_PATH%' +if defined ISO_BOOT_EFI_OPTIONS ( + echo EFI boot file: '%efisys_file%' +) +echo. + + +set DUPLICATES_ONCE= +CHOICE /c YN /n /m "Do you want to store duplicated files only once (reduces the size!\n!of the ISO image) [Y,N]? " +echo. +if %ERRORLEVEL% equ 1 set DUPLICATES_ONCE=-duplicates-once + + +echo Creating the ISO image... +echo. + +:: Create a mkisofs sort file to specify an explicit ordering for the boot files +:: to place them at the beginning of the image (makes ISO image analysis easier). +:: See mkisofs/schilytools/mkisofs/README.sort and boot/boot_images.cmake script +:: in the ReactOS source tree for more details. +:: https://stackoverflow.com/a/29635767 +( + ::echo ${CMAKE_CURRENT_BINARY_DIR}/empty/boot.catalog 4 + echo boot.catalog 4 + :: NOTE: Convert forward to backslashes. + echo %INPUT_DIR%\%isoboot_file:/=\% 3 + echo %INPUT_DIR%\%isobtrt_file:/=\% 2 + echo %INPUT_DIR%\%efisys_file:/=\% 1 +) > "%TEMP%\bootfiles.sort" + +:: Finally, create the ISO image proper. +"%MKISOFS%" ^ + -o "%OUTPUT_ISO%" -iso-level 4 ^ + -publisher %ISO_MANUFACTURER% -preparer %ISO_MANUFACTURER% ^ + -volid %ISO_VOLNAME% -volset %ISO_VOLNAME% ^ + -eltorito-boot %ISOBOOT_PATH% -no-emul-boot -boot-load-size 4 ^ + %ISO_BOOT_EFI_OPTIONS% ^ + -hide boot.catalog -sort "%TEMP%\bootfiles.sort" ^ + %DUPLICATES_ONCE% -no-cache-inodes "%INPUT_DIR%" +:: -graft-points -path-list "some/directory/iso_image.lst" +echo. + +if errorlevel 1 ( +del "%TEMP%\bootfiles.sort" + echo An error %ERRORLEVEL% happened while creating the ISO image "%OUTPUT_ISO%". + goto :Quit +) else ( +del "%TEMP%\bootfiles.sort" + echo The ISO image "%OUTPUT_ISO%" has been successfully created. +) +echo. + + +:: Check whether ISOHYBRID is also available and if so, propose to post-process +:: the generated ISO image to allow hybrid booting as a CD-ROM or as a hard disk. +set TOOL_PATH= +for /f "delims=" %%f in ('WHERE "%TOOL_DIR%":%ISOHYBRID% 2^>NUL') do ( + set "TOOL_PATH=%%f" & goto :isohybrid_found) +if not defined TOOL_PATH ( + for /f "delims=" %%f in ('WHERE %ISOHYBRID% 2^>NUL') do ( + set "TOOL_PATH=%%f" + goto :isohybrid_found + ) + if not defined TOOL_PATH ( + echo %ISOHYBRID% patching skipped. + goto :Success + ) +) +:isohybrid_found +set "ISOHYBRID=%TOOL_PATH%" + + +CHOICE /c YN /n /m "Do you want to post-process the ISO image to allow hybrid booting!\n!as a CD-ROM or as a hard disk [Y,N]? " +echo. +if %ERRORLEVEL% neq 1 goto :Success + +echo Patching the ISO image... + +:: NOTE: Convert forward to backslashes. +"%ISOHYBRID%" -b %INPUT_DIR%\%isombr_file:/=\% -t 0x96 "%OUTPUT_ISO%" +echo. + +if errorlevel 1 ( + echo An error %ERRORLEVEL% happened while patching the ISO image "%OUTPUT_ISO%". + goto :Quit +) +:: else ( +:: echo The ISO image "%OUTPUT_ISO%" has been successfully patched. +:: ) +:: echo. + + +:Success +echo Success^^! +:Quit +endlocal +echo Press any key to quit... +pause > NUL +exit /b diff --git a/boot/remaster.sh b/boot/remaster.sh new file mode 100644 index 00000000000..73491353b39 --- /dev/null +++ b/boot/remaster.sh @@ -0,0 +1,231 @@ +#!/bin/sh +# shellcheck disable=SC3003,SC3045 + +## +## PROJECT: ReactOS ISO Remastering Script +## LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) +## PURPOSE: Allows easy remastering of customized ReactOS ISO images. +## Based on the boot/boot_images.cmake script in the ReactOS +## source tree. Requires a MKISOFS-compatible utility. +## COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto +## +## POSIX shell compliance checker: https://www.shellcheck.net +## + +## echo -ne "\033]0;ReactOS ISO Remastering Script\007" + +## +## Customizable settings +## +## ISO image identifier names +ISO_MANUFACTURER="ReactOS Project" # For both the publisher and the preparer +ISO_VOLNAME="ReactOS" # For both the Volume ID and the Volume set ID + +## Image names of the MKISOFS and ISOHYBRID tools +MKISOFS=mkisofs +ISOHYBRID=isohybrid + + +## +## Main script +## +clear +echo "*******************************************************************************" +echo "* *" +echo "* ReactOS ISO Remastering Script *" +echo "* *" +echo "*******************************************************************************" +echo + +exit_script() +{ + ERRLVL=${1:-$?} # Use parameter, otherwise default to $? + [ "$ERRLVL" -eq 0 ] && echo "Success!" + # shellcheck disable=SC3045 + read -n 1 -r -s -p $'Press any key to quit...\n' + exit "$ERRLVL" +} + +## +## Prompt the user for a choice. +## Usage example: +## choice YN "Yes or No [Y,N]? " +## +## Return in $REPLY the index of the reply in the choices list. +## +## See also: +## https://stackoverflow.com/q/226703 +## +choice() +{ + ## Normalize the choices list to lowercase, and prepare + ## the pattern to match for only one single character. + CHOICE_LIST="$(echo "$1" | tr '[:upper:]' '[:lower:]')" + CHOICE_PATTERN="^[$CHOICE_LIST]$" + + ## Echo prompt and wait for keypress + shift 1 + # shellcheck disable=SC3037 + echo -n "$@" + REPLY_LOWER= + until # Adapted from https://unix.stackexchange.com/a/249036 + # shellcheck disable=SC3045 + read -N 1 -r -s + REPLY_LOWER="$(echo "$REPLY" | tr '[:upper:]' '[:lower:]')" + expr "$REPLY_LOWER" : "$CHOICE_PATTERN" 1>/dev/null + do :; done + echo "$REPLY" + + ## Return the index of the reply in the choices list + #REPLY=$(expr index "$CHOICE_LIST" "$REPLY_LOWER") + REPLY=${CHOICE_LIST%%"$REPLY_LOWER"*} + #REPLY=$(( $(expr "$REPLY" : ".*")+1 )) + REPLY=$(( ${#REPLY}+1 )) +} + + +## Verify that we have access to a temporary directory. +for TMPDIR in "$TMPDIR" "$TEMP" "$TMP" "/tmp"; do + [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ] && break # Valid directory found +done +if [ -z "$TMPDIR" ] || [ ! -d "$TMPDIR" ]; then + echo No temporary directory exists on your system. + echo Please create one and assign it to the TMPDIR environment variable. + echo + exit_script 1 +fi + + +## Try to auto-locate MKISOFS and if not, prompt the user for a directory. +TOOL_DIR= +TOOL_PATH=$(type -p $MKISOFS) +if [ -z "$TOOL_PATH" ]; then + read -e -r -p $'Please enter the directory path where '$MKISOFS$' can be found:\n' TOOL_DIR + echo + # Non-POSIX-compatible test: [ "${TOOL_DIR:(-1)}" = "/" ] + [ "${TOOL_DIR#"${TOOL_DIR%?}"}" = "/" ] && TOOL_DIR=${TOOL_DIR%?} + TOOL_PATH=$TOOL_DIR/$MKISOFS +else + # Get the directory without the '/filename' part (and doesn't include trailing /) + TOOL_DIR=${TOOL_PATH%/*} +fi +MKISOFS=$TOOL_PATH + + +read -e -r -p $'Please enter the path of the directory tree to image into the ISO:\n' INPUT_DIR +echo +read -e -r -p $'Please enter the file path of the ISO image that will be created:\n' OUTPUT_ISO +echo + + +## Retrieve the full paths to the 'isombr', 'isoboot', 'isobtrt' and 'efisys' files +isombr_file=loader/isombr.bin +isoboot_file=loader/isoboot.bin +isobtrt_file=loader/isobtrt.bin +efisys_file=loader/efisys.bin + +#choice 12 $'Please choose the ISO boot file: 1) isoboot.bin ; 2) isobtrt.bin\n[default: 1]: ' +choice YN $'Do you want the ReactOS media to wait for a key-press before booting [Y,N]? ' +echo +ISOBOOT_PATH=$isoboot_file +if [ "$REPLY" -eq 1 ]; then + ISOBOOT_PATH=$isoboot_file +elif [ "$REPLY" -eq 2 ]; then + ISOBOOT_PATH=$isobtrt_file +fi + +## Enable (U)EFI boot support if possible: check the +## presence of '$efisys_file' in the ISO directory tree. +#ISO_EFI_BOOT_PARAMS +ISO_BOOT_EFI_OPTIONS=$([ -f "$INPUT_DIR/$efisys_file" ] && \ + echo "-eltorito-alt-boot -eltorito-platform efi -eltorito-boot $efisys_file -no-emul-boot") + +## Summary of the boot files. +echo "ISO boot file: '$ISOBOOT_PATH'" +[ -n "$ISO_BOOT_EFI_OPTIONS" ] && echo "EFI boot file: '$efisys_file'" +echo + + +choice YN $'Do you want to store duplicated files only once (reduces the size\nof the ISO image) [Y,N]? ' +echo +DUPLICATES_ONCE=$([ "$REPLY" -eq 1 ] && echo "-duplicates-once") + + +echo "Creating the ISO image..." +echo + +## Create a mkisofs sort file to specify an explicit ordering for the boot files +## to place them at the beginning of the image (makes ISO image analysis easier). +## See mkisofs/schilytools/mkisofs/README.sort and boot/boot_images.cmake script +## in the ReactOS source tree for more details. + +## echo ${CMAKE_CURRENT_BINARY_DIR}/empty/boot.catalog 4 +cat > "$TMPDIR/bootfiles.sort" << EOF +boot.catalog 4 +$INPUT_DIR/$isoboot_file 3 +$INPUT_DIR/$isobtrt_file 2 +$INPUT_DIR/$efisys_file 1 +EOF + +## Finally, create the ISO image proper. +#echo "Running command: +#$MKISOFS \ +# -o \"$OUTPUT_ISO\" -iso-level 4 \ +# -publisher \"$ISO_MANUFACTURER\" -preparer \"$ISO_MANUFACTURER\" \ +# -volid \"$ISO_VOLNAME\" -volset \"$ISO_VOLNAME\" \ +# -eltorito-boot $ISOBOOT_PATH -no-emul-boot -boot-load-size 4 \ +# $ISO_BOOT_EFI_OPTIONS \ +# -hide boot.catalog -sort \"$TMPDIR/bootfiles.sort\" \ +# $DUPLICATES_ONCE -no-cache-inodes \"$INPUT_DIR\" +#" +$MKISOFS \ + -o "$OUTPUT_ISO" -iso-level 4 \ + -publisher "$ISO_MANUFACTURER" -preparer "$ISO_MANUFACTURER" \ + -volid "$ISO_VOLNAME" -volset "$ISO_VOLNAME" \ + -eltorito-boot $ISOBOOT_PATH -no-emul-boot -boot-load-size 4 \ + $ISO_BOOT_EFI_OPTIONS \ + -hide boot.catalog -sort "$TMPDIR/bootfiles.sort" \ + $DUPLICATES_ONCE -no-cache-inodes "$INPUT_DIR"; ERRLVL=$? +## -graft-points -path-list "some/directory/iso_image.lst" +echo +rm "$TMPDIR/bootfiles.sort" + +if [ "$ERRLVL" -ne 0 ]; then + echo "An error $ERRLVL happened while creating the ISO image \"$OUTPUT_ISO\"." + exit_script $ERRLVL +fi +echo "The ISO image \"$OUTPUT_ISO\" has been successfully created." +echo + + +## Check whether ISOHYBRID is also available and if so, propose to post-process +## the generated ISO image to allow hybrid booting as a CD-ROM or as a hard disk. +TOOL_PATH=$TOOL_DIR/$ISOHYBRID +if [ -z "$TOOL_PATH" ] || [ ! -f "$TOOL_PATH" ]; then + TOOL_PATH=$(type -p $ISOHYBRID) + if [ -z "$TOOL_PATH" ]; then + echo "$ISOHYBRID patching skipped." + exit_script 0 + fi +fi +ISOHYBRID=$TOOL_PATH + + +choice YN $'Do you want to post-process the ISO image to allow hybrid booting\nas a CD-ROM or as a hard disk [Y,N]? ' +echo +[ "$REPLY" -ne 1 ] && exit_script 0 + +echo "Patching the ISO image..." + +"$ISOHYBRID" -b "$INPUT_DIR/$isombr_file" -t 0x96 "$OUTPUT_ISO"; ERRLVL=$? +echo + +if [ "$ERRLVL" -ne 0 ]; then + echo "An error $ERRLVL happened while patching the ISO image \"$OUTPUT_ISO\"." + exit_script $ERRLVL +## else +## echo "The ISO image \"$OUTPUT_ISO\" has been successfully patched." +fi +echo + +exit_script 0