#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VERSION_MAX_LEN (size_t)(35)
#define METROID_BUILD_INFO_TAG "!#$MetroidBuildInfo!#$"

void* memmem(const void* l, size_t l_len, const void* s, size_t s_len) {
  register char *cur, *last;
  const char* cl = (const char*)l;
  const char* cs = (const char*)s;

  /* we need something to compare */
  if (l_len == 0 || s_len == 0)
    return NULL;

  /* "s" must be smaller or equal to "l" */
  if (l_len < s_len)
    return NULL;

  /* special case where s_len == 1 */
  if (s_len == 1)
    return memchr(l, (int)*cs, l_len);

  /* the last position where its possible to find "s" in "l" */
  last = (char*)cl + l_len - s_len;

  for (cur = (char*)cl; cur <= last; cur++)
    if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
      return cur;

  return NULL;
}

int main(int argc, const char* argv[]) {
  if (argc < 3) {
    fprintf(stdout, "\t--- METROID BUILD INFO ---\n"
                    "\tWritten by Phillip \"Antidote\" Stephens\n"
                    "\tReleased under the MIT License\n\n"
                    "\tSets the MetroidBuildInfo tag value in a given binary\n"
                    "\tThe version string can be a maximum of 35 characters,\n"
                    "\texcluding null terminator\n"
                    "\t--------------------------\n"

    );
    fprintf(stdout, "Usage:\n"
                    "\tmetroidbuildinfo <binary> <build_file>\n");
    return -1;
  }

  /* Let's try to get the source binary */
  FILE* source = fopen(argv[1], "rb");
  if (!source) {
    fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[1]);
    return -2;
  }

  char build_string[36] = {0};
  FILE* build = fopen(argv[2], "rb");

  if (!build) {
    fprintf(stderr, "Unable to open '%s'\nPlease ensure the file exists!\n", argv[2]);
    return -3;
  }
  size_t read_len = fread(build_string, 1, 35, build);
  fclose(build);

  if (read_len <= 0) {
    fprintf(stderr, "Empty file %s specified for build version!\n", argv[2]);
    return -4;
  }

  build_string[strcspn(build_string, "\r\n")] = '\0';

  /* Get source length */
  fseek(source, 0, SEEK_END);
  size_t source_len = ftell(source);
  rewind(source);
  void* source_buf = malloc(source_len);
  if (source_buf == NULL) {
    fprintf(stderr, "Unable to allocate buffer of size %zubytes!\n", source_len);
    return -5;
  }
  fread(source_buf, 1, source_len, source);
  fclose(source);

  /* Find the build info tag so we can stuff our version info in the binary */
  void* ptr =
      memmem(source_buf, source_len, METROID_BUILD_INFO_TAG, strlen(METROID_BUILD_INFO_TAG));
  if (ptr == NULL) {
    fprintf(stderr, "Unable to find build info tag in source!\n");
    return -6;
  }

  /* Lets actually copy over the build string */
  strcpy(ptr + strlen(METROID_BUILD_INFO_TAG), build_string);

  /* Now attempt to open the target file */
  FILE* target = fopen(argv[1], "wb");

  if (!target) {
    fprintf(stderr, "Unable to open '%s'\nPlease ensure you have write permissions!\n", argv[1]);
    return -7;
  }

  /* Finally write the buffer to the target file */
  fwrite(source_buf, 1, source_len, target);
  fclose(target);

  /* Don't leak */
  free(source_buf);

  return 0;
}