/*
*      WinSOS NT utility for writing image files to floppy.
*
*      Toomas Kiisk <[email protected]>, Mar 27 1999
*
*      This program is provided AS IS, with NO WARRANTY, either
*      expressed or implied.
*
*      This program or parts of it may be used, reused, abused,
*      misused and disused for any purpose that is legal in
*      your country. Names of the author and contributors may not
*      be used to promote products derived from this software.
*      Redistribution is not limited.
*
*
*      Last modified Apr 1 1999
*              some cleanups, 2 (harmless) bugs fixed and new ones
*              introduced, of course :)
*
*
*/

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>


unsigned long int    dummy_variable_for_that_stupid_ioctl_function;
#define dummy dummy_variable_for_that_stupid_ioctl_function

#define LOCK_DRIVE( x ) ( DeviceIoControl( x, FSCTL_LOCK_VOLUME, \
                       NULL, 0, NULL, 0, &dummy, NULL ) )

#define UNLOCK_DRIVE( x ) ( DeviceIoControl( x, FSCTL_UNLOCK_VOLUME, \
                       NULL, 0, NULL, 0, &dummy, NULL ) )

#define GET_GEOMETRY( x, y ) ( DeviceIoControl( x, IOCTL_DISK_GET_DRIVE_GEOMETRY, \
                       NULL, 0, y, sizeof( *y ), &dummy, NULL ) )

HANDLE  src = INVALID_HANDLE_VALUE, dst = INVALID_HANDLE_VALUE;
unsigned char    *buf = NULL;
unsigned int    bufsize;

void die( int r, char *fmt, ... );
void usage_exit( char *arg0 );


int main( int argc, char *argv[] )
{
       char dev[7], *fn;
       DISK_GEOMETRY   g;
       OSVERSIONINFO   v;
       unsigned long br, bw, bwt;

       v.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
       if ( ! GetVersionEx( &v ) )
               die( 2, "GetVersionEx()" );

       if ( v.dwPlatformId != VER_PLATFORM_WIN32_NT )
               die( 1, "This program works under Windows NT only\n" );

       if ( argc != 3 )
               usage_exit( argv[0] );

       fn = argv[1];
       if ( strlen( argv[2] ) != 2 )
               die( 1, "Invalid arg -- %s", argv[ 2 ] );
       sprintf( dev, "\\\\.\\%s", argv[2] );

       src = CreateFile( fn, GENERIC_READ, FILE_SHARE_READ,
               NULL, OPEN_EXISTING, 0, NULL );

       if ( src == INVALID_HANDLE_VALUE )
               die( 2, "Could not open %s", fn );

       dst = CreateFile( dev, GENERIC_READ | GENERIC_WRITE,
               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );

       if ( dst == INVALID_HANDLE_VALUE )
               die( 2, "Could not open %s", argv[2] );

       if ( ! LOCK_DRIVE( dst ) )
               die( 2, "Could not lock %s", argv[2] );

       if ( ! GET_GEOMETRY( dst, &g ) )
               die( 2, "Could not get disk information for %s", argv[2] );

       switch ( g.MediaType ) {
       case F5_1Pt2_512:
               printf( "5.25\", 1.2MB, 512 bytes/sector\n" );
               break;
       case F3_1Pt44_512:
               printf( "3.5\", 1.44MB, 512 bytes/sector\n" );
               break;
       case F3_2Pt88_512:
               printf( "3.5\", 2.88MB, 512 bytes/sector\n" );
               break;
       case F3_20Pt8_512:
               printf( "3.5\", 20.8MB, 512 bytes/sector\n" );
               break;
       case F3_720_512:
               printf( "3.5\", 720KB, 512 bytes/sector\n" );
               break;
       case F5_360_512:
               printf( "5.25\", 360KB, 512 bytes/sector\n" );
               break;
       case F5_320_512:
               printf( "5.25\", 320KB, 512 bytes/sector\n" );
               break;
       case F5_320_1024:
               printf( "5.25\", 320KB, 1024 bytes/sector\n" );
               break;
       case F5_180_512:
               printf( "5.25\", 180KB, 512 bytes/sector\n" );
               break;
       case F5_160_512:
               printf( "5.25\", 160KB, 512 bytes/sector\n" );
               break;
       case RemovableMedia:
               die( 1, "Unsupported media type\n" );
       case FixedMedia:
               die( 1, "No suicides today, please!\n" );
       default:
               die( 1, "Unknown media type\n" );
       }

       bufsize = g.BytesPerSector * g.SectorsPerTrack;
       buf = VirtualAlloc( NULL, bufsize, MEM_COMMIT, PAGE_READWRITE );
       if ( buf == NULL )
               die( 2, "VirualAlloc(): %d bytes", bufsize );

       printf( "bufsize is %d\n", bufsize );
       for ( bwt=0 ;ReadFile( src, buf, bufsize, &br, NULL ) && br; bwt += br ) {
               if ( ! WriteFile( dst, buf, bufsize, &bw, NULL ) )
                       die( 2, "WriteFile(): %s", argv[ 2 ] );
       }

       if ( GetLastError() != ERROR_SUCCESS )
               die( 2, "ReadFile(): %s", fn );

       printf( "%d bytes written\n", bwt );
       die( 0, NULL );

       /* NOTREACHED   */
       return 0;
}

void usage_exit( char *arg0 )
{
       char    *prog;

       prog = (prog = strrchr( arg0, '\\' ))?++prog:arg0;
       die( 1, "Usage: %s file drive:\n", prog );
}

void die( int r, char *fmt, ... )
{
       char *msg, defmsg[] = "[FormatMessage failed]";
       va_list a;
       unsigned int err;

       err = GetLastError();
       if ( r > 1 ) { /* OS error      */
               if ( ! FormatMessage(
                               FORMAT_MESSAGE_ALLOCATE_BUFFER |
                               FORMAT_MESSAGE_FROM_SYSTEM,
                               NULL,    err,
                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                               (LPTSTR)&msg, 0, NULL ) )
                       msg = defmsg;

       } else
               msg = NULL;


       if ( fmt != NULL ) {
               va_start( a, fmt );
               vfprintf( stderr, fmt, a );
               va_end( a );
       }

       if ( msg ) {
               fprintf( stderr, " -- %s\n", msg );
               if ( msg != defmsg )
                       LocalFree( msg );
       }


       if ( src != INVALID_HANDLE_VALUE )
               CloseHandle( src );

       if ( dst != INVALID_HANDLE_VALUE ) {
               UNLOCK_DRIVE( dst );
               CloseHandle( dst );
       }

       if ( buf != NULL )
               VirtualFree( buf, bufsize, MEM_DECOMMIT );

       exit( r );
}