Index: crash-logger.cpp
===================================================================
--- crash-logger.cpp	(revision 5529ab5b9d198d22e02965d6f804738b4e3f36f5)
+++ crash-logger.cpp	(revision 5529ab5b9d198d22e02965d6f804738b4e3f36f5)
@@ -0,0 +1,276 @@
+#include "crash-logger.hpp"
+
+#include <cstdlib>
+#include <cstdio>
+#include <csignal>
+#include <cstring>
+
+#include <fcntl.h>
+
+#include "Compiler.h"
+#include "Consts.h"
+
+// TODO: Double-check which includes are necessary
+
+#ifdef WINDOWS
+   // Check if this is necessary or lets me remove any windows includes
+   // also check if it's needed in Linux
+   #include <cstdint>
+
+   #include <windows.h>
+   #include <io.h>
+   #include <sys/stat.h>
+
+   #include "FileStackWalker.h"
+
+   // The windows analogues to the unix open, close, and write functions
+   // are _open, _close, and _write. These #defines let me use the same name in all cases.
+   #define open _open
+   #define close _close
+   #define write _write
+
+   #define STDERR_FILENO 2
+
+   bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread);
+#else
+   #include <unistd.h>
+   #include <execinfo.h>
+   #include <cxxabi.h>
+
+   // CHeck if these are needed in Linux
+   //#include <errno.h>
+   //#include <cstring>
+
+   void abortHandler(int signum);
+   static inline void printStackTrace(int fd_out = STDERR_FILENO);
+#endif
+
+void printInfo(int fd_out);
+
+CrashLogger::CrashLogger(int(*mainFunc)(int, char*[]), int argc, char* argv[]) {
+   write(STDERR_FILENO, "Calling main\n", 13);
+
+   #ifdef WINDOWS
+      __try {
+         mainFunc(argc, argv);
+         // maybe do this and call a function inside CrashLogger
+         // In that case, also pass GetCurrentThread() as a parameter to then pass to handleException
+         // I could also move almost all of this into CrashLogger by creating a function in CrashLogger that takes a reference
+         // to the effective main function and, for Windows, wraps it in all this error-handling stuff
+      } __except (handleException(
+            GetExceptionCode(),
+            GetExceptionInformation(),
+            GetCurrentThread())
+            ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER) {
+      }
+   #else
+      // Apparently, sigaction should be used instead for Linux
+      // It gives more info. Check if I should bother switching
+
+      signal(SIGABRT, abortHandler);
+      signal(SIGSEGV, abortHandler);
+      signal(SIGILL, abortHandler);
+      signal(SIGFPE, abortHandler);
+
+      write(STDERR_FILENO, "Handlers attached\n", 18);
+
+      mainFunc(argc, argv);
+   #endif
+}
+
+CrashLogger::~CrashLogger() {
+}
+
+#ifdef WINDOWS
+
+bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread) {
+   int crash_log = open(CRASH_LOG_FILE, O_RDWR | O_CREAT | O_APPEND, _S_IREAD | _S_IWRITE);
+
+   if (crash_log == -1) {
+      // TODO: Figure out exactly what perror does and if I should use it
+      perror("opening crash.log");  // TODO: Figure out exactly what perror does and if I should use it
+   }
+
+   printInfo(crash_log);
+
+   FileStackWalker sw(crash_log == -1 ? STDERR_FILENO  : crash_log);
+
+   if (pExp != NULL) {
+      sw.ShowCallstack(thread, pExp->ContextRecord);
+   } else {
+      write(crash_log, "Could not get the stack trace\n", 30);
+   }
+
+   close(crash_log);
+
+   if (expCode == EXCEPTION_ACCESS_VIOLATION) {
+      write(STDERR_FILENO, "ACCESS VIOLATION\n", 17);
+   }
+
+   return true;
+}
+
+#else
+
+void abortHandler(int signum) {
+   int crash_log = open(CRASH_LOG_FILE, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+
+   if (crash_log == -1) {
+      // TODO: Figure out exactly what perror does and if I should use it
+      perror("opening crash.log"); 
+   }
+
+   printInfo(crash_log);
+
+   printStackTrace(crash_log);
+
+   close(crash_log);
+
+   write(STDERR_FILENO, "The game has crashed. Check crash.log for more info\n", 52);
+
+   exit(signum);
+}
+
+static inline void printStackTrace(int fd_out) {
+   write(fd_out, "stack trace:\n", 13);
+
+   // storage array for stack trace address data
+   void* addrlist[64];
+
+   // retrieve current stack addresses
+   unsigned int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
+
+   if (addrlen == 0) {
+      write(fd_out, "  \n", 3);
+      return;
+   }
+
+   // create readable strings to each frame.
+   char** symbollist = backtrace_symbols(addrlist, addrlen);
+
+   size_t funcnamesize = 1024;
+   char* funcname = (char*)malloc(sizeof(char) * funcnamesize);
+
+   // iterate over the returned symbol lines
+   // skip the first few, since those are printStackTrace, abortHandler,
+   // and a couple others called after the crash
+   for (unsigned int i = 0; i < addrlen; i++) {
+      char* begin_name = NULL;
+      char* begin_offset = NULL;
+      char* end_offset = NULL;
+
+#ifdef MAC
+      for (char *p = symbollist[i]; *p; p++) {
+         if ((*p == '_') && (*(p-1) == ' ')) {
+            begin_name = p-1;
+         } else if (*p == '+') {
+            begin_offset = p-1;
+         }
+      }
+
+      write(fd_out, "  ", 2);
+      if (begin_name && begin_offset && (begin_name < begin_offset)) {
+         *begin_name++ = '\0';
+         *begin_offset++ = '\0';
+
+         // mangled name is now in [begin_name, begin_offset) and caller
+         // offset in [begin_offset, end_offset). now apply
+         // __cxa_demangle():
+         int status;
+         char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
+
+         if (status == 0)  {
+            funcname = ret; // use possibly realloc()-ed string
+            write(fd_out, symbollist[i], strlen(symbollist[i]));
+            write(fd_out, " ", 1);
+            write(fd_out, funcname, strlen(funcname));
+            write(fd_out, " ", 1);
+         } else {
+            // demangling failed. Output function name as a C function with no arguments.
+            write(fd_out, symbollist[i], strlen(symbollist[i]));
+            write(fd_out, " ", 1);
+            write(fd_out, begin_name, strlen(begin_name));
+            write(fd_out, "() ", 3);
+         }
+         write(fd_out, begin_offset, strlen(begin_offset));
+      } else {
+         // couldn't parse the line? print the whole line.
+         write(fd_out, symbollist[i], strlen(symbollist[i]));
+      }
+      write(fd_out, "\n", 1);
+#else
+      for (char *p = symbollist[i]; *p; p++) {
+         if (*p == '(') {
+            begin_name = p;
+         } else if (*p == '+') {
+            begin_offset = p;
+         } else if (*p == ')' && (begin_offset || begin_name)) {
+            end_offset = p;
+         }
+      }
+
+      write(fd_out, "  ", 2);
+      if (begin_name && end_offset && (begin_name < end_offset)) {
+         *begin_name++ = '\0';
+         *end_offset++ = '\0';
+         if (begin_offset) {
+            *begin_offset++ = '\0';
+         }
+
+         // mangled name is now in [begin_name, begin_offset) and caller
+         // offset in [begin_offset, end_offset). now apply
+         // __cxa_demangle():
+         int status;
+         char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
+
+         write(fd_out, symbollist[i], strlen(symbollist[i]));
+         write(fd_out, " ( ", 3);
+         if (status == 0) {
+            write(fd_out, ret, strlen(ret));
+         } else {
+            write(fd_out, begin_name, strlen(begin_name));
+         }
+
+         if (begin_offset) {
+            write(fd_out, " + ", 3);
+            write(fd_out, begin_offset, strlen(begin_offset));
+         } else {
+            write(fd_out, "         ", 9);
+         }
+         write(fd_out, ") ", 1);
+         write(fd_out, end_offset, strlen(end_offset));
+      } else {
+         // couldn't parse the line? print the whole line.
+         write(fd_out, symbollist[i], strlen(symbollist[i]));
+      }
+      write(fd_out, "\n", 1);
+#endif
+   }
+
+   free(funcname);
+   free(symbollist);
+
+   write(fd_out, "End of stack trace\n", 19);
+}
+
+#endif
+
+void printInfo(int fd_out) {
+   write(fd_out, "Game Version: ", 14);
+   write(fd_out, GAME_VERSION, strlen(GAME_VERSION));
+   write(fd_out, "\n", 1);
+
+   write(fd_out, "OS: ", 4);
+   #if defined WINDOWS
+      write(fd_out, "Windows", 7);
+   #elif defined LINUX
+      write(fd_out, "Linux", 5);
+   #elif defined MAC
+      write(fd_out, "Mac", 3);
+   #else
+      write(fd_out, "Unknown", 7);
+   #endif
+   write(fd_out, "\n", 1);
+
+   write(fd_out, "\n", 1);
+}
