// sqexe - Squeak Easy Launcher for windows
//
// Takashi Yamamiya
//
// How to Debug;
// sqexe.exe --build Squeak.exe Squeak.image
// cp sqapp.exe sqexe.exe
// Run on your bash or [F5] at VC++

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<io.h>
#include<process.h>

#define FOOTER_SIZE 100L
#define OUTFILE "sqapp.exe"
#define BUF_SIZE 4096

int launch(char*);
int build(char*, char*, char*);
int testRun(char *selfName, char *vmName, char* imageName);

int main(int argc, char ** argv) {
  char *selfName = argv[0];

  if (argc == 1) {
	return launch(selfName);
  }
  if ((argc == 4) && (strcmp(argv[1], "--build") == 0)) {
	return build(selfName, argv[2], argv[3]);
  }
  if ((argc == 4) && (strcmp(argv[1], "--test") == 0)) {
	return testRun(selfName, argv[2], argv[3]);
  }
  fprintf(stderr, "usage: sqexe --build vmpath imagepath\n");
  fprintf(stderr, "usage: sqexe --test vmpath imagepath\n");
  return 1;
}

// Test build and run
int testRun(char *selfName, char *vmName, char* imageName) {
  if (build(selfName, vmName, imageName) != 0) {
	return 1;
  }
  _spawnl(_P_WAIT, OUTFILE, OUTFILE, NULL);
  return 0;
}

FILE * openFile(char *selfName, char *mode)
{
  FILE *f;
  if ((f = fopen(selfName, mode)) == NULL) {
	fprintf(stderr, "File access error: %s mode: %s\n", selfName, mode);
	exit(1);
  }
  return f;
}

void copyFile(FILE * src, FILE * dist, size_t size)
{
  char buf[BUF_SIZE];
  size_t remain;
  size_t expect, result;

  remain = size;
  while (remain > 1) {
	expect = (remain < BUF_SIZE) ? remain : BUF_SIZE;
	result = fread(buf, sizeof(char), expect, src);
	if (expect != result) {	
	  fprintf(stderr, "Couldn't copy file.");
	  exit(1);
	}
	result = fwrite(buf, sizeof(char), expect, dist);
	if (expect != result) {	
	  fprintf(stderr, "Couldn't copy file.");
	  exit(1);
	}
	remain -= expect;
  }
}

// Build executable file from vm and image
int build(char *selfName, char *vmName, char* imageName)
{

  FILE *srcFile, *vmFile, *imageFile, *outFile;
  long srcLength, vmLength, imageLength;
  long expectSize, resultSize;
  char footer[FOOTER_SIZE];
  int i;

  srcFile = openFile(selfName, "rb");
  vmFile = openFile(vmName, "rb");
  imageFile = openFile(imageName, "rb");

  srcLength = _filelength(_fileno(srcFile));
  vmLength = _filelength(_fileno(vmFile));
  imageLength = _filelength(_fileno(imageFile));

  sprintf(footer, "%d %d %d %d",
		  srcLength, vmLength, srcLength + vmLength, imageLength);

  outFile = openFile(OUTFILE, "wb");

  copyFile(srcFile, outFile, srcLength);
  copyFile(vmFile, outFile, vmLength);
  copyFile(imageFile, outFile, imageLength);

  for (i = 0; i < FOOTER_SIZE; i++) {
	putc(footer[i], outFile);
  }

  fclose(srcFile);
  fclose(vmFile);
  fclose(imageFile);

  fflush(outFile);
  expectSize = srcLength + vmLength + imageLength + FOOTER_SIZE;
  resultSize = _filelength(_fileno(outFile));
  if (resultSize != expectSize) {
	fprintf(stderr, "Write failed: %s size: %d", OUTFILE, resultSize);
	return 1;
  };
  fclose(outFile);
  return 0;
}

void getTempName(char * prefix, char * postfix, char * fileName)
{
  char pidPrefix[_MAX_PATH];
  char * tmpName;
  sprintf(pidPrefix, "%s%d", prefix, _getpid());
  if((tmpName = _tempnam( "c:\\tmp", pidPrefix ) ) == NULL ) {
	fprintf(stderr, "Could not create tmp file name");
	exit(1);
  }
  sprintf(fileName, "%s.%s", tmpName, postfix);
}

// Execute Squeak VM and image
int launch(char *selfName) {
  FILE *exefile;
  FILE *vmFile, *imageFile;

  long vmPos;
  long vmSize;
  long imagePos;
  long imageSize;
  char *tmpName;
  char vmName[_MAX_PATH];
  char imageName[_MAX_PATH];

  //  printf("%s\n", selfName);

  // Fetch vm and image file.
  exefile = openFile(selfName, "rb");

  if (fseek(exefile, 0 - FOOTER_SIZE, SEEK_END) != 0) {
	perror("fseek failed");
	return 1;
  }
  if (fscanf(exefile, "%d %d %d %d", &vmPos, &vmSize, &imagePos, &imageSize) == 0) {
	perror("fscanf failed");
	return 1;
  }
  //  printf("%d %d %d %d", vmPos, vmSize, imagePos, imageSize);

  // Get file name.
  getTempName("sqvm", "exe", vmName);
  getTempName("sqim", "image", imageName);

  // Extract vm and image.
  vmFile = openFile(vmName, "wb");
  imageFile = openFile(imageName, "wb");

  if (fseek(exefile, vmPos, SEEK_SET) != 0) {
	perror("fseek failed");
	return 1;
  }
  copyFile(exefile, vmFile, vmSize);

  if (fseek(exefile, imagePos, SEEK_SET) != 0) {
	perror("fseek failed");
	return 1;
  }
  copyFile(exefile, imageFile, imageSize);

  fclose(exefile);
  fclose(vmFile);
  fclose(imageFile);

  _spawnl(_P_WAIT, vmName, vmName, imageName, NULL);
  //  _spawnl(_P_OVERLAY, vmName, vmName, imageName, NULL);

  _unlink(vmName);
  _unlink(imageName);
  return 0;
}


