// ----------------------------------------------------------------------------
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ----------------------------------------------------------------------------

namespace BootsectorUtils
{
    using System;
    using System.IO;
    
    /// <summary>
    /// VpcFixedDiskInstaller
    ///     Console utility to install a bootsector onto a VirtualPC FIXED hard disk partition
    /// </summary>
    class VpcFixedDiskInstaller
    {
        [STAThread]
        static void Main(string[] args)
        {
            try {
                if (args.Length != 3)
                    throw new Exception("Error:  The correct argument format is <VHD Name> <Boot Sector File> <partition>");
                VpcFixedDiskInstaller i = new VpcFixedDiskInstaller();
                i.Install(args[0], args[1], Convert.ToInt32(args[2]));
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }

        public void Install(string VHDName, string BSName, int whichPartition)
        {
            FileStream  HDFile, BSFile, BKFile;
            int         size = 512;
            int         skipsize;
            byte []     buffer = new Byte[size];
            byte []     oldBootSector = new Byte[size];
            byte []     newBootSector = new Byte[size];
            int         bytesread;
            int         MBRIndex = 446;
            int         LBA;
            string      backupFilename = "backup" + System.DateTime.Now.Ticks.ToString() + ".bootsector";


            Console.WriteLine("Setting bootsector of disk {0} in HD {1} to contents of file {2} and backing up old bootsector to {3}", 
                whichPartition, VHDName, BSName, backupFilename);

            if (whichPartition > 3 || whichPartition < 0) {
                throw new Exception("Error:  You must choose a partition between 0 and 3.");
            }

            HDFile = File.Open(VHDName, FileMode.Open, FileAccess.ReadWrite);
            
            // read the MBR from the VPC HDD file
            bytesread=HDFile.Read(buffer, 0, size);

            // branch based on partition type
            MBRIndex += ((16*whichPartition)+4);
            if ((buffer[MBRIndex] != 0x0c) && (buffer[MBRIndex] != 0x0e)) {
                throw new Exception("The partition you selected is not FAT32 type 0c or 0e.");
            }

            LBA = (int) buffer[MBRIndex+7];
            LBA = LBA*8 + buffer[MBRIndex+6];
            LBA = LBA*8 + buffer[MBRIndex+5];
            LBA = LBA*8 + buffer[MBRIndex+4];            
            
            // now that we know the location of the boot sector, let's grab it...
            HDFile.Seek((LBA)*size, SeekOrigin.Begin);
            HDFile.Read(oldBootSector, 0, size);

            // read the new BootSector
            BSFile = File.Open(BSName, FileMode.Open, FileAccess.Read);
            bytesread = BSFile.Read(newBootSector,0,size);
            if (bytesread != size) {
                throw new Exception("Error:  The boot sector is not big enough.");
            }
            if (newBootSector[510] != 0x55 || newBootSector[511] != 0xAA) {
                throw new Exception("Error:  The boot sector file has an invalid signature.");
            }
            BSFile.Close();

            skipsize = newBootSector[1]+2;

            // save the old boot sector:
            BKFile = File.OpenWrite(backupFilename);
            BKFile.Write(oldBootSector, 0, size);
            BKFile.Close();

            // generate the new boot sector by copying the BPB and FAT parameters...
            for (int i = 3; i < skipsize; i++) {
                newBootSector[i] = oldBootSector[i];
            }
            
            // point of no return:
            // write the new boot sector:
            HDFile.Seek((LBA)*size, SeekOrigin.Begin);
            HDFile.Write(newBootSector, 0, size);
            HDFile.Close();
        }
    }
}
