text:
#include "Array.h"
#include "Button.h"
#include "DeviceType.h"

#include <stdio.h>

const Button Button_Null                 = 0;
const Button Button_First                = 1;

const Button Button_Keyboard_First       = Button_First;
const Button Button_Keyboard_A           = Button_Keyboard_First + 0;
const Button Button_Keyboard_B           = Button_Keyboard_First + 1;
const Button Button_Keyboard_C           = Button_Keyboard_First + 2;
const Button Button_Keyboard_D           = Button_Keyboard_First + 3;
const Button Button_Keyboard_E           = Button_Keyboard_First + 4;
const Button Button_Keyboard_F           = Button_Keyboard_First + 5;
const Button Button_Keyboard_G           = Button_Keyboard_First + 6;
const Button Button_Keyboard_H           = Button_Keyboard_First + 7;
const Button Button_Keyboard_I           = Button_Keyboard_First + 8;
const Button Button_Keyboard_J           = Button_Keyboard_First + 9;
const Button Button_Keyboard_K           = Button_Keyboard_First + 10;
const Button Button_Keyboard_L           = Button_Keyboard_First + 11;
const Button Button_Keyboard_M           = Button_Keyboard_First + 12;
const Button Button_Keyboard_N           = Button_Keyboard_First + 13;
const Button Button_Keyboard_O           = Button_Keyboard_First + 14;
const Button Button_Keyboard_P           = Button_Keyboard_First + 15;
const Button Button_Keyboard_Q           = Button_Keyboard_First + 16;
const Button Button_Keyboard_R           = Button_Keyboard_First + 17;
const Button Button_Keyboard_S           = Button_Keyboard_First + 18;
const Button Button_Keyboard_T           = Button_Keyboard_First + 19;
const Button Button_Keyboard_U           = Button_Keyboard_First + 20;
const Button Button_Keyboard_V           = Button_Keyboard_First + 21;
const Button Button_Keyboard_W           = Button_Keyboard_First + 22;
const Button Button_Keyboard_X           = Button_Keyboard_First + 23;
const Button Button_Keyboard_Y           = Button_Keyboard_First + 24;
const Button Button_Keyboard_Z           = Button_Keyboard_First + 25;
const Button Button_Keyboard_N0          = Button_Keyboard_First + 26;
const Button Button_Keyboard_N1          = Button_Keyboard_First + 27;
const Button Button_Keyboard_N2          = Button_Keyboard_First + 28;
const Button Button_Keyboard_N3          = Button_Keyboard_First + 29;
const Button Button_Keyboard_N4          = Button_Keyboard_First + 30;
const Button Button_Keyboard_N5          = Button_Keyboard_First + 31;
const Button Button_Keyboard_N6          = Button_Keyboard_First + 32;
const Button Button_Keyboard_N7          = Button_Keyboard_First + 33;
const Button Button_Keyboard_N8          = Button_Keyboard_First + 34;
const Button Button_Keyboard_N9          = Button_Keyboard_First + 35;
const Button Button_Keyboard_F1          = Button_Keyboard_First + 36;
const Button Button_Keyboard_F2          = Button_Keyboard_First + 37;
const Button Button_Keyboard_F3          = Button_Keyboard_First + 38;
const Button Button_Keyboard_F4          = Button_Keyboard_First + 39;
const Button Button_Keyboard_F5          = Button_Keyboard_First + 40;
const Button Button_Keyboard_F6          = Button_Keyboard_First + 41;
const Button Button_Keyboard_F7          = Button_Keyboard_First + 42;
const Button Button_Keyboard_F8          = Button_Keyboard_First + 43;
const Button Button_Keyboard_F9          = Button_Keyboard_First + 44;
const Button Button_Keyboard_F10         = Button_Keyboard_First + 45;
const Button Button_Keyboard_F11         = Button_Keyboard_First + 46;
const Button Button_Keyboard_F12         = Button_Keyboard_First + 47;
const Button Button_Keyboard_F13         = Button_Keyboard_First + 48;
const Button Button_Keyboard_F14         = Button_Keyboard_First + 49;
const Button Button_Keyboard_F15         = Button_Keyboard_First + 50;
const Button Button_Keyboard_F16         = Button_Keyboard_First + 51;
const Button Button_Keyboard_F17         = Button_Keyboard_First + 52;
const Button Button_Keyboard_F18         = Button_Keyboard_First + 53;
const Button Button_Keyboard_F19         = Button_Keyboard_First + 54;
const Button Button_Keyboard_F20         = Button_Keyboard_First + 55;
const Button Button_Keyboard_F21         = Button_Keyboard_First + 56;
const Button Button_Keyboard_F22         = Button_Keyboard_First + 57;
const Button Button_Keyboard_F23         = Button_Keyboard_First + 58;
const Button Button_Keyboard_F24         = Button_Keyboard_First + 59;
const Button Button_Keyboard_KP0         = Button_Keyboard_First + 60;
const Button Button_Keyboard_KP1         = Button_Keyboard_First + 61;
const Button Button_Keyboard_KP2         = Button_Keyboard_First + 62;
const Button Button_Keyboard_KP3         = Button_Keyboard_First + 63;
const Button Button_Keyboard_KP4         = Button_Keyboard_First + 64;
const Button Button_Keyboard_KP5         = Button_Keyboard_First + 65;
const Button Button_Keyboard_KP6         = Button_Keyboard_First + 66;
const Button Button_Keyboard_KP7         = Button_Keyboard_First + 67;
const Button Button_Keyboard_KP8         = Button_Keyboard_First + 68;
const Button Button_Keyboard_KP9         = Button_Keyboard_First + 69;
const Button Button_Keyboard_KPNumLock   = Button_Keyboard_First + 70;
const Button Button_Keyboard_KPDivide    = Button_Keyboard_First + 71;
const Button Button_Keyboard_KPMultiply  = Button_Keyboard_First + 72;
const Button Button_Keyboard_KPSubtract  = Button_Keyboard_First + 73;
const Button Button_Keyboard_KPAdd       = Button_Keyboard_First + 74;
const Button Button_Keyboard_KPEnter     = Button_Keyboard_First + 75;
const Button Button_Keyboard_KPDecimal   = Button_Keyboard_First + 76;
const Button Button_Keyboard_Backspace   = Button_Keyboard_First + 77;
const Button Button_Keyboard_Escape      = Button_Keyboard_First + 78;
const Button Button_Keyboard_Return      = Button_Keyboard_First + 79;
const Button Button_Keyboard_Space       = Button_Keyboard_First + 80;
const Button Button_Keyboard_Tab         = Button_Keyboard_First + 81;
const Button Button_Keyboard_Backtick    = Button_Keyboard_First + 82;
const Button Button_Keyboard_CapsLock    = Button_Keyboard_First + 83;
const Button Button_Keyboard_Minus       = Button_Keyboard_First + 84;
const Button Button_Keyboard_Equals      = Button_Keyboard_First + 85;
const Button Button_Keyboard_LBracket    = Button_Keyboard_First + 86;
const Button Button_Keyboard_RBracket    = Button_Keyboard_First + 87;
const Button Button_Keyboard_Backslash   = Button_Keyboard_First + 88;
const Button Button_Keyboard_Semicolon   = Button_Keyboard_First + 89;
const Button Button_Keyboard_Apostrophe  = Button_Keyboard_First + 90;
const Button Button_Keyboard_Comma       = Button_Keyboard_First + 91;
const Button Button_Keyboard_Period      = Button_Keyboard_First + 92;
const Button Button_Keyboard_Slash       = Button_Keyboard_First + 93;
const Button Button_Keyboard_PrintScreen = Button_Keyboard_First + 94;
const Button Button_Keyboard_ScrollLock  = Button_Keyboard_First + 95;
const Button Button_Keyboard_Pause       = Button_Keyboard_First + 96;
const Button Button_Keyboard_Insert      = Button_Keyboard_First + 97;
const Button Button_Keyboard_Delete      = Button_Keyboard_First + 98;
const Button Button_Keyboard_Home        = Button_Keyboard_First + 99;
const Button Button_Keyboard_End         = Button_Keyboard_First + 100;
const Button Button_Keyboard_PageUp      = Button_Keyboard_First + 101;
const Button Button_Keyboard_PageDown    = Button_Keyboard_First + 102;
const Button Button_Keyboard_Right       = Button_Keyboard_First + 103;
const Button Button_Keyboard_Left        = Button_Keyboard_First + 104;
const Button Button_Keyboard_Down        = Button_Keyboard_First + 105;
const Button Button_Keyboard_Up          = Button_Keyboard_First + 106;
const Button Button_Keyboard_LCtrl       = Button_Keyboard_First + 107;
const Button Button_Keyboard_LShift      = Button_Keyboard_First + 108;
const Button Button_Keyboard_LAlt        = Button_Keyboard_First + 109;
const Button Button_Keyboard_LMeta       = Button_Keyboard_First + 110;
const Button Button_Keyboard_RCtrl       = Button_Keyboard_First + 111;
const Button Button_Keyboard_RShift      = Button_Keyboard_First + 112;
const Button Button_Keyboard_RAlt        = Button_Keyboard_First + 113;
const Button Button_Keyboard_RMeta       = Button_Keyboard_First + 114;
const Button Button_Keyboard_Last        = Button_Keyboard_First + 115;

const Button Button_Mouse_First          = Button_Keyboard_Last + 1;
const Button Button_Mouse_Left           = Button_Mouse_First + 0;
const Button Button_Mouse_Middle         = Button_Mouse_First + 1;
const Button Button_Mouse_Right          = Button_Mouse_First + 2;
const Button Button_Mouse_X1             = Button_Mouse_First + 3;
const Button Button_Mouse_X2             = Button_Mouse_First + 4;
const Button Button_Mouse_X              = Button_Mouse_First + 5;
const Button Button_Mouse_Y              = Button_Mouse_First + 6;
const Button Button_Mouse_ScrollX        = Button_Mouse_First + 7;
const Button Button_Mouse_ScrollY        = Button_Mouse_First + 8;
const Button Button_Mouse_Last           = Button_Mouse_First + 8;

const Button Button_Gamepad_First        = Button_Mouse_Last + 1;
const Button Button_Gamepad_Button_First = Button_Gamepad_First + 0;
const Button Button_Gamepad_A            = Button_Gamepad_First + 0;
const Button Button_Gamepad_B            = Button_Gamepad_First + 1;
const Button Button_Gamepad_X            = Button_Gamepad_First + 2;
const Button Button_Gamepad_Y            = Button_Gamepad_First + 3;
const Button Button_Gamepad_Back         = Button_Gamepad_First + 4;
const Button Button_Gamepad_Guide        = Button_Gamepad_First + 5;
const Button Button_Gamepad_Start        = Button_Gamepad_First + 6;
const Button Button_Gamepad_LStick       = Button_Gamepad_First + 7;
const Button Button_Gamepad_RStick       = Button_Gamepad_First + 8;
const Button Button_Gamepad_LBumper      = Button_Gamepad_First + 9;
const Button Button_Gamepad_RBumper      = Button_Gamepad_First + 10;
const Button Button_Gamepad_Up           = Button_Gamepad_First + 11;
const Button Button_Gamepad_Down         = Button_Gamepad_First + 12;
const Button Button_Gamepad_Left         = Button_Gamepad_First + 13;
const Button Button_Gamepad_Right        = Button_Gamepad_First + 14;
const Button Button_Gamepad_Button_Last  = Button_Gamepad_First + 14;
const Button Button_Gamepad_Axis_First   = Button_Gamepad_First + 15;
const Button Button_Gamepad_LTrigger     = Button_Gamepad_First + 15;
const Button Button_Gamepad_RTrigger     = Button_Gamepad_First + 16;
const Button Button_Gamepad_LStickX      = Button_Gamepad_First + 17;
const Button Button_Gamepad_LStickY      = Button_Gamepad_First + 18;
const Button Button_Gamepad_RStickX      = Button_Gamepad_First + 19;
const Button Button_Gamepad_RStickY      = Button_Gamepad_First + 20;
const Button Button_Gamepad_Axis_Last    = Button_Gamepad_First + 20;
const Button Button_Gamepad_Last         = Button_Gamepad_First + 20;

const Button Button_System_First         = Button_Gamepad_Last + 1;
const Button Button_System_Exit          = Button_System_First + 0;
const Button Button_System_Last          = Button_System_First + 0;

const Button Button_Last                 = Button_System_Last;

DeviceType Button_ToDeviceType (Button button) {
       if (button == Button_Null)          return DeviceType_Null;
  else if (button <= Button_Keyboard_Last) return DeviceType_Keyboard;
  else if (button <= Button_Mouse_Last)    return DeviceType_Mouse;
  else if (button <= Button_Gamepad_Last)  return DeviceType_Gamepad;
  else if (button <= Button_System_Last)   return DeviceType_Null;
  else {
    Fatal("Button_ToDeviceType: Unknown Button: %i", button);
    return DeviceType_Null;
  }
}

cstr Button_ToString (Button button) {
  switch (button) {
    default:
      static char buffer[512];
      snprintf(buffer, (size_t) Array_GetSize(buffer), "Unknown (%i)", button);
      return buffer;

      case Button_Null:                 return "Button_Null";
      case Button_Keyboard_A:           return "Button_Keyboard_A";
      case Button_Keyboard_B:           return "Button_Keyboard_B";
      case Button_Keyboard_C:           return "Button_Keyboard_C";
      case Button_Keyboard_D:           return "Button_Keyboard_D";
      case Button_Keyboard_E:           return "Button_Keyboard_E";
      case Button_Keyboard_F:           return "Button_Keyboard_F";
      case Button_Keyboard_G:           return "Button_Keyboard_G";
      case Button_Keyboard_H:           return "Button_Keyboard_H";
      case Button_Keyboard_I:           return "Button_Keyboard_I";
      case Button_Keyboard_J:           return "Button_Keyboard_J";
      case Button_Keyboard_K:           return "Button_Keyboard_K";
      case Button_Keyboard_L:           return "Button_Keyboard_L";
      case Button_Keyboard_M:           return "Button_Keyboard_M";
      case Button_Keyboard_N:           return "Button_Keyboard_N";
      case Button_Keyboard_O:           return "Button_Keyboard_O";
      case Button_Keyboard_P:           return "Button_Keyboard_P";
      case Button_Keyboard_Q:           return "Button_Keyboard_Q";
      case Button_Keyboard_R:           return "Button_Keyboard_R";
      case Button_Keyboard_S:           return "Button_Keyboard_S";
      case Button_Keyboard_T:           return "Button_Keyboard_T";
      case Button_Keyboard_U:           return "Button_Keyboard_U";
      case Button_Keyboard_V:           return "Button_Keyboard_V";
      case Button_Keyboard_W:           return "Button_Keyboard_W";
      case Button_Keyboard_X:           return "Button_Keyboard_X";
      case Button_Keyboard_Y:           return "Button_Keyboard_Y";
      case Button_Keyboard_Z:           return "Button_Keyboard_Z";
      case Button_Keyboard_N0:          return "Button_Keyboard_N0";
      case Button_Keyboard_N1:          return "Button_Keyboard_N1";
      case Button_Keyboard_N2:          return "Button_Keyboard_N2";
      case Button_Keyboard_N3:          return "Button_Keyboard_N3";
      case Button_Keyboard_N4:          return "Button_Keyboard_N4";
      case Button_Keyboard_N5:          return "Button_Keyboard_N5";
      case Button_Keyboard_N6:          return "Button_Keyboard_N6";
      case Button_Keyboard_N7:          return "Button_Keyboard_N7";
      case Button_Keyboard_N8:          return "Button_Keyboard_N8";
      case Button_Keyboard_N9:          return "Button_Keyboard_N9";
      case Button_Keyboard_F1:          return "Button_Keyboard_F1";
      case Button_Keyboard_F2:          return "Button_Keyboard_F2";
      case Button_Keyboard_F3:          return "Button_Keyboard_F3";
      case Button_Keyboard_F4:          return "Button_Keyboard_F4";
      case Button_Keyboard_F5:          return "Button_Keyboard_F5";
      case Button_Keyboard_F6:          return "Button_Keyboard_F6";
      case Button_Keyboard_F7:          return "Button_Keyboard_F7";
      case Button_Keyboard_F8:          return "Button_Keyboard_F8";
      case Button_Keyboard_F9:          return "Button_Keyboard_F9";
      case Button_Keyboard_F10:         return "Button_Keyboard_F10";
      case Button_Keyboard_F11:         return "Button_Keyboard_F11";
      case Button_Keyboard_F12:         return "Button_Keyboard_F12";
      case Button_Keyboard_F13:         return "Button_Keyboard_F13";
      case Button_Keyboard_F14:         return "Button_Keyboard_F14";
      case Button_Keyboard_F15:         return "Button_Keyboard_F15";
      case Button_Keyboard_F16:         return "Button_Keyboard_F16";
      case Button_Keyboard_F17:         return "Button_Keyboard_F17";
      case Button_Keyboard_F18:         return "Button_Keyboard_F18";
      case Button_Keyboard_F19:         return "Button_Keyboard_F19";
      case Button_Keyboard_F20:         return "Button_Keyboard_F20";
      case Button_Keyboard_F21:         return "Button_Keyboard_F21";
      case Button_Keyboard_F22:         return "Button_Keyboard_F22";
      case Button_Keyboard_F23:         return "Button_Keyboard_F23";
      case Button_Keyboard_F24:         return "Button_Keyboard_F24";
      case Button_Keyboard_KP0:         return "Button_Keyboard_KP0";
      case Button_Keyboard_KP1:         return "Button_Keyboard_KP1";
      case Button_Keyboard_KP2:         return "Button_Keyboard_KP2";
      case Button_Keyboard_KP3:         return "Button_Keyboard_KP3";
      case Button_Keyboard_KP4:         return "Button_Keyboard_KP4";
      case Button_Keyboard_KP5:         return "Button_Keyboard_KP5";
      case Button_Keyboard_KP6:         return "Button_Keyboard_KP6";
      case Button_Keyboard_KP7:         return "Button_Keyboard_KP7";
      case Button_Keyboard_KP8:         return "Button_Keyboard_KP8";
      case Button_Keyboard_KP9:         return "Button_Keyboard_KP9";
      case Button_Keyboard_KPNumLock:   return "Button_Keyboard_KPNumLock";
      case Button_Keyboard_KPDivide:    return "Button_Keyboard_KPDivide";
      case Button_Keyboard_KPMultiply:  return "Button_Keyboard_KPMultiply";
      case Button_Keyboard_KPSubtract:  return "Button_Keyboard_KPSubtract";
      case Button_Keyboard_KPAdd:       return "Button_Keyboard_KPAdd";
      case Button_Keyboard_KPEnter:     return "Button_Keyboard_KPEnter";
      case Button_Keyboard_KPDecimal:   return "Button_Keyboard_KPDecimal";
      case Button_Keyboard_Backspace:   return "Button_Keyboard_Backspace";
      case Button_Keyboard_Escape:      return "Button_Keyboard_Escape";
      case Button_Keyboard_Return:      return "Button_Keyboard_Return";
      case Button_Keyboard_Space:       return "Button_Keyboard_Space";
      case Button_Keyboard_Tab:         return "Button_Keyboard_Tab";
      case Button_Keyboard_Backtick:    return "Button_Keyboard_Backtick";
      case Button_Keyboard_CapsLock:    return "Button_Keyboard_CapsLock";
      case Button_Keyboard_Minus:       return "Button_Keyboard_Minus";
      case Button_Keyboard_Equals:      return "Button_Keyboard_Equals";
      case Button_Keyboard_LBracket:    return "Button_Keyboard_LBracket";
      case Button_Keyboard_RBracket:    return "Button_Keyboard_RBracket";
      case Button_Keyboard_Backslash:   return "Button_Keyboard_Backslash";
      case Button_Keyboard_Semicolon:   return "Button_Keyboard_Semicolon";
      case Button_Keyboard_Apostrophe:  return "Button_Keyboard_Apostrophe";
      case Button_Keyboard_Comma:       return "Button_Keyboard_Comma";
      case Button_Keyboard_Period:      return "Button_Keyboard_Period";
      case Button_Keyboard_Slash:       return "Button_Keyboard_Slash";
      case Button_Keyboard_PrintScreen: return "Button_Keyboard_PrintScreen";
      case Button_Keyboard_ScrollLock:  return "Button_Keyboard_ScrollLock";
      case Button_Keyboard_Pause:       return "Button_Keyboard_Pause";
      case Button_Keyboard_Insert:      return "Button_Keyboard_Insert";
      case Button_Keyboard_Delete:      return "Button_Keyboard_Delete";
      case Button_Keyboard_Home:        return "Button_Keyboard_Home";
      case Button_Keyboard_End:         return "Button_Keyboard_End";
      case Button_Keyboard_PageUp:      return "Button_Keyboard_PageUp";
      case Button_Keyboard_PageDown:    return "Button_Keyboard_PageDown";
      case Button_Keyboard_Right:       return "Button_Keyboard_Right";
      case Button_Keyboard_Left:        return "Button_Keyboard_Left";
      case Button_Keyboard_Down:        return "Button_Keyboard_Down";
      case Button_Keyboard_Up:          return "Button_Keyboard_Up";
      case Button_Keyboard_LCtrl:       return "Button_Keyboard_LCtrl";
      case Button_Keyboard_LShift:      return "Button_Keyboard_LShift";
      case Button_Keyboard_LAlt:        return "Button_Keyboard_LAlt";
      case Button_Keyboard_LMeta:       return "Button_Keyboard_LMeta";
      case Button_Keyboard_RCtrl:       return "Button_Keyboard_RCtrl";
      case Button_Keyboard_RShift:      return "Button_Keyboard_RShift";
      case Button_Keyboard_RAlt:        return "Button_Keyboard_RAlt";
      case Button_Keyboard_RMeta:       return "Button_Keyboard_RMeta";
      case Button_Mouse_Left:           return "Button_Mouse_Left";
      case Button_Mouse_Middle:         return "Button_Mouse_Middle";
      case Button_Mouse_Right:          return "Button_Mouse_Right";
      case Button_Mouse_X1:             return "Button_Mouse_X1";
      case Button_Mouse_X2:             return "Button_Mouse_X2";
      case Button_Mouse_X:              return "Button_Mouse_X";
      case Button_Mouse_Y:              return "Button_Mouse_Y";
      case Button_Mouse_ScrollX:        return "Button_Mouse_ScrollX";
      case Button_Mouse_ScrollY:        return "Button_Mouse_ScrollY";
      case Button_Gamepad_A:            return "Button_Gamepad_A";
      case Button_Gamepad_B:            return "Button_Gamepad_B";
      case Button_Gamepad_X:            return "Button_Gamepad_X";
      case Button_Gamepad_Y:            return "Button_Gamepad_Y";
      case Button_Gamepad_Back:         return "Button_Gamepad_Back";
      case Button_Gamepad_Guide:        return "Button_Gamepad_Guide";
      case Button_Gamepad_Start:        return "Button_Gamepad_Start";
      case Button_Gamepad_LStick:       return "Button_Gamepad_LStick";
      case Button_Gamepad_RStick:       return "Button_Gamepad_RStick";
      case Button_Gamepad_LBumper:      return "Button_Gamepad_LBumper";
      case Button_Gamepad_RBumper:      return "Button_Gamepad_RBumper";
      case Button_Gamepad_Up:           return "Button_Gamepad_Up";
      case Button_Gamepad_Down:         return "Button_Gamepad_Down";
      case Button_Gamepad_Left:         return "Button_Gamepad_Left";
      case Button_Gamepad_Right:        return "Button_Gamepad_Right";
      case Button_Gamepad_LTrigger:     return "Button_Gamepad_LTrigger";
      case Button_Gamepad_RTrigger:     return "Button_Gamepad_RTrigger";
      case Button_Gamepad_LStickX:      return "Button_Gamepad_LStickX";
      case Button_Gamepad_LStickY:      return "Button_Gamepad_LStickY";
      case Button_Gamepad_RStickX:      return "Button_Gamepad_RStickX";
      case Button_Gamepad_RStickY:      return "Button_Gamepad_RStickY";
      case Button_System_Exit:          return "Button_System_Exit";
  }
}

bool Button_IsAutoRelease (Button button) {
  switch (button) {
    default:
      return false;

    case Button_Mouse_ScrollX:
    case Button_Mouse_ScrollY:
    case Button_System_Exit:
      return true;
  }
}

Button Button_FromSDLScancode (SDL_Scancode scancode) {
  switch(scancode) {
    /* NOTE : We don't support every possible scan code. */
    default: return Button_Null;

    case SDL_SCANCODE_UNKNOWN:      return Button_Null;
    case SDL_SCANCODE_A:            return Button_Keyboard_A;
    case SDL_SCANCODE_B:            return Button_Keyboard_B;
    case SDL_SCANCODE_C:            return Button_Keyboard_C;
    case SDL_SCANCODE_D:            return Button_Keyboard_D;
    case SDL_SCANCODE_E:            return Button_Keyboard_E;
    case SDL_SCANCODE_F:            return Button_Keyboard_F;
    case SDL_SCANCODE_G:            return Button_Keyboard_G;
    case SDL_SCANCODE_H:            return Button_Keyboard_H;
    case SDL_SCANCODE_I:            return Button_Keyboard_I;
    case SDL_SCANCODE_J:            return Button_Keyboard_J;
    case SDL_SCANCODE_K:            return Button_Keyboard_K;
    case SDL_SCANCODE_L:            return Button_Keyboard_L;
    case SDL_SCANCODE_M:            return Button_Keyboard_M;
    case SDL_SCANCODE_N:            return Button_Keyboard_N;
    case SDL_SCANCODE_O:            return Button_Keyboard_O;
    case SDL_SCANCODE_P:            return Button_Keyboard_P;
    case SDL_SCANCODE_Q:            return Button_Keyboard_Q;
    case SDL_SCANCODE_R:            return Button_Keyboard_R;
    case SDL_SCANCODE_S:            return Button_Keyboard_S;
    case SDL_SCANCODE_T:            return Button_Keyboard_T;
    case SDL_SCANCODE_U:            return Button_Keyboard_U;
    case SDL_SCANCODE_V:            return Button_Keyboard_V;
    case SDL_SCANCODE_W:            return Button_Keyboard_W;
    case SDL_SCANCODE_X:            return Button_Keyboard_X;
    case SDL_SCANCODE_Y:            return Button_Keyboard_Y;
    case SDL_SCANCODE_Z:            return Button_Keyboard_Z;
    case SDL_SCANCODE_0:            return Button_Keyboard_N0;
    case SDL_SCANCODE_1:            return Button_Keyboard_N1;
    case SDL_SCANCODE_2:            return Button_Keyboard_N2;
    case SDL_SCANCODE_3:            return Button_Keyboard_N3;
    case SDL_SCANCODE_4:            return Button_Keyboard_N4;
    case SDL_SCANCODE_5:            return Button_Keyboard_N5;
    case SDL_SCANCODE_6:            return Button_Keyboard_N6;
    case SDL_SCANCODE_7:            return Button_Keyboard_N7;
    case SDL_SCANCODE_8:            return Button_Keyboard_N8;
    case SDL_SCANCODE_9:            return Button_Keyboard_N9;
    case SDL_SCANCODE_F1:           return Button_Keyboard_F1;
    case SDL_SCANCODE_F2:           return Button_Keyboard_F2;
    case SDL_SCANCODE_F3:           return Button_Keyboard_F3;
    case SDL_SCANCODE_F4:           return Button_Keyboard_F4;
    case SDL_SCANCODE_F5:           return Button_Keyboard_F5;
    case SDL_SCANCODE_F6:           return Button_Keyboard_F6;
    case SDL_SCANCODE_F7:           return Button_Keyboard_F7;
    case SDL_SCANCODE_F8:           return Button_Keyboard_F8;
    case SDL_SCANCODE_F9:           return Button_Keyboard_F9;
    case SDL_SCANCODE_F10:          return Button_Keyboard_F10;
    case SDL_SCANCODE_F11:          return Button_Keyboard_F11;
    case SDL_SCANCODE_F12:          return Button_Keyboard_F12;
    case SDL_SCANCODE_F13:          return Button_Keyboard_F13;
    case SDL_SCANCODE_F14:          return Button_Keyboard_F14;
    case SDL_SCANCODE_F15:          return Button_Keyboard_F15;
    case SDL_SCANCODE_F16:          return Button_Keyboard_F16;
    case SDL_SCANCODE_F17:          return Button_Keyboard_F17;
    case SDL_SCANCODE_F18:          return Button_Keyboard_F18;
    case SDL_SCANCODE_F19:          return Button_Keyboard_F19;
    case SDL_SCANCODE_F20:          return Button_Keyboard_F20;
    case SDL_SCANCODE_F21:          return Button_Keyboard_F21;
    case SDL_SCANCODE_F22:          return Button_Keyboard_F22;
    case SDL_SCANCODE_F23:          return Button_Keyboard_F23;
    case SDL_SCANCODE_F24:          return Button_Keyboard_F24;
    case SDL_SCANCODE_KP_0:         return Button_Keyboard_KP0;
    case SDL_SCANCODE_KP_1:         return Button_Keyboard_KP1;
    case SDL_SCANCODE_KP_2:         return Button_Keyboard_KP2;
    case SDL_SCANCODE_KP_3:         return Button_Keyboard_KP3;
    case SDL_SCANCODE_KP_4:         return Button_Keyboard_KP4;
    case SDL_SCANCODE_KP_5:         return Button_Keyboard_KP5;
    case SDL_SCANCODE_KP_6:         return Button_Keyboard_KP6;
    case SDL_SCANCODE_KP_7:         return Button_Keyboard_KP7;
    case SDL_SCANCODE_KP_8:         return Button_Keyboard_KP8;
    case SDL_SCANCODE_KP_9:         return Button_Keyboard_KP9;
    case SDL_SCANCODE_NUMLOCKCLEAR: return Button_Keyboard_KPNumLock;
    case SDL_SCANCODE_KP_DIVIDE:    return Button_Keyboard_KPDivide;
    case SDL_SCANCODE_KP_MULTIPLY:  return Button_Keyboard_KPMultiply;
    case SDL_SCANCODE_KP_MINUS:     return Button_Keyboard_KPSubtract;
    case SDL_SCANCODE_KP_PLUS:      return Button_Keyboard_KPAdd;
    case SDL_SCANCODE_KP_ENTER:     return Button_Keyboard_KPEnter;
    case SDL_SCANCODE_KP_DECIMAL:   return Button_Keyboard_KPDecimal;
    case SDL_SCANCODE_BACKSPACE:    return Button_Keyboard_Backspace;
    case SDL_SCANCODE_ESCAPE:       return Button_Keyboard_Escape;
    case SDL_SCANCODE_RETURN:       return Button_Keyboard_Return;
    case SDL_SCANCODE_SPACE:        return Button_Keyboard_Space;
    case SDL_SCANCODE_TAB:          return Button_Keyboard_Tab;
    case SDL_SCANCODE_GRAVE:        return Button_Keyboard_Backtick;
    case SDL_SCANCODE_CAPSLOCK:     return Button_Keyboard_CapsLock;
    case SDL_SCANCODE_MINUS:        return Button_Keyboard_Minus;
    case SDL_SCANCODE_EQUALS:       return Button_Keyboard_Equals;
    case SDL_SCANCODE_LEFTBRACKET:  return Button_Keyboard_LBracket;
    case SDL_SCANCODE_RIGHTBRACKET: return Button_Keyboard_RBracket;
    case SDL_SCANCODE_BACKSLASH:    return Button_Keyboard_Backslash;
    case SDL_SCANCODE_SEMICOLON:    return Button_Keyboard_Semicolon;
    case SDL_SCANCODE_APOSTROPHE:   return Button_Keyboard_Apostrophe;
    case SDL_SCANCODE_COMMA:        return Button_Keyboard_Comma;
    case SDL_SCANCODE_PERIOD:       return Button_Keyboard_Period;
    case SDL_SCANCODE_SLASH:        return Button_Keyboard_Slash;
    case SDL_SCANCODE_PRINTSCREEN:  return Button_Keyboard_PrintScreen;
    case SDL_SCANCODE_SCROLLLOCK:   return Button_Keyboard_ScrollLock;
    case SDL_SCANCODE_PAUSE:        return Button_Keyboard_Pause;
    case SDL_SCANCODE_INSERT:       return Button_Keyboard_Insert;
    case SDL_SCANCODE_DELETE:       return Button_Keyboard_Delete;
    case SDL_SCANCODE_HOME:         return Button_Keyboard_Home;
    case SDL_SCANCODE_END:          return Button_Keyboard_End;
    case SDL_SCANCODE_PAGEUP:       return Button_Keyboard_PageUp;
    case SDL_SCANCODE_PAGEDOWN:     return Button_Keyboard_PageDown;
    case SDL_SCANCODE_RIGHT:        return Button_Keyboard_Right;
    case SDL_SCANCODE_LEFT:         return Button_Keyboard_Left;
    case SDL_SCANCODE_DOWN:         return Button_Keyboard_Down;
    case SDL_SCANCODE_UP:           return Button_Keyboard_Up;
    case SDL_SCANCODE_LCTRL:        return Button_Keyboard_LCtrl;
    case SDL_SCANCODE_LSHIFT:       return Button_Keyboard_LShift;
    case SDL_SCANCODE_LALT:         return Button_Keyboard_LAlt;
    case SDL_SCANCODE_LGUI:         return Button_Keyboard_LMeta;
    case SDL_SCANCODE_RCTRL:        return Button_Keyboard_RCtrl;
    case SDL_SCANCODE_RSHIFT:       return Button_Keyboard_RShift;
    case SDL_SCANCODE_RALT:         return Button_Keyboard_RAlt;
    case SDL_SCANCODE_RGUI:         return Button_Keyboard_RMeta;
  }
}

SDL_Scancode Button_ToSDLScancode (Button button) {
  switch(button) {
    default: Fatal("Button_ToSDLScancode: Unhandled case: %i", button);

    case Button_Null:                 return SDL_SCANCODE_UNKNOWN;
    case Button_Keyboard_A:           return SDL_SCANCODE_A;
    case Button_Keyboard_B:           return SDL_SCANCODE_B;
    case Button_Keyboard_C:           return SDL_SCANCODE_C;
    case Button_Keyboard_D:           return SDL_SCANCODE_D;
    case Button_Keyboard_E:           return SDL_SCANCODE_E;
    case Button_Keyboard_F:           return SDL_SCANCODE_F;
    case Button_Keyboard_G:           return SDL_SCANCODE_G;
    case Button_Keyboard_H:           return SDL_SCANCODE_H;
    case Button_Keyboard_I:           return SDL_SCANCODE_I;
    case Button_Keyboard_J:           return SDL_SCANCODE_J;
    case Button_Keyboard_K:           return SDL_SCANCODE_K;
    case Button_Keyboard_L:           return SDL_SCANCODE_L;
    case Button_Keyboard_M:           return SDL_SCANCODE_M;
    case Button_Keyboard_N:           return SDL_SCANCODE_N;
    case Button_Keyboard_O:           return SDL_SCANCODE_O;
    case Button_Keyboard_P:           return SDL_SCANCODE_P;
    case Button_Keyboard_Q:           return SDL_SCANCODE_Q;
    case Button_Keyboard_R:           return SDL_SCANCODE_R;
    case Button_Keyboard_S:           return SDL_SCANCODE_S;
    case Button_Keyboard_T:           return SDL_SCANCODE_T;
    case Button_Keyboard_U:           return SDL_SCANCODE_U;
    case Button_Keyboard_V:           return SDL_SCANCODE_V;
    case Button_Keyboard_W:           return SDL_SCANCODE_W;
    case Button_Keyboard_X:           return SDL_SCANCODE_X;
    case Button_Keyboard_Y:           return SDL_SCANCODE_Y;
    case Button_Keyboard_Z:           return SDL_SCANCODE_Z;
    case Button_Keyboard_N0:          return SDL_SCANCODE_0;
    case Button_Keyboard_N1:          return SDL_SCANCODE_1;
    case Button_Keyboard_N2:          return SDL_SCANCODE_2;
    case Button_Keyboard_N3:          return SDL_SCANCODE_3;
    case Button_Keyboard_N4:          return SDL_SCANCODE_4;
    case Button_Keyboard_N5:          return SDL_SCANCODE_5;
    case Button_Keyboard_N6:          return SDL_SCANCODE_6;
    case Button_Keyboard_N7:          return SDL_SCANCODE_7;
    case Button_Keyboard_N8:          return SDL_SCANCODE_8;
    case Button_Keyboard_N9:          return SDL_SCANCODE_9;
    case Button_Keyboard_F1:          return SDL_SCANCODE_F1;
    case Button_Keyboard_F2:          return SDL_SCANCODE_F2;
    case Button_Keyboard_F3:          return SDL_SCANCODE_F3;
    case Button_Keyboard_F4:          return SDL_SCANCODE_F4;
    case Button_Keyboard_F5:          return SDL_SCANCODE_F5;
    case Button_Keyboard_F6:          return SDL_SCANCODE_F6;
    case Button_Keyboard_F7:          return SDL_SCANCODE_F7;
    case Button_Keyboard_F8:          return SDL_SCANCODE_F8;
    case Button_Keyboard_F9:          return SDL_SCANCODE_F9;
    case Button_Keyboard_F10:         return SDL_SCANCODE_F10;
    case Button_Keyboard_F11:         return SDL_SCANCODE_F11;
    case Button_Keyboard_F12:         return SDL_SCANCODE_F12;
    case Button_Keyboard_F13:         return SDL_SCANCODE_F13;
    case Button_Keyboard_F14:         return SDL_SCANCODE_F14;
    case Button_Keyboard_F15:         return SDL_SCANCODE_F15;
    case Button_Keyboard_F16:         return SDL_SCANCODE_F16;
    case Button_Keyboard_F17:         return SDL_SCANCODE_F17;
    case Button_Keyboard_F18:         return SDL_SCANCODE_F18;
    case Button_Keyboard_F19:         return SDL_SCANCODE_F19;
    case Button_Keyboard_F20:         return SDL_SCANCODE_F20;
    case Button_Keyboard_F21:         return SDL_SCANCODE_F21;
    case Button_Keyboard_F22:         return SDL_SCANCODE_F22;
    case Button_Keyboard_F23:         return SDL_SCANCODE_F23;
    case Button_Keyboard_F24:         return SDL_SCANCODE_F24;
    case Button_Keyboard_KP0:         return SDL_SCANCODE_KP_0;
    case Button_Keyboard_KP1:         return SDL_SCANCODE_KP_1;
    case Button_Keyboard_KP2:         return SDL_SCANCODE_KP_2;
    case Button_Keyboard_KP3:         return SDL_SCANCODE_KP_3;
    case Button_Keyboard_KP4:         return SDL_SCANCODE_KP_4;
    case Button_Keyboard_KP5:         return SDL_SCANCODE_KP_5;
    case Button_Keyboard_KP6:         return SDL_SCANCODE_KP_6;
    case Button_Keyboard_KP7:         return SDL_SCANCODE_KP_7;
    case Button_Keyboard_KP8:         return SDL_SCANCODE_KP_8;
    case Button_Keyboard_KP9:         return SDL_SCANCODE_KP_9;
    case Button_Keyboard_KPNumLock:   return SDL_SCANCODE_NUMLOCKCLEAR;
    case Button_Keyboard_KPDivide:    return SDL_SCANCODE_KP_DIVIDE;
    case Button_Keyboard_KPMultiply:  return SDL_SCANCODE_KP_MULTIPLY;
    case Button_Keyboard_KPSubtract:  return SDL_SCANCODE_KP_MINUS;
    case Button_Keyboard_KPAdd:       return SDL_SCANCODE_KP_PLUS;
    case Button_Keyboard_KPEnter:     return SDL_SCANCODE_KP_ENTER;
    case Button_Keyboard_KPDecimal:   return SDL_SCANCODE_KP_DECIMAL;
    case Button_Keyboard_Backspace:   return SDL_SCANCODE_BACKSPACE;
    case Button_Keyboard_Escape:      return SDL_SCANCODE_ESCAPE;
    case Button_Keyboard_Return:      return SDL_SCANCODE_RETURN;
    case Button_Keyboard_Space:       return SDL_SCANCODE_SPACE;
    case Button_Keyboard_Tab:         return SDL_SCANCODE_TAB;
    case Button_Keyboard_Backtick:    return SDL_SCANCODE_GRAVE;
    case Button_Keyboard_CapsLock:    return SDL_SCANCODE_CAPSLOCK;
    case Button_Keyboard_Minus:       return SDL_SCANCODE_MINUS;
    case Button_Keyboard_Equals:      return SDL_SCANCODE_EQUALS;
    case Button_Keyboard_LBracket:    return SDL_SCANCODE_LEFTBRACKET;
    case Button_Keyboard_RBracket:    return SDL_SCANCODE_RIGHTBRACKET;
    case Button_Keyboard_Backslash:   return SDL_SCANCODE_BACKSLASH;
    case Button_Keyboard_Semicolon:   return SDL_SCANCODE_SEMICOLON;
    case Button_Keyboard_Apostrophe:  return SDL_SCANCODE_APOSTROPHE;
    case Button_Keyboard_Comma:       return SDL_SCANCODE_COMMA;
    case Button_Keyboard_Period:      return SDL_SCANCODE_PERIOD;
    case Button_Keyboard_Slash:       return SDL_SCANCODE_SLASH;
    case Button_Keyboard_PrintScreen: return SDL_SCANCODE_PRINTSCREEN;
    case Button_Keyboard_ScrollLock:  return SDL_SCANCODE_SCROLLLOCK;
    case Button_Keyboard_Pause:       return SDL_SCANCODE_PAUSE;
    case Button_Keyboard_Insert:      return SDL_SCANCODE_INSERT;
    case Button_Keyboard_Delete:      return SDL_SCANCODE_DELETE;
    case Button_Keyboard_Home:        return SDL_SCANCODE_HOME;
    case Button_Keyboard_End:         return SDL_SCANCODE_END;
    case Button_Keyboard_PageUp:      return SDL_SCANCODE_PAGEUP;
    case Button_Keyboard_PageDown:    return SDL_SCANCODE_PAGEDOWN;
    case Button_Keyboard_Right:       return SDL_SCANCODE_RIGHT;
    case Button_Keyboard_Left:        return SDL_SCANCODE_LEFT;
    case Button_Keyboard_Down:        return SDL_SCANCODE_DOWN;
    case Button_Keyboard_Up:          return SDL_SCANCODE_UP;
    case Button_Keyboard_LCtrl:       return SDL_SCANCODE_LCTRL;
    case Button_Keyboard_LShift:      return SDL_SCANCODE_LSHIFT;
    case Button_Keyboard_LAlt:        return SDL_SCANCODE_LALT;
    case Button_Keyboard_LMeta:       return SDL_SCANCODE_LGUI;
    case Button_Keyboard_RCtrl:       return SDL_SCANCODE_RCTRL;
    case Button_Keyboard_RShift:      return SDL_SCANCODE_RSHIFT;
    case Button_Keyboard_RAlt:        return SDL_SCANCODE_RALT;
    case Button_Keyboard_RMeta:       return SDL_SCANCODE_RGUI;
  }
}

Button Button_FromSDLMouseButton (uint8 mouseButton) {
  switch (mouseButton) {
    default: Fatal("Button_FromSDLMouseButton: Unhandled case: %i", mouseButton);

    case SDL_BUTTON_LEFT:   return Button_Mouse_Left;
    case SDL_BUTTON_MIDDLE: return Button_Mouse_Middle;
    case SDL_BUTTON_RIGHT:  return Button_Mouse_Right;
    case SDL_BUTTON_X1:     return Button_Mouse_X1;
    case SDL_BUTTON_X2:     return Button_Mouse_X2;
  }
}

uint8 Button_ToSDLMouseButton (Button button) {
  switch (button) {
    default: Fatal("Button_ToSDLMouseButton: Unhandled case: %i", button);

    case Button_Mouse_Left:   return SDL_BUTTON_LEFT;
    case Button_Mouse_Middle: return SDL_BUTTON_MIDDLE;
    case Button_Mouse_Right:  return SDL_BUTTON_RIGHT;
    case Button_Mouse_X1:     return SDL_BUTTON_X1;
    case Button_Mouse_X2:     return SDL_BUTTON_X2;
  }
}

Button Button_FromSDLControllerAxis (SDL_GameControllerAxis controllerAxis) {
  switch (controllerAxis) {
    default: Fatal("Button_FromSDLControllerAxis: Unhandled case: %i", controllerAxis);

    case SDL_CONTROLLER_AXIS_LEFTX:        return Button_Gamepad_LStickX;
    case SDL_CONTROLLER_AXIS_LEFTY:        return Button_Gamepad_LStickY;
    case SDL_CONTROLLER_AXIS_RIGHTX:       return Button_Gamepad_RStickX;
    case SDL_CONTROLLER_AXIS_RIGHTY:       return Button_Gamepad_RStickY;
    case SDL_CONTROLLER_AXIS_TRIGGERLEFT:  return Button_Gamepad_LTrigger;
    case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return Button_Gamepad_RTrigger;
  }
}

SDL_GameControllerAxis Button_ToSDLControllerAxis (Button button) {
  switch (button) {
    default: Fatal("Button_ToSDLControllerAxis: Unhandled case: %i", button);

    case Button_Gamepad_LStickX:  return SDL_CONTROLLER_AXIS_LEFTX;
    case Button_Gamepad_LStickY:  return SDL_CONTROLLER_AXIS_LEFTY;
    case Button_Gamepad_RStickX:  return SDL_CONTROLLER_AXIS_RIGHTX;
    case Button_Gamepad_RStickY:  return SDL_CONTROLLER_AXIS_RIGHTY;
    case Button_Gamepad_LTrigger: return SDL_CONTROLLER_AXIS_TRIGGERLEFT;
    case Button_Gamepad_RTrigger: return SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
  }
}

Button Button_FromSDLControllerButton (SDL_GameControllerButton controllerButton) {
  switch (controllerButton) {
    default: Fatal("Button_FromSDLControllerButton: Unhandled case: %i", controllerButton);

    case SDL_CONTROLLER_BUTTON_A:             return Button_Gamepad_A;
    case SDL_CONTROLLER_BUTTON_B:             return Button_Gamepad_B;
    case SDL_CONTROLLER_BUTTON_X:             return Button_Gamepad_X;
    case SDL_CONTROLLER_BUTTON_Y:             return Button_Gamepad_Y;
    case SDL_CONTROLLER_BUTTON_BACK:          return Button_Gamepad_Back;
    case SDL_CONTROLLER_BUTTON_GUIDE:         return Button_Gamepad_Guide;
    case SDL_CONTROLLER_BUTTON_START:         return Button_Gamepad_Start;
    case SDL_CONTROLLER_BUTTON_LEFTSTICK:     return Button_Gamepad_LStick;
    case SDL_CONTROLLER_BUTTON_RIGHTSTICK:    return Button_Gamepad_RStick;
    case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:  return Button_Gamepad_LBumper;
    case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return Button_Gamepad_RBumper;
    case SDL_CONTROLLER_BUTTON_DPAD_UP:       return Button_Gamepad_Up;
    case SDL_CONTROLLER_BUTTON_DPAD_DOWN:     return Button_Gamepad_Down;
    case SDL_CONTROLLER_BUTTON_DPAD_LEFT:     return Button_Gamepad_Left;
    case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:    return Button_Gamepad_Right;
  }
}

SDL_GameControllerButton Button_ToSDLControllerButton (Button button) {
  switch (button) {
    default: Fatal("Button_ToSDLControllerButton: Unhandled case: %i", button);

    case Button_Gamepad_A:       return SDL_CONTROLLER_BUTTON_A;
    case Button_Gamepad_B:       return SDL_CONTROLLER_BUTTON_B;
    case Button_Gamepad_X:       return SDL_CONTROLLER_BUTTON_X;
    case Button_Gamepad_Y:       return SDL_CONTROLLER_BUTTON_Y;
    case Button_Gamepad_Back:    return SDL_CONTROLLER_BUTTON_BACK;
    case Button_Gamepad_Guide:   return SDL_CONTROLLER_BUTTON_GUIDE;
    case Button_Gamepad_Start:   return SDL_CONTROLLER_BUTTON_START;
    case Button_Gamepad_LStick:  return SDL_CONTROLLER_BUTTON_LEFTSTICK;
    case Button_Gamepad_RStick:  return SDL_CONTROLLER_BUTTON_RIGHTSTICK;
    case Button_Gamepad_LBumper: return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
    case Button_Gamepad_RBumper: return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
    case Button_Gamepad_Up:      return SDL_CONTROLLER_BUTTON_DPAD_UP;
    case Button_Gamepad_Down:    return SDL_CONTROLLER_BUTTON_DPAD_DOWN;
    case Button_Gamepad_Left:    return SDL_CONTROLLER_BUTTON_DPAD_LEFT;
    case Button_Gamepad_Right:   return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
  }
}


text:
#include "Audio.h"
#include "File.h"
#include "FMODError.h"
#include "PhxMemory.h"
#include "Resource.h"
#include "Sound.h"
#include "SoundDesc.h"
#include "SoundDef.h"
#include "PhxString.h"
#include "fmod/fmod.h"

void SoundDesc_FinishLoad (SoundDesc* self, cstr func) {
  bool warned = false;
  FMOD_OPENSTATE openState;
  while (true) {
    FMODCALL(FMOD_Sound_GetOpenState(self->handle, &openState, 0, 0, 0));

    if (openState == FMOD_OPENSTATE_ERROR)
      Fatal("%s: Background file load has failed.\n  Path: %s", func, self->path);

    if (openState == FMOD_OPENSTATE_READY || openState == FMOD_OPENSTATE_PLAYING)
      break;

    if (!warned) {
      warned = true;
      Warn("%s: Background file load hasn't finished. Blocking the main thread.\n  Path: %s", func, self->path);
    }
  }
}
#define SoundDesc_FinishLoad(...) SoundDesc_FinishLoad(__VA_ARGS__, __func__)

SoundDesc* SoundDesc_Load (cstr name, bool immediate, bool isLooped, bool is3D) {
  cstr mapKey = StrAdd(isLooped ? "LOOPED:" : "UNLOOPED:", name);
  SoundDesc* self = Audio_AllocSoundDesc(mapKey);
  StrFree(mapKey);

  if (!self->name) {
    cstr path = Resource_GetPath(ResourceType_Sound, name);
    FMOD_MODE mode = 0;
    mode |= FMOD_CREATESAMPLE;
    mode |= FMOD_IGNORETAGS;
    mode |= FMOD_ACCURATETIME;
    mode |= isLooped ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF;
    mode |= is3D ? (FMOD_3D | FMOD_3D_WORLDRELATIVE) : FMOD_2D;
    if (!immediate)
      mode |= FMOD_NONBLOCKING;

    FMODCALL(FMOD_System_CreateSound((FMOD_SYSTEM*) Audio_GetHandle(), path, mode, 0, &self->handle));
    FMODCALL(FMOD_Sound_SetUserData(self->handle, self));

    self->name = StrDup(name);
    self->path = StrDup(path);
    RefCounted_Init(self);
  } else {
    RefCounted_Acquire(self);

    if (immediate)
      SoundDesc_FinishLoad(self);
  }

  return self;
}

void SoundDesc_Acquire (SoundDesc* self) {
  RefCounted_Acquire(self);
}

void SoundDesc_Free (SoundDesc* self) {
  RefCounted_Free(self) {
    cstr name = self->name;
    cstr path = self->path;
    FMODCALL(FMOD_Sound_Release(self->handle));
    Audio_DeallocSoundDesc(self);
    StrFree(name);
    StrFree(path);
    /* TODO : Remove when StrMap_Remove is implemented */
    MemZero(self, sizeof(SoundDesc));
  }
}

float SoundDesc_GetDuration (SoundDesc* self) {
  SoundDesc_FinishLoad(self);

  uint32 duration;
  FMODCALL(FMOD_Sound_GetLength(self->handle, &duration, FMOD_TIMEUNIT_MS));
  return (float) duration / 1000.0f;
}

cstr SoundDesc_GetName (SoundDesc* self) {
  return self->name;
}

cstr SoundDesc_GetPath (SoundDesc* self) {
  return self->path;
}

void SoundDesc_ToFile (SoundDesc* self, cstr name) {
  /* TODO : Finish this.
   *        There's some sort of signed/unsigned issue with the current
   *        implementation. 8-bit PCM is unsigned according to the spec.
   *        However, inspecting thybidding.wav in a hex editor sure looks like
   *        signed data to me. The data read from FMOD is 'correct' but appears
   *        to be unsigned (e.g. offset by a constant 0x80 / 0d128).
   *
   *        I'm extremely confused hy this as the FMOD data seems to be correct
   *        while the actual file appears to be incorrect, yet VLC and Audacity
   *        both play the original file correctly and the file output here
   *        sounds like ass. Further, exporting the original wav to a new file
   *        looks like the original file, further supporting that it is correct
   *        and well formed.
   *
   *        Possible solutions:
   *        1) Post a question on the FMOD site to gather more information.
   *        2) Use Sound::readData instead (probably going to get the same result)
   *        3) Create a second System and use System::setOutput to set
   *           FMOD_OUTPUTTYPE_WAVWRITER_NRT
   *
   *        I've already spent too much time on this so I'm tabling it until
   *        more of the audio API is fleshed out (namely, FMOD Studio is
   *        integrated, Rigidbody updates are processed, and HRTF solutions are
   *        explored).
   */
  SoundDesc_FinishLoad(self);

  uint32 length;
  int32 channels;
  int32 bitsPerSample;
  FMODCALL(FMOD_Sound_GetLength(self->handle, &length, FMOD_TIMEUNIT_RAWBYTES));
  FMODCALL(FMOD_Sound_GetFormat(self->handle, 0, 0, &channels, &bitsPerSample));
  int32 bytesPerSample = bitsPerSample / 8;

  float sampleRate;
  FMOD_Sound_GetDefaults(self->handle, &sampleRate, 0);

  void* ptr1; uint32 len1;
  void* ptr2; uint32 len2;
  FMODCALL(FMOD_Sound_Lock(self->handle, 0, length, &ptr1, &ptr2, &len1, &len2));
  Assert(ptr2 == 0 && len2 == 0);
  Assert(len1 == length);

  /* Write the file */ {
    File* file = File_Create(name);
    if (!file)
      Fatal("SoundDesc_ToFile: Failed to create file.\nPath: %s", name);

    File_Write   (file, "RIFF", 4                                       ); // Chunk ID
    File_WriteI32(file, 36 + length                                     ); // Chunk Size
    File_Write   (file, "WAVE", 4                                       ); // Wave ID
    File_Write   (file, "fmt ", 4                                       ); // Chunk ID
    File_WriteI32(file, 16                                              ); // Chunk Size
    File_WriteI16(file, 1                                               ); // Format Code (PCM)
    File_WriteI16(file, (int16) channels                                ); // Channels
    File_WriteI32(file, (int32) sampleRate                              ); // Sample Rate
    File_WriteI32(file, (int32) (bytesPerSample * channels * sampleRate)); // Data Rate
    File_WriteI16(file, (int16) (bytesPerSample * channels)             ); // Frame Size
    File_WriteI16(file, (int16) (bitsPerSample)                         ); // Bits Per Sample
    File_Write   (file, "data", 4                                       );
    File_WriteI32(file, length                                          );
    File_Write   (file, ptr1, length                                    );
    File_Close   (file);
  }

  FMODCALL(FMOD_Sound_Unlock(self->handle, ptr1, ptr2, len1, len2));
}

repo_name: JoshParnell/libphx

repo_language: C

mime_type: text/x-c++


text:
#include "File.h"
#include "PhxMemory.h"
#include "Resource.h"
#include "PhxString.h"

struct PathElem {
  cstr format;
  PathElem* next;
};

static PathElem* paths[ResourceType_COUNT] = { 0 };

inline static cstr Resource_Resolve (ResourceType type, cstr name, bool failhard) {
  static char buffer[256];
  for (PathElem* elem = paths[type]; elem; elem = elem->next) {
    int res = snprintf(buffer, sizeof(buffer), elem->format, name);
    if (res > 0 && res < (int)sizeof(buffer)) {
      if (File_Exists(buffer))
        return buffer;
    }
  }

  /* Attempt to fall back to literal path if no path rule worked. */
  if (name && File_Exists(name))
    return name;

  if (failhard)
    Fatal("Resource_Resolve: Failed to find %s <%s>",
      ResourceType_ToString(type), name);

  return 0;
}

void Resource_AddPath (ResourceType type, cstr format) {
  PathElem* self = MemNew(PathElem);
  self->format = StrDup(format);
  self->next = paths[type];
  paths[type] = self;
}

bool Resource_Exists (ResourceType type, cstr name) {
  return Resource_Resolve(type, name, false) != 0;
}

cstr Resource_GetPath (ResourceType type, cstr name) {
  return Resource_Resolve(type, name, true);
}

Bytes* Resource_LoadBytes (ResourceType type, cstr name) {
  cstr path = Resource_Resolve(type, name, true);
  Bytes* data = File_ReadBytes(path);
  if (!data)
    Fatal("Resource_LoadBytes: Failed to load %s <%s> at <%s>",
      ResourceType_ToString(type), name, path);
  return data;
}

cstr Resource_LoadCstr (ResourceType type, cstr name) {
  cstr path = Resource_Resolve(type, name, true);
  cstr data = File_ReadCstr(path);
  if (!data)
    Fatal("Resource_LoadCstr: Failed to load %s <%s> at <%s>",
      ResourceType_ToString(type), name, path);
  return data;
}

void Resource_Init () {
#ifndef BUILD_DISTRIBUTABLE
  Resource_AddPath(ResourceType_Font,    "../shared/res/font/%s.ttf");
  Resource_AddPath(ResourceType_Font,    "../shared/res/font/%s.otf");
  Resource_AddPath(ResourceType_Mesh,    "../shared/res/mesh/%s.bin");
  Resource_AddPath(ResourceType_Mesh,    "../shared/res/mesh/%s.obj");
  Resource_AddPath(ResourceType_Other,   "../shared/res/%s");
  Resource_AddPath(ResourceType_Script,  "../shared/res/script/%s.lua");
  Resource_AddPath(ResourceType_Shader,  "../shared/res/shader/%s.glsl");
  Resource_AddPath(ResourceType_Sound,   "../shared/res/sound/%s.mp3");
  Resource_AddPath(ResourceType_Sound,   "../shared/res/sound/%s.ogg");
  Resource_AddPath(ResourceType_Sound,   "../shared/res/sound/%s.ogx");
  Resource_AddPath(ResourceType_Sound,   "../shared/res/sound/%s.wav");
  Resource_AddPath(ResourceType_Tex1D,   "../shared/res/tex1d/%s.bin");
  Resource_AddPath(ResourceType_Tex2D,   "../shared/res/tex2d/%s.jpg");
  Resource_AddPath(ResourceType_Tex2D,   "../shared/res/tex2d/%s.png");
  Resource_AddPath(ResourceType_Tex3D,   "../shared/res/tex3d/%s.bin");
  Resource_AddPath(ResourceType_TexCube, "../shared/res/texcube/%s");
#endif

  Resource_AddPath(ResourceType_Font,    "./res/font/%s.ttf");
  Resource_AddPath(ResourceType_Font,    "./res/font/%s.otf");
  Resource_AddPath(ResourceType_Mesh,    "./res/mesh/%s.bin");
  Resource_AddPath(ResourceType_Mesh,    "./res/mesh/%s.obj");
  Resource_AddPath(ResourceType_Other,   "./res/%s");
  Resource_AddPath(ResourceType_Script,  "./res/script/%s.lua");
  Resource_AddPath(ResourceType_Shader,  "./res/shader/%s.glsl");
  Resource_AddPath(ResourceType_Sound,   "./res/sound/%s.mp3");
  Resource_AddPath(ResourceType_Sound,   "./res/sound/%s.ogg");
  Resource_AddPath(ResourceType_Sound,   "./res/sound/%s.ogx");
  Resource_AddPath(ResourceType_Sound,   "./res/sound/%s.wav");
  Resource_AddPath(ResourceType_Tex1D,   "./res/tex1d/%s.bin");
  Resource_AddPath(ResourceType_Tex2D,   "./res/tex2d/%s.jpg");
  Resource_AddPath(ResourceType_Tex2D,   "./res/tex2d/%s.png");
  Resource_AddPath(ResourceType_Tex3D,   "./res/tex3d/%s.bin");
  Resource_AddPath(ResourceType_TexCube, "./res/texcube/%s");

  Resource_AddPath(ResourceType_Font,    "%s.ttf");
  Resource_AddPath(ResourceType_Font,    "%s.otf");
  Resource_AddPath(ResourceType_Mesh,    "%s.bin");
  Resource_AddPath(ResourceType_Mesh,    "%s.obj");
  Resource_AddPath(ResourceType_Other,   "%s");
  Resource_AddPath(ResourceType_Script,  "%s.lua");
  Resource_AddPath(ResourceType_Shader,  "%s.glsl");
  Resource_AddPath(ResourceType_Sound,   "%s.mp3");
  Resource_AddPath(ResourceType_Sound,   "%s.ogg");
  Resource_AddPath(ResourceType_Sound,   "%s.ogx");
  Resource_AddPath(ResourceType_Sound,   "%s.wav");
  Resource_AddPath(ResourceType_Tex1D,   "%s.bin");
  Resource_AddPath(ResourceType_Tex2D,   "%s.jpg");
  Resource_AddPath(ResourceType_Tex2D,   "%s.png");
  Resource_AddPath(ResourceType_Tex3D,   "%s.bin");
  Resource_AddPath(ResourceType_TexCube, "%s");
}


text:
#include "Intersect.h"
#include "Matrix.h"
#include "Plane.h"
#include "Polygon.h"
#include "Ray.h"
#include "Sphere.h"
#include "PhxMath.h"
#include "Triangle.h"
#include "Vec3.h"
#include "Vec4.h"

/* NOTE: On Epsilons
 *  - PLANE_THICKNESS_EPSILON
 *
 *    1) Limited by floating point precision.
 *    Before building a BSP we normalize the mesh by dividing by the bounds
 *    size such that vertex positions are between [-1, 1] on all axes. When
 *    choosing splitting planes we call Polygon_ToPlane for autopartitioning
 *    cuts. As the vertices and center of the triangle can be anywhere in that
 *    [-1, 1] range, our precision is capped around 1e-7. Even if all inputs
 *    are double precision, simply doing the vertex-plane distance calculation
 *    in float will end up rounding the distance to somewhere around 1e-8 in
 *    many cases.
 *
 *    2) Limited by overall BSP size (in bytes)
 *    The smaller the epsilon is, the more eagerly we end up cutting polygons.
 *    In order to make a cut, an edge must cross from 'in front' of the plane
 *    to 'behind' the plane. The thinner the plane is, the easier it is to
 *    satisfy this condition and we end up making a lot more cuts, which leads
 *    to more triangles, which leads to more memory usage. At last checl (r631)
 *    Dropping from 1e-4 to 1e-5 raised the overall memory usage by 25% while
 *    only gaining 0.1 us.
 */

/* TODO : Need to handle epsilons properly in these intersection tests */

bool Intersect_PointBox (Matrix* src, Matrix* dst) {
  Matrix* inv = Matrix_Inverse(dst);
  Vec3f srcPt; Matrix_GetPos(src, &srcPt);
  Vec3f dstPt; Matrix_MulPoint(inv, &dstPt, UNPACK3(srcPt));
  Matrix_Free(inv);
  return
    -1.0f < dstPt.x && dstPt.x < 1.0f &&
    -1.0f < dstPt.y && dstPt.y < 1.0f &&
    -1.0f < dstPt.z && dstPt.z < 1.0f;
}

bool Intersect_PointTriangle_Barycentric (Vec3f const* p, Triangle const* tri) {
  Vec3f const* v = tri->vertices;

  Vec3f pv0 = Vec3f_Sub(v[0], *p);
  Vec3f pv1 = Vec3f_Sub(v[1], *p);
  Vec3f pv2 = Vec3f_Sub(v[2], *p);

  /* NOTE: Any scale factor on plane.n will fall out of the calcuations for A and B. */
  Plane plane; Triangle_ToPlaneFast(tri, &plane);

  float areaABC = Vec3f_Dot(plane.n, plane.n);
  float areaPBC = Vec3f_Dot(plane.n, Vec3f_Cross(pv1, pv2));
  float areaPCA = Vec3f_Dot(plane.n, Vec3f_Cross(pv2, pv0));

  float A = areaPBC / areaABC;
  float B = areaPCA / areaABC;
  float C = 1.0f - A - B;

  /* TODO : Need a proper epsilon */
  float fuzzyMin = 0.0f - 0.01f;
  float fuzzyMax = 1.0f + 0.01f;

  return A > fuzzyMin && A < fuzzyMax
    && B > fuzzyMin && B < fuzzyMax
    && C > fuzzyMin && C < fuzzyMax;
}

bool Intersect_RayPlane (
  Ray const* ray,
  Plane const* plane,
  Vec3f* pHit)
{
  /* TODO : Shouldn't we handle denom == 0? */
  float dist = plane->d - Vec3f_Dot(plane->n, ray->p);
  float denom = Vec3f_Dot(plane->n, ray->dir);
  float t = dist / denom;

  if (t >= ray->tMin && t <= ray->tMax) {
    *pHit = Vec3f_Add(ray->p, Vec3f_Muls(ray->dir, t));
    return true;
  }

  return false;
}

bool Intersect_RayTriangle_Barycentric (
  Ray const* ray,
  Triangle const* tri,
  float tEpsilon,
  float* tHit)
{
  /* NOTE: Any scale factor on plane.n falls out of the calculation for t.
   * denom and dist are both off by the scale factor, but we don't need
   * them for anything other than t.
   */
  Plane plane; Triangle_ToPlaneFast(tri, &plane);

  float dist = Vec3f_Dot(plane.n, ray->p) - plane.d;
  float denom = -Vec3f_Dot(plane.n, ray->dir);

  if (denom != 0.0f) {
    float t =  dist / denom;
    if (t > ray->tMin - tEpsilon && t < ray->tMax + tEpsilon) {
      Vec3f const* v = tri->vertices;
      Vec3f p; Ray_GetPoint(ray, t, &p);

      Vec3f pv0 = Vec3f_Sub(v[0], p);
      Vec3f pv1 = Vec3f_Sub(v[1], p);
      Vec3f pv2 = Vec3f_Sub(v[2], p);

      float areaABC = Vec3f_Dot(plane.n, plane.n);
      float areaPBC = Vec3f_Dot(plane.n, Vec3f_Cross(pv1, pv2));
      float areaPCA = Vec3f_Dot(plane.n, Vec3f_Cross(pv2, pv0));

      float A = areaPBC / areaABC;
      float B = areaPCA / areaABC;
      float C = 1.0f - A - B;

      /* TODO : Need a proper epsilon */
      float fuzzyMin = 0.0f - 0.01f;
      float fuzzyMax = 1.0f + 0.01f;

      if (A > fuzzyMin && A < fuzzyMax &&
          B > fuzzyMin && B < fuzzyMax &&
          C > fuzzyMin && C < fuzzyMax) {
        *tHit = t;
        return true;
      }
    }
    else {
      /* TODO : Handle parallel but in triangle (or its thick plane) */
    }
  }

  return false;
}

/* http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/ */
bool Intersect_RayTriangle_Moller1 (
  Ray const* ray,
  Triangle const* tri,
  float* tHit)
{
  /* Rewritten test sign of determinant; division is at the end. */
  Vec3f const* vt = tri->vertices;
  Vec3f edge1 = Vec3f_Sub(vt[1], vt[0]);
  Vec3f edge2 = Vec3f_Sub(vt[2], vt[0]);

  float u, v;
  Vec3f qvec;

  /* Begin calculating determinant - also used to calculate U parameter. */
  Vec3f pvec = Vec3f_Cross(ray->dir, edge2);

  /* TODO : Need a proper epsilon */
  const float epsilon = .000001f;

  /* If determinant is near zero, ray lies in plane of triangle. */
  float det = Vec3f_Dot(edge1, pvec);
  if (det > epsilon) {
    /* Calculate distance from vert0 to ray origin. */
    Vec3f tvec = Vec3f_Sub(ray->p, vt[0]);

    /* Calculate U parameter and test bounds. */
    u = Vec3f_Dot(tvec, pvec);
    if (u < 0.0 || u > det)
      return false;

    /* Prepare to test V parameter. */
    qvec = Vec3f_Cross(tvec, edge1);

    /* Calculate V parameter and test bounds. */
    v = Vec3f_Dot(ray->dir, qvec);

    if (v < 0.0 || (u + v) > det)
      return false;
  }

  else if (det < -epsilon) {
    /* Calculate distance from vert0 to ray origin. */
    Vec3f tvec = Vec3f_Sub(ray->p, vt[0]);

    /* Calculate U parameter and test bounds. */
    u = Vec3f_Dot(tvec, pvec);
    if (u > 0.0 || u < det)
      return false;

    /* Prepare to test V parameter. */
    qvec = Vec3f_Cross(tvec, edge1);

    /* Calculate V parameter and test bounds. */
    v = Vec3f_Dot(ray->dir, qvec);

    if (v > 0.0 || u + v < det)
      return false;
  }

  else {
    /* Ray is parallel to the plane of the triangle */
    return false;
  }

  float inv_det = 1.0f / det;

  /* Ray intersects; calculate t. */
  *tHit = Vec3f_Dot(edge2, qvec) * inv_det;
  return true;
}

/* http://www.cs.virginia.edu/~gfx/courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf */
bool Intersect_RayTriangle_Moller2 (
  Ray const* ray,
  Triangle const* tri,
  float* tHit)
{
  Vec3f const* vt = tri->vertices;

  /* Find vectors for two edges sharing vert. */
  Vec3f edge1 = Vec3f_Sub(vt[1], vt[0]);
  Vec3f edge2 = Vec3f_Sub(vt[2], vt[0]);

  /* Begin calculating determinant - also used to calculate U parameter. */
  Vec3f pvec = Vec3f_Cross(ray->dir, edge2);

  /* If determinant is near zero ray lies in plane of triangle. */
  float det = Vec3f_Dot(edge1, pvec);

  /* TODO : Need a proper epsilon */
  if (Abs(det) < 0.000001f)
    return false;

  float inv_det = 1.0f / det;

  /* Calculate distance from vert to ray origin. */
  Vec3f tvec = Vec3f_Sub(ray->p, vt[0]);

  /* TODO : Need a proper epsilon */
  float fuzzyMin = 0.0f - 0.01f;
  float fuzzyMax = 1.0f + 0.01f;

  /* Calculate U and test bounds. */
  float u = Vec3f_Dot(tvec, pvec) * inv_det;
  if (u < fuzzyMin || u > fuzzyMax)
    return false;

  /* Prepare to test V. */
  Vec3f qvec = Vec3f_Cross(tvec, edge1);

  /* Calculate V and test bounds. */
  float v = Vec3f_Dot(ray->dir, qvec) * inv_det;
  if (v < fuzzyMin || (u + v) > fuzzyMax)
    return false;

  /* Ray intersects; calculate t. */
  *tHit = Vec3f_Dot(edge2, qvec) * inv_det;
  return true;
}

bool Intersect_LineSegmentPlane (
  LineSegment const* lineSegment,
  Plane const* plane,
  Vec3f* pHit)
{
  Vec3f dir = Vec3f_Sub(lineSegment->p1, lineSegment->p0);
  Ray ray = { lineSegment->p0, dir, 0.0f, 1.0f };
  return Intersect_RayPlane(&ray, plane, pHit);
}

bool Intersect_RectRect (Vec4f const* a, Vec4f const* b) {
  Vec4f a2 = { a->x + Min(a->z, 0.0f), a->y + Min(a->w, 0.0f), Abs(a->z), Abs(a->w) };
  Vec4f b2 = { b->x + Min(b->z, 0.0f), b->y + Min(b->w, 0.0f), Abs(b->z), Abs(b->w) };
  return Intersect_RectRectFast(&a2, &b2);
}

bool Intersect_RectRectFast (Vec4f const* a, Vec4f const* b) {
  bool result = true;
  result &= a->x < b->x + b->z;
  result &= b->x < a->x + a->z;
  result &= a->y < b->y + b->w;
  result &= b->y < a->y + a->w;
  return result;
}

/* Realtime Collision Detection, pp 141-142 */
inline static Vec3f ClosestPoint_PointToTriangle (
  Vec3f const* p,
  Triangle const* tri)
{
  Vec3f a = tri->vertices[0];
  Vec3f b = tri->vertices[1];
  Vec3f c = tri->vertices[2];

  /* Check if P in vertex region outside A */
  Vec3f ab = Vec3f_Sub(b,  a);
  Vec3f ac = Vec3f_Sub(c,  a);
  Vec3f ap = Vec3f_Sub(*p, a);
  float d1 = Vec3f_Dot(ab, ap);
  float d2 = Vec3f_Dot(ac, ap);
  if (d1 <= 0.0f && d2 <= 0.0f) return a; // (1, 0, 0)

  /* Check if P in vertex region outside B. */
  Vec3f bp = Vec3f_Sub(*p, b);
  float d3 = Vec3f_Dot(ab, bp);
  float d4 = Vec3f_Dot(ac, bp);
  if (d3 >= 0.0f && d4 <= d3) return b; // (0, 1, 0)

  /* Check if P in edge region of AB, if so return projection of P onto AB. */
  float vc = d1*d4 - d3*d2;
  if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
    float v = d1 / (d1 - d3);
    return Vec3f_Add(a , Vec3f_Muls(ab, v)); // (1 - v, v, 0)
  }

  /* Check if P in vertex region outside C. */
  Vec3f cp = Vec3f_Sub(*p, c);
  float d5 = Vec3f_Dot(ab, cp);
  float d6 = Vec3f_Dot(ac, cp);
  if (d6 >= 0.0f && d5 <= d6) return c; // (0, 0, 1)

  /* Check if P in edge region of AC, if so return projection of P onto AC. */
  float vb = d5*d2 - d1*d6;
  if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
    float w = d2 / (d2 - d6);
    return Vec3f_Add(a, Vec3f_Muls(ac, w)); // (1 - w, 0, w)
  }

  /* Check if P in edge region of BC, if so return projection of P onto BC. */
  float va = d3*d6 - d5*d4;
  float d4m3 = d4 - d3;
  float d5m6 = d5 - d6;
  if (va <= 0.0f && d4m3 >= 0.0f && d5m6 >= 0.0f) {
    float w = d4m3 / (d4m3 + d5m6);
    Vec3f bc = Vec3f_Sub(c, b);
    return Vec3f_Add(b, Vec3f_Muls(bc, w)); // (0, 1 - w, w)
  }

  /* P inside face region. Compute barycentric coordinates (1 - v - w, v, w) */
  float denom = 1.0f / (va + vb + vc);
  float v = vb * denom;
  float w = vc * denom;
  return Vec3f_Add(Vec3f_Add(a, Vec3f_Muls(ab, v)), Vec3f_Muls(ac, w));
}

bool Intersect_SphereTriangle (
  Sphere const* sphere,
  Triangle const* triangle,
  Vec3f* pHit)
{
  Vec3f pClosest = ClosestPoint_PointToTriangle(&sphere->p, triangle);
  float distSq = Vec3f_DistanceSquared(sphere->p, pClosest);
  if (distSq < sphere->r * sphere->r) {
    *pHit = pClosest;
    return true;
  }
  return false;
}

#if 0
/* TODO : This is not yet working properly
 * TODO : Need to precompute index of the largest normal component */
bool Intersect_RayTriangle_Badouel (Ray* ray, Triangle* triangle, float tEpsilon, float* tHit) {
  /* NOTE: There's some great information in the following reference, including
   * comparisons of multiple algorithms and code for each.
   * http://erich.realtimerendering.com/ptinpoly/
   */

  Plane plane = Triangle_ToPlaneFast(triangle);

  float dist = Vec3f_Dot(plane.n, ray->p) - plane.d;
  float denom = -Vec3f_Dot(plane.n, ray->dir);

  if (denom != 0.0f) {
    float t =  dist / denom;

    if (t > ray->tMin - tEpsilon && t < ray->tMax + tEpsilon) {
      Vec3f* pgon = triangle->vertices;
      Vec3f point = Ray_GetPoint(ray, t);

      Vec3f pg1, pg2;
      float tx, ty, u0, u1, u2, v0, v1, vx0, vy0, alpha, beta, denom2;
      int inside_flag;

      tx = point.x;
      ty = point.y;
      vx0 = pgon[0].x;
      vy0 = pgon[0].y;
      u0 = tx - vx0;
      v0 = ty - vy0;

      inside_flag = 0;

      /* TODO : v1 is uninitialized in the first if */
      v1 = 0;

      pg1 = pgon[1];
      pg2 = pgon[2];
      u1 = pg1.x - vx0;
      if (u1 == 0.0) {
        /* 0 and 1 vertices have same X value */

        u2 = pg2.x - vx0;
        if (
          /* compute beta and check bounds */
          ((beta = u0 / u2) < -0.01f) || (beta > 1.01f) ||

          /* compute alpha and check bounds */
          ((alpha = (v0 - beta * (pg2.y - vy0)) / v1) < 0.0)) {

          /* whew! missed! */
          return inside_flag != 0;
        }
      }
      else {
        /* 0 and 1 vertices have different X value */

        /* compute denom2 */
        u2 = pg2.x - vx0;
        v1 = pg1.y - vy0;
        denom2 = (pg2.y - vy0) * u1 - u2 * v1;
        if (
          /* compute beta and check bounds */
          ((beta = (v0 * u1 - u0 * v1) / denom2) < -0.01f) || (beta > 1.01f) ||

          /* compute alpha & check bounds */
          ((alpha = (u0 - beta * u2) / u1) < 0.0)) {

          /* whew! missed! */
          return inside_flag != 0;
        }
      }

      /* check gamma */
      if (alpha + beta <= 1.01f) {
        /* survived */
        inside_flag = !inside_flag;
      }

      return inside_flag != 0;
    }
  }
  else {
    /* TODO : Handle parallel but in triangle (or its thick plane) */
  }

  /* TODO : Coalesce returns? */
  return false;
}

#endif


text:
#include "Directory.h"
#include "Engine.h"
#include "Gamepad.h"
#include "Input.h"
#include "Joystick.h"
#include "Keyboard.h"
#include "Metric.h"
#include "Mouse.h"
#include "PhxSignal.h"
#include "Profiler.h"
#include "Resource.h"
#include "SDL.h"
#include "ShaderVar.h"
#include "TimeStamp.h"

#include <stdio.h>

/* On Windows, request usage of the dedicated GPU if the machine switches
 * between on-board and dedicated GPUs dynamically. Only works when exported
 * by the exe, not when exported by a dll. */
#if WINDOWS
  extern "C" {
    __declspec(dllexport) ulong NvOptimusEnablement = 0x00000001;
    __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
  }
#endif

const uint32 subsystems =
  SDL_INIT_EVENTS |
  SDL_INIT_VIDEO |
  SDL_INIT_TIMER |
  SDL_INIT_HAPTIC |
  SDL_INIT_JOYSTICK |
  SDL_INIT_GAMECONTROLLER;

static cstr versionString = __DATE__ " " __TIME__;
static TimeStamp initTime = 0;

void Engine_Init (int glVersionMajor, int glVersionMinor) {
  static bool firstTime = true;
  Signal_Init();

  if (firstTime) {
    firstTime = false;
    /* Check SDL version compatibility. */ {
      SDL_version compiled;
      SDL_version linked;
      SDL_VERSION(&compiled);
      SDL_GetVersion(&linked);
      if (compiled.major != linked.major ||
          compiled.minor != linked.minor ||
          compiled.patch != linked.patch)
      {
        puts("Engine_Init: Detected SDL version mismatch:");
        printf("  Version (Compiled) : %d.%d.%d\n", compiled.major, compiled.minor, compiled.patch);
        printf("  Version (Linked)   : %d.%d.%d\n", linked.major, linked.minor, linked.patch);
        Fatal("Engine_Init: Terminating.");
      }
    }

    if (SDL_Init(0) != 0)
      Fatal("Engine_Init: Failed to initialize SDL");
    if (!Directory_Create("log"))
      Fatal("Engine_Init: Failed to create log directory.");
    atexit(SDL_Quit);
  }

  if (SDL_InitSubSystem(subsystems) != 0)
    Fatal("Engine_Init: Failed to initialize SDL's subsystems");

  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, glVersionMajor);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, glVersionMinor);
  SDL_GL_SetAttribute(
    SDL_GL_CONTEXT_PROFILE_MASK,
    SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
  SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

  Keyboard_Init();
  Metric_Reset();
  Mouse_Init();
  Input_Init();
  Resource_Init();
  ShaderVar_Init();

  initTime = TimeStamp_Get();
}

void Engine_Free () {
  ShaderVar_Free();
  Keyboard_Free();
  Mouse_Free();
  Input_Free();
  Signal_Free();
  SDL_QuitSubSystem(subsystems);
}

void Engine_Abort () {
  abort();
}

int Engine_GetBits () {
  return (int)(8 * sizeof(void*));
}

double Engine_GetTime () {
  return TimeStamp_GetElapsed(initTime);
}

cstr Engine_GetVersion () {
  return versionString;
}

bool Engine_IsInitialized () {
  return initTime != 0;
}

void Engine_Terminate () {
  exit(0);
}

void Engine_Update () {
  FRAME_BEGIN;
  Metric_Reset();
  Keyboard_UpdatePre();
  Mouse_Update();
  Joystick_Update();
  Gamepad_Update();
  Input_Update();
  Keyboard_UpdatePost();
  FRAME_END;
}


text:
#include "MemPool.h"
#include "PhxMemory.h"
#include "PhxMath.h"

#define PAGE_SIZE 0x1000

/* NOTE : At the moment, a great deal of the CTypes code relies on mempool
 *        cells being zero-initialized on allocation. */
#define ZERO_MEMORY 1

/* Zero cell memory upon being released back to the pool. Should not be used
 * in release builds due to performance impact, but very useful for debugging
 * use-after-free memory errors. */
#define ZERO_ON_FREE 0

struct MemPool {
  uint32 size;
  uint32 capacity;
  void*  freeList;
  uint32 cellSize;
  uint32 blockSize;
  uint16 blockCount;
  void** blocks;
};

static void MemPool_Grow (MemPool* self) {
  uint16 newBlockIndex = self->blockCount++;
  self->capacity += self->blockSize;

  /* Grow the list of pool blocks. */
  self->blocks = (void**)MemRealloc(self->blocks, self->blockCount * sizeof(void*));

  /* Allocate a new block and place at the back of the list. */
  void* newBlock = MemAlloc(self->cellSize * self->blockSize);
  self->blocks[newBlockIndex] = newBlock;

  /* Wire up the free list for this block. Note that we can assume the existing
   * free list is empty if the pool is requesting to grow, hence we overwrite
   * the existing list head. The block's initial freelist is wired sequentially
   * ( 0 -> 1 -> 2 ) for optimal cache locality. */
  void** prev = &self->freeList;
  char* pCurr = (char*)newBlock;
  for (uint32 i = 0; i < self->blockSize; ++i) {
    *prev = (void*)pCurr;
    prev = (void**)pCurr;
    pCurr += self->cellSize;
  }
  *prev = 0;
}

MemPool* MemPool_Create (uint32 cellSize, uint32 blockSize) {
  MemPool* self = MemNew(MemPool);
  self->size = 0;
  self->capacity = 0;
  self->freeList = 0;
  self->cellSize = cellSize;
  self->blockSize = blockSize;
  self->blockCount = 0;
  self->blocks = 0;
  return self;
}

MemPool* MemPool_CreateAuto (uint32 elemSize) {
  return MemPool_Create(elemSize, PAGE_SIZE / elemSize);
}

void MemPool_Free (MemPool* self) {
  for (uint16 i = 0; i < self->blockCount; ++i)
    MemFree(self->blocks[i]);
  MemFree(self->blocks);
}

void* MemPool_Alloc (MemPool* self) {
  IF_UNLIKELY (self->size == self->capacity)
    MemPool_Grow(self);

  /* Grab the freeList's head block and replace freeList with the value therein
   * (the internal freelist next pointer) */
  void* freeCell = self->freeList;
  self->freeList = *(void**)freeCell;
  self->size++;

#if ZERO_MEMORY
  MemZero(freeCell, self->cellSize);
#endif

  return freeCell;
}

void MemPool_Clear (MemPool* self) {
  self->size = 0;
  self->freeList = 0;

  /* We must take care to wire the freelist properly over block boundaries when
   * clearing. */
  void** prev = &self->freeList;
  for (uint32 i = 0; i < self->blockCount; ++i) {
    char* pCurr = (char*)self->blocks[i];
    for (uint32 j = 0; j < self->blockSize; ++j) {
      *prev = (void*)pCurr;
      prev = (void**)pCurr;
      pCurr += self->cellSize;
    }
  }
  *prev = 0;
}

void MemPool_Dealloc (MemPool* self, void* ptr) {
#if ZERO_ON_FREE
  MemZero(ptr, self->cellSize);
#endif
  *(void**)ptr = self->freeList;
  self->freeList = ptr;
  self->size--;
}

uint32 MemPool_GetCapacity (MemPool* self) {
  return self->capacity;
}

uint32 MemPool_GetSize (MemPool* self) {
  return self->size;
}


text:
#include "ArrayList.h"
#include "BoxMesh.h"
#include "Matrix.h"
#include "Mesh.h"
#include "Vec3.h"

struct Box {
  Vec3f p, s, r, b;
};

struct BoxMesh {
  ArrayList(Box, elem);
};

static Vec3f kFaceOrigin[] = {
  { -1, -1,  1 }, { -1, -1, -1 }, {  1, -1, -1 },
  { -1, -1, -1 }, { -1,  1, -1 }, { -1, -1, -1 },
};

static Vec3f kFaceU[] = {
  { 2, 0, 0 }, { 0, 2, 0 }, { 0, 2, 0 },
  { 0, 0, 2 }, { 0, 0, 2 }, { 2, 0, 0 },
};

static Vec3f kFaceV[] = {
  { 0, 2, 0 }, { 2, 0, 0 }, { 0, 0, 2 },
  { 0, 2, 0 }, { 2, 0, 0 }, { 0, 0, 2 },
};

BoxMesh* BoxMesh_Create () {
  BoxMesh* self = MemNew(BoxMesh);
  ArrayList_Init(self->elem);
  return self;
}

void BoxMesh_Free (BoxMesh* self) {
  ArrayList_Free(self->elem);
  MemFree(self);
}

void BoxMesh_Add (BoxMesh* self,
  float px, float py, float pz,
  float sx, float sy, float sz,
  float rx, float ry, float rz,
  float bx, float by, float bz)
{
  ArrayList_Grow(self->elem);
  Box* box = self->elem_data + (self->elem_size++);
  box->p = Vec3f_Create(px, py, pz);
  box->s = Vec3f_Create(sx, sy, sz);
  box->r = Vec3f_Create(rx, ry, rz);
  box->b = Vec3f_Create(bx, by, bz);
}

Mesh* BoxMesh_GetMesh (BoxMesh* self, int res) {
  Mesh* mesh = Mesh_Create();
  Mesh_ReserveVertexData(mesh, 6 * res * res * self->elem_size);
  Mesh_ReserveIndexData(mesh, 12 * (res - 1) * (res - 1));

  for (int i = 0; i < self->elem_size; ++i) {
    Box* box = self->elem_data + i;
    Vec3f lower = Vec3f_Create(box->b.x - 1.0f, box->b.y - 1.0f, box->b.z - 1.0f);
    Vec3f upper = Vec3f_Create(1.0f - box->b.x, 1.0f - box->b.y, 1.0f - box->b.z);
    Matrix* rot = Matrix_YawPitchRoll(box->r.x, box->r.y, box->r.z);

    for (int face = 0; face < 6; ++face) {
      Vec3f o = kFaceOrigin[face];
      Vec3f du = kFaceU[face];
      Vec3f dv = kFaceV[face];
      Vec3f n = Vec3f_Normalize(Vec3f_Cross(du, dv));

      for (int iu = 0; iu < res; ++iu) {
        float u = (float)iu / (float)(res - 1);
        for (int iv = 0; iv < res; ++iv) {
          float v = (float)iv / (float)(res - 1);
          Vec3f p = Vec3f_Add(o, Vec3f_Add(Vec3f_Muls(du, u), Vec3f_Muls(dv, v)));
          Vec3f clamped = Vec3f_Clamp(p, lower, upper);
          Vec3f proj = Vec3f_Sub(p, clamped);
          p = Vec3f_Add(clamped, Vec3f_Mul(Vec3f_SNormalize(proj), box->b));
          p = Vec3f_Mul(p, box->s);
          Vec3f rp;
          Matrix_MulPoint(rot, &rp, p.x, p.y, p.z);
          p = Vec3f_Add(rp, box->p);

          if (iu && iv) {
            int off = Mesh_GetVertexCount(mesh);
            Mesh_AddQuad(mesh, off, off - res, off - res - 1, off - 1);
          }
          Mesh_AddVertex(mesh, p.x, p.y, p.z, n.x, n.y, n.z, u, v);
        }
      }
    }

    Matrix_Free(rot);
  }

  return mesh;
}


text:
#include "Keyboard.h"
#include "PhxMemory.h"
#include "SDL.h"

static uint64 lastAction;
static uchar* stateLast;
static uchar* stateCurr;

void Keyboard_Init () {
  int size;
  uchar const* state = SDL_GetKeyboardState(&size);
  stateLast = MemNewArray(uchar, size);
  stateCurr = MemNewArray(uchar, size);
  MemCpy(stateLast, state, size);
  MemCpy(stateCurr, state, size);
  lastAction = SDL_GetPerformanceCounter();
}

void Keyboard_Free () {
  MemFree(stateLast);
  MemFree(stateCurr);
}

void Keyboard_UpdatePre () {
  int size; uchar const* state = SDL_GetKeyboardState(&size);
  MemCpy(stateLast, state, size);
}

void Keyboard_UpdatePost () {
  int size; uchar const* state = SDL_GetKeyboardState(&size);
  MemCpy(stateCurr, state, size);

  for (int i = 0; i < size; ++i) {
    if (stateCurr[i] != stateLast[i]) {
      lastAction = SDL_GetPerformanceCounter();
      break;
    }
  }
}

bool Keyboard_Down (Key key) {
   return stateCurr[key] != 0;
}

bool Keyboard_Pressed (Key key) {
  return stateCurr[key] && !stateLast[key];
}

bool Keyboard_Released (Key key) {
  return !stateCurr[key] && stateLast[key];
}

double Keyboard_GetIdleTime () {
  uint64 now = SDL_GetPerformanceCounter();
  return (double)(now - lastAction) / (double)SDL_GetPerformanceFrequency();
}

bool KeyMod_Alt () {
  return stateCurr[SDL_SCANCODE_LALT] || stateCurr[SDL_SCANCODE_RALT];
}

bool KeyMod_Ctrl () {
  return stateCurr[SDL_SCANCODE_LCTRL] || stateCurr[SDL_SCANCODE_RCTRL];
}

bool KeyMod_Shift () {
  return stateCurr[SDL_SCANCODE_LSHIFT] || stateCurr[SDL_SCANCODE_RSHIFT];
}


text:
#include "OS.h"
#include "SDL.h"

cstr OS_GetClipboard () {
  return SDL_GetClipboardText();
}

int OS_GetCPUCount () {
  return SDL_GetCPUCount();
}

cstr OS_GetVideoDriver () {
  return SDL_GetCurrentVideoDriver();
}

void OS_SetClipboard (cstr text) {
  if (SDL_SetClipboardText(text) != 0)
    Fatal("OS_SetClipboard: %s", SDL_GetError());
}


text:
#include "Mouse.h"
#include "SDL.h"
#include "Vec2.h"

int lastX;
int lastY;
uint32 lastState;

static uint64 lastAction;
static int scrollAmount;

void Mouse_Init () {
  lastState = SDL_GetMouseState(&lastX, &lastY);
  lastAction = SDL_GetPerformanceCounter();
  scrollAmount = 0;
}

void Mouse_Free () {
}

void Mouse_SetScroll (int amount) {
  scrollAmount = amount;
}

void Mouse_Update () {
  int lx = lastX, ly = lastY;
  uint32 state = lastState;
  lastState = SDL_GetMouseState(&lastX, &lastY);
  if (lx != lastX || ly != lastY || state != lastState)
    lastAction = SDL_GetPerformanceCounter();
  scrollAmount = 0;
}

void Mouse_GetDelta (Vec2i* out) {
  SDL_GetMouseState(&out->x, &out->y);
  out->x -= lastX;
  out->y -= lastY;
}

double Mouse_GetIdleTime () {
  uint64 now = SDL_GetPerformanceCounter();
  return (double)(now - lastAction) / (double)SDL_GetPerformanceFrequency();
}

void Mouse_GetPosition (Vec2i* out) {
  SDL_GetMouseState(&out->x, &out->y);
}

void Mouse_GetPositionGlobal (Vec2i* out) {
  SDL_GetGlobalMouseState(&out->x, &out->y);
}

int Mouse_GetScroll () {
  return scrollAmount;
}

void Mouse_SetPosition (int x, int y) {
  SDL_WarpMouseInWindow(0, x, y);
}

void Mouse_SetVisible (bool visible) {
  SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
}

bool Mouse_Down (MouseButton button) {
  button = SDL_BUTTON(button);
  return (SDL_GetMouseState(0, 0) & button) > 0;
}

bool Mouse_Pressed (MouseButton button) {
  button = SDL_BUTTON(button);
  uint32 current = SDL_GetMouseState(0, 0);
  return (current & button) && !(lastState & button);
}

bool Mouse_Released (MouseButton button) {
  button = SDL_BUTTON(button);
  uint32 current = SDL_GetMouseState(0, 0);
  return !(current & button) && (lastState & button);
}


text:
#include "Metric.h"
#include "OpenGL.h"
#include "Profiler.h"
#include "RenderTarget.h"
#include "Tex2D.h"
#include "Tex3D.h"
#include "TexCube.h"
#include "TexFormat.h"
#include "Viewport.h"
#include "Vec2.h"
#include "Vec3.h"

/* TODO : FBO caching mechanism to avoid driver-side validation. */

#define MAX_COLOR_ATTACHMENTS 4
#define MAX_STACK_DEPTH 16

struct FBO {
  uint handle;
  int colorIndex;
  int sx, sy;
  bool depth;
};

static int fboIndex = -1;
static FBO fboStack[MAX_STACK_DEPTH];

inline static FBO* GetActive () {
  return fboStack + fboIndex;
}

inline static void SetDrawBuffers (int count) {
  static const GLenum bufs[MAX_COLOR_ATTACHMENTS] = {
    GL_COLOR_ATTACHMENT0,
    GL_COLOR_ATTACHMENT1,
    GL_COLOR_ATTACHMENT2,
    GL_COLOR_ATTACHMENT3,
  };

  GLCALL(glDrawBuffers(count, bufs))
}

void RenderTarget_Push (int sx, int sy) {
  FRAME_BEGIN;
  if (fboIndex + 1 >= MAX_STACK_DEPTH)
    Fatal("RenderTarget_Push: Maximum stack depth exceeded");

  fboIndex++;
  FBO* self = GetActive();
  self->handle = 0;
  self->colorIndex = 0;
  self->sx = sx;
  self->sy = sy;
  self->depth = false;
  Metric_Inc(Metric_FBOSwap);
  GLCALL(glGenFramebuffers(1, &self->handle))
  GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, self->handle))
  Viewport_Push(0, 0, sx, sy, false);
  FRAME_END;
}

void RenderTarget_Pop () {
  FRAME_BEGIN;
  if (fboIndex < 0)
    Fatal("RenderTarget_Pop: Attempting to pop an empty stack");

  /* Delete current FBO. */
  for (int i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
    GLCALL(glFramebufferTexture2D(
      GL_FRAMEBUFFER,
      GL_COLOR_ATTACHMENT0 + i,
      GL_TEXTURE_2D, 0, 0))

  GLCALL(glFramebufferTexture2D(
    GL_FRAMEBUFFER,
    GL_DEPTH_ATTACHMENT,
    GL_TEXTURE_2D, 0, 0))

  GLCALL(glDeleteFramebuffers(1, &fboStack[fboIndex].handle))
  fboIndex--;

  /* Activate previous FBO. */
  Metric_Inc(Metric_FBOSwap);
  if (fboIndex >= 0)
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, GetActive()->handle))
  else
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0))

  Viewport_Pop();
  FRAME_END;
}

void RenderTarget_BindTex2D (Tex2D* self) {
  RenderTarget_BindTex2DLevel(self, 0);
}

/* TODO : Check whether it is valid to bind a mip level of a depth buffer?
 *        (Do depth formats even have levels??) */
void RenderTarget_BindTex2DLevel (Tex2D* tex, int level) {
  FBO* self = GetActive();
  uint handle = Tex2D_GetHandle(tex);

  /* Color attachment. */
  if (TexFormat_IsColor(Tex2D_GetFormat(tex))) {
    if (self->colorIndex >= MAX_COLOR_ATTACHMENTS)
      Fatal("RenderTarget_BindTex2D: Max color attachments exceeded");
    GLCALL(glFramebufferTexture2D(
      GL_FRAMEBUFFER,
      GL_COLOR_ATTACHMENT0 + self->colorIndex++,
      GL_TEXTURE_2D, handle, level))
    SetDrawBuffers(self->colorIndex);
  }

  /* Depth attachment. */
  else {
    if (self->depth)
      Fatal("RenderTarget_BindTex2D: Target already has a depth buffer");
    GLCALL(glFramebufferTexture2D(
      GL_FRAMEBUFFER,
      GL_DEPTH_ATTACHMENT,
      GL_TEXTURE_2D, handle, level))
    self->depth = true;
  }
}

void RenderTarget_BindTex3D (Tex3D* self, int layer) {
  RenderTarget_BindTex3DLevel(self, layer, 0);
}

void RenderTarget_BindTex3DLevel (Tex3D* tex, int layer, int level) {
  FBO* self = GetActive();
  if (self->colorIndex >= MAX_COLOR_ATTACHMENTS)
    Fatal("RenderTarget_BindTex3D: Max color attachments exceeded");

  uint handle = Tex3D_GetHandle(tex);
  GLCALL(glFramebufferTexture3D(
    GL_FRAMEBUFFER,
    GL_COLOR_ATTACHMENT0 + self->colorIndex++,
    GL_TEXTURE_3D, handle, level, layer))
  SetDrawBuffers(self->colorIndex);
}

void RenderTarget_BindTexCube (TexCube* self, CubeFace face) {
  RenderTarget_BindTexCubeLevel(self, face, 0);
}

void RenderTarget_BindTexCubeLevel (TexCube* tex, CubeFace face, int level) {
  FBO* self = GetActive();
  if (self->colorIndex >= MAX_COLOR_ATTACHMENTS)
    Fatal("RenderTarget_BindTexCubeLevel: Max color attachments exceeded");

  uint handle = TexCube_GetHandle(tex);
  GLCALL(glFramebufferTexture2D(
    GL_FRAMEBUFFER,
    GL_COLOR_ATTACHMENT0 + self->colorIndex++,
    face, handle, level))
  SetDrawBuffers(self->colorIndex);
}

void RenderTarget_PushTex2D (Tex2D* self) {
  RenderTarget_PushTex2DLevel(self, 0);
}

void RenderTarget_PushTex2DLevel (Tex2D* self, int level) {
  Vec2i size; Tex2D_GetSizeLevel(self, &size, level);
  RenderTarget_Push(size.x, size.y);
  RenderTarget_BindTex2DLevel(self, level);
}

void RenderTarget_PushTex3D (Tex3D* self, int layer) {
  RenderTarget_PushTex3DLevel(self, layer, 0);
}

void RenderTarget_PushTex3DLevel (Tex3D* self, int layer, int level) {
  Vec3i size; Tex3D_GetSizeLevel(self, &size, level);
  RenderTarget_Push(size.x, size.y);
  RenderTarget_BindTex3DLevel(self, layer, level);
}


text:
#include "Hash.h"
#include "PhxMemory.h"
#include "StrMap.h"
#include "PhxString.h"

/* TODO : More rigorous tests for this structure, which has been the victim
 *        of some pretty serious implementation bugs in the past. */

struct Node {
  cstr key;
  Node* next;
  void* value;
};

struct StrMap {
  uint32 capacity;
  uint32 size;
  Node* data;
};

struct StrMapIter {
  StrMap* map;
  Node* node;
  uint32 slot;
};

inline static uint64 Hash (cstr key) {
  return Hash_XX64(key, StrLen(key), 0x0ULL);
}

inline static Node* StrMap_GetBucket (StrMap* self, cstr key) {
  return self->data + (Hash(key) % self->capacity);
}

static void StrMap_Grow (StrMap* self) {
  StrMap newMap = { self->capacity * 2, 0, 0 };
  newMap.data = MemNewArrayZero(Node, newMap.capacity);

  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key)
      continue;
    StrMap_Set(&newMap, node->key, node->value);
    StrFree(node->key);
    node = node->next;
    while (node) {
      Node* next = node->next;
      StrMap_Set(&newMap, node->key, node->value);
      StrFree(node->key);
      MemFree(node);
      node = next;
    }
  }

  MemFree(self->data);
  *self = newMap;
}

StrMap* StrMap_Create (uint32 capacity) {
  StrMap* self = MemNewZero(StrMap);
  self->capacity = capacity;
  self->data = MemNewArrayZero(Node, capacity);
  return self;
}

void StrMap_Free (StrMap* self) {
  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key) continue;
    StrFree(node->key);
    node = node->next;
    while (node) {
      Node* next = node->next;
      StrFree(node->key);
      MemFree(node);
      node = next;
    }
  }
  MemFree(self->data);
  MemFree(self);
}

void StrMap_FreeEx (StrMap* self, void (*freeFn)(cstr, void*)) {
  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key) continue;
    freeFn(node->key, node->value);
    StrFree(node->key);
    node = node->next;
    while (node) {
      Node* next = node->next;
      freeFn(node->key, node->value);
      StrFree(node->key);
      MemFree(node);
      node = next;
    }
  }
  MemFree(self->data);
  MemFree(self);
}

void* StrMap_Get (StrMap* self, cstr key) {
  Node* node = StrMap_GetBucket(self, key);
  if (!node->key)
    return 0;
  while (node) {
    if (StrEqual(node->key, key))
      return node->value;
    node = node->next;
  }
  return 0;
}

uint32 StrMap_GetSize (StrMap* self) {
  return self->size;
}

void StrMap_Remove (StrMap* self, cstr key) {
  Node** prev = 0;
  Node* node = StrMap_GetBucket(self, key);

  while (node && node->key) {
    if (StrEqual(node->key, key)) {
      StrFree(node->key);
      Node* next = node->next;
      if (next) {
        node->key = next->key;
        node->next = next->next;
        node->value = next->value;
        MemFree(next);
      } else {
        node->key = 0;
        node->value = 0;
      }

      if (prev)
        *prev = next;
      return;
    }

    prev = &node->next;
    node = node->next;
  }

  Fatal("StrMap_Remove: Map does not contain key <%s>", key);
}

void StrMap_Set (StrMap* self, cstr key, void* value) {
  self->size++;
  if (3 * self->capacity < 4 * self->size)
    StrMap_Grow(self);

  Node* node = StrMap_GetBucket(self, key);
  if (!node->key) {
    node->key = StrDup(key);
    node->value = value;
    return;
  }

  Node** prev = 0;
  while (node) {
    if (StrEqual(node->key, key)) {
      node->value = value;
      return;
    }
    prev = &node->next;
    node =  node->next;
  }

  node = MemNew(Node);
  node->key = StrDup(key);
  node->value = value;
  node->next = 0;
  *prev = node;
}

#include <stdio.h>

void StrMap_Dump (StrMap* self) {
  printf("StrMap @ %p:\n", self);
  printf("      size: %d\n", self->size);
  printf("  capacity: %d\n", self->capacity);
  printf("      load: %f\n", (float)self->size / (float)self->capacity);
  puts("");

  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key) continue;
    printf("  [%03i]:\n", i);
    while (node) {
      printf("    (%lx) %s -> %p\n", Hash(node->key), node->key, node->value);
      node = node->next;
    }
  }
}

StrMapIter* StrMap_Iterate (StrMap* self) {
  StrMapIter* it = MemNew(StrMapIter);
  it->map = self;
  it->slot = 0;
  it->node = 0;

  for (uint32 i = 0; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key) continue;
    it->slot = i;
    it->node = node;
    break;
  }

  return it;
}

void StrMapIter_Free (StrMapIter* self) {
  MemFree(self);
}

void StrMapIter_Advance (StrMapIter* it) {
  StrMap* self = it->map;
  it->node = it->node->next;
  if (it->node) return;
  it->slot++;
  for (uint32 i = it->slot; i < self->capacity; ++i) {
    Node* node = self->data + i;
    if (!node->key) continue;
    it->slot = i;
    it->node = node;
    break;
  }
}

bool StrMapIter_HasMore (StrMapIter* it) {
  return it->node != 0;
}

cstr StrMapIter_GetKey (StrMapIter* it) {
  return it->node->key;
}

void* StrMapIter_GetValue (StrMapIter* it) {
  return it->node->value;
}


text:
#include "BoxTree.h"
#include "Draw.h"
#include "Matrix.h"
#include "PhxMemory.h"
#include "Mesh.h"
#include "RNG.h"
#include "Vertex.h"

struct Node {
  Box3f box;
  void* data;
  Node* sub[2];
};

struct BoxTree {
  Node* root;
};

inline static Node* Node_Create (Box3f box, void* data) {
  Node* self = MemNew(Node);
  self->box = box;
  self->sub[0] = 0;
  self->sub[1] = 0;
  self->data = data;
  return self;
}

BoxTree* BoxTree_Create () {
  BoxTree* self = MemNew(BoxTree);
  self->root = 0;
  return self;
}

static void Node_Free (Node* self) {
  if (self->sub[0]) Node_Free(self->sub[0]);
  if (self->sub[1]) Node_Free(self->sub[1]);
  MemFree(self);
}

void BoxTree_Free (BoxTree* self) {
  if (self->root) Node_Free(self->root);
  MemFree(self);
}

BoxTree* BoxTree_FromMesh (Mesh* mesh) {
  BoxTree* self = BoxTree_Create();
  int indexCount = Mesh_GetIndexCount(mesh);
  int const* indexData = Mesh_GetIndexData(mesh);
  Vertex const* vertexData = Mesh_GetVertexData(mesh);

  for (int i = 0; i < indexCount; i += 3) {
    Vertex const* v0 = vertexData + indexData[i + 0];
    Vertex const* v1 = vertexData + indexData[i + 1];
    Vertex const* v2 = vertexData + indexData[i + 2];
    Box3f box = Box3f_Create(
      Vec3f_Min(v0->p, Vec3f_Min(v1->p, v2->p)),
      Vec3f_Max(v0->p, Vec3f_Max(v1->p, v2->p)));
    BoxTree_Add(self, box, 0);
  }
  return self;
}

inline static float Cost (Box3f box) {
  return Box3f_Volume(box);
}

inline static float CostMerge (Box3f a, Box3f b) {
  return Cost(Box3f_Union(a, b));
}

static void Node_Merge (Node* self, Node* src, Node** prev) {
  if (!self) {
    *prev = src;
    return;
  }

  /* Leaf node. */
  if (!self->sub[0]) {
    Node* parent = Node_Create(Box3f_Union(self->box, src->box), 0);
    *prev = parent;
    parent->sub[0] = self;
    parent->sub[1] = src;
    self = parent;
    return;
  }

  /* Contained by current sub-tree. */
  if (Box3f_ContainsBox(self->box, src->box)) {
    float cost0 = CostMerge(self->sub[0]->box, src->box) + Cost(self->sub[1]->box);
    float cost1 = CostMerge(self->sub[1]->box, src->box) + Cost(self->sub[0]->box);
    if (cost0 < cost1)
      Node_Merge(self->sub[0], src, &self->sub[0]);
    else
      Node_Merge(self->sub[1], src, &self->sub[1]);
  }

  /* Not contained, need new parent. */
  else {
    Node* parent = Node_Create(Box3f_Union(self->box, src->box), 0);
    *prev = parent;

    float costBase = Cost(self->box) + Cost(src->box);
    float cost0 = CostMerge(self->sub[0]->box, src->box) + Cost(self->sub[1]->box);
    float cost1 = CostMerge(self->sub[1]->box, src->box) + Cost(self->sub[0]->box);

    if (costBase <= cost0 && costBase <= cost1) {
      parent->sub[0] = self;
      parent->sub[1] = src;
    }

    else if (cost0 <= costBase && cost0 <= cost1) {
      parent->sub[0] = self->sub[0];
      parent->sub[1] = self->sub[1];
      MemFree(self);
      Node_Merge(parent->sub[0], src, &parent->sub[0]);
    }

    else {
      parent->sub[0] = self->sub[0];
      parent->sub[1] = self->sub[1];
      MemFree(self);
      Node_Merge(parent->sub[1], src, &parent->sub[1]);
    }
  }
}

void BoxTree_Add (BoxTree* self, Box3f box, void* data) {
  Node_Merge(self->root, Node_Create(box, data), &self->root);
}

static int Node_GetMemory (Node* self) {
  int memory = sizeof(Node);
  if (self->sub[0]) memory += Node_GetMemory(self->sub[0]);
  if (self->sub[1]) memory += Node_GetMemory(self->sub[1]);
  return memory;
}

int BoxTree_GetMemory (BoxTree* self) {
  int memory = sizeof(BoxTree);
  if (self->root) memory += Node_GetMemory(self->root);
  return memory;
}

static bool Node_IntersectRay (Node* self, Vec3f o, Vec3f di) {
  if (!Box3f_IntersectsRay(self->box, o, di))
    return false;

  if (self->sub[0]) {
    if (Node_IntersectRay(self->sub[0], o, di)) return true;
    if (Node_IntersectRay(self->sub[1], o, di)) return true;
    return false;
  } else {
    return true;
  }
}

bool BoxTree_IntersectRay (
  BoxTree* self,
  Matrix* matrix,
  Vec3f const* ro,
  Vec3f const* rd)
{
  if (!self->root) return false;
  Matrix* inv = Matrix_Inverse(matrix);
  Vec3f invRo; Matrix_MulPoint(inv, &invRo, ro->x, ro->y, ro->z);
  Vec3f invRd; Matrix_MulDir(inv, &invRd, rd->x, rd->y, rd->z);
  Matrix_Free(inv);
  return Node_IntersectRay(self->root, invRo, Vec3f_Rcp(invRd));
}

static void BoxTree_DrawNode (Node* self, int maxDepth) {
  if (maxDepth < 0) return;
  if (self->sub[0] || self->sub[1]) {
    Draw_Color(1, 1, 1, 1);
    Draw_Box3(&self->box);
  } else {
    Draw_Color(0, 1, 0, 1);
    Draw_Box3(&self->box);
  }
  if (self->sub[0]) BoxTree_DrawNode(self->sub[0], maxDepth - 1);
  if (self->sub[1]) BoxTree_DrawNode(self->sub[1], maxDepth - 1);
}

void BoxTree_Draw (BoxTree* self, int maxDepth) {
  if (self->root)
    BoxTree_DrawNode(self->root, maxDepth);
}