@echo off
setlocal EnableExtensions DisableDelayedExpansion
cd /d "%~dp0"

:MENU
cls
echo ============================================================
echo  NETWORK SCANNER (PING + ARP MAC LOOKUP)
echo ============================================================
echo.

set /p "CIDR=Enter CIDR (e.g. 192.168.1.0/24): "
if "%CIDR%"=="" goto MENU

set /p "TOUT=Timeout ms [100]: "
if "%TOUT%"=="" set "TOUT=100"

set /p "CNT=Pings [1]: "
if "%CNT%"=="" set "CNT=1"

REM ============================================================
REM PARSE & VALIDATE
REM ============================================================
for /f "tokens=1,2 delims=/" %%A in ("%CIDR%") do (
    set "IP_ADDR=%%A"
    set "PREFIX=%%B"
)

if "%PREFIX%"=="" set "PREFIX=24"

REM Trim spaces/CR from prefix
for /f "delims= " %%T in ("%PREFIX%") do set "PREFIX=%%T"

set /a PNUM=%PREFIX% 2>nul
if errorlevel 1 (
    echo [Error] Prefix must be numeric.
    pause
    goto MENU
)

if %PNUM% LSS 1 goto ERR_RANGE
if %PNUM% GTR 30 goto ERR_RANGE

for /f "tokens=1-4 delims=." %%A in ("%IP_ADDR%") do (
    set "O1=%%A"
    set "O2=%%B"
    set "O3=%%C"
    set "O4=%%D"
)

if "%O4%"=="" (
    echo [Error] Invalid IP format.
    pause
    goto MENU
)

for %%V in (%O1% %O2% %O3% %O4%) do (
    if %%V LSS 0 goto ERR_RANGE
    if %%V GTR 255 goto ERR_RANGE
)

REM ============================================================
REM CALCULATE MASK
REM ============================================================
set "M1=0"
set "M2=0"
set "M3=0"
set "M4=0"

if %PNUM% LEQ 8 (
    set /a "M1=256-(1<<(8-%PNUM%))"
    goto MASK_DONE
)

set "M1=255"

if %PNUM% LEQ 16 (
    set /a "M2=256-(1<<(16-%PNUM%))"
    goto MASK_DONE
)

set "M2=255"

if %PNUM% LEQ 24 (
    set /a "M3=256-(1<<(24-%PNUM%))"
    goto MASK_DONE
)

set "M3=255"
set /a "M4=256-(1<<(32-%PNUM%))"

:MASK_DONE

REM ============================================================
REM CALCULATE HOST COUNT
REM ============================================================
set /a "HOSTS=(1<<(32-%PNUM%))-2"

if %PNUM% GEQ 31 set "HOSTS=0"
if %PNUM% EQU 31 set "HOSTS=2"

if "%HOSTS%"=="0" (
    echo [Error] No usable hosts in this range.
    pause
    goto MENU
)

REM ============================================================
REM CALCULATE NETWORK ADDRESS
REM ============================================================
set /a "N1=O1&M1"
set /a "N2=O2&M2"
set /a "N3=O3&M3"
set /a "N4=O4&M4"

REM ============================================================
REM PREPARE OUTPUT FILE
REM ============================================================
set "SAFE_IP=%IP_ADDR:.=_%"
set "SAFE_IP=%SAFE_IP:/=_%"
set "OUT_FILE=ping_%SAFE_IP%_p%PNUM%.txt"

set "NETWORK_ADDR=%N1%.%N2%.%N3%.%N4%/%PNUM%"

call :GET_TIME_CS START_CS
set "START_STAMP=%DATE% %TIME%"

echo.
echo Network: %NETWORK_ADDR%
echo Hosts:   %HOSTS%
echo Timeout: %TOUT% ms
echo Pings:   %CNT%
echo File:    %OUT_FILE%
echo.
echo Scanning... Please wait.
echo.

REM ============================================================
REM WRITE REPORT HEADER
REM ============================================================
> "%OUT_FILE%" (
    echo ============================================================
    echo NETWORK SCANNER REPORT
    echo ============================================================
    echo.
    echo Input CIDR;%CIDR%
    echo Parsed IP;%IP_ADDR%
    echo Prefix;%PNUM%
    echo Network;%NETWORK_ADDR%
    echo Timeout ms;%TOUT%
    echo Pings;%CNT%
    echo Hosts to scan;%HOSTS%
    echo Started;%START_STAMP%
    echo.
    echo ------------------------------------------------------------
    echo RESULTS
    echo ------------------------------------------------------------
    echo IP;MAC
)

REM ============================================================
REM INIT COUNTERS
REM ============================================================
set /a IP1=N1
set /a IP2=N2
set /a IP3=N3
set /a IP4=N4+1

if %IP4% GTR 255 (
    set /a IP4=0
    set /a IP3+=1
)

if %IP3% GTR 255 (
    set /a IP3=0
    set /a IP2+=1
)

if %IP2% GTR 255 (
    set /a IP2=0
    set /a IP1+=1
)

set /a CUR=0
set /a ALIVE=0
set /a PE=HOSTS/50

if %PE% LSS 1 set PE=1

REM ============================================================
REM SCAN LOOP
REM ============================================================
setlocal EnableDelayedExpansion

for /L %%I in (1,1,%HOSTS%) do (
    set /a CUR+=1
    set "CIP=!IP1!.!IP2!.!IP3!.!IP4!"

    ping -n %CNT% -w %TOUT% !CIP! >nul 2>&1

    if !errorlevel! EQU 0 (
        set /a ALIVE+=1

        REM Clear MAC before every ARP lookup
        set "MAC="

        REM Search MAC address in local ARP table
        for /f "tokens=1,2" %%A in ('arp -a') do (
            if "%%A"=="!CIP!" set "MAC=%%B"
        )

        if not defined MAC set "MAC=UNKNOWN"

        echo !CIP!;!MAC!>> "%OUT_FILE%"
        echo Alive: !CIP! MAC: !MAC!
    )

    set /a MOD=CUR%%PE

    if !MOD! EQU 0 (
        echo Progress: !CUR!/%HOSTS% Alive: !ALIVE!
    )

    set /a IP4+=1

    if !IP4! GTR 255 (
        set /a IP4=0
        set /a IP3+=1
    )

    if !IP3! GTR 255 (
        set /a IP3=0
        set /a IP2+=1
    )

    if !IP2! GTR 255 (
        set /a IP2=0
        set /a IP1+=1
    )
)

REM Save ALIVE value before leaving local delayed expansion scope
endlocal & set "ALIVE=%ALIVE%"

REM ============================================================
REM CALCULATE SCAN TIME
REM ============================================================
call :GET_TIME_CS END_CS
set "END_STAMP=%DATE% %TIME%"

set /a ELAPSED_CS=END_CS-START_CS

REM Midnight rollover protection
if %ELAPSED_CS% LSS 0 set /a ELAPSED_CS+=24*60*60*100

call :FORMAT_ELAPSED %ELAPSED_CS% ELAPSED_TEXT

REM ============================================================
REM WRITE REPORT SUMMARY
REM ============================================================
>> "%OUT_FILE%" (
    echo.
    echo ------------------------------------------------------------
    echo SUMMARY
    echo ------------------------------------------------------------
    echo Started;%START_STAMP%
    echo Finished;%END_STAMP%
    echo Elapsed;%ELAPSED_TEXT%
    echo Input CIDR;%CIDR%
    echo Network;%NETWORK_ADDR%
    echo Timeout ms;%TOUT%
    echo Pings;%CNT%
    echo Hosts scanned;%HOSTS%
    echo Active hosts;%ALIVE%
    echo ============================================================
)

echo.
echo ============================================================
echo Complete!
echo Found:   %ALIVE% active hosts
echo Time:    %ELAPSED_TEXT%
echo Results: %OUT_FILE%
echo ============================================================
pause
goto MENU

REM ============================================================
REM ERROR HANDLER
REM ============================================================
:ERR_RANGE
echo [Error] Value out of range. Prefix must be 1-30, octets must be 0-255.
pause
goto MENU

REM ============================================================
REM SUBROUTINE: GET CURRENT TIME IN CENTISECONDS
REM ============================================================
:GET_TIME_CS
set "T=%TIME: =0%"

for /f "tokens=1-4 delims=:., " %%A in ("%T%") do (
    set /a "%~1=((1%%A-100)*360000)+((1%%B-100)*6000)+((1%%C-100)*100)+(1%%D-100)"
)

exit /b

REM ============================================================
REM SUBROUTINE: FORMAT CENTISECONDS AS HH:MM:SS.CC
REM ============================================================
:FORMAT_ELAPSED
set /a _CS=%~1
set /a _H=_CS/360000
set /a _CS=_CS%%360000
set /a _M=_CS/6000
set /a _CS=_CS%%6000
set /a _S=_CS/100
set /a _C=_CS%%100

if %_H% LSS 10 set "_H=0%_H%"
if %_M% LSS 10 set "_M=0%_M%"
if %_S% LSS 10 set "_S=0%_S%"
if %_C% LSS 10 set "_C=0%_C%"

set "%~2=%_H%:%_M%:%_S%.%_C%"

exit /b