@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "SCRIPT_DIR=%~dp0"
set "INPUT_FILE=%~1"
set "OUTPUT_FILE=%~2"
set "OBJCOPY=C:\Users\zshield\AppData\Roaming\C-Sky\CDK\CSKY\MinGW\csky-abiv2-elf-toolchain\bin\csky-elfabiv2-objcopy.exe"
set "TEMP_OUTPUT="
set "CONVERSION_STARTED="
set "INPUT_NEEDS_VERIFY="

if /I "%~1"=="--help" goto :help
if /I "%~1"=="-h" goto :help

if defined INPUT_FILE set "INPUT_NEEDS_VERIFY=1"
if not defined INPUT_FILE (
    echo Searching for C-SKY ELF...
    call :find_elf
)
if not defined INPUT_FILE (
    echo ERROR: No ELF-formatted file was found under:
    echo        %SCRIPT_DIR%
    echo.
    echo Pass the input file explicitly or run this script from the project root.
    goto :failed
)

if not "%INPUT_FILE:~1,1%"==":" if not "%INPUT_FILE:~0,2%"=="\\" set "INPUT_FILE=%SCRIPT_DIR%%INPUT_FILE%"
for %%F in ("%INPUT_FILE%") do set "INPUT_FILE=%%~fF"
if not exist "%INPUT_FILE%" (
    echo ERROR: Input file does not exist:
    echo        %INPUT_FILE%
    goto :failed
)

if defined INPUT_NEEDS_VERIFY (
    call :verify_elf "%INPUT_FILE%"
    if errorlevel 1 goto :failed
)

if not defined OUTPUT_FILE for %%F in ("%INPUT_FILE%") do set "OUTPUT_FILE=%%~dpnF_raw.bin"
if not "%OUTPUT_FILE:~1,1%"==":" if not "%OUTPUT_FILE:~0,2%"=="\\" set "OUTPUT_FILE=%SCRIPT_DIR%%OUTPUT_FILE%"
for %%F in ("%OUTPUT_FILE%") do set "OUTPUT_FILE=%%~fF"

if /I "%INPUT_FILE%"=="%OUTPUT_FILE%" (
    echo ERROR: Input and output paths must be different.
    goto :failed
)

if not exist "%OBJCOPY%" (
    echo ERROR: objcopy does not exist:
    echo        %OBJCOPY%
    goto :failed
)

echo Input:   %INPUT_FILE%
for %%F in ("%INPUT_FILE%") do echo Modified: %%~tF
echo Output:  %OUTPUT_FILE%
echo Objcopy: %OBJCOPY%
echo.

set "TEMP_OUTPUT=%OUTPUT_FILE%.tmp"
set "CONVERSION_STARTED=1"

if exist "%TEMP_OUTPUT%" del /F /Q "%TEMP_OUTPUT%" >nul 2>&1
if exist "%OUTPUT_FILE%" del /F /Q "%OUTPUT_FILE%" >nul 2>&1
if exist "%OUTPUT_FILE%" (
    echo ERROR: Existing output file could not be removed:
    echo        %OUTPUT_FILE%
    goto :failed
)

"%OBJCOPY%" -O binary "%INPUT_FILE%" "%TEMP_OUTPUT%"
if errorlevel 1 (
    echo ERROR: objcopy failed.
    goto :failed
)

call :verify_raw "%TEMP_OUTPUT%"
if errorlevel 1 goto :failed

move /Y "%TEMP_OUTPUT%" "%OUTPUT_FILE%" >nul
if errorlevel 1 (
    echo ERROR: Validated BIN could not be moved to:
    echo        %OUTPUT_FILE%
    goto :failed
)

echo Raw BIN generated successfully.
echo.
pause
exit /b 0

:find_elf
set "ELF_SCAN_ROOT=%SCRIPT_DIR%"
for /f "usebackq delims=" %%F in (`powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ^
    "$priorities = @{ '.elf' = 0; '.bin' = 1 };" ^
    "$roots = @((Join-Path $env:ELF_SCAN_ROOT 'Obj'), $env:ELF_SCAN_ROOT);" ^
    ":rootSearch foreach ($root in $roots) {" ^
    "  if (-not (Test-Path -LiteralPath $root -PathType Container)) { continue };" ^
    "  $candidates = foreach ($file in Get-ChildItem -LiteralPath $root -File -ErrorAction SilentlyContinue) {" ^
    "    $extension = $file.Extension.ToLowerInvariant();" ^
    "    if (-not $priorities.ContainsKey($extension)) { continue };" ^
    "    $stream = $null;" ^
    "    try {" ^
    "      $stream = [IO.File]::OpenRead($file.FullName);" ^
    "      $header = New-Object byte[] 20;" ^
    "      $count = $stream.Read($header, 0, 20);" ^
    "      $machine = if ($count -ge 20) { [BitConverter]::ToUInt16($header, 18) } else { 0 };" ^
    "      if ($count -ge 20 -and $header[0] -eq 0x7F -and $header[1] -eq 0x45 -and $header[2] -eq 0x4C -and $header[3] -eq 0x46 -and $header[4] -eq 1 -and $header[5] -eq 1 -and $machine -eq 0x00FC) {" ^
    "        [PSCustomObject]@{ File = $file; Priority = $priorities[$extension] }" ^
    "      }" ^
    "    } catch { [Console]::Error.WriteLine('Skipping unreadable file: ' + $file.FullName + ' (' + $_.Exception.Message + ')') }" ^
    "    finally { if ($null -ne $stream) { $stream.Dispose() } }" ^
    "  };" ^
    "  $selected = $candidates | Sort-Object @{ Expression = { $_.Priority }; Ascending = $true }, @{ Expression = { $_.File.LastWriteTime }; Descending = $true } | Select-Object -First 1;" ^
    "  if ($null -ne $selected) { $selected.File.FullName; break rootSearch }" ^
    "}"`) do if not defined INPUT_FILE set "INPUT_FILE=%%F"
exit /b 0

:verify_elf
set "VERIFY_FILE=%~1"
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ^
    "$b = [IO.File]::ReadAllBytes($env:VERIFY_FILE);" ^
    "if ($b.Length -lt 20 -or $b[0] -ne 0x7F -or $b[1] -ne 0x45 -or $b[2] -ne 0x4C -or $b[3] -ne 0x46) { Write-Error ('Input is not ELF-formatted: ' + $env:VERIFY_FILE); exit 1 };" ^
    "if ($b[4] -ne 1) { Write-Error 'Input is not ELF32'; exit 1 };" ^
    "if ($b[5] -ne 1) { Write-Error 'Input is not little-endian ELF'; exit 1 };" ^
    "$machine = [BitConverter]::ToUInt16($b, 18);" ^
    "if ($machine -ne 0x00FC) { Write-Error ('Input is not C-SKY ELF; e_machine=0x{0:X4}' -f $machine); exit 1 }"
exit /b %errorlevel%

:verify_raw
set "VERIFY_FILE=%~1"
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ^
    "$b = [IO.File]::ReadAllBytes($env:VERIFY_FILE);" ^
    "if ($b.Length -lt 4) { Write-Error 'Output BIN is too small'; exit 1 };" ^
    "if ($b[0] -eq 0x7F -and $b[1] -eq 0x45 -and $b[2] -eq 0x4C -and $b[3] -eq 0x46) { Write-Error 'Output is still ELF-formatted'; exit 1 };" ^
    "'Size:       {0} bytes (0x{0:X})' -f $b.Length;" ^
    "'Entry word: 0x{0:X8}' -f [BitConverter]::ToUInt32($b, 0)"
exit /b %errorlevel%

:help
echo Usage:
echo   generate_raw_bin.bat [input] [output]
echo.
echo With no arguments, the script:
echo   - Checks the Obj directory first, without recursion.
echo   - Checks the script directory only if Obj has no valid ELF.
echo   - Always prefers .elf, then ELF-formatted .bin files.
echo   - Selects the newest file only within the same extension priority.
echo   - Uses the configured C-SKY objcopy path.
echo   - Writes ^<input-name^>_raw.bin beside the input file.
echo   - Verifies that the output is a raw binary.
echo.
echo Examples:
echo   generate_raw_bin.bat
echo   generate_raw_bin.bat "Obj\SN_Prog.elf"
echo   generate_raw_bin.bat "Obj\SN_Prog.elf" "Obj\SN_Prog_raw.bin"
echo.
pause
exit /b 0

:failed
if defined TEMP_OUTPUT if exist "%TEMP_OUTPUT%" del /F /Q "%TEMP_OUTPUT%" >nul 2>&1
if defined CONVERSION_STARTED if exist "%OUTPUT_FILE%" del /F /Q "%OUTPUT_FILE%" >nul 2>&1
echo.
echo Conversion failed.
echo.
pause
exit /b 1
