// SPDX-License-Identifier: GPL-2.0-or-later
/* **************************************************************************

   This program creates a modified 16bit checksum used for the Netgear
   DGN3500 series routers. The difference between this and a standard
   checksum is that every 0x100 bytes added 0x100 have to be subtracted
   from the sum.

   (C) 2013 Marco Antonio Mauro <marcus90 at gmail.com>

   Based on previous unattributed work.

 ************************************************************************* */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

unsigned char PidDataWW[70] =
{
    0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32,
    0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x37,
    0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73,
    0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D,
} ;

unsigned char PidDataDE[70] =
{
    0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32,
    0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x37,
    0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73,
    0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D,
} ;

unsigned char PidDataNA[70] =
{
    0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32,
    0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x37,
    0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73,
    0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D,
} ;

/* *******************************************************************
   Reads the file into memory and returns pointer to the buffer. */
static char *readfile(char *filename, int *size)
{
	FILE		*fp;
	char		*buffer;
	struct stat	info;

	if (stat(filename,&info)!=0)
		return NULL;

	if ((fp=fopen(filename,"r"))==NULL)
		return NULL;

	buffer=NULL;
	for (;;)
	{
		if ((buffer=(char *)malloc(info.st_size+1))==NULL)
			break;

		if (fread(buffer,1,info.st_size,fp)!=info.st_size)
		{
			free(buffer);
			buffer=NULL;
			break;
		}

		buffer[info.st_size]='\0';
		if(size) *size = info.st_size;

		break;
	}

	(void)fclose(fp);

	return buffer;
}


/* ******************************************************************* */
int main(int argc, char** argv)
{
  unsigned long start, i;
  char *endptr, *buffer, *p;
  int count;  // size of file in bytes
  unsigned short sum = 0, sum1 = 0;
  char sumbuf[8 + 8 + 1];

  if(argc < 3) {
    printf("ERROR: Argument missing!\n\nUsage %s filename starting offset in hex [PID code]\n\n", argv[0]);
    return 1;
  }


  FILE *fp = fopen(argv[1], "a");
  if(!fp) {
    printf("ERROR: File not writeable!\n");
    return 1;
  }
  if(argc == 4)
  {
    printf("%s: PID type: %s\n", argv[0], argv[3]);
    if(strcmp(argv[3], "DE")==0)
      fwrite(PidDataDE, sizeof(PidDataDE), sizeof(char), fp);  /* write DE pid */
    else if(strcmp(argv[3], "NA")==0)
      fwrite(PidDataNA, sizeof(PidDataNA), sizeof(char), fp);  /* write NA pid */
    else /* if(strcmp(argv[3], "WW")) */
      fwrite(PidDataWW, sizeof(PidDataWW), sizeof(char), fp);  /* write WW pid */
  }
  else
    fwrite(PidDataWW, sizeof(PidDataWW), sizeof(char), fp);  /* write WW pid if unspecified */

  fclose(fp);

  /* Read the file to calculate the checksums */
  buffer = readfile(argv[1], &count);
  if(!buffer) {
    printf("ERROR: File %s not found!\n", argv[1]);
    return 1;
  }

  p = buffer;
  for(i = 0; i < count; i++)
  {
	sum += p[i];
  }

  start = strtol(argv[2], &endptr, 16);
  p = buffer+start;
  for(i = 0; i < count - start; i++)
  {
	sum1 += p[i];
  }

  sprintf(sumbuf,"%04X%04X",sum1,sum);
  /* Append the 2 checksums to end of file */
  fp = fopen(argv[1], "a");
  if(!fp) {
    printf("ERROR: File not writeable!\n");
    return 1;
  }
  fwrite(sumbuf, 8, sizeof(char), fp);
  fclose(fp);
  free(buffer);
  return 0;
}
