#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


const char hexdigits[16] = "0123456789ABCDEF";

const unsigned char findtexthandler[] = {
  0x66, 0x8B, 0xF0, 0x25, 0xFF, 0x07, 0x00, 0x00,
  0x66, 0x81, 0xE6, 0x00, 0xF8, 0x74, 0x15, 0x66,
  0xC1, 0xEE, 0x08, 0x66, 0x83, 0xEE, 0x08, 0x0F,
  0xB7, 0xEE, 0x8B, 0xAD
};

const size_t classtextoffsets[0x16] = { 4, 4, 4, 18,
					4, 18, 4, 4,
					4, 4, 4, 4,
					4, 16, 0, 4,
					4, 4, 4, 4,
					4, 4 };

const unsigned texttypelast[0x17] = { 0x0334, 0x0810, 0x1024, 0x1818,
				      0x205C, 0x2810, 0x306C, 0x3807,
				      0x4010, 0x483B, 0x5029, 0x5807,
				      0x6018, 0x6838, 0x707F, 0,
				      0x8107, 0x886B, 0x9037, 0x9842,
				      0xA043, 0,      0xB005 };



#define OPHANDLERTABLE_OFF sizeof(findtexthandler)
#define TEXT_TYPE_00_OFF (sizeof(findtexthandler) + 11)

#define getoffset(p)  (*(size_t*)(p))   // nonportable!

const unsigned char *findbinstring(const unsigned char *data, size_t datasize, const unsigned char *string, size_t strsize)
{
  size_t n;

  while (datasize--) {
    for (n = 0; n < strsize; n++) {
      if (data[n] != string[n]) goto nextbyte;
    }
    return data;
    nextbyte: data++;
  }
  return NULL;
}


#define TRANSFORM_STRING_BUFSIZE 4096
char *trstring(const unsigned char *data)
{
  static char buf[TRANSFORM_STRING_BUFSIZE];
  char *p = buf;

  if (strlen((char*)data) >= TRANSFORM_STRING_BUFSIZE/4) exit((fputs("ttdstrextr: string too long to transform\n", stderr), EXIT_FAILURE));

  do {
    if (*data == 0 || (*data >= 0x20 && *data < 0x7B && *data != '"' && *data != '\\')) *p++ = *data;
    else {
      *p++ = '\\';
      *p++ = 'x';
      *p++ = hexdigits[*data >> 4];
      *p++ = hexdigits[*data & 0xF];
    }
  } while(*data++);

  return buf;
}


void printstring(const unsigned char *data, size_t offset, unsigned id)
{
  printf("%08lX,  %04X,  \"%s\",\n", (unsigned long)offset, id, trstring(data+offset));
}

void processtable(const unsigned char *const data, size_t offset, const unsigned baseid)
{
  unsigned id;

  for (id = baseid; id <= texttypelast[baseid>>11]; id++) {
    printstring(data, getoffset(data+offset), id);
    offset += 4;
  }

  puts("");
}

void processallclasses(const unsigned char *const data, size_t table)
{
  int c;

  for (c = 0; c <= 0x15; c++) {
    size_t handtab = getoffset(data+table);
    size_t handler = getoffset(data+handtab+8);
    if (classtextoffsets[c]) {
      processtable(data, getoffset(data+handler+classtextoffsets[c]), (c+1)<<11);
    }
    table += 8;
  }
}


int main(int argc, char **argv)
{
  FILE *f;
  long fsize;
  unsigned char *databuffer;
  const unsigned char *loc;
  size_t type00tab, classtab;

  assert(sizeof(size_t) == 4);

  if (argc != 2) {
    printf("Usage: %s <dumpfile>\n", (argv[0] && *argv[0]) ? argv[0] : "ttdstrextr");
    return EXIT_SUCCESS;
  }

  f = fopen(argv[1], "rb");
  if (!f) return perror(argv[2]), EXIT_FAILURE;

  if (fseek(f, 0, SEEK_END)) return perror("ttdstrextr: can't get file length"), EXIT_FAILURE;
  fsize = ftell(f);
  if (fsize == -1 || fsize != (size_t)fsize) return perror("ttdstrextr: can't get file length"), EXIT_FAILURE;

  databuffer = malloc(fsize);
  if (!databuffer) return fputs("ttdstrextr: malloc failed\n", stderr), EXIT_FAILURE;

  rewind(f);
  if (fread(databuffer, 1, fsize, f) != fsize) return perror("ttdstrextr: read fail"), EXIT_FAILURE;
  fclose(f);

  loc = findbinstring(databuffer, fsize, findtexthandler, sizeof(findtexthandler));
  if (!loc) return fputs("ttdstrextr: search failed\n", stderr), EXIT_FAILURE;

  if (findbinstring(loc + 1, fsize - (loc - databuffer) - 1, findtexthandler, sizeof(findtexthandler))) {
    return fputs("ttdstrextr: search failed\n", stderr), EXIT_FAILURE;
  }

  type00tab = getoffset(loc+TEXT_TYPE_00_OFF);
  classtab = getoffset(loc+OPHANDLERTABLE_OFF);
  printf("# TTD text handler found at 0x%lX\n# main string table at 0x%lX\n# main class table at 0x%lX\n\n",
	 (unsigned long)(loc - databuffer), (unsigned long)type00tab, (unsigned long)classtab);
  printf("# Address    ID   String\n\n");

  processtable(databuffer, type00tab, 0);
  processallclasses(databuffer, classtab);

  return EXIT_SUCCESS;
}

