/*--------------------------------------------------------------*/
/* Hard disk editor - Niccol• Rigacci - April 1998		*/
/*--------------------------------------------------------------*/
#include <conio.h>
#include <stdio.h>
#include <bios.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define SEC_LEN 512
#define PATT_LEN 50
#define CHOICE_LINE 45
#define MENU_LINE 36

/*--------------------------------------------------------------*/
/* Prototipi di funzione					*/
/*--------------------------------------------------------------*/
void edita_settore(unsigned char *buffer);
char edita_settore_ASCII(unsigned char *buffer, int *i);
void mostra_settore(unsigned char *buffer);
char incrementa(char c);
char decrementa(char c);
void prompt(char *prompt);
void salva_settore(unsigned char *buffer);
void cancella_menu(void);
void mostra_menu(void);
void mostra_menu_edit(void);
void cerca(unsigned char *pattern, unsigned char *buffer);
char trova(char *pattern, char *buffer);
void my_scanf(char *format, void *address);

/*--------------------------------------------------------------*/
/* Variabili esterne 						*/
/*--------------------------------------------------------------*/
int head = 0, cylinder = 0, sector = 1;
int max_sector, max_head, max_cylinder;

/*--------------------------------------------------------------*/
/* Pogramma principale						*/
/*--------------------------------------------------------------*/
void main(int argc, char *argv[])
   {
   int i;
   unsigned char buffer[SEC_LEN], pattern[PATT_LEN + 1], c;
   char finito = FALSE, slash_pi = FALSE, rileggi = TRUE;
   struct text_info ti;

   if (argc > 1)
      if (argv[1][1] == 'p' || argv[1][1] == 'P')
	 slash_pi = TRUE;
   gettextinfo(&ti);
   textmode(C4350);
   _setcursortype(_SOLIDCURSOR);
   clrscr();
   printf("HARD DISK EDITOR for Compaq Aero\n");
   printf("April 1998 - Niccol• Rigacci - Firenze, Italy\n\n");

   /* Get disk geometry from BIOS */
   biosdisk(8, 0x80, 0, 0, 1, 1, buffer);
   printf("HD1 Geometry (BIOS INT 13h, service 8)\n");
   printf("Cylinders...... 0-%d\n", 4 * ((int)buffer[0] & 192) + (int)buffer[1]);
   printf("Heads.......... 0-%d\n", buffer[3]);
   printf("Sectors/track.. 1-%d\n\n", buffer[0] & 63);

   /* Ask geometry */
   gotoxy(1, 9); printf("Max cylinder ");
   my_scanf("%d", &max_cylinder);
   gotoxy(1, 10); printf("Max head     ");
   my_scanf("%d", &max_head);
   gotoxy(1, 11); printf("Max sector   ");
   my_scanf("%d", &max_sector);
   do
      {
      if (rileggi)
	 {
	 clrscr();
	 if (biosdisk(2, 0x80, head, cylinder, sector, 1, buffer) != 0)
	    {
	    putch('\a');
	    for (i = 0; i < SEC_LEN; buffer[i++] = 0);
	    biosdisk(0, 0x80, head, cylinder, sector, 1, buffer);
	    }
	 mostra_settore(buffer);
	 }
      rileggi = TRUE;
      prompt("Your choice:");
      c = getch();
      switch (c)
	 {
	 case 's': rileggi = incrementa('s'); break;
	 case 'S': rileggi = decrementa('s'); break;
	 case 'h': rileggi = incrementa('h'); break;
	 case 'H': rileggi = decrementa('h'); break;
	 case 'c': rileggi = incrementa('c'); break;
	 case 'C': rileggi = decrementa('c'); break;
	 case 'e':
	 case 'E': edita_settore(buffer); break;
	 case 'f':
	 case 'F': pattern[0] = '\0'; cerca(pattern, buffer); break;
	 case 'n':
	 case 'N': cerca(pattern, buffer); break;
	 case 'Q':
	 case 'q': if (!slash_pi) finito = TRUE; break;
	 case 'g':
	 case 'G':
	    prompt("New cylinder value:");
	    my_scanf("%d", &cylinder);
	    if (cylinder > max_cylinder) cylinder = max_cylinder;
	    if (cylinder < 0) cylinder = 0;
	    break;
	 default: rileggi = FALSE;
	 }
      }
   while (!finito);
   textmode(ti.currmode);
   _setcursortype(_NORMALCURSOR);
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void edita_settore(unsigned char *buffer)
   {
   int i = 0, mask, v;
   int su_secondo = FALSE;
   char c, cursor;

   mostra_menu_edit();
   do
      {
      if (i >= SEC_LEN) i = SEC_LEN - 1;
      if (i < 0) i = 0;
      gotoxy(8 + (i % 16) * 3 + su_secondo, 1 + (i / 16));
      cursor = ((c = toupper(getch())) == 0 ? getch() : 0);
      if (c == '\t'  ) c = edita_settore_ASCII(buffer, &i);
      if (c == '\x1B') return;
      if ((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'))
	 {
	 putch(c);
	 v = c < 'A' ? c - '0' : c - 'A' + 0xA;
	 v = su_secondo ? v : v * 0x10;
	 mask = su_secondo ? 0xF0 : 0x0F;
	 buffer[i] = (buffer[i] & mask) | v;
	 gotoxy(58 + (i % 16), 1 + (i / 16));
	 putch(buffer[i] >= ' ' ? buffer[i] : '\xFA');
	 i += su_secondo;
	 su_secondo = !su_secondo;
	 }
      switch (cursor)
	 {
	 case 77: i += su_secondo; su_secondo = !su_secondo; break;
	 case 75: i -= !su_secondo; su_secondo = !su_secondo; break;
	 case 72: i -= 16; break;
	 case 80: i += 16; break;
	 case 68: salva_settore(buffer); break;
	 }
      }
   while (TRUE);
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
char edita_settore_ASCII(unsigned char *buffer, int *i)
   {
   char c, cursor;

   do
      {
      if (*i >= SEC_LEN) *i = SEC_LEN - 1;
      if (*i < 0) *i = 0;
      gotoxy(58 + (*i % 16), 1 + (*i / 16));
      cursor = ((c = getch()) == 0 ? getch() : 0);
      if (c == '\x1B') return c;
      if (c == '\t') return 'z';
      if (c >= ' ')
	 {
	 putch(c);
	 buffer[*i] = c;
	 gotoxy(8 + (*i % 16) * 3, 1 + (*i / 16));
	 printf("%02X", buffer[*i]);
	 (*i)++;
	 }
      switch (cursor)
	 {
	 case 77: (*i)++; break;
	 case 75: (*i)--; break;
	 case 72: (*i) -= 16; break;
	 case 80: (*i) += 16; break;
	 case 68: salva_settore(buffer); break;
	 }
      }
   while (TRUE);
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void mostra_settore(unsigned char *buffer)
   {
   int i, j, k;
   unsigned char c;

   for (i = 0; i < 32; i++)
      {
      gotoxy(1, 1 + i);
      printf("%04X   ", i * 16);
      for (j = 0; j < 16; j++)
	 printf("%02X ", buffer[i * 16 + j]);
      printf("  ");
      for (j = 0; j < 16; j++)
	 printf("%c", ((c = buffer[i * 16 + j]) >= ' ' ? c : '\xFA'));
      }
   gotoxy(1, 34);
   printf("Cylinder %d, Head %d, Sector %d", cylinder, head, sector);
   mostra_menu();
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
char incrementa(char c)
   {
   char rit;

   switch (c)
      {
      case 's':
	 if ((++sector) > max_sector)
	    {
	    rit = incrementa('h');
	    sector = (rit ? 1 : max_sector);
	    }
	 break;
      case 'h':
	 if ((++head) > max_head)
	    {
	    rit = incrementa('c');
	    head = (rit ? 0 : max_head);
	    }
	 break;
      case 'c':
	 if ((++cylinder) > max_cylinder)
	    {
	    cylinder--;
	    return FALSE;
	    }
	 else
	    return TRUE;
      }
   return rit;
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
char decrementa(char c)
   {
   char rit;

   switch (c)
      {
      case 's':
	 if ((--sector) < 1)
	    {
	    rit = decrementa('h');
	    sector = (rit ? max_sector : 1);
	    }
	 break;
      case 'h':
	 if ((--head) < 0)
	    {
	    rit = decrementa('c');
	    head = (rit ? max_head : 0);
	    }
	 break;
      case 'c':
	 if ((--cylinder) < 0)
	    {
	    cylinder++;
	    return FALSE;
	    }
	 else
	    return TRUE;
      }
   return rit;
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void prompt(char *prompt)
   {
   int i;

   gotoxy(1, CHOICE_LINE);
   for (i = 0; i < 80; i ++) putch(' ');
   gotoxy(1, CHOICE_LINE);
   printf("%s ", prompt);
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void salva_settore(unsigned char *buffer)
   {
   prompt("Are you sure? (Y/N)");
   putch('\a');
   if (toupper(getch()) == 'Y')
      biosdisk(3, 0x80, head, cylinder, sector, 1, buffer);
   prompt("");
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void cancella_menu(void)
   {
   int i;

   gotoxy(1, MENU_LINE);
   for (i = 0; i < 800; i ++) putch(' ');
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void mostra_menu(void)
   {
   cancella_menu();
   gotoxy(1, MENU_LINE + 0); printf("s  next sector (S previous)");
   gotoxy(1, MENU_LINE + 1); printf("h  next head (H previous)");
   gotoxy(1, MENU_LINE + 2); printf("c  next cylinder (C previous)");
   gotoxy(1, MENU_LINE + 3); printf("g  go to cylinder #");
   gotoxy(1, MENU_LINE + 4); printf("f  find a string");
   gotoxy(1, MENU_LINE + 5); printf("n  find next");
   gotoxy(1, MENU_LINE + 6); printf("e  edit");
   gotoxy(1, MENU_LINE + 7); printf("q  quit");
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void mostra_menu_edit(void)
   {
   cancella_menu();
   gotoxy(1, MENU_LINE + 0); printf("F10 write changes to disk");
   gotoxy(1, MENU_LINE + 1); printf("TAB toggle HEX-ASCII editing");
   gotoxy(1, MENU_LINE + 2); printf("ESC back to main menu");
   }

/*--------------------------------------------------------------*/
/* Avanza di settore in settore cercando il pattern.		*/
/*--------------------------------------------------------------*/
void cerca(unsigned char *pattern, unsigned char *buffer)
   {
   int i;
   char trovato = FALSE, cerca_questo = TRUE;

   if (pattern[0] != '\0')
      cerca_questo = FALSE;
   else
      {
      prompt("Find what (50 chars, case sensitive):");
      my_scanf("%s", pattern);
      }
   prompt("Press ESC to stop searching");
   do
      {
      if (!cerca_questo || !trova(pattern, buffer))
	 {
	 cerca_questo = TRUE;
	 trovato = !incrementa('s');
	 if (biosdisk(2, 0x80, head, cylinder, sector, 1, buffer) != 0)
	    {
	    for (i = 0; i < SEC_LEN; buffer[i++] = 0);
	    biosdisk(0, 0x80, head, cylinder, sector, 1, buffer);
	    }
	 }
      else
	 trovato = TRUE;
      if (kbhit())
	 if (getch() == '\x1B')
	    trovato = TRUE;
      }
   while (!trovato);
   }

/*--------------------------------------------------------------*/
/* Ritorna TRUE se trova un'occorrenza di patt in buff.		*/
/*--------------------------------------------------------------*/
char trova(char *patt, char *buff)
   {
   int i = 0, j, k;

   k = SEC_LEN + 1 - strlen(patt);
   do
      {
      while (i < k && buff[i] != patt[0]) i++;
      if (i < k)
	 {
	 for (j = 0; patt[j] != '\0' && patt[j] == buff[i + j]; j++);
	 if (patt[j] == '\0')
	    return TRUE;
	 else
	    i++;
	 }
      }
   while (i < k);
   return FALSE;
   }

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/
void my_scanf(char *format, void *address)
   {
   int i = 0;
   char buffer[51];
   char finito = FALSE;
   char c;

   do
      {
      c = getch();
      if (c >= ' '  && i < 50) putch(buffer[i++] = c);
      if (c == '\x0D') finito = TRUE;
      if (c == '\b' && i > 0 )
	 {
	 putch(c);
	 putch(' ');
	 putch(c);
	 i--;
	 }
      }
   while (!finito);
   buffer[i] = '\0';
   switch (format[1])
      {
      case 'd': *(int *)address = atoi(buffer); break;
      case 's': strcpy((char *)address, buffer); break;
      }
   }

