/*  author: ccexplore (ccexplore@yahoo.com)
    version: 1.1                         
    last updated: 12/26/02
 */
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "fullsec.h"

#define ExeLen            128     /* includes null terminator */
char ExeIn[ExeLen + 4];  /* + 4 to allow for appending ".exe" */
char ExeOut[ExeLen + 4]; /* + 4 to allow for appending ".exe" */
/* hold the temp filename used during saving */
/* ExeLen + 4 for prepending rest of path (see getTmpFile) */
char ExeTemp[ExeLen + 4 + L_tmpnam];

/* hold user response to questions, typically a yes/no answer */
/* includes room for null terminator */
char YesNo[2];
#define AnswerYes        (YesNo[0] == 'Y' || YesNo[0] == 'y')
#define ExeOutF_CREATE    0
#define ExeOutF_REPLACE   1

unsigned int tmpCount = 0;

int main() {

  int status, outStatus, whattowrite;
  FILE *ExeInF = NULL, *ExeOutF = NULL;

  printf("This is fullsec.exe, version 1.1\n\n");
  status = getInFile(ExeIn, &ExeInF);
  switch (status) {
    case FF_Original:
      whattowrite = AFTER;
      outStatus = getOutFile("patched", ExeIn, ExeOut, &ExeOutF);
      break;
    case FF_Newpatch:
      whattowrite = BEFORE;
      outStatus = getOutFile("unpatched", ExeIn, ExeOut, &ExeOutF);
      break;
    default:
      return status;
  }

  strcpy(ExeTemp, ExeOut);
  switch (outStatus) {
    case ExeOutF_REPLACE:
	   printf ("\nCreating temporary file...");
	   if (getTmpFile(ExeTemp, &ExeOutF) == 0) {
	     printf ("success!\n"
		          "Now writing to temporary file %s...", ExeTemp);
        if (patch(ExeInF, ExeOutF, whattowrite) == 0) {
		    printf ("success!\n"
	               "Now deleting your old %s file...", ExeOut);
          fclose(ExeInF);
          fclose(ExeOutF);
          if (remove(ExeOut) == 0) {
		      printf ("success!\n");
		      printf ("And finally, renaming the new file %s to %s...",
                    ExeTemp, ExeOut);
            if (rename(ExeTemp, ExeOut) == 0) {
			     printf ("success!\n");
              status = 0;
              break;
            }
            else
			     printf ("FAILED!\n");
		    }
		    else {
		      printf ("\nError! Cannot delete file!\n");
		    }
		    printf ("\nAs a result, the new file has been written to %s instead of %s.\n"
                  "Sorry for the inconvenience!\n", ExeTemp, ExeOut);
		    return -1;
		  }
		  else
		    printf ("\nError while writing to temporary file %s!\n", ExeTemp);
	   }
	   else
	     printf ("\nError creating temporary file!\n");

	   printf ("Program must directly overwrite %s! Proceed (Y/N)? ", ExeOut);
	   scanf (" %1s", YesNo);
	   if (!AnswerYes) {
	     status = -1;
		  break;
	   }

	   /* purposely no BREAK here */

    case ExeOutF_CREATE:
      fclose(ExeOutF);
	   if ((ExeOutF = fopen(ExeOut, "wb")) != NULL) {
	     printf ("\nWriting to file %s...", ExeOut);
		  if ((status = patch(ExeInF, ExeOutF, whattowrite)) == 0) {
		    printf ("success!\n");
		    break;
		  }
	   }
	   printf ("\nError writing to file %s! Program gives up!\n", ExeOut);
	   status = -1;
	   break;

    default:  /* defensive programming; code should never reached here */
      printf ("Internal error occurred in program during SAVE!\n"
              "Please e-mail a bug report to ccexplore@yahoo.com\n");
      return -1;
  }

  fclose (ExeInF);
  fclose (ExeOutF);

  if (status == 0) {
    printf ("%s has been successfully patched/unpatched.\n", ExeIn);
	return 0;
  }
  else {
    printf ("%s was NOT successfully patched/unpatched!\n", ExeIn);
	return -1;
  }
}

void prependext(char *exefilename) {
  char *c;

  c = strrchr(exefilename, '\\');
  if (c == NULL) {
    c = strrchr(exefilename, ':');
    if (c == NULL)
      c = exefilename;
  }
  c = strrchr(c, '.');

  /* only append an extension if extension not already exists, including
     an empty extension */
  if (c == NULL) {
    strcat(exefilename, ".exe");
  }
}

int getInFile(char* ExeIn, FILE **ExeInF) {

  int status;

  do {
    if (*ExeInF != NULL)
      fclose(*ExeInF);

    printf ("\nEnter file to patch/unpatch: ");
    scanf ("%127s",ExeIn);
    prependext(ExeIn);

    if ((*ExeInF = fopen(ExeIn,"rb")) == NULL) {
      printf ("\nError opening %s for input!",ExeIn);
      status = -1;
    }
    else {
      printf ("\nFile successfully opened! Examining file...\n");
      status = checkFileType(*ExeInF);
      switch (status) {
        case FF_Invalid:
          printf ("%s does not appear to be a valid CHIPS.EXE file!\n"
                  "Proceed anyway (Y/N)? ", ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes) {
            do {
              printf("\npatch or unpatch (P/U)? ");
              scanf ("%1s", YesNo);
              if (YesNo[0] == 'p' || YesNo[0] == 'P')
                return FF_Original;
              else if (YesNo[0] == 'u' || YesNo[0] == 'U')
                return FF_Newpatch;
              else
                printf("please enter either p or u.\n");
            } while (1);
          }
          else {
            status = -1;
            break;
          }

        case -FF_Original:
          printf ("%s looks similar to the original CHIPS.EXE file, but\n"
                  "the filesize is larger!\n"
                  "Proceed to patch anyway (Y/N)? ", ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes)
            return FF_Original;
          else {
            status = -1;
            break;
          }

        case FF_Original:
          printf ("%s has been recognized as an original CHIPS.EXE file.\n"
                  "Proceed to perform the (updated) fullsec patch (Y/N)? ",
                  ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes)
            return FF_Original;
          else {
            status = 0;
            break;
          }

        case -FF_Patched:
          printf ("%s looks similar to an OLD-VERSION fullsec-patched CHIPS.EXE"
                  " file, but\nthe filesize is larger!\n"
                  "Proceed anyway (Y/N)? ", ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes) {
            do {
              printf ("\nThis program can update it to the new version, or it "
                      "can revert it back to an original, unpatched CHIPS.EXE\n");
              printf("Your choice (1=update, 2=unpatch)? ");
              scanf ("%1s", YesNo);
              if (YesNo[0] == '1')
                return FF_Original;
              else if (YesNo[0] == '2')
                return FF_Newpatch;
              else
                printf("please enter either 1 or 2.\n");
            } while (1);
          }
          else {
            status = -1;
            break;
          }

        case FF_Patched:
          printf ("%s has been recognized as an OLD-VERSION fullsec-patched "
                  "CHIPS.EXE file.\n"
                  "This program can update it to the new version, or it "
                  "can revert it back to an original, unpatched CHIPS.EXE\n\n",
                  ExeIn);

          printf("Your choice (1=update, 2=unpatch, other=different file)? ");
          scanf ("%1s", YesNo);
          if (YesNo[0] == '1')
            return FF_Original;
          else if (YesNo[0] == '2')
            return FF_Newpatch;
          else {
            printf("okay, closing current file...\n");
            status = 0;
            break;
          }

        case -FF_Newpatch:
          printf ("%s looks similar to the updated version of fullsec-patched "
                  "CHIPS.EXE file, but\n"
                  "the filesize is larger!\n"
                  "Proceed to unpatch anyway (Y/N)? ", ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes)
            return FF_Newpatch;
          else {
            status = -1;
            break;
          }

        case FF_Newpatch:
          printf ("%s has been recognized as an updated version of "
                  "fullsec-patched CHIPS.EXE file.\n"
                  "Proceed to unpatch (revert back to original CHIPS.EXE) "
                  "(Y/N)? ", ExeIn);
          scanf ("%1s", YesNo);
          if (AnswerYes)
            return FF_Newpatch;
          else {
            status = 0;
            break;
          }

        default:   /* defensive programming; code should never reached here */
          printf ("unrecognized status code %d in function getInFile!\n"
                  "Please e-mail a bug report to ccexplore@yahoo.com\n",status);
          return -1;
      }
    }
    printf ("\nTry a different file (Y/N)? ");
    scanf ("%1s",YesNo);
    if (AnswerYes)
      continue;
    else
      return status;
  }
  while (1);
}

int checkFileType(FILE *ExeInF) {
  int returnCode, i;
  long int len;
  int b, beforeaft;

  fseek(ExeInF, 0, SEEK_END);
  len = ftell(ExeInF);

  if (len < CHIPFILESIZE)
    return FF_Invalid;

  /* determines whether file is patched or unpatched */
  fseek(ExeInF, FS_PATCHLOC3, SEEK_SET);
  b = fgetc(ExeInF);
  if ((char)b == fs_patchpart3[0][BEFORE]) {
    beforeaft = BEFORE;
    returnCode = FF_Original;
  }
  else if ((char)b == fs_patchpart3[0][AFTER]) {
    beforeaft = AFTER;
    returnCode = FF_Patched;
  }
  else
    return FF_Invalid;

  /* check rest of location to ensure they are as expected */
  fseek(ExeInF, FS_PATCHLOC1, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart1) / sizeof(fs_patchpart1[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart1[i][beforeaft])
      return FF_Invalid;  /* file size too small, or signature not recognized */
  }

  fseek(ExeInF, FS_PATCHLOC2, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart2) / sizeof(fs_patchpart2[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart2[i][beforeaft])
      return FF_Invalid;
  }

  fseek(ExeInF, FS_PATCHLOC3, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart3) / sizeof(fs_patchpart3[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart3[i][beforeaft])
      return FF_Invalid;
  }

  /* now determine whether it is the old version of patch or the new version */
  if (returnCode == FF_Original)
    goto finish;

  fseek(ExeInF, FS_PATCHLOC5, SEEK_SET);
  b = fgetc(ExeInF);
  if ((char)b == fs_patchpart5[0][BEFORE]) {
    beforeaft = BEFORE;
    returnCode = FF_Patched;
  }
  else if ((char)b == fs_patchpart5[0][AFTER]) {
    beforeaft = AFTER;
    returnCode = FF_Newpatch;
  }
  else
    return FF_Invalid;

  fseek(ExeInF, FS_PATCHLOC4, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart4) / sizeof(fs_patchpart4[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart4[i][beforeaft])
      return FF_Invalid;
  }

  fseek(ExeInF, FS_PATCHLOC5, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart5) / sizeof(fs_patchpart5[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart5[i][beforeaft])
      return FF_Invalid;
  }

  fseek(ExeInF, FS_PATCHLOC6, SEEK_SET);
  for (i = 0; i < sizeof(fs_patchpart6) / sizeof(fs_patchpart6[0]); i++) {
    b = fgetc(ExeInF);
    if (b == EOF || (char)b != fs_patchpart6[i][beforeaft])
      return FF_Invalid;
  }

  /* negate return code to signal larger-than-expected filesize */
finish:
  if (len > CHIPFILESIZE)
    return -returnCode;
  else
    return returnCode;
}

int getOutFile(char* filetypeString, char* ExeIn, char* ExeOut, FILE **ExeOutF){

  do {
    int fileexist;

    printf("\nEnter pathname to save this %s CHIPS.EXE to: ",
           filetypeString);
    scanf("%127s", ExeOut);
    prependext(ExeOut);

    if (*ExeOutF != NULL)
      fclose(*ExeOutF);
    *ExeOutF = fopen(ExeOut,"rb");
    fileexist = (*ExeOutF != NULL);

    if (fileexist) {
      fclose(*ExeOutF);
      printf("\nFile already exists! Overwrite (Y/N)? ");
	   scanf("%1s", YesNo);
	   if (!AnswerYes) {
        *ExeOutF = NULL;
        continue;
      }
    }
    else {
      if ((*ExeOutF = fopen(ExeOut,"ab")) == NULL) {
        printf("\nTrouble creating new file %s. "
               "You must try a different pathname.\n", ExeOut);
        continue;
	   }
	 }

	 return fileexist;
  }
  while (1);
}


int patch(FILE *ExeInF, FILE *ExeOutF, int beforeaft) {
  int i, b;

  if (fseek(ExeInF, 0L, SEEK_SET) != 0)
    return -1;
  if (fseek(ExeOutF, 0L, SEEK_SET) != 0)
    return -1;

  /* first just copy the whole thing */
  while ((b = fgetc(ExeInF)) != EOF) {
    b = fputc((char)b, ExeOutF);
    if (b == EOF) return -1;  /* EOF on fputc means error */
  }

  /* now do the patches */
  if (fseek(ExeOutF, FS_PATCHLOC1, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart1)/sizeof(fs_patchpart1[0]); i++) {
    b = fputc((char)fs_patchpart1[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  if (fseek(ExeOutF, FS_PATCHLOC2, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart2)/sizeof(fs_patchpart2[0]); i++) {
    b = fputc((char)fs_patchpart2[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  if (fseek(ExeOutF, FS_PATCHLOC3, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart3)/sizeof(fs_patchpart3[0]); i++) {
    b = fputc((char)fs_patchpart3[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  if (fseek(ExeOutF, FS_PATCHLOC4, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart4)/sizeof(fs_patchpart4[0]); i++) {
    b = fputc((char)fs_patchpart4[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  if (fseek(ExeOutF, FS_PATCHLOC5, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart5)/sizeof(fs_patchpart5[0]); i++) {
    b = fputc((char)fs_patchpart5[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  if (fseek(ExeOutF, FS_PATCHLOC6, SEEK_SET) != 0)
    return -1;
  for (i = 0; i < sizeof(fs_patchpart6)/sizeof(fs_patchpart6[0]); i++) {
    b = fputc((char)fs_patchpart6[i][beforeaft], ExeOutF);
    if (b == EOF) return -1;
  }

  return 0;
}

int getTmpFile(char *tmpFilename, FILE **ExeOutF) {

  FILE *tmpF = NULL;
  char *filenamepart;

  if ((filenamepart = strrchr(tmpFilename, '\\')) == NULL &&
      (filenamepart = strrchr(tmpFilename, ':')) == NULL)
    filenamepart = tmpFilename;
  else
    filenamepart++;

  while (tmpCount < TMP_MAX) {
   tmpCount++;

   tmpnam(filenamepart);

   fclose(tmpF);
	if ((tmpF = fopen(tmpFilename, "rb")) != NULL) {
     continue;
   }
	else {
	  if ((tmpF = fopen(tmpFilename, "wb")) == NULL)
	    continue;
	  else {
       *ExeOutF = tmpF;
	    return 0;
     }
	}
  }
  return -1;
}
